Hallo,
da ich gerne auf 3 Achsen die quadrature encoder meiner Schrittmotoren
auslesen wuerde, verwende ich entsprechend der AVR appnote AVR1600
das Event System und einen 16-bit Counter.
Den Zaehlerstand moechte ich als relative Positionsangabe verwenden.
Mein Encoder hat 1000 Zeilen, wodurch ich pro Umdrehung 4000
Counter-Takte erhalte.
Bei einem 16-bit Counter kommt es somit spaetestens alle
(2^16-1)/4000 ~= 16 Umdrehungen zu einem Overflow.
Um das Problem zu loesen, verwende ich einen zweiten kaskadierten 16-bit
Counter, sodass ein 32-bit quadrature counter entsteht.
Den zweiten Counter takte ich ebenfalls ueber das Event System, jedoch
loese ich die UP/DOWN count Events manuell in der Counter Overflow ISR
aus.
Das funktioniert soweit ganz gut, allerdings kann es leider ab und zu
etwa durch Jitter vorkommen, dass zwei Overflow Interrupts ausgeloest
werden, obwohl sich der Encoder nur minimal bewegt hat.
Dieses Problem scheint ebenso bei Anderen aufzutreten:
http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=89873
Basierend auf der Idee, dass bei zwei gueltigen Overflows die
Counter-Mitte zumindest einmal ueberschritten werden muss, wuerde ich
gerne folgenden Lösungsansatz umsetzen:
Grundsaetzlich werden aufeinanderfolgende gleiche Overflows ignoriert,
solange nicht zwischen den Overflows auch die Counter-Mitte
ueberschritten wird.
Dazu setze ich fuer den unteren Counter eines der Compare-Register (z.B.
TCC0.CCA) auf die Mitte der Counter Periode (TCC0.PER/2).
Im der ISR zum compare-match Interrupt kann ich dann eine globale
lock-Variable steuern, die angibt, ob bei ein jeweiliger
Counter-Overflow gezaehlt werden soll oder nicht.
Ich kann domit auf einfache Weise entscheiden, ob ein Counter Overflow
gueltig ist, oder ob der Overflow etwa durch Jitter aufgetreten ist.
Leider funktioniert die praktische Umsetzung aus mir derzeit nicht
bekannten Gruenden nicht, da je nach verwendetem Compare-Register
unterschiedliches "Counter-Fehler" auftreten:
Ohne compare matching funktioniert der up/down Counter. Bei jedem
under-/overflow wird der Counter korrekt zurueckgesetzt.
Setze ich etwa die Periode auf 4000 (TCC0.PER=4000), so arbeitet der
Counter korrekt:
Wird hinaufgezaehlt und es kommt zu einem Overflow, springt der Counter
von 4000-->0.
Wird hinuntergezaehlt und es kommt zu einem Underfow, springt der
Counter von 0-->4000.
Verwende ich nun das Compare-A Register (TCC0.CCA) und setze es auf 2000
(dh. die Haelfte der Periode), so stimmen zwar die Compare-Match
Interrupts, jedoch wird der Counter nur mehr bei einem Underflow
zurueckgesetzt.
Wird hinuntergezaehlt und es kommt zu einem Underfow, springt der
Counter von 0-->4000.
Wird hinaufgezaehlt, kommt es allerdings nicht mehr bei TCC0.PER (also
bei 4000) zum Overflow.
Stattdessen zaehlt der Counter einfach weiter hinauf.
Verwende ich die Compare-Register B, C oder D, so bekomme ich auf einmal
Compare-Matches und Overflows bei wesentlich niedrigeren und scheinbar
zufaelligen Counter-Staenden (z.B. bei 7, 10, ....).
* Weiss jemand warum dieses eigenartige Verhalten auftritt ?
Ich wuerde darauf tippen, dass bei einem up/down Counter intern evt.
schon alle 4 Compare-Register
verwendet werden, obwohl ich nichts entsprechendes im Datenblatt finden
konnte.
Meine derzeitige Implementation sieht wie folgt aus:
lg,
Stef
1 | #define ENC_PORT PORTF // quadrature encoder port
|
2 |
|
3 | ...
|
4 | int main()
|
5 | {
|
6 | ...
|
7 | ENC_PORT.DIRCLR = PIN0_bm | PIN1_bm; // encoder0 pins
|
8 | PORTCFG.MPCMASK = PIN0_bm | PIN1_bm; // multi-pin config mask
|
9 | ENC_PORT.PIN0CTRL = (ENC_PORT.PIN0CTRL & ~PORT_OPC_gm) | PORT_OPC_PULLUP_gc; // enable pull-up on those pins
|
10 |
|
11 | PORTCFG.MPCMASK = PIN0_bm | PIN1_bm; // multi-pin config mask
|
12 | ENC_PORT.PIN0CTRL = (ENC_PORT.PIN0CTRL & ~PORT_ISC_gm) | PORT_ISC_BOTHEDGES_gc; // sense both edges
|
13 |
|
14 | // setup event system, use channel0 for encoder0 input sense event
|
15 | EVSYS.CH0MUX = EVSYS_CHMUX_PORTF_PIN0_gc; // use events from PF0 on event channel 0
|
16 | EVSYS.CH0CTRL = EVSYS_QDEN_bm | EVSYS_DIGFILT_2SAMPLES_gc; // enable quadrature decoder and 2 sample filtering
|
17 |
|
18 | // setup PORTF timer0 to handle the quadrature decoding action on event channel 0 (i.e. encoder0)
|
19 | TCC0.CTRLD = TC_EVACT_QDEC_gc | TC_EVSEL_CH0_gc;
|
20 | TCC0.PER = 4000; // period
|
21 | TCC0.CTRLA = TC_CLKSEL_EVCH0_gc;
|
22 | TCC0.INTCTRLA = TC_OVFINTLVL_LO_gc; // low level interrupt on over-/underflow
|
23 |
|
24 | // set up compare matching in the middle of the period
|
25 | TCC0.CCA = TCC0.PER/2;
|
26 | TCC0.CTRLB |= TC0_CMPA_bm; // enable compare match on compare channel A
|
27 | TCC0.INTCTRLB |= TC_CCAINTLVL_LO_gc; // low level interrupt on compare match
|
28 |
|
29 | // setting up an event based 32-bit up/down counter is tricky, as
|
30 | // there only is an overflow but no underflow event.
|
31 | // Our solution is to determine the type of overflow in software and
|
32 | // send an UP/DOWN counting event manually.
|
33 |
|
34 | // setup event system, don't catch any hardware events on channel1
|
35 | EVSYS.CH1MUX = EVSYS_CHMUX_OFF_gc;
|
36 |
|
37 | TCC1.CTRLD = TC_EVACT_QDEC_gc | TC_EVSEL_CH1_gc; // up/down counting based the up/down events on event ch1
|
38 | TCC1.PER = 0xffff; // 16 bit period
|
39 | TCC1.CTRLA = TC_CLKSEL_EVCH1_gc; // enable timer with prescaler 1
|
40 |
|
41 | PMIC.CTRL |= PMIC_LOLVLEN_bm; // allow low level inetrrupts
|
42 | sei(); // enable interrupts
|
43 |
|
44 | while(1)
|
45 | {
|
46 | // loop forever
|
47 | }
|
48 | }
|
49 |
|
50 | ISR(TCC0_CCA_vect, ISR_BLOCK)
|
51 | {
|
52 | printf_P(PSTR("middle: %i\n"), TCC0.CNT);
|
53 | }
|
54 |
|
55 | ISR(TCC0_OVF_vect, ISR_BLOCK)
|
56 | {
|
57 | // check if we're counting up
|
58 | if (!(TCC0.CTRLFSET & TC0_DIR_bm))
|
59 | {
|
60 | if (TCC0.CNT==0)
|
61 | {
|
62 | printf_P(PSTR("up OV: %i\n"), TCC0.CNT);
|
63 |
|
64 | // send an increment event, see Table 6-2
|
65 | EVSYS.DATA |= (1<<1);
|
66 | EVSYS.STROBE |= (1<<1);
|
67 | }
|
68 | }
|
69 | // otherwise we're counting down
|
70 | else
|
71 | {
|
72 | if (TCC0.CNT>0)
|
73 | {
|
74 | printf_P(PSTR("dn OV: %i\n"), TCC0.CNT);
|
75 |
|
76 | // send a decrement event
|
77 | EVSYS.DATA &= ~(1<<1);
|
78 | EVSYS.STROBE |= (1<<1);
|
79 | }
|
80 | }
|
81 | }
|