4Draht-Touchpanel direkt am AVR und die Portschaltung

Gast #1525387
Lesenswert?

Hi,

der Titel sagt es eigentlich schon, ich wollte mal schauen, wie sich die 
Auswertung eines 4-Wire-Touchpanels ohne irgendwelche Hilsmittel direkt 
am AVR macht. Also:

PA0/ADC0 -> x1
PA1/ADC1 -> x2
PA2/ADC2 -> y1
PA3/ADC3 -> y2

Mir ist nicht 100% klar, wie man zur Abfrage nun die Ports schaltet, da 
es an irgend einem Punkt zu kurzschlüssen kommen kann. Finde aber den 
Beitrag dazu nicht mehr. Der Ansatz


PA2/ADC2 sowie PA3/ADC3 als Eingang ohne Pullup, einen zum Messen des 
Wertes verwenden
PA0/ADC0 als Ausgang logisch 1
PA1/ADC1 als Ausgang logisch 0

Achsentausch:

PA0/ADC0 sowie PA1/ADC2 als Eingang ohne Pullup, einen zum Messen des 
Wertes verwenden
PA2/ADC3 als Ausgang logisch 1
PA3/ADC3 als Ausgang logisch 0

natürlich muss zusätzlich noch bei Pinfunktionswechsel darauf geachtet 
werden, dass man nicht plötzlich durch den vorher gesetzten Ausgang auf 
logisch 1 man plötzlich beim Input den Pullup aktiviert hat.

Funktioniert das so ?

Wo ist der kritische Punkt an dem ein Kurzschluss entstehen kann ? War 
damit eventuell gemeint, dass man unachtsamerweise an einer Stelle z.B. 
versehentlich x1 auf logisch 1 und zugleich y2 auf logisch 0 ziehen 
könnte, deren Kontaktstellen an einem Punkt nahezu aneinander grenzen ?
Gast #1525895
Lesenswert?

Mh, die Beschreibung sagt mir leider nichts dazu, wie die Ports zu 
schalten sind, zudem bringt sie einen neuen Aspekt in´s Spiel: nämlich 
die Erkennung eines Druck bevor überhaupt eine Position eingelesen wird. 
Kleine Vorteile mag das ja haben, der große Sinn bleibt mir allerdings 
verborgen.

Hatte ja selber schon mal ein 4-Wire-Touchpanel ausgewertet, da 
allerdings mit Transistoren geabeitet, die x2 oder y2 gegen Masse 
schalten. Also eine vorherige Toucherkennung war dabei nicht nötig 
sondern er wurden einfach kontinuierliche AD-Wandlungen durchgeführt und 
mit den Daten weitergeabeitet.

Weicht aber vom Thema ab, primär geht es immer noch darum, wie die Ports 
zu schalten sind und auf was man unbedingt achten sollte.
(Firma: OCL) #1528950
Lesenswert?

Guck mal Seite 4 in dem Dokument von Atmel zu Touchscreens.
Die Tabelle die ich als Bild angehängt habe steht da auch.
Das sollte deine Frage klären.

Der Interrupt wird benutzt um nicht ständig den ADC laufen zulassen, 
bzw. den Controller schlafen zu legen solange das Display nicht benutzt 
wird.
Angehängte Dateien:
#1529234
Lesenswert?

Hallo,

nachfolgendes ist noch nicht optimal, funzt aber prima bei mir.
1
#define ungueltig   0
2
#define gueltig    1
3

