Forum: Mikrocontroller und Digitale Elektronik ATtiny1624, Timer B läuft zu schnell ab


von Holger L. (symetron)


Lesenswert?

Moin

Bisweilen etwas frustrierend, woran man(n) scheitert...

Ich möchte Timer/Interruptgesteuert eine LED im Sekundentakt blinken 
lassen. Also folgendes zu Papier gebracht:
1
#include <xc.h>
2
#include <avr/interrupt.h>
3
4
volatile uint32_t milli_ticks;
5
6
void led1on()... 
7
void led1off()...
8
 
9
ISR(TCB0_INT_vect) {
10
  milli_ticks++;
11
}
12
13
void main(void) {
14
15
  //Timer auf 20000 (bei 20MHz Takt -> 1 ms)
16
  TCB0.CCMPL = 0b00100000;
17
  TCB0.CCMPH = 0b01001110;
18
  //Enable Timer
19
  TCB0.CTRLA |= 1;
20
  //Enable Capture
21
  //DS: Bit 0 – CAPT Capture Interrupt Enable
22
  //    Writing this bit to ‘1’ enables interrupt on capture.
23
  TCB0.INTCTRL |= 1;
24
25
  sei();
26
27
28
  while (1) {
29
    //0-1000 an, 1001-2000 aus
30
    if (milli_ticks > 100000) {
31
      milli_ticks = 0;
32
    } else if (milli_ticks > 50000) {
33
      led1off();
34
    } else {
35
      led1on();
36
    }
37
38
  }
39
}

Jetzt war meine Idee, dass ich jeweils 1.000 1-ms-Ticks abwarte, und 
dann ist die Sekunde um. Tatsächlich muss ich etwa 50.000 Ticks warten.

Denk- oder Programmfehler?

Gruß
Holger


Auszug aus dem DS:

By default, the TCB is in Periodic Interrupt mode. Follow these steps to 
start using it:
* 1.Write a TOP value to the Compare/Capture (TCBn.CCMP) register.
* 2.Optional: Write the Compare/Capture Output Enable (CCMPEN) bit in 
the Control B (TCBn.CTRLB) register to
‘1’. This will make the waveform output available on the corresponding 
pin, overriding the value in the
corresponding PORT output register. The corresponding pin direction must 
be configured as an output in the
PORT peripheral.
* 3.Enable the counter by writing a ‘1’ to the ENABLE bit in the Control 
A (TCBn.CTRLA) register.
The counter will start counting clock ticks according to the prescaler 
setting in the Clock Select (CLKSEL) bit
field in the Control A (TCBn.CTRLA) register.
* 4.The counter value can be read from the Count (TCBn.CNT) register. 
The peripheral will generate a CAPT
interrupt and event when the CNT value reaches TOP.
* 4.1. If the Compare/Capture register is modified to a value lower than 
the current CNT, the peripheral will
count to MAX and wrap around.
* 4.2.At MAX, an OVF interrupt and event will be generated.

von Volker B. (Firma: L-E-A) (vobs)


Lesenswert?

Holger L. schrieb:

> Denk- oder Programmfehler?

Ich sehe zwei Programmierfehler:
1. Dein Zugriff auf die 32-Bit-Zählervariable ist nicht atomic
2. Konstanten größer 2^15 sollten explizit als long definiert werden, 
also
   z.B. 50000ul

Grüßle,
Volker

von Εrnst B. (ernst)


Lesenswert?

Ein Programmierfehler ist zumindest der nicht-atomare Zugriff auf 
"millis".

Besser:
1
uint32_t get_millis() {
2
  ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
3
     return milli_ticks;
4
  }
5
}

und das konsequent zum Zugriff auf die millis-Variable verwenden.

Dann: die Variable durchlaufen lassen. Deine Timer dann so 
umformulieren:
1
static uint32_t last_action_millis;
2
if (get_millis() - last_action_millis > 5000) {
3
   do_something();
4
   last_action_millis = get_millis();
5
}

(Achtung, nicht "if (last_action_millis+5000 < get_millis()) {...".
Ließt sich ähnlich, ist was anderes)

: Bearbeitet durch User
von Oliver S. (oliverso)


Lesenswert?

Holger L. schrieb:
> 1415  //Timer auf 20000 (bei 20MHz Takt -> 1 ms)
> 16  TCB0.CCMPL = 0b00100000;
> 17  TCB0.CCMPH = 0b01001110;

Nun ja. Mag ja das gewünschte sein, weiß aber niemand. Der Compiler kann 
aber tatsächlich mit 16-Bit Registern umgehen, du musstest ihm nur 
zutrauen.

Oliver

von S. L. (sldt)


Lesenswert?

"CAPT Capture Interrupt Flag ... This bit is cleared by writing a ‘1’ to 
it or ..."

Fehlt - die ISR wird also wohl permanent aufgerufen.

Und CCMP sollte auf 20000-1 gesetzt werden.

von Holger L. (symetron)


