Hi, ich verwende einen MEGA8535. Der Timer Interrupt sieht bei mir so aus: // Timer 0 overflow interrupt service routine interrupt [TIM1_OVF] void timer1_ovf_isr(void) { if (zehntelsek < 9) ++zehntelsek; else { zehntelsek = 0; if (sekunden < 59) ++sekunden; else { sekunden = 0; if (minuten < 59) ++minuten; else { minuten = 0; if (stunden < 23) ++stunden; else { stunden = 0; if (wochentag < 6) ++wochentag; else wochentag = 0; if ((monat == 1)||(monat == 3)||(monat == 5)|| (monat == 7)||(monat == 8)||(monat == 10)) { if (tag < 31) ++tag; else { tag = 1; ++monat; } } else if (monat == 12) { if (tag < 31) ++tag; else { tag = 1; monat = 1; ++jahr; } } else if (monat == 2) { if (((jahr%4==0) && (jahr%100!=0)) || (jahr%400==0)) { if (tag < 29) ++tag; else { tag = 1; ++monat; } } else { if (tag < 28) ++tag; else { tag = 1; ++monat; } } } else { if (tag < 30) ++tag; else { tag = 1; ++monat; } } } } } } TCNT1H=0xCF; TCNT1L=0x2C; } void main(void) { TCCR1A=0x00; TCCR1B=0x03; TCNT1H=0xCF; TCNT1L=0x2C; OCR1AH=0x00; OCR1AL=0x00; OCR1BH=0x00; OCR1BL=0x00; TIMSK=0x04; #asm("sei") while (1)# { Ausgabe.... } } Mein Board ist mit einem 8Mhz Quarz bestückt. Wie kann ich meine Uhr genauer machen, ohne dass ich einen anderen Timer Interrupt verwenden muss?
Hi, also ich würde in der ISR nur ein Flag setzen, und die Auswertung nicht in der ISR ausführen, sondern mittels einer Funktion die aus der main-loop aufgerufen wird. Denn es werden vorm Reloaden der Timerregister alle Befehle abgearbeitet, was Zeit kostet und die Uhr ungenau macht. Oder du zählst die verbrauchten Takte in der ISR für die Abfrage und ziehst diese von den Reloadwerten ab. Grüße, Der2te_Michael
Jede Codezeile benötigt Zeit für die Ausführung !!! Deshalb muß, einen Timer zu setzen nach einer rein willkürlichen Ausführungszeit, zwangsläufig ungenau sein. Hinzukommen noch bei größeren Programmen Verzögerungen durch andere Interrupts. Und genau deshalb haben die AVR-Entwickler die "Clear on Compare Match" Funktion eingebaut. Nutze sie ! Außerdem sind MC-Quarze in der Regel etwas schneller als die Nennfrequenz, d.h. entweder genau messen oder ne Weile laufen lassen, ausstoppen und dann den Comparewert korrigieren. Peter
ich wuerde evtl auch hin gehen und einen 2ten externen quarz nehmen, wenn es eine uhr werden soll,, ich hatte sowas mal in asm gemacht mit einem LCD Display.. im anhang findest du das ASM programm etwas kommentiert nur achte bitte nicht darauf, wie ich die bits gesetzt habe war meuin erstes ASM programm seit 5 jahren ;) Gruss Jens
okok bevor ihr mich nun schlagen tut =) ist auch fast alles im int handler :( aber genau lief sie.. 3 tage laufen lassen mit dem gong einer uhr gestartet und immer mit dem gong auf 0:0:0 gewesen;)
Wie und wo muss ich in meiner oben aufgeführten Interrupt Routine was verändern?
"bevor ihr mich nun schlagen tut =)" Ja wollen wir. Was sollen wir denn mit einem Hex-File anfangen ? Peter
@Christoph: TCNT1H=0xCF; TCNT1L=0x2C; Diese zwei Teile DIREKT als ERSTES in der Interrupt-Routine aufrufen. Dann wird der Timer IMMER mit einer definierten und gleichbleibenden Verzögerung neu gesetzt. Zum Abgleich dann TCNT1L tunen. Gruss Jadeclaw.
Etwas grundsätzliches zu der Interrupt-Routine: da die Bedingungen, die abgearbeitet werden müssen, nicht konstant sind, fütter den Timer am Anfang der Routine mit den Reload-Werten. Und bei 10 Einsprüngen in der Sekunde soll die Abarbeitungsgeschwindigkeit schon reichen. Eleganzer ist aber auf alle Fälle Peters Vorschlag mit dem Clear on Compare. Wenn die Hardware es anbietet, warum nicht nutzen? Und im Assemblerlisting nachschauen, wieviel Pops und Pushes in der Routine vom Compiler eingesetzt werden, genauso die 4 (?) Takte, die der Controller braucht, um die Routine anzusprechen. MW
Besser noch ist es die Autoreload-funktion des Timers zu nutzen, als den wert "händisch" in der Int-routine zu setzen. Bis der erste Codeteil der Introutine ausgeführt wird, vergehen immerhin 40 microsekunden.
ooooooooopSS verzeihung =) eins zu tief geklickt hier nun die ASM datei
Zu dem Quellcode hab ich ne Frage bezüglich der Interrupt Vektoren: Ist es nicht so, das ein reti ein ein-byte-Befehl ist und ein rjmp ein zwei-byte-befehlt? Ist nicht demnach die Vektortabelle absolut korrupt?
verwchselst du nicht zwichen 1 takt und 2 takt befehlen?? bei einem 8 BIT µC wird es kaum mehr als 256 Befehle geben, darum ist jeder befehl 1 bit lang womit sollte man sonnst aus der tabelle herrauskommen?? da steht nur mach das dahin die tabelle ist dafuer ausgelegt schau dir mal hier das tutorial einfach an ;) Gruss Jens
Während der reti-Befehl ohne parameter auskommt, ist er nur ein Byte lang. Der rjmp-befehl braucht aber ein parameter, nämlich die sprungweite. Daher besteht ein rjmp nun aus dem Machinencode für "rjmp" + Parameter Sprungweite, ein weiteres Byte. Macht zwei byte. So wars zumindest bei den 8031/51ern.... Ich konnte mir meine Frage schon zwischenzeitlich selbst beantworten. Bei den AVR RISC ist jeder befehl+eventueller parameter IMMER 16bit breit. Auch ein reti. Von daher ist die Vektortabelle auch valide.
Einfachste Variante für eine genaue Uhr: Timer-Prescaler so einstellen, daß sich aus Quarzfrequenz dividiert durch Prescaler eine ganze Zahl ohne Kommastellen ergibt. Dann das Timer-Compare Register mit einem möglichst hohen Wert laden, durch den die zuvor erhaltene Zahl dividiert, wieder zu eine ganze Zahl ergibt. Dann den Timer auf "Clear on Compare match" (CTC-mode) einstellen und den Interrupt bei compare match auslösen. In dem Interrupt dann einen Zähler incrementieren und auf die Restzahl hin vergleichen und wenn sie erreicht ist, den Zähler zurücksetzen und so weiter. So erhält man eine sehr genaue Sekunde, nur abhängig von der Quarzgenauigkeit und völlig unbeeindruckt vom Code.
ich hab gedacht der timer läuft über -> interrupt -> timer läuft aber gleich wieder weiter (verwende allerdings kein AVR)
Nein, kein Autoreload. Und das retten der register per push und pop ist IMO auch nicht ganz korrekt platziert....
Hallo, ich habe ein Beispiel gesehen von Peter Danneger: SIGNAL (SIG_OUTPUT_COMPARE1A) { /*********************************************************************** */ /* Insert Key Debouncing Here */ /*********************************************************************** */ #if XTAL % DEBOUNCE // bei rest OCR1A = XTAL / DEBOUNCE - 1; // compare DEBOUNCE - 1 times #endif if( --prescaler == 0 ){ prescaler = (uchar)DEBOUNCE; second++; // exact one second over #if XTAL % DEBOUNCE // handle remainder OCR1A = XTAL / DEBOUNCE + XTAL % DEBOUNCE - 1; // compare once per second #endif } } Diesen Code habe ich an meine Applikation angepasst. Dabei habe ich die If und Endif anweisungen entfernt. Stimmt dann dieser Code so: SIGNAL (SIG_OUTPUT_COMPARE1A) { /*********************************************************************** */ /* Insert Key Debouncing Here */ /*********************************************************************** */ #if XTAL % DEBOUNCE // bei rest OCR1A = XTAL / DEBOUNCE - 1; // compare DEBOUNCE - 1 times #endif if( --prescaler == 0 ){ prescaler = (uchar)DEBOUNCE; second++; // exact one second over #if XTAL % DEBOUNCE // handle remainder OCR1A = XTAL / DEBOUNCE + XTAL % DEBOUNCE - 1; // compare once per second #endif } }
Sorry mein Code sieht dann so aus: interrupt [TIM1_COMPA] void timer1_compa_isr(void) { if (XTAL % DEBOUNCE) // bei rest OCR1A = XTAL / DEBOUNCE - 1; // compare DEBOUNCE - 1 times if( --prescaler == 0 ) { prescaler = DEBOUNCE; sekunde++; Uhrzeit(sekunde); // exact one second over if(XTAL % DEBOUNCE) // handle remainder OCR1A = XTAL / DEBOUNCE + XTAL % DEBOUNCE - 1; // compare once per second } }
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.