Timer-Problem

Gast #100060
Hallo,

ich habe bereits im Forum gesucht, aber alle Beispiele haben bei mir
nicht funktioniert.
Ich benutze den Atmega16 mit 8MHz Takt aus dem Shop und haben an Port B
ein LCD dranhängen.

Nun möchte ich noch einen Timer laufen lassen, der jede Sekunde die
Variable state1 um 1 erhöht. Mein Code sieht folgendermaßen aus:

volatile char count;

SIGNAL (SIG_OVERFLOW1) {
  if (count == 1) {
    state1++;
    count--;
    return;
  }
  if (count == 0) {
    count++;
    return;
  }
}

void timer (void) {
  TIMSK = _BV(TOIE1);            //Timer Overflow Interrupt enable
  TCNT1 = 0;                  //Rücksetzen des Timers
  TCCR1B = (1<<CS11);
  sei ();
}

int main(void)
{
  char buffer[20];
  /* initialize display, cursor off */
  lcd_init(LCD_DISP_ON);
  lcd_clrscr();
  timer ();
  for (;;) {
  ...
  }
}

Der obige Code zählt schon annähernd genau, doch er liegt bei 100
Sekunden um knapp 4 Sekunden zurück und das addiert sich dann auf.
Es liegt wohl alles an der Zeile TCCR1B = (1<<CS11), aber welche
Einstellung ich auch probiert habe, der Timer zählt entweder ganz
langsam oder viel zu schnell :-((.

Das ist echt zum Verrücktwerden. Weiß jemand Rat?

Vielen Dank

Markus
Gast #100061
Davon abgesehen, daß state1 volatile sein sollte (und nicht count),
was macht Dich glauben, daß Du hier in irgendeiner Form auf einen
Sekundentakt kommen könntest?

Du hast 8 MHz Takt, einen Vorteiler von 8, macht 1 MHz für den Timer.
Da Du den Überlauf eines 16-bit Timers auswertest, werden diese durch
65536 dividiert, TIMER_OVERFLOW1 wird also alle 65,5 ms getriggert.
Deine umständliche count-Mimik teilt nochmal durch 2, d. h. nach
meiner Rechnung sollte state1 ca. alle 131 ms inkrememtiert werden.

Da Du auf ungefähr eine Sekunde kommst, liegt die Vermutung nahe, daß
Du das weder das Datenblatt noch die FAQ gelesen hast und Dein Chip
noch vom internen RC-Oszillator mit 1 MHz betrieben wird.  Das würde
das Timing auf ca. 1,05 Sekunden verändern (was wohl Deiner
Beobachtung entspricht :), natürlich ohne jegliche Kalibrierung (=>
OSCCAL-Register) und mit der deutlich schlechteren Stabilität, die der
RC-Oszillator ohnehin mit sich bringt.

Unabhängig vom Datenblattstudium (und -verständnis), das Dir keiner
abnehmen kann, so wird das kaum was: mit binären Teilerfaktoren (und
mehr erreichst Du mit dem overflow-Interrupt nicht) kannst Du nicht
auf direktem Wege 8 MHz so teilen, daß Du auf ganze Sekunden kommst.
Der passende Trick nennt sich CTC-Modus (clear timer on compare
match).
Gast #100062
Ok, ich habe die Fusebits so gesetzt, dass der Chip nun mit 8MHz
getaktet werden sollte.

Wie kann ich das jetzt mit dem CTC-Modus einbinden?
Gast #100064
;-) Danke, aber ich versteh das Zeugs echt nicht (also, das mit dem
Timer) und glaub mir, ich suche und probiere schon seit heute Mittag
nach so nem CTC-Modus. Habe auch die header-Datei <timer.h> entdeckt,
doch bisher keine Erklärung gefunden, die mir weiterhilft. Meine
Favoriten-Liste füllt sich minütlich, nur mein Hirn läuft nicht in
dieser Geschwindigkeit.

Tut mir leid, wenn ich etwas unsanft gefragt habe, aber ich steh echt
auf dem Schlauch. Wenigsten läuft mein Controller auf alle Fälle nun
mit 8MHz.

Grüßle

Markus
Gast #100065
Ich weiß zwar nicht genau, was ich tue, aber mit diesem Code hier
benutze ich CTC und bin trotzdem bei 5 Minuten knappe 7 Sekunden hinter
der wirklichen Berechnung (Chip taktet mit 8MHz)!

#ifndef CTC1
#define CTC1 WGM12              // for compatibility with ATmega
#endif

...

SIGNAL (SIG_OUTPUT_COMPARE1A)
{
  state1++;
}


void timer (void) {
  TCCR1B = _BV(CS10) | _BV(CS12) | _BV(CTC1); // clear timer on compare
match, no prescaler
  OCR1A  =  8000;
  TIMSK  = _BV(OCIE1A);  // enable Output Compare 1 overflow interrupt
  sei ();
}
Gast #100066
Der Weg ist richtig.

Aus'm Kopf bin ich mir aber gerade nicht sicher, ob Du hier keinen
off-by-one error hast, also 7999 nehmen mußt -- steht aber in der Doku
drin (gibt's ne Formel dafür).

Außerdem mußt Du natürlich noch verifizieren, daß Dein Takt auch
wirklich exakt 8 MHz ist.  Wenn man das genau machen will, nimmt man
entweder einen passend genauen Quarzoszillator, oder man führt einen
der beiden Lastkondensatoren am Quarz als Trimmer aus (den an XTAL1
würde ich jetzt tippen -- es muß der Gatter-Eingang am AVR sein) und
zieht den Quarz damit auf die exakte Frequenz.

Das ``toggle OCx output on compare match'' feature zusammen mit
einem
Zählfrequenzmesser kann bei diesem Abgleich hilfreich sein.
Gast #100067
Ok, vielen Dank ;-). D.h. ich muss eigentlich nur noch den Wert von
OCR1A richtig anpassen, selbst wenn die Frequenz nicht exakt 8 MHz ist,
richtig?
Gast #100069
> D.h. ich muss eigentlich nur noch den Wert von OCR1A richtig
> anpassen, selbst wenn die Frequenz nicht exakt 8 MHz ist, richtig?

Diese Anpassung würde recht grob werden.  Guck mal, Du hast einen Wert
von 8000, den Du in Einzelschritten ,,kalibrieren'' kannst.  Das
macht
eine Schrittdifferenz von 1/8000 = 1,25E-4.  Der Trimmer am Quarz kann
so viel vielleicht gerade mal im Ganzen ziehen, aber dort hast Du noch
gut wenigstens 20 ,,Abstufungen'' dabei (nicht wirklich: ist ja
analog).

Wenn Du den Fehler digital korrigieren willst, dann mußt Du den
anderen Weg gehen: nach einer größeren Anzahl von Zählerumrundungen
wird mal in einer Runde die Zählweite verändert.  Damit ,,tickt''
die
Uhr dann zwar nicht mehr gleichmäßig, aber das stört in aller Regel
nicht weiter.
Gast #100070
Ok, für alle die es interessiert und denen es gleich wie mir geht (und
man erstmal stundenlang im Forum nach passenden Antworten wühlt) ist
hier mein Timer, der bislang ohne Verzögerung auf meinem ATmega16 mit
8Mhz-Quarz läuft:

SIGNAL (SIG_OUTPUT_COMPARE2) {
  static uint16_t lc=0;
  lc++;

  // bis tausend zählen, da wir ja 1ms hatten
  if (lc >= 1000) {
    // hier Code einfügen, was jede Sekunde passieren soll
    lc = 0;
  }
}

void init_timer (void) {
  TCCR2 = _BV (CS22) | _BV (WGM21); // Prescaler von 64, weil 8000000 /
64 = 125 kHz.
  OCR2 = 124; // 125 - 1; Dann kommt genau 1 kHz, also 1 ms raus
  TIMSK = _BV (OCIE2); // Compare setzen
  sei ();
}

Grüßle und viel Erfolg (in der Main-Prozedur einfach init_timer
aufrufen und schon ist das Ding fertig)

Markus

Antwort schreiben

Eine eigene Frage? Dieser Thread ist über 22 Jahre alt. Stell sie in einem eigenen Thread – dort lesen mehr Leute mit, und sie wird öfter und schneller beantwortet.

Neuen Thread starten

Eine Lösung, Ergänzung oder Rückmeldung zu diesem Thread? Dann antworte hier.

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.