Interrupt-Pointer verbiegen für Zustandsautomat

OP Persönliche Seite #3161670
Lesenswert?
• ▲
▼
Hallo zusammen,

ich möchte einen Zustandsautomaten für das 1Wire-Protokoll 
programmieren. Er soll über Timer-Interrupts "im Hintergrund" laufen.

Klassisch würde man in der Interrupt-Service-Routine (ISR) bei jedem 
Interrupt den Zustand des Zustandsautomaten in einer 
switch-case-Anweisung herausfinden.

Der eigentliche Code ist aber oft nur ganz kurz:
- Setzte den neuen Timer-Wert
- Setze ein Bit
- Setze den nächsten Zustand

Damit die ISR so schnell wie möglich abgearbeitet wird, würde ich gern 
auf die (verhältnismäßig) umfangreiche switch-case-Anweisung verzichten 
und bei "Setze den nächsten Zustand" einfach den Interrupt-Pointer - je 
nach Zustand - verbiegen.

Ist das eine Schnapsidee oder gar üblich? Ich habe mit Google nix dazu 
gefunden.

Mich interessiert allgemein, wie das geht. Speziell möchte ich es 
erstmal mit dem MSP430 versuchen. Hier liegen die Vektoren m.E. im Flash 
und können zur Laufzeit nicht geändert werden, oder?

Es gibt im Netz ein Beispiel, wie man Code ins RAM kopiert, dort könnte 
man dann die Adressen überschreiben.

http://e2e.ti.com/support/microcontrollers/msp430/f/166/p/199206/710443.aspx

Alternativ könnte man versuchen, mit Funktions-Pointern in C zu 
arbeiten, aber meine ersten Versuche hat der Compiler nicht akzeptiert 
<<a value of type "int (*)(void)" cannot be used to initialize an entity 
of type "volatile int">>.

Bevor ich nun alles ausprobieree, wollte ich mal wissen, was Ihr meint, 
wie man das am besten macht.
Gast #3161687
Lesenswert?
• ▲
▼
Torsten C. schrieb:

> Klassisch würde man in der Interrupt-Service-Routine (ISR) bei jedem
> Interrupt den Zustand des Zustandsautomaten in einer
> switch-case-Anweisung herausfinden.
[...]
> Damit die ISR so schnell wie möglich abgearbeitet wird, würde ich gern
> auf die (verhältnismäßig) umfangreiche switch-case-Anweisung verzichten
> und bei "Setze den nächsten Zustand" einfach den Interrupt-Pointer - je
> nach Zustand - verbiegen.
>
> Ist das eine Schnapsidee oder gar üblich?

Ich würde mal sagen, das der Sinngehalt schlicht davon abhängt, wie 
umfangreich der Switch-Block ist. Sobald das "verbiegen" des 
Interruptvektors billiger wird, macht es Sinn, diesen Mechanismus auch 
zu benutzen. Ist doch logisch.

Allerdings: Nur in Assembler sieht man ohne Probleme, was billiger ist.
#3161736
Lesenswert?
• ▲
▼
Wie kommst Du darauf, daß ein Switch-Case teuer sein muß?
Hast Du Dir den erzeugten Assembler schonmal angesehen?
Überlaß derartige Optimierungen mal besser dem Compiler.

Was dagegen teuer ist, sind Funktionspointer in Interrupts.
Der Compiler muß dann sämtliche Scratchpadregister sichern, da er nicht 
weiß, welche durch den indirekten Aufruf zerstört werden. Vielleicht muß 
er auch erst nen großen Stackframe anlegen.
OP Persönliche Seite #3162698
Lesenswert?
• ▲
▼
c-hater schrieb:
> Allerdings: Nur in Assembler sieht man ohne Probleme, was billiger ist.

Hmmm, OK, ich habe in Assembler nachgeschaut:
Switch Case: 12 Takte + 4 weitere für jedes weitere "case"
Call + Return: 7 Takte

Also eindeutig Funktionszeiger.

amateur schrieb:
> Es ist überhaupt kein Problem …

Danke für's Mut machen. :-) Ich hab's probiert, es geht! Die LED an 
PORT1.0 blinkt, die CPU ist aus (Low-Power-Mode) und die StateMachine 
toggelt im Hintergrund:

1
#include <msp430.h>
2
void SMState_1(void);
3
void SMState_2(void);
4
void(*StateFunc)(void) = &SMState_1;
5
int main(void) {
6
  // Initialisierung übersprungen
7
  _BIS_SR(GIE); // Low-Power-Mode mit Interrupts
8
  LPM0;
9
}
10
void SMState_1(void) {
11
  CCR0 = 0x4321;
12
  P1OUT |= BIT0;
13
  StateFunc = &SMState_2;
14
}
15
void SMState_2(void) {
16
  CCR0 = 0x1234;
17
  P1OUT &= ~BIT0;
18
  StateFunc = &SMState_1;
19
}
20
#pragma vector=TIMER0_A0_VECTOR
21
__interrupt void Port_1(void) {
22
  //StateFunc(); // das wären 30 Takte mehr
23
  asm("        CALL      &StateFunc+0          ;");
24
}

Mit dem asm(" CALL &StateFunc+0") verhindere ich, dass der Compiler alle 
Register auf dem Stack zwischenspeichert.

amateur schrieb:
> … die Funktionszeiger in einem Array
> abzulegen und später über ihren Index aufzurufen.

Darin sehe ich gegenüber dem o.g. Beispiel keinen Zusatz-Nutzen.

Jetzt werde ich mich mal an das 1Wire-Protokoll machen.

Das wird wahrscheinlich besser als bei I²C, wo ich in einer dämlichen 
while-Schleife auf das Löschen des Start-Bits warte, um das Stop-Bit zu 
setzen.

Antwort schreiben

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.

Formatierung
  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] – Link zu einem Artikel
  • Verweis auf einen anderen Beitrag: Rechtsklick auf dessen Beitrags-ID (#8078628), „Adresse kopieren“, in den Text einfügen

Mehr zur Formatierung

Längeren Sourcecode als Anhang, nicht in den Text. JPG nur für Fotos und Scans; Zeichnungen und Screenshots als PNG oder GIF. Siehe Bildformate.

Schon registriert? Anmelden

Wird nicht angezeigt. Wir schicken dir einen Link; sobald du damit bestätigst, erscheint dein Beitrag.

Mit dem Abschicken legst du einen kostenlosen Account an und erkennst die Nutzungsbedingungen und die Datenschutzerklärung an.