mikrocontroller.net

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


Autor: Pl Lp (atmega169)
Datum:

Bewertung
0 lesenswert
nicht 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.
    TCNT2 = 0; // Timer-Zählregister auf initial 0 setzen
    // Output Compare Register A (Der Vergleichswert) auf 1 setzen:
    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.

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht 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_...

Autor: Pl Lp (atmega169)
Datum:

Bewertung
0 lesenswert
nicht 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
void init() {
    __disable_interrupt();

    TIMSK2 &= ~(1<<TOIE2);   
    ASSR = (1<<AS2);        // select asynchronous operation of Timer2

    TCNT2 = 0;              // clear TCNT2A
    
    TCCR2A = (0<<CS22) | (1<<CS21) |(1<<CS20); // Vorteiler auf 32
 
  
    while((ASSR & 0x01) | (ASSR & 0x04));       // wait for TCN2UB and TCR2UB to be cleared

    TIFR2 = 0xFF;           // clear interrupt-flags

    TIMSK2 |= (1<<TOIE2);   // enable Timer2 overflow interrupt

    __enable_interrupt();
}

Interrupt-Routine:
#pragma vector = TIMER2_OVF_vect
__interrupt void TIMER2_OVF_interrupt(void)
{    
    static int overflows = 0;
    ++overflows;

    if(overflows == 4) {
        ++sekunden; // Globale volatile int
        overflows = 0;
    }
}

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

void init() {
    __disable_interrupt();

    TIMSK2 = (1<<OCIE2A) | (0<<TOIE2); 
    ASSR = (1<<AS2);        // select asynchronous operation of Timer2

    TCNT2 = 0;              // clear TCNT2A

    // Output Compare Register A auf 0 setzen:
    OCR2A = 0; /* Datenblatt S. 139: "Whenever TCNT2 equals OCR2A, the  comparator signals a match. A match will set
                                       the Output Compare Flag (OCF2A) at the next timer clock cycle." */
   
    
    TCCR2A = (0<<CS22) | (1<<CS21) |(1<<CS20) | (1<<COM2A1) | (0<<COM2A0) | (1<<WGM21) | (0<<WGM20);  // Vorteiler auf 32, CTC an.

    while((ASSR & 0x01) | (ASSR & 0x04));       // wait for TCN2UB and TCR2UB to be cleared

    TIFR2 = 0xFF;           // clear interrupt-flags

    TIMSK2 |= (1<<TOIE2);   // enable Timer2 overflow interrupt

    __enable_interrupt();
}

Interrupt-Routine:

#pragma vector = TIMER2_COMP_vect
__interrupt void TIMER2_COMP_interrupt(void)
{
    static int overflows = 0;
    ++overflows ;

    if(overflows == 1024) {
        overflows = 0;
        ++sekunden;
    }
}

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

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
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.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.