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;)
@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.
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.
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
}
}