Forum: Mikrocontroller und Digitale Elektronik 4Draht-Touchpanel direkt am AVR und die Portschaltung


von Chris (Gast)


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 ?

von Anderer Chris (Gast)


Lesenswert?


von Chris (Gast)


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.

von Potter (Gast)


Lesenswert?

Hallo Chris (Gast),

zur Beschreibung gibt es auch noch einen Quellcode - musst mal ein 
bischen suchen.

Gruß Potter

von J. M. (Firma: OCL) (lorcan)


Angehängte Dateien:

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.

von Rene Z. (rzimmermann)


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

von Konstantin M. (kmi)


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.

von Konstantin M. (kmi)


Lesenswert?

nach 30min stelle ich fest, dass die Koordinaten richtig erkannt werden, 
nur im Ruhemodus, wo eigentlich nichts erkannt werden soll, wird wild 
weiter gemessen....


Hat jemand eine Idee??

von Rene Z. (rzimmermann)


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

von (prx) A. K. (prx)


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.

von (prx) A. K. (prx)


Angehängte Dateien:

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)

von Konstantin M. (kmi)


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!!!!

von Herbert K. (avr-herbi)


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);

von Thomas F. (igel)


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

von Herbert K. (avr-herbi)


Lesenswert?

Hallo,
wie soll ich denn das Drücken erkennen, ohne das der ADC mißt?
Ich habe keinen ATmega88, der Level Interrupts kann, sondern einem 
ATmega32.
Ich überlege ja schon die ganze Zeit. Allerdings fällt mir nichts ein.
Viele Grüße Herbert

von Sascha W. (sascha-w)


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

von Thomas F. (igel)


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

von Herbert K. (avr-herbi)


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!

von Thomas F. (igel)


Lesenswert?

> PinChangeInt kann aber der ATmega32 nicht an jedem Port Pin oder?

Deshalb schrieb Sascha: "...oder auch per Polling"

von Herbert K. (avr-herbi)


Lesenswert?

Ach so .. Bitte um Entschuldigung, falls es im C Code oben schon so 
steht. Ich kann kein C und habe es dann evt. übersehen.

von Thomas F. (igel)


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

von ferdi (Gast)


Lesenswert?

Ob nun C oder Pascal oder auch Basic: schaut euch diese Erkläurungen zur 
Touchfolie an.
Die Schalttransistoren muß man nicht benutzen, das kann man auch über 
die Pin's machen.
Guckst du: 
http://www.mikroe.com/eng/products/view/272/touchscreen-article/

von Herbert K. (avr-herbi)


Lesenswert?

@ferdi

Danke für den Hinweis. Den Artikel kannte ich bereits. Nur bei meinem 
Problem hilft der nicht. Der berücksichtigt in keiner Weise ein "Touch" 
oder auch nur ein Calibrieren. Da bin ich schon wesentlich weiter.
Trotzdem auch an Dich ein Danke!

von (prx) A. K. (prx)


Lesenswert?

Für Wakeup ohne PCINT muss man eben einen der 4 Anschlüsse zusätzlich an 
einen Interrupt-Eingang anschliessen und verliert einen Pin. Das war's 
aber auch schon.

Code mit Autokalibrierung hatte ich oben bereits gepostet.

von Herbert K. (avr-herbi)


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

von Propp (Gast)


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?

von (prx) A. K. (prx)


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.

von Neuling (Gast)


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;

von (prx) A. K. (prx)


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.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.