4
uint8_t lese_touch(uint8_t *x, uint8_t *y){
5
  uint8_t daten = gueltig;
6
  DDRA |= ((1<<PA0)|(1<<PA1));                // Top (PA0) und Button (PA1) Ausgang 
7
  PORTA |= (1<<PA0);                      // Top (PA0) +3,3V
8
  PORTA &= ~(1<<PA1);                    // Button (PA1) 0V
9
  _delay_ms(10);                        // warten
10
  ADMUX = ((1<<REFS0) + 2);                  // ADC Kanal 2 (PA2 Left) auswaehlen
11
  ADCSRA |= (1<<ADSC);                            // eine ADC-Wandlung 
12
    while (ADCSRA & (1<<ADSC)){;}                // auf Abschluss der Konvertierung warten
13
  uint16_t Uy = ADCW;                      // Ergebniss nach Uy
14
  PORTA &= ~(1<<PA0);                    // Top (PA0) 0V
15
  DDRA &= ~((1<<PA0)|(1<<PA1));                // Top (PA0) und Button (PA1) Eingang
16
    
17
  if(Uy > 900){                        // Touch meldet keine benutzung
18
    daten = ungueltig;                    // merken für Rueckgabewert
19
  }
20
  Uy = (Uy - 170) * 100;                    // ADC Werte in Display-
21
  Uy = (Uy / 660) * 64 / 100;                // koordinaten umrechen
22
  *y = 64 - Uy;
23

24
  DDRA |= ((1<<PA2)|(1<<PA3));                // Left (PA2) und Right (PA3) Ausgang
25
  PORTA |= (1<<PA2);                      // Left (PA2) +3,3V
26
  PORTA &= ~(1<<PA3);                    // Right (PA3) 0V
27
  _delay_ms(10);                        // warten
28
  ADMUX = ((1<<REFS0));                    // ADC Kanal 0 (PA0 Top) auswaehlen  
29
  ADCSRA |= (1<<ADSC);                            // eine ADC-Wandlung 
30
    while (ADCSRA & (1<<ADSC)){;}                // auf Abschluss der Konvertierung warten
31
  uint16_t Ux = ADCW;                      // Ergebniss nach Ux
32
  PORTA &= ~(1<<PA2);                    // Left (PA2) 0V
33
  DDRA &= ~((1<<PA2)|(1<<PA3));                // Left (PA2) und Right (PA3) Eingang
34
        
35
  if(Ux > 900){                        // Touch meldet keine benutzung
36
    daten = ungueltig;                    // merken für Rueckgabewert
37
  }
38

39
  Ux = (Ux - 170) * 100;                    // ADC Werte in Display-
40
  Ux = (Ux / 660) * 128 / 100;                // koordinaten umrechen
41
  *x = 128 - Ux;
42
  return daten;                        // Rueckgabe ob Daten ausgewertet werden koennen
43
}
44

45
void touch_init(void){
46
  ADMUX = ((1<<REFS0));                      // AVCC with external capacitor, Channel ADC0
47
  ADCSRA = ((1<<ADEN)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0));    // ADC ON, Prescaler :128
48
  ADCSRB &= ~((1<<ADTS2)|(1<<ADTS1)|(1<<ADTS0));        // Free Running mode
49
  ADCSRA |= (1<<ADSC);                              // eine 'dummy' ADC-Wandlung 
50
    while (ADCSRA & (1<<ADSC)){;}                  // auf Abschluss der Konvertierung warten 
51
  ADCW;                              // Daten holen
52
  DDRA &= ~((1<<PA0)|(1<<PA1)|(1<<PA2)|(1<<PA3));        // Touchanschluesse alle Eingang          
53
}

die Abfrage dann etwa so:
1
uint8_t Uy, Ux;
2
if(lese_touch(&Ux, &Uy) == gueltig){
3
  //Auswertung
4
}

Gruß Rene

P.S. Ist für das Touch von einem EADOGL128
#1739099
Lesenswert?

