Forum: Mikrocontroller und Digitale Elektronik Charge Transfer Prinzip (Qtouch) auf STM32 möglich?


von Bernhard B. (schluchti)


Lesenswert?

Hallo,

mich würde interessieren, ob das Charge-Transfer-Prinzip (vgl. Qtouch) 
auch auf einem STM32 anwendbar ist. Soweit ich das den Qtouch 
Spezifikationen und diesem 
Beitrag "qtouch - sekt oder selters" Thread entnehmen konnte, 
wird hierfür nur ein Mikrocontroller benötigt der abschaltbare Pullups 
besitzt. Also sollte das Prinzip auch auf andere Mikrocontroller (in 
meinem Fall einen STM32) portierbar sein, oder irre ich da?

Ich hab bereits gestern ein paar Tests gemacht, jedoch waren diese nicht 
von Erfolg gekrönt. Bevor ich jetzt Zeit in die Fehlersuche investiere 
(bin mir im Moment überhaupt nicht sicher, ob der Fehler software - oder 
hardwareseitig begraben liegt), wollte ich mal kurz rückfragen ob das 
Charge-Transfer-Prinzip prinzipiell mit dem STM32 zu realisieren ist?

von Yalu X. (yalu) (Moderator)


Lesenswert?

QTouch sollte mit allen Mikrocontrollern funktionieren, die Tristate-
Ausgänge haben. Das dürften fast alle bis auf einige 8051-Derivate sein.

von Johnny B. (johnnyb)


Lesenswert?

Genau, sollte eigentlich mit jedem vernünftigen Mikrocontroller gehen 
auf dem man Ports per Firmware, während der Laufzeit auf Aus- oder 
Eingang konfigurieren kann.
Funktionieren tuts dann natürlich noch besser, wenn die Ports möglichst 
geringe Leckströme aufweisen, aber das ist dann eher ein Detail. 
Einigermassen funktionieren sollte es auf jeden Fall.

Als ich den Code für den MSP430 angepasst hatte, musste ich ein paar 
kleine Anpassungen beim Timing vornehmen. Eventuell musst Du das auch 
tun, denn je nach Taktfrequenz Deines Mikrocontrollers verändert sich so 
einiges... Eventuell musst Du bei der Schleife einen grösseren 
Datentypen nehmen, falls die Zählvariable grösser als 255 werden kann 
und mit dem Abbruchkriterium ein wenig experimentieren bzw. mit dem 
Debugger schauen wo die Werte in etwa liegen.

von Bernhard B. (schluchti)


Lesenswert?

Hi,

na das hört sich ja mal nicht so schlecht an, dann sollte das ja 
theoretisch auch mit dem STM32 funktionieren.
Ich hab mich da heute nochmals hingesetzt, doch irgendwie will das nicht 
so wirklich funktionieren.
Mein Hardwareaufbau sieht so aus:

PC9-----------+---- 1K ---- Touchpad
              |
             ---
             --- 47n
              |
PC13----------+

Mein Sourcecode sieht so aus:
1
int main(void){
2
   uint32_t count = 0; 
3
   RCC->APB2ENR |= RCC_APB2ENR_IOPCEN; //GPIOC clock enable
4
5
   for(;;){
6
       count = 0; 
7
8
       //PC9 & PC13 als Ausgang (Push-Pull)
9
       GPIOC->CRH = GPIOC->CRH & ~(0x00F000F0) | (0x00300030); 
10
     
11
       //und low ziehen
12
       GPIOC->BSRR = GPIO_BSRR_BR9; //reset pin PC9
13
       GPIOC->BSRR = GPIO_BSRR_BR13; //reset pin PC13
14
15
       delay_ms(1); //1ms warten (Kondensator entladen) 
16
17
18
       do{
19
         //beide als floating input schalten, damit keine Ladung abfliessen kann
20
         GPIOC->CRH = GPIOC->CRH & ~(0x00F000F0) | (0x00400040);
21
22
        //PC13 Out und high schalten, um C aufzuladen
23
         GPIOC->CRH = GPIOC->CRH & ~(0x00F00000) | (0x00300000);
24
   
25
         GPIOC->BSRR = GPIO_BSRR_BS13; //PC13 high schalten 
26
27
28
         //und PC13 wieder als floating input 
29
         GPIOC->CRH = GPIOC->CRH & ~(0x00F00000) | (0x00400000);
30
31
         //Die Ladung aus C jetzt nach PC9 auf GND abfuehren
32
         GPIOC->CRH = GPIOC->CRH & ~(0x000000F0) | (0x00000030);
33
34
         GPIOC->BSRR = GPIO_BSRR_BR9; //PC9 nach GND schalten
35
36
         //messen, ob Ladung schon fuer eine 1 an PC13 reicht, sonst von vorn
37
         if((GPIOC->IDR & 0x00002000) == 1){ 
38
               printf("Count = %d", count ); 
39
         }
40
41
         count ++; 
42
       }while(count < 1000); 
43
   }
44
}

