Hallo, kurze Frage zu einem Timer, der einen Interrupt auslösen soll, es aber nicht tut! Das kurze Programm ist aus einem Tutorial und soll auf dem STK500 im Sekundentakt die LEDs zu Leuchten bringen. Aber weder auf dem STK noch in der Simulation funktioniert das. Im Interrupt Handler befindet sich ein Zähler (counter = r17), der dekrementiert werden soll, aber das wird er nicht. Im Simulator wird dieses Register auf den Startwert 32 gesetzt und da bleibt er dann. Vielleicht kann jemand helfen. Ach so, noch eine, kurze Frage: was sollen die vielen reti Befehle im Initialisierungsteil für die Interrupthandler? Es ist doch nichts angesprungen worden, also muß auch nicht zurück gesprungen werden. Danke & Gruß, Andreas
Die RETIs haben schon ihren Sinn. Denn der erste Teil des Programmspeichers ist die Reset-/Interrupt-Sprungtabelle. Dabei wird die erste Zelle (Adresse 0) beim Reset aufgerufen, die 2. Zelle bei einem INT0-Ereignis usw... Nun hat aber jeder AVR eine etwas andere Hardware-Ausstattung. Daher ist diese Sprungtabelle für jedem AVR anders. In deinem Fall ist der benötigte Timer0-Interrupt an Adresse 6 (falls deine Tabelle stimmt, ich habe sie jetzt nicht mit dem Datenblatt verglichen). Das Ereignis "Timer0 läuft über" wird also von der Hardware erkannt und als Reaktion darauf wird der aktuelle Programmcounterstand auf Stack gelegt und Adresse 6 aufgerufen. Dort hat dann der Sprung zu stehen, der in deine ISR (Interrupt-Service-Routine) springt, in der du auf das Ereignis (Int) reagierst. RETI am Ende der ISR sorgt dafür, dass der Programmcounter vom Stack geholt wird und das Programm an der alten Stelle fortgesetzt wird. Es ist daher wichtig, dass die Interrupt-Sprungtabelle nicht durcheinander gerät. Eine einfache und übersichtliche Möglichkeit ist es, Dummy-Befehle zum Füllen der unbenutzten Speicherzellen zu benutzen. Dies ginge z.B. auch mit NOP. RETI hat aber den Vorteil, dass ein versehentlich ausgelöster Interrupt (Programmierfehler durch Missverstandenes Datenblatt) keinen Schaden anrichtet, sondern sauber beendet wird. Warum dein Programm nun nicht läuft, kann ich auf die Schnelle nicht erkennen. Ich kann aber spekulieren: - Benutzt du auch exakt den AVR-Typ, auf den die Int-Vektoren abgestimmt sind? (Wenn nicht, dann verpufft dein Int in irgendeinem RETI, weil die Vektoren nicht stimmen...) - Stimmt deine Include-Datei mit dem verwendeten AVR überein? (wenn nicht, dann stimmen diverse Konstanten nicht...) - Verstehst du den benutzten Programmcode? (wenn nicht, dann solltest du den Code analysieren und mit Hilfe des Datenblattes, des AVR-Instruction-Set und der Hilfe zum AVR-Assembler verstehen, ansonsten hast du schlechte Karten...) Bit- & Bytebruch... ...HanneS...
Hi, erstmal vielen Dank für die ausführliche Antwort. Tatsächlich stand der Einsprung in die Interrupt Service Routine an der falschen Stelle (an 7. statt an 8.). Leider funktioniert das trotzdem noch nicht. Der Timer wird mit einem Vorteiler von 1024 initialisiert, zusätzlich wird noch Register 17 mit dem Wert 32(dez) geladen. Dieses Register soll nun in der Interrupt Routine dekrementiert werden. Insgesammt soll das bei einem Takt von 4 MHz eine Verzögerung von einer Sek. ergeben. Das Problem ist wie vorher: der Simulator in AVR-Studio springt erst gar nicht in die Interrupt Routine. Auf der MCU funktioniert es übrigens auch nicht. Was ich noch merkwürdig finde, ist diese Service Routine selbst: --------- timer0: dec counter brne restart ldi counter, loops com leds out PORTB, leds restart: ldi temp, start out TCNT0, temp reti ---------- Hier wird counter (r17) also dekrementiert. Wenn r17 = 0, dann springe zu restart. Und jetzt das, was ich nicht verstehe: wenn r17 != 0 ist, wird zum nächsten Befehl nach brne gesprungen. Und hier wird counter wieder mit 32 (.equ loops =32) geladen. So kann der doch gar nicht dekrementiert werden, oder sehe ich das falsch? Mal abgesehen davon, daß diese Interruptbehandlung nie angesprungen wird. ;) Bin für alle Hinweise dankbar. Es geht mir nur um's Verständnis. Gruß, Andreas
Salve, falsch. "brne" verzweigt dann, wenn das Zero-Flag nicht gesetzt ist ("branch if not equal (zero)"). Das Zero-Flag wird bei "inc" und "dec" dann gesetzt, wenn das Ergebnis gleich Null ist. Also wird counter dann neu geladen, wenn er gleich Null war. Hoffe, das hat zur Klärung beigetragen. :) Mark
Hallo nochmal, ja, Deine Richtigstellung hat zur Klärung beigetragen. War ein blöder Fehler meinerseits. Das ganze funktioniert jetzt so, wie es soll. Allerdings nur auf dem Controller, nicht in der Simulation. Kann es sein, daß dort die Interrupts oder die Timer nicht richtig simuliert werden? Andreas
Klar geht das auch im Simulator. Habs gerade ausprobiert. Nur um das X-1000 Fache langsammer oder gar Millionen Fache im AutoStep-Mode. Setz mal zum Simulieren den Prescaler auf 64 (011) oder weniger, setze einen Breakpoint beim ersten Befehl in der ISR und klick auf Run oben in der Leiste. Dir ist schon klar, das man bei normaler Anwendung das Statusregister (SREG) und die verwendeten Register in der ISR am Anfang sichern und am Ende zurückholen sollte (Stack)? Versuche möglichst schnell, mit Variablen im SRAM zu arbeiten denn mit reservierte Register als "Variablenträger" kommt man nicht weit, abgesehen vom Tiny11/12/15 etc., die haben sowieso keinen RAM. MfG Andi
Hi Andi, auch Dir vielen Dank für die Tips! Mir ist bisher noch gar nicht aufgefallen, daß es im AVR-Studio auch sowas wie eine Stopuhr gibt. Und die läuft in Microsekunden. Kein Wunder, daß ich beim Simulieren des Programms keine Änderungen feststellen konnte. :) Was das Sichern der Register angeht, das habe ich schon öfter in diversen Programmen gesehen, aber warum ist das so wichtig? Der Stackpointer wird beim Sprung in die ISR doch sowieso gesichert und wenn ich darauf achte, keine Register doppelt zu verwenden, sollte es doch kein Problem geben, oder? Zu den Variablen: bei kleineren Programmen reichen eigentlich die Register. Und wenn ich mich nicht irre, muß bei Verwendung des SRAMs der Inhalt erst in die Register kopiert werden, wo die Werte dann manipuliert werden können, und anschließend muß zurück kopiert werden. Den Aufwand (zeitlich und auch sonst) würde ich nur machen, wenn die Register nicht reichen. Danke & Gruß, Andreas
Es geht um das Statusregister. Dieses enthält die Flags, die von Operationen und Vergleichen verändert werden und den bedingten Sprüngen (BRxx) als Bedingung dienen. Wenn nun das Hauptprogramm von einem Int in dem Moment unterbrochen wird, wo die Flags zwar verändert, aber noch nicht "ausgewertet" sind, dann verändert der Int diese Flags und das Hauptprogramm reagiert auf falsche Flags, springt also in die Irre (rechnet falsch?). Deshalb muss das SREG in der ISR gesichert und wiederhergestellt werden wenn die ISR Befehle enthält, die das SREG beeinflussen. Mit den Variablen in Registern bin ich auch noch etwas großzügig (solange die Register reichen), das kommt aber daher, dass ich mit dem AT90S1200 begann und auch oft mit Tiny12/15 hantiere. Inzwischen habe ich aber auch festgestellt, dass sich Programme leichter in (wiederverwendbare) Module aufteilen lassen, wenn man die Variablen konsequent im SRAM hält. Dafür habe ich aber auch gelernt, den Controller nicht unnötig in Warteschleifen zu schicken. Das holt die durch SRAM-Zugriffe vertrödelte Zeit mehrfach wieder raus. ...
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.