Forum: Mikrocontroller und Digitale Elektronik Länge Interrupt-Routine


von Jörg (Gast)


Lesenswert?

Hallo zusammen,

ich habe folgenden Programmcode als Interrupt-Routine für den Timer0
Overflow für einen Atmel AT90S2313 geschrieben.

Funktion ist einen 4 Byte Zähler hochzuzählen und die beiden
INT-Interupts getrennt für eine bestimmte Zeit zu verzögern (erst
wieder an wenn Zeit abgelaufen).

Der Timer ist so eingestellt, dass er jede 1/1000 sec aufgerufen wird.

Jetzt hab ich schon des öfteren gelesen, dass man Code in einer
Interupt-Routine so klein wie möglich halten sollte.

Meine Frage ist nun, ob der Code kurz genug ist bzw. ob ich Teile
anders programmieren muss (wenn ja wie?). Kann man Teile daraus
auslagern?

Was passiert eigentlich, wenn mein Programm in einer Interupt-Routine
arbeitet und ein Timer-Overflow-Interrupt wird ausgelöst?


OVF0:
  in  saveSREG, SREG

  out  TCNT0, timer  ; Startwert des Timers neu einstellen:

  inc  z1      ; Zähler1 erhöhen
  brne  step1      ; bei Überlauf
  inc  z2      ; Zähler2 erhöhen
  brne  step1      ; usw
  inc  z3
  brne  step1
  inc  z4

step1:
  tst  warte1      ; INT0 Verzögerung
  breq  step2      ; warte1 ist 0 -> step2
  dec  warte1      ; warte1--
  brne  step2      ; warte1 ist nicht 0 -> step2
  in  tmp, GIFR    ; lade GIFR
  sbr  tmp, (1<<INTF0)    ; lösche INTF0 Bit
  out  GIFR, tmp    ; schreibe GIFR
  in  tmp, GIMSK    ; lade GIMSK
  sbr  tmp, (1<<INT0)    ; setze INT0 Bit; IRQ0 einschalten
  out  GIMSK, tmp    ; schreibe GIMSK

step2:
  tst  warte2      ; INT1 Verzögerung
  breq  step3      ; warte2 ist 0 -> step3
  dec  warte2      ; warte2--
  brne  step3      ; warte2 ist nicht 0 -> step3
  in  tmp, GIFR    ; lade GIFR
  sbr  tmp, (1<<INTF1)    ; lösche INTF1 Bit
  out  GIFR, tmp    ; schreibe GIFR
  in  tmp, GIMSK    ; lade GIMSK
  sbr  tmp, (1<<INT1)    ; setze INT1 Bit; IRQ1 einschalten
  out  GIMSK, tmp    ; schreibe GIMSK

step3:
  out  SREG, saveSREG
reti

Für eure Hilfe wäre ich dankbar.

Gruß Jörg

von Florian Pfanner (Gast)


Lesenswert?

Hallo,

also ob die Interruptroutinen zu lange sind, oder nicht, hängt von
deinen anderen Programmteilen ab. Sie sollten halt so kurz sein, dass
diese das Hauptprogramm möglichst wenig beeinflussen.

Wenn der AVR in eine Interruptroutine Springt, wird automatisch das
I-Flag im SREG gelöscht. Also kann kein weiterer Interrupt ausgelöst
werden. Wird wärend der Zeit ein andere Interrupt-Flag gesetzt (z.B.
Ext Int0), so wird zuerst der 'alte' Interrupt abgearbeitet. Erst
danach wird der 'neue' Interrupt ausgeführt.
Damit sind wir wieder bei deiner ersten frage: Wenn solche Ereignisse
(Mehrere Interruptaufrufe gleichzeitig) öffters vorkommen, dann soltest
du wirklich prüfen, ob nicht doch eine Routine zu lange ist. Denn diese
behindert dann ja die Ausführung der anderen.

Auslagern ist z.B. dadurch möglich, dass in der Interruproutine nur
eine Variable gesetzt wird. Im Hauptprogramm wird dann, wenn der
Controller nichts wichtigeres zum tun hat, die Variable abgefragt und
dementsprechend Reagiert.

