Hallo zusammen, ich habe leider ein "kleines" Problem mit der "genauen Sekunde". Habe diese aus dem WIKI rauskopiert, bekomme es aber leider im AVR-Simulator nicht gescheit zum laufen. Der PORTB sollte eigentlich die Sekunde im Bin-Wert ausgeben, aber wie es scheint, wird der Timer-IRQ nur einmal ausgeführt, denn wenn ich dort den PORTB auf 0xFF setze, ist dieser auch HIGH. Habe ich irgendwo einen Fatalen Fehler gemacht? Danke für eure Hilfe!
Also ich habe es immer so gemacht:
1 | #include <avr/io.h> |
2 | #include <avr/interrupt.h> |
3 | #include <util/delay.h> |
4 | |
5 | #define TIMER_VALUE 34286 /* Bei 16MHz und prescaler von 256 => 500ms |
6 | Int. */
|
7 | |
8 | ISR(TIMER1_OVF_vect){ |
9 | timercount++; |
10 | TCNT1 = TIMER_VALUE; |
11 | /* Hier könntest du dann was ausgeben */
|
12 | }
|
13 | |
14 | int main(void){ |
15 | |
16 | TCCR1A = 0x00; /* kein PWM usw. */ |
17 | TCCR1B |= (4<<CS10); /* Prescaler = 256 */ |
18 | TCNT1 = TIMER_VALUE; /* => erzeugt alle 500ms nen Int. */ |
19 | TIMSK |= (1 << TOIE1); |
20 | sei(); |
21 | while(1); |
22 | }
|
Die Overflow-Methode sollte man vermeiden, wenn immer möglich. Sie ist prinzipbedingt mit Unsicherheiten und Jitter durch die Interruptlatenz behaftet. Bis auf ganz wenige Ausnahmen hat der Timer 1 doch überall einen CTC-Modus, den man benutzen kann. (Bei neueren AVRs hat Timer 0 diesen Modus auch.)
Mhh, wusste ich nicht. Aber was sollte der Comparemodus daran ändern? Der Zähler ist der selbe, Takt ist auch gleich. Und wenn ich das mal so über nen Tag messe, kann ich eigentlich keine Ungenauigkeit feststellen.
> Aber was sollte der Comparemodus daran ändern?
Dass das Rücksetzen des Zählers in der Hardware erledigt
wird und damit unabhängig von der Interruptlatenz erfolgt.
Einen Interrupt kannst du dir ja dennoch generieren lassen
(damit du auch erfährst, dass jetzt wieder Zeit vergangen
ist), aber die Latenz in der Interruptannahme spielt dann
keine Rolle mehr; dein Programm erfährt zwar u. U. erst etwas
später, dass es jetzt später geworden ist, der Zähler hat es
aber schon vorher erfahren und kann weiterzählen.
im Prinzip hat Jörg mit den Latenzzeiten schon recht, aber man sollte auch die Auswirkungen auf die Applikation betrachten. In diesem Beispiel würde der Fehler 365 24 60 60 62,5 ns = +1,97 Sek pro Jahr ausmachen (unter der Annahme dass der int immer bei 2-zyklischen Befehlen zuschlägt, statistisch wird er auch etliche 1-zyklische erwischen und der Fehler wird kleiner). Steht kein CTC Mode zur Verfügung und der Prescaler ist groß genug könnte auch ein TCNT = TCNT + VALUE helfen, dann muß man nur aufpassen dass man das "Prescaler-Fenster" erwischt, ab pres > 1:16 sollte das aber kein Problem sein. Wenn es auf absolute Genauigkeit ankommt könnte man auch einen "Vorinterrupt" generieren der GIE per SW enabled und eine Folge von nops startet, dann unterbricht der echte Interrupt jedenfalls einen 1-zyklischen Befehl und der Fehler der Latenzzeit reduziert sich auf -0 bis +1 Taktzyklus. Eine weitere Möglichkeit wäre den up schlafen zu lassen, dann ist die Latenzzeit ebenfalls konstant. grüße leo9
@leo9, die Interruptlatenz ist vielleicht nicht so erheblich, aber man hat ja noch andere Interuptquellen oder Programmabschnitte unter Interruptsperre und dann gibt es schon erhebliche Verzögerungen. Der AVR hat ja keine Prioritäten, wenn also ein anderer Interrupt z.B. 1ms dauert, wird Deine RTC-Routine so lange verzögert. Peter
Nicht nur im Prinzip hat Jörg recht gewöhnlich sieht die ISR Routine so aus (volatile unsigned char timercount;) ISR(TIMER1_OVF_vect){ timercount++; //tue evt. noch was TCNT1 = TIMER_VALUE; } da kommen noch 4 Takte eintritt in den Interrupt hinzu + push der benötigten Register ca. x*3 Takte + code bis TCNTx = Timervalue =x Takte wenn man die Suchfunktion dieses Forums benutzt kann man die Auswirkungen hervoragend nachvollziehen. Dabei steht schon im Datenblatt der Hinweis.
Danke für die Zahlreichen Hinweise... Ich dachte eiegntlich dass die Lösung die ich verwendet habe In Ordnung sei, da diese aus einem Articel von hier stammt: http://www.mikrocontroller.net/articles/AVR_-_Die_genaue_Sekunde_/_RTC Evt. sollte diesen jemand überarbeiten, und die hier Diskutieren Anhaltspunkte miteinbeziehen.
@Manuel, die Kommentare beziehen sich nur auf den Code von Timmo. Dein Code benutzt ja das Compareregister. Warum er bei Dir nicht funktioniert, könnte daran liegen, daß Du den falschen AVR-Typ angegeben hast, dann zeigen die Interrupts in den Wald. Peter
Hi Peter, danke für die Info. Also ich verwende einen Mega8 und habe diesen auch im Simulator ausgewählt. Der Code sollte wie er ist ja auf einem Mega8 laufen, oder liege ich da falsch?
Hallo nochmals, also die Interrupt Vektoren für den Mega8 stimmen mit denen in dem Code Sample überein. Die initialisierung der Register und Compare-Werte stammen ja aus dem Artikel (s.O.), sollten somit auch stimmen. Ist dies evt. ein Problem mit dem Simulator aus dem AVR-Studio?
Zum Simulator kann ich nichts sagen, ich teste immer im real-life, z.B. aufm STK500. Peter
Eigenartig... Evt. mach ich da ja irgendwo einen fatalen Fehler!? Ich bin bisher einfach ins AVR-Studio und dann habe ich direkt über File -> Open File das Hex-File geladen. Danach kann man Ihm ja dann noch den AVR Typ angeben, da habe ich natürlich den Mega8 ausgewählt. Und dann gehts ja schon los. Im I/O View Window sehe ich auch, dass der DDRB usw. richtig gesetzt wird und auch der Timer schön hoch zählt. Ich habe gerade noch auf die Version 4.12 mit SP2 geupdatet.
Hey, es scheint nun doch zu funktioneren. Sorry für die Umstände! Werde es demnächst noch in Real-Life testen..
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.