C: Interrupt Routine on the fly tauschen

Gast #363868
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....
Moderator Persönliche Seite #363869
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.
Moderator Persönliche Seite #363870
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
}
Gast #363871
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
Gast #363872
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.
Moderator Persönliche Seite #363873
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).

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren