Forum: Mikrocontroller und Digitale Elektronik ATmega: Frage zu Timer Compare Interrupt


von Pl L. (atmega169)


Lesenswert?

Hallo!

Wie in einem anderen Thread schon geschrieben, nutze ich einen 32768Hz 
Quarz, mit dem ich nur Sekunden exakt zählen kann. Jetzt wollte ich die 
Millisekunden wenigstens genähert berechnen. Meine Idee:

Ich setze den Prescaler beim Timer auf 32. D.h. exakte Sekunden erhalte 
ich alle 32768Hz/32 = 1024Hz

Mein Ansatz war nun, nicht den normalen Overflow-Interrupt zu verwenden, 
sondern den Compare-Interrupt.
1
    TCNT2 = 0; // Timer-Zählregister auf initial 0 setzen
2
    // Output Compare Register A (Der Vergleichswert) auf 1 setzen:
3
    OCR2A = 1;

Damit will ich meinen Compare-Interrupt 1024 Mal pro Sekunde aufrufen. 
Jeder Aufruf entspricht grob einer Millisekunde, sind die 1024 Aufrufe 
erfolgt erhöhe ich die Sekunden um 1.

Problem:
-------

Das zählen scheint nicht korrekt zu funktionieren. Ich erhalte die 
ganzen Sekunden bereits nach 512 Compare-Interrupt-Aufrufen. Das liegt 
wohl daran, dass laut Datenblatt die Interrupt-Routine erst ein Takt 
nach einem erfolgreichen Vergleich aufgerufen wird. Wie kann ich das 
Problem lösen? Den Vergleichswert OCR2A auf 0 setzen sorgt lediglich 
dafür, dass der Microkontroller ewig hängt ;-)

Wenn jemand eine bessere Idee hat Sekunden genau zu zählen und 
Millisekunden zu nähern, nehm ich die auch gerne an!

Ist es im allgemeinen problematisch, wenn ich eine Interrupt-Routine 
über 1000 Mal pro Sekunde aufrufe? Der Inhalt beschränkt sich auf wenige 
einfache Anweisungen.

von Falk B. (falk)


Lesenswert?

@Pl Lp (atmega169)

>Wenn jemand eine bessere Idee hat Sekunden genau zu zählen und
>Millisekunden zu nähern, nehm ich die auch gerne an!

http://www.mikrocontroller.net/articles/AVR_-_Die_genaue_Sekunde_/_RTC#Echtzeituhr_mit_Uhrenquarz

von Pl L. (atmega169)


Lesenswert?

So, nachdem ich noch weiter recherchiert und im Datenblatt gelesen habe, 
konnte ich zumindest den Compare-Interrupt korrekt implementieren.
Ich zeige ich mal meinen Code und vlt. seht ihr ja, wo mein Fehler ist:

1. Variante mit Overflow-Interrupt
1
void init() {
2
    __disable_interrupt();
3
4
    TIMSK2 &= ~(1<<TOIE2);   
5
    ASSR = (1<<AS2);        // select asynchronous operation of Timer2
6
7
    TCNT2 = 0;              // clear TCNT2A
8
    
9
    TCCR2A = (0<<CS22) | (1<<CS21) |(1<<CS20); // Vorteiler auf 32
10
 
11
  
12
    while((ASSR & 0x01) | (ASSR & 0x04));       // wait for TCN2UB and TCR2UB to be cleared
13
14
    TIFR2 = 0xFF;           // clear interrupt-flags
15
16
    TIMSK2 |= (1<<TOIE2);   // enable Timer2 overflow interrupt
17
18
    __enable_interrupt();
19
}

Interrupt-Routine:
1
#pragma vector = TIMER2_OVF_vect
2
__interrupt void TIMER2_OVF_interrupt(void)
3
{    
4
    static int overflows = 0;
5
    ++overflows;
6
7
    if(overflows == 4) {
8
        ++sekunden; // Globale volatile int
9
        overflows = 0;
10
    }
11
}

Diese Variante arbeitet korrekt. Auch nach mehreren Minuten stimmen die 
gezählten Sekunden mit den Sekunden meiner Vergleichsuhr überein! 
Funktioniert also.


2. Variante: CTC und Compareinterrupt

1
void init() {
2
    __disable_interrupt();
3
4
    TIMSK2 = (1<<OCIE2A) | (0<<TOIE2); 
5
    ASSR = (1<<AS2);        // select asynchronous operation of Timer2
6
7
    TCNT2 = 0;              // clear TCNT2A
8
9
    // Output Compare Register A auf 0 setzen:
10
    OCR2A = 0; /* Datenblatt S. 139: "Whenever TCNT2 equals OCR2A, the  comparator signals a match. A match will set
11
                                       the Output Compare Flag (OCF2A) at the next timer clock cycle." */
12
   
13
    
14
    TCCR2A = (0<<CS22) | (1<<CS21) |(1<<CS20) | (1<<COM2A1) | (0<<COM2A0) | (1<<WGM21) | (0<<WGM20);  // Vorteiler auf 32, CTC an.
15
16
    while((ASSR & 0x01) | (ASSR & 0x04));       // wait for TCN2UB and TCR2UB to be cleared
17
18
    TIFR2 = 0xFF;           // clear interrupt-flags
19
20
    TIMSK2 |= (1<<TOIE2);   // enable Timer2 overflow interrupt
21
22
    __enable_interrupt();
23
}

Interrupt-Routine:

1
#pragma vector = TIMER2_COMP_vect
2
__interrupt void TIMER2_COMP_interrupt(void)
3
{
4
    static int overflows = 0;
5
    ++overflows ;
6
7
    if(overflows == 1024) {
8
        overflows = 0;
9
        ++sekunden;
10
    }
11
}

Die Idee bei dieser Variante ist, jeden der 32768/32=1024 "Ticks" im 
Compare-Interrupt abzugreifen. Diese Variante zählt FAST richtig. Jede 
Minute geht allerdings 1 Sekunde verloren. Nach 180 Sekunden realer Zeit 
zeigt mir mein Microkontroller 177 Sekunden an.

Meine Vermutung (aus diesem Thread: 
Beitrag "(Atmel) CTC counter zählen unterschiedlich schnell"):

Kann es an den 1024 Interrupt-Aufrufen pro Sekunde liegen?
Zitat: "Aber wenn Du Interrupts aktiv hast,
ist es ja wohl klar, dass ab einer gewissen "Geschwindigkeit" was
verloren geht,[...]"

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.