mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik AVR: Timer-Interrupt Frage


Autor: Andreas (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: Hannes Lux (hannes)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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...

Autor: Andreas (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: Mark Hämmerling (haemi)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: Andreas (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: Andi K. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: Andreas (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: Hannes Lux (hannes)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

...

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.