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
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
hi es gibt Programme, die sich fast nur in Interrupt´s abspielen einfach Testpin toggeln und mit Oszi schauen, wie lang es dauert
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...
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
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
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.