www.mikrocontroller.net

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


Autor: Mark Schau (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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....

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
#include <avr/io.h>
#include <avr/interrupt.h>

void (*funcptr)(void);

ISR(INT0_vect)
{
        funcptr();
}

void vect0(void)
{
        PORTB = 0;
}

void vect1(void)
{
        PORTB = 0xFF;
}

int
main(void)
{
        funcptr = vect0;
        // initialize interrupts here

        for (;;) {
                if (funcptr == vect0)
                        funcptr = vect1;
                else
                        funcptr = vect0;
        }
        return 42;
}

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.

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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.
#include <avr/io.h>
#include <avr/interrupt.h>

void (*funcptr)(void);

void INT0_vect(void) __attribute__((naked));
void INT0_vect(void)
{
        asm volatile("push r30" "\n\t"
                     "push r31");
        funcptr();
        asm volatile("pop r31" "\n\t"
                     "pop r30");
}

ISR(vect0)
{
        PORTB = 0;
}

ISR(vect1)
{
        PORTB = 0xFF;
}

int
main(void)
{
        funcptr = vect0;
        // initialize interrupts here

        for (;;) {
                if (funcptr == vect0)
                        funcptr = vect1;
                else
                        funcptr = vect0;
        }
        return 42;
}

Autor: Detlef A (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Mark Schau (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.