Hallo, ich habe eben den Code von Rene ausprobiert, bekomme leider nur 
Zufallszahlen, zwar im definierten Bereich (128*64, Größe des 
Touchscreens) und es scheint auch so, dass es auf Touchs reagiert, aber 
die Zahlen sind total wild, wenn man nichts anfasst kommt z.B. für x 121 
und gleich dahinter 22 :(

Woran kann das liegen? den Code habe ich nur minimal an meinen ATMega32 
angepasst. Gebe die Werte per USART an meinen Notebooke.
#1739153
Lesenswert?

Hallo,

deswegen gibt die Funktion ja auch ein gueltig oder ungueltig zurück.

Hast du folgende Zeilen noch im Code ?
1
uint8_t Uy, Ux;
2
if(lese_touch(&Ux, &Uy) == gueltig){
3
  //Auswertung
4
}

Wenn ja, wirst du wohl folgende Zeilen anpassen müssen.
1
  if(Uy > 900){                        // Touch meldet keine benutzung
2
    daten = ungueltig;                    // merken für Rueckgabewert
3
  }
1
  if(Ux > 900){                        // Touch meldet keine benutzung
2
    daten = ungueltig;                    // merken für Rueckgabewert
3
  }

Gruß Rene
#1739168
Lesenswert?

Ausserdem funzt das besser, wenn man durch Selbstkalibrierung den nicht 
immer vernachlässigbaren stark temperatur- und 
betriebsspannungsabhängigen Innenwiderstand der Porttreiber rausrechnet. 
Immerhin fliesst bei solchen Touchpanels mitunter deutlich Strom.

Das geht recht einfach, denn netterweise kann man bei AVRs die Spannung 
eines als Ausgang konfigurierten Pins messen. Ergibt zwar 6 Messungen 
statt 2, aber klappt recht gut.
#1739175
Lesenswert?

Bei mir sah das im Test mal so aus. Hier Auszug aus config.h, Rest 
Anhang.
1
#define GLCD_X    128
2
#define GLCD_Y    64
3

4
// 4-wire touchscreen
5
#define TOUCH_PORT  A
6
// #define TOUCH_PINCHG  0
7
#define TOUCH_N_CHANNEL  7
8
#define TOUCH_N_BIT  (1<<PA7)
9
#define TOUCH_E_CHANNEL  6
10
#define TOUCH_E_BIT  (1<<PA6)
11
#define TOUCH_S_CHANNEL  5
12
#define TOUCH_S_BIT  (1<<PA5)
13
#define TOUCH_W_CHANNEL  4
14
#define TOUCH_W_BIT  (1<<PA4)
15
// effective area in display size units
16
// undefine and measure edges to obtain actual values
17
#if 1
18
#define TOUCH_X_MIN  18
19
#define TOUCH_X_MAX  122
20
#define TOUCH_Y_MIN  9
21
#define TOUCH_Y_MAX  56
22
#endif
23

24
// A/D
25
#define ADC_PRESCALER  6    // 8.64MHz/64 = 135KHz
26

27
#define GLUE(a, b)  a##b
28
#define PORT(x)    GLUE(PORT, x)
29
#define PIN(x)    GLUE(PIN, x)
30
#define DDR(x)    GLUE(DDR, x)
31

32
#define setBits(port,mask)  do{ (port) |=  (mask); }while(0)
33
#define clrBits(port,mask)  do{ (port) &= ~(mask); }while(0)
34

35
#define PCIE(n)    GLUE(PCIE, n)
36
#define PCMSK(n)  GLUE(PCMSK, n)
Angehängte Dateien:
#1739711
Lesenswert?

Innenwiderstand der Porttreiber war die Ursache, vielen Dank an A. K.!

Ich habe den Code von Rene einfach um eine Zeile erweitert und schon 
läuft die Messung, wie geschmiert.

Durch den Pullup Widerstand im Ruhezustand wird nun immer ~1024 gemessen 
und somit ergibt sich ungültige Messung.
1
uint8_t lese_touch(uint8_t *x, uint8_t *y){
2
  uint8_t daten = gueltig;
3
  DDRA |= ((1<<PA0)|(1<<PA1));    // Top (PA0) und Button (PA1) Ausgang 
4
  PORTA |= (1<<PA0);              // Top (PA0) +3,3V
5
  PORTA &= ~(1<<PA1);             // Button (PA1) 0V
6
  
7
  PORTA = (1<<PA2)|(1<<PA3);      // für Left und Right Pullups einschalten
8

9
  _delay_ms(10);                  // warten
10
  ADMUX = ((1<<REFS0) + 2);       // ADC Kanal 2 (PA2 Left) auswaehlen
11
  ADCSRA |= (1<<ADSC);            // eine ADC-Wandlung 
12
    while (ADCSRA & (1<<ADSC)){;} // auf Abschluss der Konvertierung warten
13
  uint16_t Uy = ADCW;             // Ergebniss nach Uy
14
  PORTA &= ~(1<<PA0);             // Top (PA0) 0V
15
  DDRA &= ~((1<<PA0)|(1<<PA1));   // Top (PA0) und Button (PA1) Eingang
16
...


Danke an alle, die geholfen haben!!!!
#1767512
Lesenswert?

Hallo,
ich habe ein ähnliches Problem.
Bleiben die Touchpanel Pärchen Anschlüsse des jeweiligen ADC High-Z, 
dann springen die Werte des ADC wie wild, sofern man das Display nicht 
berührt.

So sollte es ja laut den Specs aber ja gemacht werden (Atmel oder auch 
die TouchSreen Controller wie XPT2046 oder ADS7843).

Ein wenig Abhilfe habe ich auch bekommen, wenn wie beim Posting hier 
vor, bei den ADC Pärchen bei der Messung Anstelle High-Z beide Pullup´s 
einschalte.

Manchmal bekomme ich 03FF zurück (wäre ja ok). Aber manchmal gehen die 
Werte auch bis 03B0 herunter. (ATmega32 ADC=0-1023 = 03FF Hex)

Den ADC 2x hintereinanderlesen bringt auch nichts.

Bei gewolltem "Touch" an irgendeiner Stelle, sind die ADC Werte auf den 
ersten Blick ok.

Was kann ich noch tun?

Danke schon mal für hoffentlich konstruktive Vorschläge - auch wenn es 
warm draußen ist. Herbert


(das ist Pascal, Anschlüsse wie 1. Posting)

  Touch_X_Plus_Direction  := Direction_Eingang;
  Touch_X_Minus_Direction := Direction_Eingang;
  Touch_Y_Plus_Direction  := Direction_Ausgang;
  Touch_Y_Minus_Direction := Direction_Ausgang;

//  Touch_X_Plus_Output  := wenn_Input_High_Z;
  Touch_X_Plus_Output  := wenn_Input_Pull_Up_Ein; // TEST
//  Touch_X_Minus_Output := wenn_Input_High_Z;
  Touch_X_Minus_Output := wenn_Input_Pull_Up_Ein; // TEST
  Touch_Y_Plus_Output  := 0;
  Touch_Y_Minus_Output := 1;

//  Touchpanel_Delay_20ms;
//  Touch_ADC_Y := ADC_Read(Touch_X_Minus_ADC);
  Touchpanel_Delay_20ms;
  Touch_ADC_Y := ADC_Read(Touch_X_Minus_ADC);
#1767836
Lesenswert?

> ich habe ein ähnliches Problem.
> Bleiben die Touchpanel Pärchen Anschlüsse des jeweiligen ADC High-Z,
> dann springen die Werte des ADC wie wild, sofern man das Display nicht
> berührt.

Das ist normal, da die Eingänge undefiniert sind.
Wenn nicht gedrückt wird, sollte der ADC gar nicht in Betrieb sein. Erst 
nachdem ein Drücken erkannt wurde, wird der ADC in der Software 
aktiviert und dann in 2 Schritten die X und Y-Position per ADC gemessen.

Beitrag "Re: 4Draht-Touchpanel direkt am AVR und die Portschaltung"

Thomas
#1767890
Lesenswert?

du legst die X-Achse an Masse (einen oder auch beide Anschllüsse), an 
der Y-Achse lässt du einen auf Eingang ohne Pullup und den anderen auf 
Eingang mit PullUp. Diesen letzten überwachst du per PinChangeInt oder 
auch per polling - im Ruhezustand liegt der auf High (pullup) Touch'st 
du geht er auf Low.

Sascha
#1767899
Lesenswert?

> Ich habe keinen ATmega88, der Level Interrupts kann, sondern einem
> ATmega32.

Macht nix. Schalte die vier Leitungen wie in der Tabelle.

Ich mache es so:

Beide X-Leitungen als Ausgang und Low.
Beide Y-Leitungen als Eingang.
Bei einer Y-Leitung den internen Pull-up einschalten.

In einem Timer-Interrupt wird die Y-Leitung mit Pull-up abgefragt. Im 
Standby ist diese High wegen dem Pull-up.
Wenn das Touch nun gedrückt wird, ziehen die beiden X-Leitungen diese 
auf Low. Das ist das Startkriterium für die folgenden Positionsabfragen 
nach Zeile zwei und drei in der Tabelle.


Thomas
#1767907
Lesenswert?

PinChangeInt kann aber der ATmega32 nicht an jedem Port Pin oder?
Ich müßte dafür einen der beiden X/Y Pins, den ich nicht für den ADC 
benötige, auf External Int 0,1,2 umlegen, falls ich das per Interrupt 
machen will oder?
Herbert

Dieser Beitrag hatte sich mit dem von Thomas beim Tippen überschnitten. 
Werde mir das mal durch den Kopf gehen lassen.

Danke!
#1768047
Lesenswert?

> Bitte um Entschuldigung, falls es im C Code oben schon so
> steht.

Keine Ahnung, hab ihn nicht gelesen, da ich auch kein c kann. ;-)
Naja, lesen schon, aber nicht schreiben. Daher habe ich meine 
Touch-Abfrage nach dem Atmel-Datenblatt dann in ASM selbst geschrieben.

Thomas
#1768306
Lesenswert?

@Thomas Forster (igel)

Hallo Thomas,

"Beide X-Leitungen als Ausgang und Low.
Beide Y-Leitungen als Eingang.
Bei einer Y-Leitung den internen Pull-up einschalten. ..."

Ich habe das eben noch in der Art eingebaut (als Poll). Funktioniert 
prima! Danke! Manchmal sieht man den Wald vor lauter Bäumen nicht.

"In ASM programmiert..." -> Hut ab! Das mache ich nur im Notfall.

Viele Grüße an alle und ein schönes Wochenende wünscht Euch Herbert
Gast #2048459
Lesenswert?

Hallo, was mich noch interessieren würde, ist der folgende Teil des 
Codes:
1
Ux = (Ux - 170) * 100;                    // ADC Werte in Display-
2
  Ux = (Ux / 660) * 128 / 100;                // koordinaten umrechen
3
  *x = 128 - Ux;
4
...
5
Uy = (Uy - 170) * 100;                    // ADC Werte in Display-
6
  Uy = (Uy / 660) * 64 / 100;                // koordinaten umrechen
7
  *y = 128 - Uy;

Ich dachte immer es geht so:
1
Ux = Ux * 128;
2
*x = Ux / 1024;
3
...
4

5
Uy = Uy * 128;
6
*y = Uy / 1024;

Kann mir einer bite die Rechnung erklären?
#2050284
Lesenswert?

Einerseits steckt da die Display-Grösse drin, d.h. wenn das Display 
128x128 statt 128x64 hat, dann ändert sich die Rechung. Diese Änderung 
hast du nur teilweise nachvollzogen, eine nun falsche 64 steckt bei dir 
noch drin.

Andererseits hat er darin möglicherweise den oben erwähnten 
Spannungsabfall durch die Porttreiber (oder externe Widerstände?) sehr 
grob über den Daumen gepeilt und direkt ins Programm eingetragen.
Gast #2050551
Lesenswert?

Ja klar, sorry. Das sollte
1
Ux = Ux * 128;
2
*x = Ux / 1024;
3
...
4

5
Uy = Uy * 64;
6
*y = Uy / 1024;
heissen.

Rene Zimmermann schrieb:
1
> if(Uy > 900){                        // Touch meldet keine benutzung
2
>     daten = ungueltig;                    // merken für Rueckgabewert
3
>   }
4

5
> if(Ux > 900){                        // Touch meldet keine benutzung
6
>     daten = ungueltig;                    // merken für Rueckgabewert
7
>   }

A. K. schrieb:
> Andererseits hat er darin möglicherweise den oben erwähnten
> Spannungsabfall durch die Porttreiber (oder externe Widerstände?) sehr
> grob über den Daumen gepeilt und direkt ins Programm eingetragen.

Also dem nach liegt die max. Spannung nur noch bei 4.4V (ADC: 900) ?

könnte es dann auch so berechnet werden?
1
Ux = Ux * 128;
2
*x = Ux / 900;
3
...
4

5
Uy = Uy * 64;
6
*y = Uy / 900;
#2050579
Lesenswert?

Neuling schrieb:

> Also dem nach liegt die max. Spannung nur noch bei 4.4V (ADC: 900) ?

Woher soll ich das wissen? Das hängt vom Touchpad, der Temperatur des 
AVR und von der Zeit ab, denn der Port erwärmt sich mit der Zeit durch 
den ggf. recht hohen Strom und verändert dadurch seinen Innenwiderstand. 
Daher habe ich das selbstkalibierend implementiert, siehe oben.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren