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
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).
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?
RTFM. Oder: http://www.catb.org/~esr/faqs/smart-questions.html oder auf deutsch: http://www.lugbz.org/documents/smart-questions_de.html
;-) 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
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 (); }
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.
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?
Ich hab gemerkt, der Compiler (Präprozessor) ist besser im Kopfrechnen als ich. Deshalb lasse ich einfach ihn die richtigen Teilerfaktoren berechnen: http://www.mikrocontroller.net/forum/read-4-57760.html Peter
> 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.
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
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.