Forum: Compiler & IDEs [AVR][GCC] Zwischen verschiedenen Interrupt Routinen umschalten.


von Markus S. (bastetfur)


Lesenswert?

Mahlzeit Forum!

Wie kann ich zwischen mehreren INTs umschalten?

Sprich, ich will zwischen mehreren von dieser Sorte umschalten können:
1
ISR (TIMER1_COMPA_vect){...}

von Andreas Paulin (Gast)


Lesenswert?

Kannst Du genauer beschreiben, was Du meinst?

von Philipp H. (swissrookie)


Lesenswert?

Willst du verschiedene Zeiten für COMPA haben oder verschiedene Aktionen 
in der ISR ausführen?

von Markus S. (bastetfur)


Lesenswert?

Wenn ich jetzt irgendetwas Zeitkritisches habe will ich nicht erst in 
der INT Routine entscheiden was ich genau will.
Also soll die INT Routine an ihrem Ende prüfen ob sie selber oder einer 
ihrer "Freunde" als nächstes dran kommt und dafür dann den INT Vektor 
passend verbiegen.

Wenn das nur in Assembler geht soll mir das auch recht sein. :)

von Peter (Gast)


Lesenswert?

auch in Assembler wird es nicht gehen, weil der adresse ja fest im Flash 
steht, du könntest natürlich das Programm zu laufzeit anpassen aber das 
macht wenig sinn.
du musst immer über eine extra Sprungtabelle gehen die im Ram liegt.

bei C z.b. über Funktionszeiger.

von Markus S. (bastetfur)


Lesenswert?

Hat der AVR kein Register als Zeiger auf die INT Routine?

Also quasi sowas:
1
;6502 code vorraus ;)
2
naechsterIRQTabLo:
3
!byte <IRQ_Eins
4
!byte <IRQ_Zwei
5
!byte <IRQ_Drei
6
naechsterIRQTabHi:
7
!byte >IRQ_Eins
8
!byte >IRQ_Zwei
9
!byte >IRQ_Drei
10
11
IRQ_Eins:
12
;Register sichern...
13
;irgendetwas....
14
jmp IRQ_Ende
15
IRQ_Zwei:
16
;Register sichern...
17
;irgendetwas anderes....
18
jmp IRQ_Ende
19
IRQ_Drei:
20
;Register sichern...
21
;irgendetwas noch anderes....
22
;jmp IRQ_Ende ;Hier flüssig....
23
24
IRQ_Ende:
25
ldx #naechsterIRQ
26
lda naechsterIRQTabLo,x
27
sta $fffe ;6502 IRQ Vektor
28
lda naechsterIRQTabHi,x
29
sta $ffff ;6502 IRQ Vektor
30
;Register wiederherstellen...
31
rti

von Klaus (Gast)


Lesenswert?

nein, hat er nicht. Die Interrupt Tabelle steht im Flash. Du kommst um 
eine Auswertung der nötigen Aktionen innerhalb der Interruptroutine 
meiner Meinung nach nicht drumherum.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Funktionszeiger kann man nehmen, aber der Aufruf externer Funktionen
führt dazu, dass der Compiler alle Register sichern muss, die laut
ABI durch den Aufruf zerstört werden könnten, da der Compiler dann
keine Möglichkeit mehr hat zu testen, was die Funktionen wirklich
benötigen.  (Effektiv hat er diese Möglichkeit eigentlich nur bei
Funktionen, die "static" deklariert sind.)

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Markus Stehr wrote:

> Also soll die INT Routine an ihrem Ende prüfen ob sie selber oder einer
> ihrer "Freunde" als nächstes dran kommt und dafür dann den INT Vektor
> passend verbiegen.

Sowas mache ich in einer Timer-ISR, die lange dauert weil dort Sachen 
erledigt werden müssen, die nicht in der Hauptschleife erledigt werden 
können weil das alles echtzeitig sein muss. Die Hauptschleife macht 
irgendwelche komplexen Berechnungen, aber die Aktion in der ISR muss 
innerhalt von ca. 100-150 Ticks erfolgen. Damit ist das Setzen eines 
Merkers und Abarbeitung der Aktion in der Hauptschleife obsolet:

Am Ende der ISR wird überprüft, ob das IRQ-Flag, das für die ISR 
triggert, gesetzt ist. Falls nein, wird die ISR ganz normal verlassen. 
Falls ja, wird das Flag von Hand gelöscht und zurückgesprungen an den 
Anfang der ISR.

