Hier mal ein neuer Ansatz zum Auswerten von Drehimpulsgebern.... In der Praxis hat man häufig Drehimpulsgeber (rotary switches) die Raststellungen bei 00 und 11 haben. Die Zwischenstellungen 01 der 10 treten nur dynamisch während der Drehung auf. Solche Geber sind z.B. als Eingabeinstrument üblich. Der sehr gute Code aus diesem Thread http://www.mikrocontroller.net/forum/read-4-37992.html#new hat passt für diesen Typ nicht so gut denn: a) Bei den oben beschriebenen Drehimpulsgebern zählt er doppelt weil er bei jedem Wechsel inc- oder decrementiert. D.h. wird um eine Raststellung weitergedreht, zählt der Zähler +/-2. b) Wärend des Kontaktprellens ist der Ausgangswert nicht konstant sondern kann +/-1 wechseln. Das kann ein Problem sein, wenn das Anwenderprogram ungünstig ausliest. c) Er ist nicht unbedingt einfach zu verstehen. Hier nun mein Code für AVR: static uint8_t last_state = 0; static uint8_t last_cnt = 0; uint8_t new_state; new_state=PINE & (_BV(PINE4) | _BV(PINE3)); if ((new_state^last_cnt)==(_BV(PINE4) | _BV(PINE3)) ) { if ((new_state ^ last_state)==_BV(PINE4)) enc_delta+=1; else enc_delta-=1; last_cnt=new_state; } last_state=new_state; Er funktioniert folgendermassen: - Am Anfang werden die zwei Input-Ports eingelesen. - Dann wird geprüft, ob sich beide Bit's unterscheiden - Wenn nein wars das schon - Wenn ja, schauen wir woher wir kommen kommen wir von rechts (je nach Beschaltung) zählen wir hoch, sonst runter - Dann speichern wir uns noch den State bei dem wir gezählt haben - Am Ende merken wir uns den ausgewerteten State damit wir nächstes mal wissen woher wir kommen Dabei wird natürlich automatisch entprellt. Have Fun.
Hallo Christian! Bin in C noch nicht so bewandert. Kannst du mit erklären, was "_BV" bedeutet? Danke!
Ich schreibe das hier mal auf basis von Port B Bit 0 und 1. 1. Zwischenspeichern von PB0 & PB1 2. PB0 XOR PB1 = 0 -> nix passiert nix tun = 1 -> es wurde gedreht 3. PB0 XOR Zwischenspeicher PB0 = 1 es wurde nach links gedreht = 0 es wurde nach rechts gedreht 4. (Entprellung) wenn bei 3. das Ergebnis 1 war dann nicht neu bei 1. anfangen bevor nicht auch PB1 den status gewechselt hat, wenn bei 3. das Ergebnis 0 war dann nicht neu bei 1. anfangen bevor nicht PB0 den status gewechselt hat.
@Techniker: _BV ist letztlich nur ein Makro:(1 << (x)). Schöner wäre es, wenn Du _BV nicht nimmst, sondern das Makro ausschreibst. Dann ist es portabler (andere Compiler). Mit _BV wird der Code allenfalls ein bißchen leserlicher.
Kann mir jemand den übersetzten C-Code von oben geben (AVR-ASM)? Habe mit meinen Unkenntnissen in C versucht, das in ASM zu wandeln aber ich komme auf keinen Nenner. Kann leider nur ASM. Danke im Voraus! MfG Andi
Hier, nur kopiert, nicht näher angeschaut.... a) Optimization for size 70: new_state=PINE & (_BV(PINE4) | _BV(PINE3)); +000003A4: 91800000 LDS R24,0x0000 Load direct from data space +000003A6: 7188 ANDI R24,0x18 Logical AND with immediate +000003A7: 8389 STD Y+1,R24 Store indirect with displacement 71: if ((new_state^last_cnt)==(_BV(PINE4) | _BV(PINE3)) ) // we are either in main state 00 or 11 +000003A8: 8199 LDD R25,Y+1 Load indirect with displacement +000003A9: 9180014C LDS R24,0x014C Load direct from data space +000003AB: 2789 EOR R24,R25 Exclusive OR +000003AC: 3188 CPI R24,0x18 Compare with immediate +000003AD: F4A1 BRNE PC+0x15 Branch if not equal 73: if ((new_state ^ last_state)==_BV(PINE4)) +000003AE: 8189 LDD R24,Y+1 Load indirect with displacement +000003AF: 9190014B LDS R25,0x014B Load direct from data space +000003B1: 2789 EOR R24,R25 Exclusive OR +000003B2: 3180 CPI R24,0x10 Compare with immediate +000003B3: F431 BRNE PC+0x07 Branch if not equal 74: enc_delta+=1; +000003B4: 9180014A LDS R24,0x014A Load direct from data space +000003B6: 5F8F SUBI R24,0xFF Subtract immediate +000003B7: 9380014A STS 0x014A,R24 Store direct to data space +000003B9: C005 RJMP PC+0x0006 Relative jump 76: enc_delta-=1; +000003BA: 9180014A LDS R24,0x014A Load direct from data space +000003BC: 5081 SUBI R24,0x01 Subtract immediate +000003BD: 9380014A STS 0x014A,R24 Store direct to data space 77: last_cnt=new_state; +000003BF: 8189 LDD R24,Y+1 Load indirect with displacement +000003C0: 9380014C STS 0x014C,R24 Store direct to data space 79: last_state=new_state; +000003C2: 8189 LDD R24,Y+1 Load indirect with displacement +000003C3: 9380014B STS 0x014B,R24 Store direct to data space +000003C5: 9621 ADIW R28,0x01 Add immediate to word b) Optimization for speed 70: new_state=PINE & (_BV(PINE4) | _BV(PINE3)); +000003A4: 91800000 LDS R24,0x0000 Load direct from data space +000003A6: 7188 ANDI R24,0x18 Logical AND with immediate +000003A7: 8389 STD Y+1,R24 Store indirect with displacement 71: if ((new_state^last_cnt)==(_BV(PINE4) | _BV(PINE3)) ) // we are either in main state 00 or 11 +000003A8: 8199 LDD R25,Y+1 Load indirect with displacement +000003A9: 9180014C LDS R24,0x014C Load direct from data space +000003AB: 2789 EOR R24,R25 Exclusive OR +000003AC: 3188 CPI R24,0x18 Compare with immediate +000003AD: F4A1 BRNE PC+0x15 Branch if not equal 73: if ((new_state ^ last_state)==_BV(PINE4)) +000003AE: 8189 LDD R24,Y+1 Load indirect with displacement +000003AF: 9190014B LDS R25,0x014B Load direct from data space +000003B1: 2789 EOR R24,R25 Exclusive OR +000003B2: 3180 CPI R24,0x10 Compare with immediate +000003B3: F431 BRNE PC+0x07 Branch if not equal 74: enc_delta+=1; +000003B4: 9180014A LDS R24,0x014A Load direct from data space +000003B6: 5F8F SUBI R24,0xFF Subtract immediate +000003B7: 9380014A STS 0x014A,R24 Store direct to data space +000003B9: C005 RJMP PC+0x0006 Relative jump 76: enc_delta-=1; +000003BA: 9180014A LDS R24,0x014A Load direct from data space +000003BC: 5081 SUBI R24,0x01 Subtract immediate +000003BD: 9380014A STS 0x014A,R24 Store direct to data space 77: last_cnt=new_state; +000003BF: 8189 LDD R24,Y+1 Load indirect with displacement +000003C0: 9380014C STS 0x014C,R24 Store direct to data space 79: last_state=new_state; +000003C2: 8189 LDD R24,Y+1 Load indirect with displacement +000003C3: 9380014B STS 0x014B,R24 Store direct to data space
Danke Christian! Funktioniert jetzt. Allerdings scheint es sich genau so wie mein bisheriger Algorythmus zu verhalten (siehe Datenblatt des DDM427 von ALTRON unten rechts). Erst ab 2KHz Abtastrate geht beim schnellen Drehen nichts verloren. MfG Andi
Hallo Andi, ich verwende 500Hz Abtastrate. Läuft allerdings nur als manuelles Eingabegerät. Dass heisst, es stört eigentlich nicht, wenn beim schnellen drehen per Hand Pulse verloren gehen. Der User kann beim Kurbeln sowieso nicht mitzählen... Bei der langsamen Feineinstellung - solange man mitzählen kann - reichen die 500Hz. Grüße Christian
Oder mach die ganze geschichte mit externen Interrupts, oder in Hardware.
@Christian Aeh, das kapier ich nicht : if ((new_state^last_cnt)==(_BV(PINE4) | _BV(PINE3)) ) { if ((new_state ^ last_state)==_BV(PINE4)) Nur einer dieser beiden Ausdruecke kann wahr sein. Folglich kann die Routine nur rueckwaerts zaehlen, oder ?
Wieso? last_cnt und last_state sind verschieden, kann man leicht übersehen :-) last_cnt ist die Bitkombination bei der zuletzt gezählt wurde (00) oder (11) last_state ist der letzte Zustand (10)/(01) d.h. woher ich komme Gruesse Christian
Damn, du hast recht. Lesen hilft ungemein :)
Ich weis net, wies euch da geht, aber ich krieg immer PINEx nicht deklariert und damit bricht der gcc ab!
Hallo Felix, ist das dein erstes Program für AVR? Ein #include <avr/io.h> (beim gcc) Sollte natürlich nicht fehlen und dein µC im makefile definiert sein...
Sorry, mein oberes Posting is absolouter Mist, habs jetzt schon kapiert was PINE sein soll. Brett vorm Kopf.
Hallo, habe den Code so erweitert, dass man auch mehrere Drehgeber einlesen kann.
1 | void evalRots(unsigned int rotData) |
2 | //Erkennt die Umdrehungen der Encoder
|
3 | {
|
4 | static unsigned int last_state = 0; |
5 | static unsigned int last_cnt = 0; |
6 | |
7 | for(unsigned char i=0; i< 12; i+=2) //Encoder Anzahl: 6 |
8 | {
|
9 | if (((rotData^last_cnt) & (1 << i)) && ((rotData^last_cnt) & (1 << |
10 | (i+1)))) |
11 | {
|
12 | if ((rotData ^ last_state) & (1 << i)) |
13 | enc_delta[(i>>1)]--; |
14 | else
|
15 | enc_delta[(i>>1)]++; |
16 | |
17 | if((last_cnt^rotData) & (1 << i)) //bits verschieden? |
18 | last_cnt^=(1 << i); //togglen |
19 | if((last_cnt^rotData) & (1 << (i+1))) //bits verschieden? |
20 | last_cnt^=(1 << (i+1)); //togglen |
21 | |
22 | }
|
23 | if((last_state^rotData) & (1 << i)) //bits verschieden? |
24 | last_state^=(1 << i); //togglen |
25 | if((last_state^rotData) & (1 << (i+1))) //bits verschieden? |
26 | last_state^=(1 << (i+1)); //togglen |
27 | }
|
28 | }
|
Bei mir befinden sich die Graycode werte in einem Integer, da ich die Encoder über zwei schieberegister einlese. rotData kann aber auch einfach in Port des uc sein. Der Code macht im prinzip genau das gleiche wie der Orginalcode des Threads, wahrscheinlich lässt sich da noch einiges vereinfachen, laufen und funktionieren tut er aber. Tubbu
Hallo Christian, kannst Du mal das hier ausprobieren ? http://www.mikrocontroller.net/forum/read-4-37992.html#360188 Peter
Habe einen Drehimpulsgeber von Alps (STEC12E06 von Conrad) der mit jeder Rasterstellung den Zähler um 4 weiterzählt, was für meinen Fall nicht gewünscht ist. Ich habe das Beispiel von Christian ausprobiert, leider nicht mit dem gewünschten Erolg. Der Code aus dem dem Thread http://www.mikrocontroller.net/forum/read-4-37992.html# von Peter Dannegger lässt sich auch dann prima verwenden wenn zwischen den Rasterstellungen 2 oder 4 Impulse kommen, aber nur einer gezählt werden soll. ISR(TIMER0_OVF_vect) { static char enc_last = 0x01; char i = 0; if( PHASE_A ) i = 1; if( PHASE_B ) i ^= 3; // convert gray to binary i -= enc_last; // difference new - last if( i & 1 ){ // bit 0 = value (1) enc_last += i; // store new as next last enc_delta += (i & 2) - 1; // bit 1 = direction (+/-) if (!(enc_delta % 4)) { // nur jeden 4. Schritt zählen if ((i & 2)) { // prüfen ob auf oder ab count++; // 0-255 } else { count--; // 255-0 } } } } Läuft super. Arne
Ich habe erfolglos versucht den Code hier zu verwenden. Wäre jemand so freundlich mir das ganze nochmals klar und deutlich zu erklären? Die Codeschnippsel oben sind eher verwirrend!! last_cnt ist die Bitkombination bei der zuletzt gezählt wurde (00) oder (11) last_state ist der letzte Zustand (10)/(01) d.h. woher ich komme wo wird bei 00 und 11 etwas gezählt? da soll ja gerade nichts passieren... Mit freundlichen Grüssen
Dies ist mein Code: void encoder(void) { struct { unsigned int speicher_A :1; unsigned int speicher_B :1; }encoder; if(PF.PORT.BIT.B1 == PF.PORT.BIT.B2) { encoder.speicher_A = PF.PORT.BIT.B1; encoder.speicher_B = PF.PORT.BIT.B2; } if(PF.PORT.BIT.B1 ^ PF.PORT.BIT.B2) //Bits unterschiedlich? { if(encoder.speicher_B ^ PF.PORT.BIT.B2) // Hat Bit2 geändert? { while(PF.PORT.BIT.B1 == encoder.speicher_A); //warten LCD_clear(0); LCD_writeString("Links", 0, 0,0); } else { while(PF.PORT.BIT.B2 == encoder.speicher_B); LCD_clear(0); LCD_writeString("Rechts", 0, 60,0); } } } 90 % der Fälle funtkioniert er, jedoch werden manchmal Drehungen nicht erkannt (auch wenn sie sehr langsam gemacht wurden). Es muss also irgendwo ein Fehler sein! Ausserdem finde ich das mit der while schleife ebenfalls nicht gut, aber irgendwie muss ich ja die Änderung des andern Bits abwarten, oder? MFG
Irgendwie kann ich bei deinem Code keine grosse Ähnlichkeit mit dem Original erkennen... Vielleicht ist wichtig, dass _BV(PINE4) und _BV(PINE3) verschieden sind. _BV(PINE4) bedeutet z.B. (1<<3) d.h. 8, und _BV(PINE3) = 1<<2 = 4. Ist bei deinem PF.PORT.BIT.B1 vermutlich nicht so? Ausserdem kostet dein LCD_clear() und LCD_write() vmtl. soviel Zeit, dass die Drehpulse verloren gehen. Der Code sollte schon mit einer gewissen Poll-Frequenz laufen (bei mir 400Hz). Hier nocheinmal der Original-Code damit man nicht scrollen muss... void encoder(void) { static uint8_t last_state = 0; static uint8_t last_cnt = 0; uint8_t new_state; new_state=PINE & (_BV(PINE4) | _BV(PINE3)); if ((new_state^last_cnt)==(_BV(PINE4) | _BV(PINE3)) ) { if ((new_state ^ last_state)==_BV(PINE4)) enc_delta+=1; else enc_delta-=1; last_cnt=new_state; } last_state=new_state; }
hallo ich versuche gerade das zum laufen zu bekommen scheitere aber kläglich hier mal ein paar schnipsel: // belegung des Rotary Encoders #define ROTARY_DDR DDRD #define ROTARY_PIN PIND #define ROTARY_PORT PORTD #define ROTARY_A 5 #define ROTARY_B 6 #define ROTARY_P 7 int enc_delta=0; void encoder(void) { static uint8_t last_state = 0; static uint8_t last_cnt = 0; uint8_t new_state; new_state=ROTARY_PIN & ((1<<ROTARY_A) | (1<<ROTARY_B)); if ((new_state^last_cnt)== (1<<ROTARY_A) | (1<<ROTARY_B)) { if ((new_state ^ last_state)==(1<<ROTARY_A)) enc_delta+=1; else enc_delta-=1; last_cnt=new_state; } last_state=new_state; } int main (void) { ROTARY_DDR &=~ (1<<ROTARY_A)|(1<<ROTARY_B)|(1<<ROTARY_P); ROTARY_PORT |= (1<<ROTARY_A)|(1<<ROTARY_B)|(1<<ROTARY_P); for(;;) { encoder(); itoa(enc_delta,out,10); lcd_gotoxy(13,0); lcd_puts(out); } } es kommt aber leider nur ein extrem schnelles zählen auf das LCD new_state und last_state sind immer auf 0 es passiert nix bei drehung könnte mir bitte jemand sagen warum ? hab schin gesucht , finde aber nix Danke
Vielleicht enc_delta als volatile deklarieren, damit im Hauptprogramm Änderungen an der Variable erkannt werden...
Im Anhang ein Code der bei mir bestens funktioklappt.
Nachtrag: PullUpwiderstände sind bei mir am Drehgeber. Daher im Code keine eingeschaltet.
hi also ich habe es nun zum laufen bekommen zwar mit anderem code da dieser aber immer 2 schritte zählt teile ich das ganze immer durch 2 #define ROTARY_DDR DDRD #define ROTARY_PIN PIND #define ROTARY_PORT PORTD #define ROTARY_A 5 #define ROTARY_B 6 #define ROTARY_P 7 volatile int enc_delta =0 ; SIGNAL (SIG_OVERFLOW0) { static char enc_last = 0x01; char i = 0; if( (ROTARY_PIN & (1<<ROTARY_B) )) i = 1; if( (ROTARY_PIN & (1<<ROTARY_A) )) i ^= 3; i -= enc_last; if( i & 1 ) { enc_last += i; enc_delta += (i & 2) - 1; } }
Hallo, habe hier nen Drehimpulsgeber (noname), der immer in der Raststellung 00 hat. Dadurch kann ich mit dem hier vorhandenen Code keine Auswertung machen. Das Ding zählt halt nur in eine Richtung. Also, R ist die Raststellung : links (R)00 - 01 - 11 - 01 - (R)00 rechts (R)00 - 10 - 11 - 10 - (R)00 Ich bitte mal um ne kleine hilfe. gruss tomgr
Warum sollte der Code mit deinem Drehgeber nicht funktionieren? Er wird halt nur doppelt zählen...
Ich verwende Encoder mit 30 oder 40iger Auflösung. Da kriegt der AVR ganz schön was zu tun. Sollte man den kleinsten Eingang über IRQ aufnehmen, anstatt mit hoher Samplerate zu arbeiten? Das Softwareentprellen ist auch nicht ganz ohne. Habe ich das auch bei Drehgebern mit HALL-IC? P.S. Habe meine Drehgeberreste jetzt in EBay eingestellt. Wer noch welche braucht...
Hi folks, I´m very sorry but when I see this : "_BV(PINE4) | _BV(PINE3" . I´m just wondering how it functions. Because you cannot write in the register PINX. Or I´m wrong ? or I´m messed up ? somebody has to explain me that...
That are defines for the port register and the register bits. _BV() is a macro that gives the bit vector for the given bit.
@ wilfried mühlenhoff (Gast) >Ich verwende Encoder mit 30 oder 40iger Auflösung. >Da kriegt der AVR ganz schön was zu tun. Nö. >Sollte man den kleinsten Eingang über IRQ aufnehmen, >anstatt mit hoher Samplerate zu arbeiten? Nein, siehe Artikel Drehgeber. >Habe ich das auch bei Drehgebern mit HALL-IC? Ja. Siehe Artikel.
@ Harry (Gast) >Because you cannot write in the register PINX. Yes, you can! ;-) In the new AVRs, writing a PINX bit will toggle the corresponding PORTX Bit! Regards Falk
Hallo, ich habe den Code verwendet, nur ergibt sich bei mir bei der Abfrage new_state ^ last_state = 11 Also ein unerwünschte Ergebnis, da ja nur 10 oder 01 erwartet wird. Kann mir jemand helfen?
1 | ISR( TIMER2_OVF_vect ) |
2 | {
|
3 | |
4 | new_state = PINL & ((1<<5) | (1<<3)) ; // save state |
5 | |
6 | // Change in State on both important positions?
|
7 | if ( (new_state ^ last_count) == ((1<<5 )|(1<<3)) ) |
8 | {
|
9 | // right or left turn?
|
10 | if( (new_state ^ last_state) == (1<<3 ) ) |
11 | {enc_delta += 1;} |
12 | else // Falle für unerwünschten Zustand |
13 | {enc_delta -= 1;} |
14 | |
15 | enc_last_count = enc_new_state; // save Bitcombination of last count |
16 | }
|
17 | enc_last_state = enc_new_state; // save last state |
18 | |
19 | }
|
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.