Forum: Compiler & IDEs C: Interrupt Routine on the fly tauschen


von Mark Schau (Gast)


Lesenswert?

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....

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


Lesenswert?

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.

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


Lesenswert?

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
}

von Detlef A (Gast)


Lesenswert?

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

von Mark Schau (Gast)


Lesenswert?

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.

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


Lesenswert?

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