Dadurch spare ich die Zeit eines ISR-Frames (Prolog+Epilog) von ca. 100 
Ticks, was in der Anwendung mächtig viel Zeit ist (die IRQs kommen alle 
500 Ticks, und IRQs können kaskadieren).

Falls in einer ISR ein anderes IRQ-Flag als gesetzt erkannt wird, würd 
ich aber anraten, die ISR normal zu beenden und die andere IRQ triggern 
zu lassen.

Allerdings ist denkbar, auch das Zeug der anderen IRQ zu erledigen und 
auch deren Flag von Hand zurückzusetzen. Dadurch würde man evtl. auch 
ISR-Frames sparen; nicht nur was Zeit angeht, sondern auch Stack-Bedarf. 
Abzuchecken, welche IRQ-Flags gesetzt sind und die notwendige Aktion zu 
starten dürfte je nach Größe des ISR-Frames schneller gehen. Was das für 
den Code bedeutet (entweder static inline oder Funktionsaufrufe) muss 
man sich im einzelnen überlegen, denn die Aktion wird ja in mindestens 
zwei ISRs implementiert/benötigtund.

Johann

von Axel S. (a-za-z0-9)


Lesenswert?

Markus Stehr wrote:
> Wenn ich jetzt irgendetwas Zeitkritisches habe will ich nicht erst in
> der INT Routine entscheiden was ich genau will.
> Also soll die INT Routine an ihrem Ende prüfen ob sie selber oder einer
> ihrer "Freunde" als nächstes dran kommt und dafür dann den INT Vektor
> passend verbiegen.
>
> Wenn das nur in Assembler geht soll mir das auch recht sein. :)

Dann skizziere ich mal, wie ich das in Assembler lösen würde. Die echte 
(also über den Vektor verdrahtete) ISR ist nur ein Stub und holt sich 
die Adresse der gewünschten ISR aus dem RAM (besser: einem dezidierten 
Doppelregister) und springt sie entweder über IJMP an oder pusht sie auf 
den Stack und macht RET.

Ein paar Register müßte man schon im Stub retten und natürlich alle ISRs 
so auslegen, daß sie einen geeigneten Epilog verwenden.

Beispiel mit ISR Vektor im RAM:

.data
ISRVEC: .byte 0
        .byte 0

.text

.org Timer1_CompA_addr
         rjmp STUB
...

STUB:    push r16
         in r16, SREG
         push r16
         ;das war der minimale Prolog
         lds r16, ISRVEC
         push r16
         lds r16, ISRVEC+1
         push r16
         ;das ret ist ein indirekter Sprung
         ret

und am Ziel:

         ; ... dein Code ...
         pop r16
         out SREG, r16
         pop r16
         ;das war der Epilog
         reti

Mit ein paar Verrenkungen geht das sicher auch in C. Assembler dürfte 
aber einfacher sein.


XL

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Axel Schwenke wrote:

> Mit ein paar Verrenkungen geht das sicher auch in C. Assembler dürfte
> aber einfacher sein.

Nö, sowas geht auch in C gut.  Ist letztlich weiter nichts als eine
state machine, deren state in einem Funktionszeiger gehalten wird.

Der einzige Nachteil ist halt, dass bei C erst einmal sämtliche
Register beim Eintritt in die ISR gerettet werden, die vom ABI dafür
vorgeschrieben sind.  Aber wenn die implementierte Funktionalität
hinreichend komplex ist, dann machst du das am Ende auch bei einer
Assemblerprogrammierung so.  Die wäre nur dann günstiger, wenn alle
von der ISR gerufenen Routinen nur ganz wenig machen und daher auch
nur wenige Register retten müssen.

von Peter D. (peda)


Lesenswert?

Markus Stehr wrote:
> Wenn ich jetzt irgendetwas Zeitkritisches habe will ich nicht erst in
> der INT Routine entscheiden was ich genau will.
> Also soll die INT Routine an ihrem Ende prüfen ob sie selber oder einer
> ihrer "Freunde" als nächstes dran kommt und dafür dann den INT Vektor
> passend verbiegen.

Du vermutest Probleme, wo garkeine sind.

Nimm ne Switch-Anweisung und gut is.

Das Interruptgedöns (Aufruf, Prolog, Epilog) unter AVR-GCC dauert fast 
nie unter 50 Zyklen, da fallen <10 Zyklen für das Switch überhaupt nicht 
ins Gewicht.


Peter

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.