ATmega: Frage zu Timer Compare Interrupt

OP #2085739
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.
OP #2085887
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,[...]"

Antwort schreiben

Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt. Bitte hier nur auf die ursprüngliche Frage antworten, für neue Fragen einen neuen Beitrag erstellen.

Formatierung
  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] – Link zu einem Artikel
  • Verweis auf einen anderen Beitrag: Rechtsklick auf dessen Beitrags-ID (#8078628), „Adresse kopieren“, in den Text einfügen

Mehr zur Formatierung

Längeren Sourcecode als Anhang, nicht in den Text. JPG nur für Fotos und Scans; Zeichnungen und Screenshots als PNG oder GIF. Siehe Bildformate.

Schon registriert? Anmelden

Wird nicht angezeigt. Wir schicken dir einen Link; sobald du damit bestätigst, erscheint dein Beitrag.

Mit dem Abschicken legst du einen kostenlosen Account an und erkennst die Nutzungsbedingungen und die Datenschutzerklärung an.