Hallo, für alle die Hardware nahes Programmieren in C gewohnt sind vmtl. eine Kleinigkeit... Ich möchte die Interrupt-Routine für einen Interrupt-Vektor im laufenden Program tauschen. Das sollte doch über Function pointer möglich sein. D.h. zwei Funktionen definieren und dann einfach die Adresse der Funktion, die gerade im Interrupt laufen soll an die entsprende Interrupt-Adresse kopieren. Aber wie schreibt man das in C? Ich bin dankbar für jeden Hinweis....
1 | #include <avr/io.h> |
2 | #include <avr/interrupt.h> |
3 | |
4 | void (*funcptr)(void); |
5 | |
6 | ISR(INT0_vect) |
7 | {
|
8 | funcptr(); |
9 | }
|
10 | |
11 | void vect0(void) |
12 | {
|
13 | PORTB = 0; |
14 | }
|
15 | |
16 | void vect1(void) |
17 | {
|
18 | PORTB = 0xFF; |
19 | }
|
20 | |
21 | int
|
22 | main(void) |
23 | {
|
24 | funcptr = vect0; |
25 | // initialize interrupts here
|
26 | |
27 | for (;;) { |
28 | if (funcptr == vect0) |
29 | funcptr = vect1; |
30 | else
|
31 | funcptr = vect0; |
32 | }
|
33 | return 42; |
34 | }
|
Sei aber gewarnt, dass der Aufruf einer Funktion aus einem Interruptvektor heraus den Compiler veranlasst, alle laut ABI caller-saveable Register auf den Stack zu retten. Wenn man dagegen eine ISR ohne weitere Calls implementiert, muss er nur die Register retten, die auch wirklich benutzt werden.
Mir fällt noch eine ziemlich rüde Variante ein, wie man das Retten der überflüssigen Register vermeiden könnte. Ist aber ein ziemlicher Hack, und ich bin mir gerade nicht im Klaren, ob das vorzeitige Freigeben der Interrupts (durch das RETI der indirekt gerufenen ISRs) vor dem Aufräumen des Stacks (pop r31, pop r30) nicht eventuell böse Seiteneffekte haben könnte. Die Benutzung von rr30 für den ICALL ist ja Pflicht, insofern ist die implizite Annahme, dass man genau diese beiden Register retten und rückspeichern muss, wohl gerechtfertigt.
1 | #include <avr/io.h> |
2 | #include <avr/interrupt.h> |
3 | |
4 | void (*funcptr)(void); |
5 | |
6 | void INT0_vect(void) __attribute__((naked)); |
7 | void INT0_vect(void) |
8 | {
|
9 | asm volatile("push r30" "\n\t" |
10 | "push r31"); |
11 | funcptr(); |
12 | asm volatile("pop r31" "\n\t" |
13 | "pop r30"); |
14 | }
|
15 | |
16 | ISR(vect0) |
17 | {
|
18 | PORTB = 0; |
19 | }
|
20 | |
21 | ISR(vect1) |
22 | {
|
23 | PORTB = 0xFF; |
24 | }
|
25 | |
26 | int
|
27 | main(void) |
28 | {
|
29 | funcptr = vect0; |
30 | // initialize interrupts here
|
31 | |
32 | for (;;) { |
33 | if (funcptr == vect0) |
34 | funcptr = vect1; |
35 | else
|
36 | funcptr = vect0; |
37 | }
|
38 | return 42; |
39 | }
|
Muß man nicht vor vor 'funcptr = vect1;' die Interrupts stilllegen, sonst kann einer auftreten wenn man gerade die Hälfte dieser Anweisung abgeerbeitet hat und dann geht der Sprung in den Wald ?! Cheers Detlef
Hallo Jörg, danke! Ein anderer "schmutziger" Trick wäre doch vielleicht, die zwei Interrupt-Routinen auf dem gewünschten und auf einem nicht benutzten Interrupt-Vektoren als ISR() zu definieren. Dann müsste der Compiler doch alle Register korrekt und optimal sichern. Wenn ich die Routinen dann tauschen möchte, müsste ich nur die Einsprung-Addresse der gewünschten ISR() an den richtigen Int-Vector kopieren. Leider weiss ich nicht, wie man sowas in C ausdrückt... Das: uint16_t tmp_16=_VECTOR(4); _VECTOR(4)=_VECTOR(6); funktioniert gerade nicht: rf_sim.c:137: error: `__vector_4' undeclared (first use in this function) Schön wäre es, wenn C einen dummy ISR prototypen kennen würde. Dann könnte man komfortable state-maschinen per Interrupt bauen. Bisher habe ich das mit switch() gemacht, das kostet aber einiges an Performance.
> Muß man nicht vor vor 'funcptr = vect1;' die Interrupts stilllegen, > sonst kann einer auftreten wenn man gerade die Hälfte dieser > Anweisung abgeerbeitet hat und dann geht der Sprung in den Wald?! Das ist wahr. Würde vermutlich für das einfache Beispiel funktionieren, weil die oberen 8 bits beider Adressen identisch sind, aber das sollte man wohl tun. > Wenn ich die Routinen dann tauschen möchte, müsste ich nur die > Einsprung-Addresse der gewünschten ISR() an den richtigen Int-Vector > kopieren. Die Interruptvektoren liegen im (Flash-)ROM, die kannst du also nicht einfach mal schnell überschreiben. Geht also nur sinnvoll mit einem ICALL, und das C-Äquivalent dafür ist eben der function pointer (der sich hier auch 1:1 in einen ICALL übersetzt).
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.