Bei der Count-Variable hab ich, wie Johnny B. vorgeschlagen hat, einen 
größeren Datentyp (zum Testen mal 32bit) genommen.

Mein Problem ist nun, dass die Bedingung dieser Schleife nie wahr ist:
1
if((GPIOC->IDR & 0x00002000) == 1){ 
2
  printf("Count = %d", count ); 
3
}

Im Moment steh ich grad ein bisschen auf'm Schlauch und kann mir das 
nicht ganz erklären...

Wenn ich mit dem Oszi zwischen dem Touchpad und GND messe, dann ergibt 
sich der folgende Spannungsverlauf (siehe Screenshot).

von holger (Gast)


Lesenswert?

>         if((GPIOC->IDR & 0x00002000) == 1){

Wie soll das jemals 1 werden?

         if((GPIOC->IDR & 0x00002000)){

von Johnny B. (johnnyb)


Lesenswert?

Und im do...while fehlen noch Verzögerungen um der Ladung Zeit zu geben, 
sich zu verschieben. ;-)

von Julian O. (juliano)


Lesenswert?

und der versprochene Screenshot fehlt auch :-P

von Bernhard B. (schluchti)


Angehängte Dateien:

Lesenswert?

holger schrieb:
> Wie soll das jemals 1 werden?

Du hast natürlich vollkommen Recht, danke. Ich hab irgendwie die ganze 
Zeit den Fehler bei der Portinitialisierung gesucht.

Immerhin bekomm ich jetzt ne Ausgabe, aber nur dann wenn ich mit dem 
Finger das Pad (in meinem Fall ein Lötpunkt aus 3x3 Lochrasterpunkten) 
auf der Unterseite der Platine berühre. Irgendwas passt da noch nicht 
ganz. Ich werd mal zur Sicherheit alle Lötstellen nachlöten, um 
schlechten Kontakt auszuschließen.

Johnny B. schrieb:
> Und im do...while fehlen noch Verzögerungen um der Ladung Zeit zu geben,
> sich zu verschieben. ;-)

Die hab' ich weggelassen, auf Grund des Postings von Peter Dannegger: ( 
Beitrag "Re: qtouch - sekt oder selters" )

"Es reichen die Delays durch die Ausführungszeit der Meßloop vollkommen
aus, um die wenigen pF umzuladen.
Nur für die Entladung des 33nF bis zur nächsten Messung sollte man etwas
Zeit lassen."

Die CPU wird übrigens mit 8MHz getaktet. Aber ich kann mal probieren ein 
delay einzubauen, vielleicht ändert das ja etwas. Wie lange sollte man 
da in etwa warten? 5us?

Julian O. schrieb:
> und der versprochene Screenshot fehlt auch :-P

Du hast Recht, den hab' ich glatt vergessen. Jetzt ist er im Anhang.

von Peter D. (peda)


Lesenswert?

Bernhard B. schrieb:
> //PC13 Out und high schalten, um C aufzuladen
>          GPIOC->CRH = GPIOC->CRH & ~(0x00F00000) | (0x00300000);
>
>          GPIOC->BSRR = GPIO_BSRR_BS13; //PC13 high schalten

Du must zuerst den gewünschten Pegel vorbereiten und danach auf Ausgang 
schalten. Sonst entlädst Du ja wieder.


Peter

von Bernhard B. (schluchti)


Lesenswert?

Peter Dannegger schrieb:
> Bernhard B. schrieb:
>> //PC13 Out und high schalten, um C aufzuladen
>>          GPIOC->CRH = GPIOC->CRH & ~(0x00F00000) | (0x00300000);
>>
>>          GPIOC->BSRR = GPIO_BSRR_BS13; //PC13 high schalten
>
> Du must zuerst den gewünschten Pegel vorbereiten und danach auf Ausgang
> schalten. Sonst entlädst Du ja wieder.
>
>
> Peter

Wie meinst du das genau?
Ich soll PC13 zuerst auf High schalten und dann als Ausgang 
konfigurieren?

