... Vermutlich geben die Inkrementalgeber ein Quadratursignal ... im Allgemeinen nicht die beste Lösung, dieses per ext. Interrupts ... auzuwerten ...
Die Frage ist ja, ob es Sinn macht das komplette Quadratursignal (wenn es eins ist) auszuwerten.
......Bild hier
Warum? Weil ich einigermassen sicher bin, dass der Versatz der Signalflanken nicht genau gleich ist (so lange ich den Encoder nicht selbst hergestellt oder ausführlich getestet habe). Dann kommt eine Ungenauigkeit rein, die sich bei hohen Drehzahlen - und den damit verbundenen wenigen Timerticks schon wesentlich auf die Regelung auswirken kann. Hier wäre zu überlegen, ob man nicht (nur) jede gleichsinnige Flanke einer Encoderspur auswertet. Wenns nicht gerade eine hochgenaue Drehzahlregelung für etliche zehntausend Touren sein soll dürfte das reichen und möglicherweise eine genauere Regelungsgrundlage liefern als die Auswertung aller Flanken.
Die Lösung mit dem externen Interrupt ist nicht die schlechteste. Ich fahre bei meinem Dottie, beim MiniD0 und beim Archie, alles aber KEINE Schrittmotoren, mit dem externen Interrupt für steigende Flanke (beim Archie nur für EINE Endoderspur - je Motor *gg*) und werte in der zugehörigen ISR natürlich auch gleich die zweite Spur zur korrekten Wegberechnung (Drehrichtungserkennung) aus. Der Erfolg ist eine überraschend hohe Geschwindigkeitstreue. Ob Schrittmotoren in dieser Hinsicht empfindlicher sind, kann ich aber nicht sagen.
... Inkrementalgeber gerne über Interrupts auslesen würde, um möglichst keine Schritte zu verpassen ...
Wenns nicht um genaue Drehzahlregelung geht, sondern nur um exakte Wegerfassung (Drehwinkel), dann sehe ich das CPU-Laufzeitproblem nicht so gravierend, weil es nur wenige Codezeilen sind:
Die vollständige ISR für (m)einen Encoder bei Archie (der zugehörige *lls-Auszug ist gaaaanz unten) :
Code:
// ============================================================================= =
// === Nicht unterbrechbare ISR für EXT_INT0 auf Pin 4/PD2/mega328
// Der Timer tmrE0 für Laufzeit des EXT_INT0 wird ausgelesen
// Der zugehörige Motor auf PD7/PB0 = re,li und PB1 Geschwind./PWM
ISR(INT0_vect) // INT0 triggert auf RISING edge =>
{ // => Wenn Aufruf, dann PORTD2 = high
// - - - - - - - - - - - - - - - -
// Encoderticks Iencdrx nur hochzählen, IencBx rauf- od runterzählen
Iz_diff0 = tmrE0; // Abspeichern Zeit seit dem letzten ISR-Aufruf
tmrE0 = 0; // Resetten ##>> IN der ISR ohne CLI/SEI möglich
Iencdr0 ++; // Incrementiere Encodercounter, zählt NUR aufwärts
if (IsBitSet (PIND, 4)) IencB0++; // Rad treibt vorwärts, math. negativ
else IencB0--; // Rad treibt rückwärts, math. positiv
} // Ende ISR(INT0_vect)
// ============================================================================= =
In der Timer-ISR - 20 kHz <=> 50 µs - wird der Zeitwert tmrE0 getickert:
Code:
// === Nicht unterbrechbare ISR für timer2 =================================== */
// Routine zählt hoch im Takt 20 kHz = 50 µs. Der Zählerwert wird von den ISR für
// EXT_INT0 und -INT1 ausgelesen
ISR(TIMER2_COMPA_vect) //
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
{ //
Izeit_1 --; // ###>>> Izeit_1 ist aktuell int16_t ==>>
// Izeit_1 bleibt bis 32000 in der int16-Grenze
tupUM0 ++; // Tupsicounter für Umdrehungsmessung(en)
tmrE0 ++; // Encodertimer hochtickern
tmrE1 ++; // Encodertimer hochtickern
//RCzeit1 ++; // Tupsicounter uint16_t für RC-5-Decoding
if ( Izeit_1 ) // Interrupt-Timer = 1 ... 20 000 ... (1 sec blink)
{ } // WENN Izeit_1 =|= Null => wahr => Anweisung ausgeführen
else // Izeit_1 = Null = unwahr, daher "else" ausführen
{ // Eine Sekunde ist voll =>
Izeit_1 = Izthrznt; // ansonsten: Rückstellen auf Zeithorizont
ToggleBit (PgLED, L1g); // gnLED toggeln HEARTBEAT <<####, aktuell PC1
Isecundn ++; // Sekundenzähler hochtackern, max 9 Std
} // Ende if (Izeit_1 )
if (tmrE0 > 2000) // Grenzwert für Stillstand
// ... usf
Der *lls-teil zur Encoder-ISR:
Code:
// ============================================================================= =
// === Nicht unterbrechbare ISR für EXT_INT0 auf Pin 4/PD2/mega328
// Der Timer tmrE0 für Laufzeit des EXT_INT0 wird ausgelesen
// Der zugehörige Motor auf PD7/PB0 = re,li und PB1 Geschwind./PWM
ISR(INT0_vect) // INT0 triggert auf RISING edge =>
{ // => Wenn Aufruf, dann PORTD2 = high
8d4: 1f 92 push r1
8d6: 0f 92 push r0
8d8: 0f b6 in r0, 0x3f ; 63
8da: 0f 92 push r0
8dc: 11 24 eor r1, r1
8de: 8f 93 push r24
8e0: 9f 93 push r25
// - - - - - - - - - - - - - - - -
// Encoderticks Iencdrx nur hochzählen, IencBx rauf- od runterzählen
Iz_diff0 = tmrE0; // Abspeichern Zeit seit dem letzten ISR-Aufruf
8e2: 80 91 2f 07 lds r24, 0x072F
8e6: 90 91 30 07 lds r25, 0x0730
8ea: 90 93 0f 07 sts 0x070F, r25
8ee: 80 93 0e 07 sts 0x070E, r24
tmrE0 = 0; // Resetten ##>> IN der ISR ohne CLI/SEI möglich
8f2: 10 92 30 07 sts 0x0730, r1
8f6: 10 92 2f 07 sts 0x072F, r1
Iencdr0 ++; // Incrementiere Encodercounter, zählt NUR aufwärts
8fa: 80 91 0a 06 lds r24, 0x060A
8fe: 90 91 0b 06 lds r25, 0x060B
902: 01 96 adiw r24, 0x01 ; 1
904: 90 93 0b 06 sts 0x060B, r25
908: 80 93 0a 06 sts 0x060A, r24
if (IsBitSet (PIND, 4)) IencB0++; // Rad treibt vorwärts, math. negativ
90c: 4c 9b sbis 0x09, 4 ; 9
90e: 06 c0 rjmp .+12 ; 0x91c <__stack+0x1d>
910: 80 91 48 06 lds r24, 0x0648
914: 90 91 49 06 lds r25, 0x0649
918: 01 96 adiw r24, 0x01 ; 1
91a: 05 c0 rjmp .+10 ; 0x926 <__stack+0x27>
else IencB0--; // Rad treibt rückwärts, math. positiv
91c: 80 91 48 06 lds r24, 0x0648
920: 90 91 49 06 lds r25, 0x0649
924: 01 97 sbiw r24, 0x01 ; 1
926: 90 93 49 06 sts 0x0649, r25
92a: 80 93 48 06 sts 0x0648, r24
} // Ende ISR(INT0_vect)
92e: 9f 91 pop r25
930: 8f 91 pop r24
932: 0f 90 pop r0
934: 0f be out 0x3f, r0 ; 63
936: 0f 90 pop r0
938: 1f 90 pop r1
93a: 18 95 reti
... das sind schlappe vierzig Maschinenzyklen für einen Encoder.
Lesezeichen