Hi
Ansonsten würde ich mal schlicht behaupten, das deine LCD-Routinen im
Interrupt zu lang sind.
Noch etwas. Du schreibst:
>der mit einem externen 4MHz Quarz getaktet ist.
Im Programm steht aber:
> // ((1000000/8)/1000) = 125> OCR1A = 125-1;
MfG Spess
> Im Abgleich mit einigen anderen Uhren beträgt die Ungenauigkeit nach 5> Minuten schon fast 10 Sekunden.
Erstens wie spess schon frug: läufst Du mit externem (hinreichend
genauem!) Quarz/Takt/Oszi/..?
Und zweitens sehe ich das richtig dass Du die ganzen LCD-Updates in der
ISR machst? Wundert mich nicht dass da laufend 'millisekunden' verloren
gehen (Hint: bei 10 Sekunden Abweichung auf 5 Minuten und 1
Display-Update pro Sekunde sind das gerade mal 10 Sekunden Abweichung
auf 300 Updates oder etwa 33ms Abweichung pro Update - das ist für
Display-IO durchaus haltbar).
HTH
Danke für eure Antworten. Der externe Quarz läuft mit 4MHz und die Fuses
sind auch entsprechend eingestellt. Ich hatte auch mal 1MHz (interner
Quarz) angegeben, aber das resultierte in einem "Daten-Chaos".
Im Tutorial steht, dass in die ISR keine aufwendigen LCD Ansteuerungen
gehören, aber wie lagere ich die Aktionen so aus, dass das LCD jede
Sekunde refresht wird und auch die anderen Aktionen (Weckzeitvergleich,
Tastenabfrage) ausgeführt werden?
Edit: @Spess: Im Tutorial ist ein Quarz mit 3,xx MHz angegeben und der
Wert für OCR1A wurde genauso berechnet, wie in meinem Code. Wenn man
dort 4MHz einsetzt, bekommt man 500 als Wert für OCR1A.
Wenn ich morgen in der Werkstatt bin, probiere ich mal den anderen Wert
aus.
Lukas B. schrieb:> Im Tutorial steht, dass in die ISR keine aufwendigen LCD Ansteuerungen> gehören, aber wie lagere ich die Aktionen so aus, dass das LCD jede> Sekunde refresht wird und auch die anderen Aktionen (Weckzeitvergleich,> Tastenabfrage) ausgeführt werden?
In der ISR setzt du zur vollen Sekunde nur ein Flag. In der
Endlosschleife im Hauptprogramm wird dieses Flag geprüft und damit
Vergleiche und Ausgabe gesteuert. Parallel können dann aber schon per
ISR die ersten paar ms der neuen Sekunde gezählt werden, weil der IRQ
den Ablauf unterbrechen kann.
Könntest du das eventuell etwas konkretisiseren, denn ich kann mir nicht
vorstellen, wie ich das programmieren soll. Die Idee ist verständlich,
aber die Umsetzung...
Meinst du in etwa so etwas?
Den Teil mit dem hochzählen der Uhr lässt du im Interrupt, den Teil mit
der Ausgabe verlagerst du in die Hauptschleife nach main.
Damit die Hauptschleife weiß, dass es die Anzeige aktualisieren muss,
setzt du eine globale Variable ein.
Und noch was:
Mach deine Sekunden, Minten, Stunden und Tag Variable als uint8_t und
nicht als unsigned int. Du musst deinem AVR keine 16 Bit Arithmetik
aufzwingen, wenn deine Zahlen nie größer als 60 werden können. Dazu
reichen 8 Bit locker aus.
Das Hochzählen der Zeit in der ISR dauert ein paar Takte und ist
insofern nicht zeitkritisch. Du willst die Zeit aber in der ISR komplett
hochzählen, weil es dir nicht passieren darf, dass Sekunden nicht
gewertet werden, weil das Hauptprogramm gerade längere Zeit mit etwas
anderem beschäftigt ist. So läuft die Uhr unabhängig von allem anderen
in der ISR autonom und richtig. Was du dann weiter mit der Uhrzeit
machst, Ausgeben oder mit einer Weckzeit vergleichen beeinflusst die Uhr
selber nicht mehr. Wobei: Das Auswerten der WEckzeit könnte man noch mit
in die ISR packen. Denn auch das darf auf keinen Fall verloren gehen und
soll klarerweise auch dann passieren, wenn der Benutzer zb gerade den
Wochentag einstellt.
ISR sollen so kurz wie möglich sein und nur das notwendigste machen. Du
willst so schnell wie möglich wieder aus der ISR raus. Man muss aber
auch nicht päpstlicher als der Papst sein. Ein bischen was darf man
schon auch in der ISR machen. Vor allen Dingen dann, wenn es dich in
Schwierigkeiten bringen kann, wenn du es nicht tust. Das erhöhen einer
Uhrzeit gehört dazu. Die Anzeige aber nicht. Die Anzeige kannst du auch
woanders machen oder zb überhaupt unterlassen, weil der Benutzer gerade
die Weckzeit einstellt. Aber nur weil dein Benutzer die Weckzeit
einstellt, darf ja die Uhr nicht aufhören zu laufen.
Danke dir, das hilft mir sehr weiter.
Den Abgleich mit in die ISR zu packen, macht rein theoretisch keinen
Sinn, da der Anwender kaum kurz vor dem Auslösen des Wecksignals, die
Uhrzeit umstellen wird, es sei denn, er ist Schlafwandler. ;)
Wenn es zeitlich allerdings unkritisch ist, kann man es ja
sicherheitshalber machen.
Ich habe es jetzt mal ausprobiert, aber leider besteht immer noch
dasselbe Problem und die Ungenauigkeit ist sehr groß.
Mir ist aber aufgefallen, dass ich beim Anpassen der Register
irgendetwas falsch gemacht habe. Denn im Tutorial ist der Code für einen
Attiny2313 aufgeführt, allerdoings gab es hier beim Kompilieren Fehler,
solange man in den Projekt-Optionen AtMega8 angegeben hat.
Mein Code:
1
// Timer 1 konfigurieren
2
TCCR1A=(1<<WGM11);// CTC Modus
3
TCCR1B|=(1<<CS01);// Prescaler 8
4
// ((4000000/32)/1000) = 125
5
OCR1A=125-1;
6
7
// Compare Interrupt erlauben
8
TIMSK|=(1<<OCIE1A);
9
10
// Global Interrupts aktivieren
11
sei();
Tutorial:
1
TCCR0A=(1<<WGM01);// CTC Modus
2
TCCR0B|=(1<<CS01);// Prescaler 8
3
// ((1000000/8)/1000) = 125
4
OCR0A=125-1;
5
6
// Compare Interrupt erlauben
7
TIMSK|=(1<<OCIE0A);
8
9
// Global Interrupts aktivieren
10
sei();
Ich denke, da stimmt irgendetwas nicht, oder? Da der Timer 0 beim
Atmega8 kein CTC unterstützt habe ich die Register (versucht) an den
Atmega8 anzupassen.
Wenn ich in TCCR0A WGM01 setze, gibt mir der Compiler Fehler aus.
Lukas B. schrieb:> Da der Timer 0 beim> Atmega8 kein CTC unterstützt habe ich die Register (versucht) an den> Atmega8 anzupassen.
Offensichtlich aber ohne in das schon von Spess erwähnte Datenblatt zu
schauen. Denn statt des CTC hast du einen PWM Modus eingestellt.
PS: Und wo kommt in deiner Rechnung die 32 her?
Lukas B. schrieb:> Denn im Tutorial ist der Code für einen> Attiny2313 aufgeführt, allerdoings gab es hier beim Kompilieren Fehler,> solange man in den Projekt-Optionen AtMega8 angegeben hat.
Und wenn der Compiler keine Fehler anzeigt, heißt das noch lange nicht,
dass alles richtig ist. Guck mal, in welchem Register des ATmega8 der
Teilerfaktor für den Prescaler vom Timer0 festgelegt wird.
Genau das war ja mein Fehler, die Register stimmten vorne und hinten
nicht. Aber der Fehler ist ja behoben und auf 40 Minuten konnte ich
keine Abweichung zu meiner Referenzuhr wahrnehmen. Ich schließe daraus,
dass der Timer jetzt ordnungsgemäß im CTC-Modus mit korrektem Vorteiler
und Obergrenze für den Interrupt initialisiert ist.
Im Anhang ist der aktuelle Stand des Codes mit implemetierter
Tasterabfrage zu sehen. Ich kann jetzt über einen Taster den Alarm ein
und ausschalten und über einen weiteren zwischen fünf Modi (w_stunde;
w_minute; tag; stunde; minute) toggeln und die entsprechenden Variablen
mit zwei weiteren Tastern inkrementieren bzw. dekrementieren. Was noch
nicht funktioniert, ist der Auto-Repeat bei längerem Drücken.
Hallo nochmal,
ich bin jetzt fast komplett fertig und bei der Autorepeat-Funktion der
Taster ist eine Aktuelaisierungsrate von 1Hz doch etwas unkomfortabel.
Kann man die LCD Funktionen problemlos alle 100ms aufrufen oder läuft
die ISR dann wieder zu ungenau?
@ Lukas B. (Gast)
>Kann man die LCD Funktionen problemlos alle 100ms aufrufen
Sicher, aber
> oder läuft die ISR dann wieder zu ungenau?
Nö, denn die LCD-Sachen ruft man NICHT in der ISR auf sondern in der
Hauptschleife. Dort hat man (fast) alle Zeit der Welt. Hast du ja
mittlerweile richtig in deinem Programm.
MFG
Falk