Ich dachte man kann einen IO-Port nur dann auf ein Potential ziehen, 
wenn der IO-Port als Ausgang konfiguriert wurde?

von Knut B. (Firma: TravelRec.) (travelrec) Benutzerseite


Lesenswert?

Wenn Dein Port aber "0" ist, Du ihn dann als Ausgang definierst und 
anschließend erst "1" schaltest, ist schon etwas Zeit vergangen, in der 
der Port "0" ausgegeben und somit den C schon wieder (etwas) entladen 
hat.

von Bernhard B. (schluchti)


Lesenswert?

Ich hab wie gesagt gedacht, dass ich nen IO-Port nur dann auf ein 
Potential ziehen kann (in meinem Fall "1") wenn der entsprechende Port 
als Ausgang konfiguriert wurde.

Ich hab das aber mal umgebaut:
1
int main(void){
2
   uint32_t count = 0; 
3
   RCC->APB2ENR |= RCC_APB2ENR_IOPCEN; //GPIOC clock enable
4
5
   for(;;){
6
       count = 0; 
7
8
       //PC9 & PC13 als Ausgang (Push-Pull)
9
       GPIOC->CRH = GPIOC->CRH & ~(0x00F000F0) | (0x00300030); 
10
     
11
       //und low ziehen
12
       GPIOC->BSRR = GPIO_BSRR_BR9; //reset pin PC9
13
       GPIOC->BSRR = GPIO_BSRR_BR13; //reset pin PC13
14
15
       delay_ms(1); //1ms warten
16
17
18
       do{
19
         //beide als floating input schalten, damit keine Ladung abfliessen kann
20
         GPIOC->CRH = GPIOC->CRH & ~(0x00F000F0) | (0x00400040);
21
22
      GPIOC->BSRR = GPIO_BSRR_BS13; //PC13 high schalten
23
24
        //PC13 Out und high schalten, um C aufzuladen
25
         GPIOC->CRH = GPIOC->CRH & ~(0x00F00000) | (0x00300000);
26
   
27
28
         //und PC13 wieder als floating input 
29
         GPIOC->CRH = GPIOC->CRH & ~(0x00F00000) | (0x00400000);
30
31
         //Die Ladung aus C jetzt nach PC9 auf GND abfuehren
32
         GPIOC->CRH = GPIOC->CRH & ~(0x000000F0) | (0x00000030);
33
34
         GPIOC->BSRR = GPIO_BSRR_BR9; //PC9 nach GND schalten
35
36
         //messen, ob Ladung schon fuer eine 1 an PC13 reicht, sonst von vorn
37
         if((GPIOC->IDR & 0x00002000)){ 
38
        printf("Count = %d", count ); 
39
         }
40
41
         count ++; 
42
       }while(count < 1000);

Jedoch bleibt das Problem, es wird nur etwas ausgegeben wenn ich mit dem 
Finger direkt auf den Lötpunkt greife...
Die Lötverbindungen hab ich jetzt nochmal nachgelötet, das sollte 
passen.
Jemand ne Idee was da falsch laufen könnte?

von Peter D. (peda)


Lesenswert?

Bernhard B. schrieb:
> //Die Ladung aus C jetzt nach PC9 auf GND abfuehren
>          GPIOC->CRH = GPIOC->CRH & ~(0x000000F0) | (0x00000030);
>
>          GPIOC->BSRR = GPIO_BSRR_BR9; //PC9 nach GND schalten
>
>          //messen, ob Ladung schon fuer eine 1 an PC13 reicht, sonst von vorn
>          if((GPIOC->IDR & 0x00002000)){


Wieder das gleiche, erst den Pegel einschalten und danach auf Ausgang!
Ich nehme mal an, BSR ist das Ausgangsregister und CRH das 
Richtungsregister.
Beim AVR gibt es die Besonderheit, daß er auch nen Pullup einschalten 
kann.
Daher muß ich für Tristate das Ausgangsregister auf Low setzen.
Wenn das beim  STM32 nicht so ist, dann setze die Pins einfach einmalig 
vor der Schleife und in der Schleife nur die Richtung.

Und daß ich zuerst die Zählschleife prüfe und danach den Pin, hat schon 
seinen Grund.
Beim AVR werden Eingänge gelatcht, d.h. es muß mindestens ein CPU-Zyklus 
zwischen Setzen des Ausgangs und Rücklesen des Eingangs vergehen.


Peter

von Bernhard B. (schluchti)


Lesenswert?

Danke für deinen Input. Ich hab mich von deinem Programm im "Qtouch - 
Sekt oder Selters" Thread inspirieren lassen und meines dementsprechend 
umgebaut.

Nun sieht es so aus:
1
#define COUNT_VAL 1000
2
3
int main(void){
4
   uint32_t count = 0; 
5
   RCC->APB2ENR |= RCC_APB2ENR_IOPCEN; //GPIOC clock enable
6
7
   for(;;){
8
       count = COUNT_VAL + 1; 
9
10
       //PC9 & PC13 als Ausgang (Push-Pull)
11
       GPIOC->CRH = GPIOC->CRH & ~(0x00F000F0) | (0x00300030); 
12
     
13
       //und low ziehen
14
       GPIOC->BSRR = GPIO_BSRR_BR9; //reset pin PC9
15
       GPIOC->BSRR = GPIO_BSRR_BR13; //reset pin PC13
16
17
       delay_ms(1); //1ms warten
18
19
20
       do{
21
         //beide als floating input schalten, damit keine Ladung abfliessen kann
22
         GPIOC->CRH = GPIOC->CRH & ~(0x00F000F0) | (0x00400040);
23
24
      GPIOC->BSRR = GPIO_BSRR_BS13; //PC13 high schalten
25
26
        //PC13 Out und high schalten, um C aufzuladen
27
         GPIOC->CRH = GPIOC->CRH & ~(0x00F00000) | (0x00300000);
28
   
29
30
         //und PC13 wieder als floating input 
31
         GPIOC->CRH = GPIOC->CRH & ~(0x00F00000) | (0x00400000);
32
33
      GPIOC->BSRR = GPIO_BSRR_BR9; //PC9 nach GND schalten
34
35
         //Die Ladung aus C jetzt nach PC9 auf GND abfuehren
36
         GPIOC->CRH = GPIOC->CRH & ~(0x000000F0) | (0x00000030);
37
38
39
      if(--count == 0){
40
        break;  
41
      }
42
 
43
       }while(!(GPIOC->IDR & 0x00002000)); 
44
    printf("Count = %d", COUNT_VAL - count); //hier wird dann Timeout oder "Key pressed" ausgegeben
45
   }
46
}

Wenn ich den Debugger anwerfe, dann sehe ich, dass alle IO-Ports richtig 
konfiguriert werden (Floating Input bzw. Push-Pull) und diese auch 
korrekt auf Low bzw High gezogen werden.

Ich bekommen jedoch immer nen Timeout - egal ob ich den 3x3 Lötpunkt, 
welcher bei mir als Touchpad fungiert, durch die Platine hindurch 
berühre oder nicht.
Was mir gerade noch einfällt: Mein Touchpad befindet sich auf einer 
kleinen Lochrasterplatine, wo auch der 1K Widerstand und der 47nF 
Kondensator sitzen (hab versucht die beiden Komponenten so nah ans Pad 
zu löten als möglich). Um die Touchpad-Platine mit der 
Mikrocontroller-Platine zu verbinden, verwende ich zwei ca. 15cm lange 
Drähte. Könnte das vielleicht der Übeltäter sein?

von Bernhard B. (schluchti)


Angehängte Dateien:

Lesenswert?

So, ich hab die zwei Anschlussdrähte mal auf 3cm gekappt - leider keine 
Besserung. Ich hab mir schon überlegt, den 1K Widerstand zwischen dem 
Touchpad gegen einen 10K auszutauschen. Aber ich glaub kaum das das was 
bringt, da der Spannungsverlauf am Touchpad genauso aussieht wie das in 
der Spezifikation von Atmel beschrieben wurde (siehe Anhang)

Keine Ahnung was da nicht passt...

von Peter D. (peda)


Lesenswert?

Bernhard B. schrieb:
> Ich hab mir schon überlegt, den 1K Widerstand zwischen dem
> Touchpad gegen einen 10K auszutauschen.

Warum?
Die Empfindlichkeit sollte man erst dann senken, wenn man überhaupt 
erstmal eine Empfindlichkeit hat.

Ich würde auch den 2.Widerstand zum Begrenzen des Umladestromes 
vorsehen.
Den Innenwiderstand der Ausgangstreiber zu nehmen, ist kein sauberes 
Design.

Hast Du meinen Rat mit der Wartezeit vor dem Einlesen beachtet?

Ansonsten, ob Deine kryptischen Hexwerte überhaupt das tun, was sie 
sollen, kannst nur Du feststellen.
Ich kenne den STM32 nicht. Wie schaltet der die internen Pullups ein?

Am besten Du schreibst Dir erstmal Macros (z.B. PIN_LOW, PIN_HIGH, 
PIN_INPUT, PIN_OUTPUT, PIN_TEST) und benutzt diese.
Und benutze nicht irgendwelche kryptischen Hexwerte, sondern definier 
Dir die beiden Pins, z.B. SENSKEY_A0, SENSKEY_B0.


Peter

von Bernhard B. (schluchti)


Lesenswert?

Peter Dannegger schrieb:
> Ich würde auch den 2.Widerstand zum Begrenzen des Umladestromes
> vorsehen.

Ok, werd ich ergänzen.

Peter Dannegger schrieb:
> Hast Du meinen Rat mit der Wartezeit vor dem Einlesen beachtet?

Was meinst du genau? Im Moment warte ich eine Millisekunde um 
sicherzustellen, dass der Kondensator vollkommen entladen ist.
Sonstige Verzögerungen hab' ich im Moment nicht eingebaut.

Peter Dannegger schrieb:
> Ansonsten, ob Deine kryptischen Hexwerte überhaupt das tun, was sie
> sollen, kannst nur Du feststellen.

Laut Debugger sollte das passen.

Peter Dannegger schrieb:
> Ich kenne den STM32 nicht. Wie schaltet der die internen Pullups ein?

Ich dachte die müssen ausgeschalten sein? Zumindest hab ich das diesem 
Beitrag "Re: qtouch - sekt oder selters" Posting entnommen.

von Bernhard B. (schluchti)


Lesenswert?

Wieder ein kleines Update:
Ich hab' jetzt mal nen 1ct Münze auf die Unterseite der 
Lochrasterplatine gelötet, um mein Pad zu vergrößern. Ebenso hab ich den 
zweiten Widerstand (2K2) zum Begrenzen des Umladestroms eingelötet.

Wenn ich jetzt mit dem Finger direkt auf die Kupfermünze greife, dann 
bekomme ich folgende Werte:

Count = 322
Count = 398
Count = 316
Count = 334
Count = 393
Count = 307
Count = 348
Count = 387
Count = 302
Count = 360
Count = 375
Count = 300
Count = 372
Count = 361
Count = 397

Wenn ich mit dem Finger jedoch auf die Oberseite der Lochrasterplatine 
greife dann tut sich überhaupt nichts...

von Peter D. (peda)


Lesenswert?

Bernhard B. schrieb:

> Was meinst du genau?

Die Zeit zwischen Pin_A auf low-Ausgang bis zum Einlesen von Pin_B.
Dürfte auch beim STM um ein oder mehrere IO-Takte verzögert sein.

Und deshalb mache ich das "if( --i == 0 ) break;" dazwischen zum 
Zeitschinden.


> Ich dachte die müssen ausgeschalten sein?

Ja.
Aber beim AVR muß man etwas tricksen, da die Pullups mit PORTxy:DDRxy = 
1:0 aktiv werden.
Gibts beim STM dafür ein extra Register, kann man sich die Trickserei 
sparen, d.h. Pin_B = 1 aus der Schleife rausziehen.


Peter

von Bernhard B. (schluchti)


Lesenswert?

Peter Dannegger schrieb:
> Die Zeit zwischen Pin_A auf low-Ausgang bis zum Einlesen von Pin_B.
> Dürfte auch beim STM um ein oder mehrere IO-Takte verzögert sein.
>
> Und deshalb mache ich das "if( --i == 0 ) break;" dazwischen zum
> Zeitschinden.

Alles klar, das meinst du. Ja, das hab ich bereits oben eingebaut.

Peter Dannegger schrieb:
> Gibts beim STM dafür ein extra Register, kann man sich die Trickserei
> sparen, d.h. Pin_B = 1 aus der Schleife rausziehen.

Hab' ich mal geändert, bringt aber leider auch nicht den gewünschten 
Erfolg.

PS: Hat das eigentlich nen tiefergehenden Sinn, dass viele Qtouch - 
Softwarelösungen im Thread "Qtouch - Sekt oder Selters" zwei 
unterschiedliche Ports nutzen?

von Bernhard B. (schluchti)


Lesenswert?

Ich glaub ich habs jetzt hinbekommen.

Ich musste COUNT_VAL erhöhen, da bei mir der Wert ungedrückt bereits 
1300 ist.
1
#define COUNT_VAL 1500

Wenn ich nun auf die Oberseite der Lochrasterplatine greife, dann ändert 
sich der Wert auf 1100.

Ich werd morgen noch nen ausführlichen Test machen, aber ich glaub es 
passt.

Danke an alle!

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.