Angehängte Dateien:

Lesenswert?

> "CAPT Capture Interrupt Flag ... This bit is cleared by writing a ‘1’ to
> it or ..."

Mööp. Das scheint mir die 90%-Antwort zu sein. Die restlichen 10% gehen 
an den atomic-Zugriff. Vielen Dank für die Hinweise.

Da hätte ich mir eine Zeile mehr der Datenblatt-Beschreibung gewünscht, 
wenn da schon explizit aufgeführt wird, wie man den periodischen 
Interrupt umsetzt.

Jetzt ist eine Sekunde ca. 200 Ticks lang. Müssten doch eigentlich so um 
die 1000 sein, +/- X Prozent. Andere Größenordnung, immmer noch 
unerwartet.

Ich hänge den Code nachmal an. Vielleicht können wir das auch noch 
aufklären.

von Volker B. (Firma: L-E-A) (vobs)


Lesenswert?

Holger L. schrieb:

> Jetzt ist eine Sekunde ca. 200 Ticks lang. Müssten doch eigentlich so um
> die 1000 sein, +/- X Prozent. Andere Größenordnung, immmer noch
> unerwartet.

Vorteiler des Systemtakts auf 1 umgestellt? Dieser ist auf 6 
voreingestellt.

Grüßle,
Volker

von Holger L. (symetron)


Lesenswert?

> Vorteiler des Systemtakts auf 1 umgestellt? Dieser ist auf 6
> voreingestellt.

Womit dann auch das letzte Rätsel gelöst wäre.

Jetzt kann ich meinem Söhnle stolz eine blinkende LED präsentieren, und 
mich damit aus dem Urschlamm zum Gott erheben...

von Peter D. (peda)


Lesenswert?

S. L. schrieb:
> "CAPT Capture Interrupt Flag ... This bit is cleared by writing a ‘1’ to
> it or ..."
>
> Fehlt - die ISR wird also wohl permanent aufgerufen.

Im Unterschied zu den klassik Tiny/Mega muß bei den neuen Typen jedes 
Interruptflag der Timer händisch gelöscht werden.
Automatisches Löschen bei Eintritt in die ISR gibt es nicht mehr.
ISR_NOBLOCK ist somit verboten.

: Bearbeitet durch User
von S. L. (sldt)


Lesenswert?

"With a few stupid exceptions of course" - ich hätte vollständig 
zitieren sollen:

This bit is cleared by writing a ‘1’ to it or when the Capture register 
is read in Capture mode.

von Peter D. (peda)


Lesenswert?

S. L. schrieb:
> This bit is cleared by writing a ‘1’ to it or when the Capture register
> is read in Capture mode.

Mit "händisch löschen" meinte ich eine passende Codesequenz lt. 
Datenblatt innerhalb der ISR. Je nach Flag kann es mehrere Varianten 
dafür geben.

von S. L. (sldt)


Lesenswert?

an Peter Dannegger:

Ich bezog mich auf
> Automatisches Löschen bei Eintritt in die ISR gibt es nicht mehr.

von Georg M. (g_m)


Lesenswert?

Der Code sollte nicht länger sein als nötig.
Hier ist ein Beispiel:
1
#include <avr/io.h>
2
3
uint8_t cnt;                               // SW counter
4
5
int main(void)
6
{
7
  _PROTECTED_WRITE(CLKCTRL.MCLKCTRLB, CLKCTRL_PDIV_2X_gc | CLKCTRL_PEN_bm);  // 10 MHz Main Clock
8
9
  EVSYS.CHANNEL0 = EVSYS_CHANNEL0_TCB0_CAPT_gc;       // Event generator: TCB0 CAPT
10
  EVSYS.USERTCB1COUNT = EVSYS_USER_CHANNEL0_gc;       // Event user: TCB1 COUNT
11
12
  TCB0.CCMP = 0x1387;                                 // 1 kHz
13
  TCB0.CTRLA = TCB_CLKSEL_DIV2_gc | TCB_ENABLE_bm;    // CLK_PER/2, enable TCB0
14
15
  TCB1.CCMP = 0x0031;                                 // 20 Hz
16
  TCB1.CTRLA = TCB_CLKSEL_EVENT_gc | TCB_ENABLE_bm;   // select EVENT as clock, enable TCB1
17
18
  PORTB.DIRSET = PIN0_bm;                             // PB0 output (LED)
19
20
  while(1) 
21
  {
22
    if(TCB1.INTFLAGS)
23
    {
24
      TCB1.INTFLAGS = TCB_CAPT_bm;                    // clear TCB1 interrupt flag
25
      cnt++;
26
      if(cnt == 19)
27
      {
28
        PORTB.OUTSET = PIN0_bm;                       // LED ON        
29
      }
30
      else if(cnt == 20)
31
      {
32
        PORTB.OUTCLR = PIN0_bm;                       // LED OFF
33
        cnt = 0;   
34
      }
35
    }
36
  }
37
}

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.