Gruß, Florian

von Peter Löschnig (Gast)


Lesenswert?

hi

es gibt Programme, die sich fast nur in Interrupt´s abspielen
einfach Testpin toggeln und mit Oszi schauen, wie lang es dauert

von ...HanneS... (Gast)


Lesenswert?

Hi Jörg...

Ist alles richtig, was Florian schreibt.

Trotzdem kann man (zusätzlich) noch anders an die Sache herangehen:

Anhand des Timer Vorteilers und Zählumfangs kannst du ausrechnen, alle
wieviel Takte dein Interrupt aufgerufen wird.

Durch Addieren der je Befehl benötigten Takte (oder per Simulator)
kannst du feststellen, wieviele Takte deine ISR brauch. Dazu addierst
du nochmal pauschal 10 Takte für Int-Auslösung, ISR-Aufruf und
Rücksprung.

Und nun kannst du Bilanz ziehen, du hast die Anzahl der Takte zwischen
zwei Timer-Int's und die Anzahl der Takte, die die Timer-ISR brauch.

Wenn noch andere zeitkritische Int's (z.B. ext.Int0) dazukommen, dann
sind alle ISR so kurz wie möglich zu halten. Dazu beschränkt man sich
in den ISRs auf das Nötigste und setzt ein Bit (Flag) in einem dafür
reservierten Register. Das Hauptprogramm fragt dann in einer Schleife
alle Flags ab und verzweigt zu den erforderlichen Programmteilen.

Bei deinem 4-Byte-Zähler würde die ISR dann nur das untere Byte erhöhen
und bei einem Überlauf ein Flag setzen. Nun hat das Hauptprogramm 255
ISR-Aufrufe Zeit, das Flag zu erkennen, die restlichen Bytes (abhängig
vom Übertrag) hochzuzählen und (wichtig!) das Flag wieder zu löschen,
damit es das nur einmal tut...

Wenn du für die Timer-ISR ein anderes (eigenes) Register zur
SREG-Sicherung nimmst, dann kannst du auch in der Timer-ISR mittels SEI
die anderen Interrupts erlauben. Diese unterbrechen dann die Timer-ISR
und springen dorthin zurück. Auch hier ist natürlich darauf zu achten,
dass noch genügend Rechenzeit für das Hauptprogramm übrig bleibt.

...HanneS...

von Florian Pfanner (Gast)


Lesenswert?

Vorsicht:
Bevor in einer Interruptroutine wieder die Interrupts freigegeben
werden, muss der Interrupt, welcher gerade abgearbeitet wird
deaktiviert werden. Denn so wird zum einen ein mehrfachaufruf der
gleichen Routine (was den Stack zum überlaufen bringt bzw. Wichtige
Register löschen kann....) verhindert und zum anderen wird z.B. der
UART-RX-Complete Interrupt erst gelöscht, wenn das Datenbyte aus dem
Empfangsregister gelesen wird. Wenn dann am Anfang der Routine wieder
die Interrups freigegeben werden, so wird dieser Interrupt sofort
wieder ausgeführt => Stacküberlauf, absturz.

Gruß, Florian

von Jörg (Gast)


Lesenswert?

Hallo,

Super, vielen Dank für die Antworten. Ich werde dann mal zählen und den
Tipp mit Flag setzen und diesen im Hauptprogramm auswerten umsetzen.

Gruß Jörg

von ...HanneS... (Gast)


Lesenswert?

Hi Florian...

Richtig...

Ich empfahl das auch nur für den Timer-Interrupt, und das auch nur,
wenn zwischen den Timer-Interrupts genügend Rechenzeit für andere
Programmteile bleibt. Ansonsten gibt es Kuddelmuddel.

Ich nutze diese Art in einigen Modellbau-Fernsteuer-Modulen mit Tiny15,
wo Timer0 die PWM-Motorsteuerung übernimmt, Ext-INT zur
Impulsbreitenmessung aber nicht warten darf (Messfehler). Weitere INTs
werden im Programm nicht genutzt.

Bei großen komplexen Programmen geht sowas nicht mehr...

...HanneS...

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
Noch kein Account? Hier anmelden.