www.mikrocontroller.net

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


Autor: Bernhard B. (schluchti)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Yalu X. (yalu) (Moderator)
Datum:

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

Autor: Johnny B. (johnnyb)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Bernhard B. (schluchti)
Datum:

Bewertung
0 lesenswert
nicht 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:
int main(void){
   uint32_t count = 0; 
   RCC->APB2ENR |= RCC_APB2ENR_IOPCEN; //GPIOC clock enable

   for(;;){
       count = 0; 

       //PC9 & PC13 als Ausgang (Push-Pull)
       GPIOC->CRH = GPIOC->CRH & ~(0x00F000F0) | (0x00300030); 
     
       //und low ziehen
       GPIOC->BSRR = GPIO_BSRR_BR9; //reset pin PC9
       GPIOC->BSRR = GPIO_BSRR_BR13; //reset pin PC13

       delay_ms(1); //1ms warten (Kondensator entladen) 


       do{
         //beide als floating input schalten, damit keine Ladung abfliessen kann
         GPIOC->CRH = GPIOC->CRH & ~(0x00F000F0) | (0x00400040);

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


         //und PC13 wieder als floating input 
         GPIOC->CRH = GPIOC->CRH & ~(0x00F00000) | (0x00400000);

         //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) == 1){ 
               printf("Count = %d", count ); 
         }

         count ++; 
       }while(count < 1000); 
   }
}

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:
if((GPIOC->IDR & 0x00002000) == 1){ 
  printf("Count = %d", count ); 
}

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

Autor: holger (Gast)
Datum:

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

Wie soll das jemals 1 werden?

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

Autor: Johnny B. (johnnyb)
Datum:

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

Autor: Julian O. (juliano)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
und der versprochene Screenshot fehlt auch :-P

Autor: Bernhard B. (schluchti)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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.

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Bernhard B. (schluchti)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Knut Ballhause (Firma: TravelRec.) (travelrec) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Bernhard B. (schluchti)
Datum:

Bewertung
0 lesenswert
nicht 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:
int main(void){
   uint32_t count = 0; 
   RCC->APB2ENR |= RCC_APB2ENR_IOPCEN; //GPIOC clock enable

   for(;;){
       count = 0; 

       //PC9 & PC13 als Ausgang (Push-Pull)
       GPIOC->CRH = GPIOC->CRH & ~(0x00F000F0) | (0x00300030); 
     
       //und low ziehen
       GPIOC->BSRR = GPIO_BSRR_BR9; //reset pin PC9
       GPIOC->BSRR = GPIO_BSRR_BR13; //reset pin PC13

       delay_ms(1); //1ms warten


       do{
         //beide als floating input schalten, damit keine Ladung abfliessen kann
         GPIOC->CRH = GPIOC->CRH & ~(0x00F000F0) | (0x00400040);

      GPIOC->BSRR = GPIO_BSRR_BS13; //PC13 high schalten

        //PC13 Out und high schalten, um C aufzuladen
         GPIOC->CRH = GPIOC->CRH & ~(0x00F00000) | (0x00300000);
   

         //und PC13 wieder als floating input 
         GPIOC->CRH = GPIOC->CRH & ~(0x00F00000) | (0x00400000);

         //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)){ 
        printf("Count = %d", count ); 
         }

         count ++; 
       }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?

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Bernhard B. (schluchti)
Datum:

Bewertung
0 lesenswert
nicht 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:
#define COUNT_VAL 1000

int main(void){
   uint32_t count = 0; 
   RCC->APB2ENR |= RCC_APB2ENR_IOPCEN; //GPIOC clock enable

   for(;;){
       count = COUNT_VAL + 1; 

       //PC9 & PC13 als Ausgang (Push-Pull)
       GPIOC->CRH = GPIOC->CRH & ~(0x00F000F0) | (0x00300030); 
     
       //und low ziehen
       GPIOC->BSRR = GPIO_BSRR_BR9; //reset pin PC9
       GPIOC->BSRR = GPIO_BSRR_BR13; //reset pin PC13

       delay_ms(1); //1ms warten


       do{
         //beide als floating input schalten, damit keine Ladung abfliessen kann
         GPIOC->CRH = GPIOC->CRH & ~(0x00F000F0) | (0x00400040);

      GPIOC->BSRR = GPIO_BSRR_BS13; //PC13 high schalten

        //PC13 Out und high schalten, um C aufzuladen
         GPIOC->CRH = GPIOC->CRH & ~(0x00F00000) | (0x00300000);
   

         //und PC13 wieder als floating input 
         GPIOC->CRH = GPIOC->CRH & ~(0x00F00000) | (0x00400000);

      GPIOC->BSRR = GPIO_BSRR_BR9; //PC9 nach GND schalten

         //Die Ladung aus C jetzt nach PC9 auf GND abfuehren
         GPIOC->CRH = GPIOC->CRH & ~(0x000000F0) | (0x00000030);


      if(--count == 0){
        break;  
      }
 
       }while(!(GPIOC->IDR & 0x00002000)); 
    printf("Count = %d", COUNT_VAL - count); //hier wird dann Timeout oder "Key pressed" ausgegeben
   }
}

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?

Autor: Bernhard B. (schluchti)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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...

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Bernhard B. (schluchti)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Bernhard B. (schluchti)
Datum:

Bewertung
0 lesenswert
nicht 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...

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Bernhard B. (schluchti)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Bernhard B. (schluchti)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich glaub ich habs jetzt hinbekommen.

Ich musste COUNT_VAL erhöhen, da bei mir der Wert ungedrückt bereits 
1300 ist.
#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!

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.