Forum: Mikrocontroller und Digitale Elektronik Wo genau unterbricht ein Interrupt das laufende Programm?


von Torsten S. (grizu)


Lesenswert?

Hallo,

wenn ein Interrupt das laufende Programm unterbricht, kann dies auch 
INNERHALB einer IF-Anweisung passieren?

Also zum Beispiel:
1
if (  (a < b)
2
   && (c < d)  // <-- Unterbrechung möglich???
3
   && (e < f)
4
   && (g < h)
5
   )
6
{
7
   .
8
   .
9
   .
10
}

Wenn ein Interrupt an der markierten Stelle möglich wäre, würden die 
ersten beiden Bedingungen also vor dem Interrupt auf wahr/falsch 
überprüft und die letzten beiden Bedingungen erst nach dem Interrupt.

Oder wird die IF-Anweisung immer als Ganzes (also in dem Beispiel alle 4 
Bedingungen) abgearbeitet, bevor der Interrupt beginnen kann?

Anders gefragt: wo genau kann ein Interrupt das laufende Programm 
unterbrechen?

Verweise auf Links (Lexika,...), wo dies erklärt wird, sind sehr 
willkommen!

Vielen Dank!

Torsten

von Maxx (Gast)


Lesenswert?

Der Interrupt kann auch innerhalb der IF auftreten.
Ein Interrupt kann auch innerhalb einer Zuweisung auftreten.

Maßstab sind hier die einzelnen Maschinenbefehle. Eine C-Anweisung wird 
i.d.R. in eine Mehrzahl davon zerlegt und solange Interrupts 
eingeschaltet sind kann ein Interrupt zwischen jedem dieser Befehle 
angesprungen werden.

von Guest (Gast)


Lesenswert?

Wenn keine besonderen Maßnahmen getroffen werden kann das von dir 
geschilderte Szenario auftreten.

Wenn dies aus Programmtechnischen Gründen nicht erlaubt sein darf, 
kannst du vor Beginn deiner wichtigen Routine die Interrupts abschalten 
und danach wieder einschalten. Das kann dann aber zu Verzögerungen bei 
der Ausführung des Interrupts führen.

von Falk B. (falk)


Lesenswert?

@  Torsten Schmidt (grizu)

>wenn ein Interrupt das laufende Programm unterbricht, kann dies auch
>INNERHALB einer IF-Anweisung passieren?

Ja.


>if (  (a < b)
>   && (c < d)  // <-- Unterbrechung möglich???

Ja.

>Wenn ein Interrupt an der markierten Stelle möglich wäre, würden die
>ersten beiden Bedingungen also vor dem Interrupt auf wahr/falsch
>überprüft und die letzten beiden Bedingungen erst nach dem Interrupt.

Ja.

>Oder wird die IF-Anweisung immer als Ganzes (also in dem Beispiel alle 4
>Bedingungen) abgearbeitet, bevor der Interrupt beginnen kann?

Nein.

>Anders gefragt: wo genau kann ein Interrupt das laufende Programm
>unterbrechen?

jetderzeit, auch "in" C-Anweisungen, weil diese ja meist aus vielen 
Maschinenbefehlen bestehen.

>Verweise auf Links (Lexika,...), wo dies erklärt wird, sind sehr
>willkommen!

Siehe Interrupt. Stichwort atomar.

MfG
Falk

von spess53 (Gast)


Lesenswert?

Hi

>Wenn keine besonderen Maßnahmen getroffen werden kann das von dir
>geschilderte Szenario auftreten.

Wenn die Befehlszeile mehrere Assemblerbefehle repräsentiert kann die 
Unterbrechung vor oder auch nach dem eigentlichen Vergleich auftreten.

Mfg Spess

von Oliver (Gast)


Lesenswert?

Torsten Schmidt schrieb:
> Verweise auf Links (Lexika,...), wo dies erklärt wird, sind sehr
> willkommen!

Das Lexikon aller Weisheiten zu einem Prozessor heißt Datenblatt.

Das steht sehr detailliert drin, was in welcher Reihenfolge bei 
auftreten eines Interrupts alles passiert. Das ist von Prozessor zu 
Prozessor unterschiedlich.

Aber bei allen gilt: Der Prozessor kennt nur seine Maschinensprache, und 
Interrups können in der Regel nach jedem Befehl daraus unterbrechen. Ob 
das Programm dabei aus einer höheren Programmiersprache kompiliert 
wurde, oder nicht, ist dem egal.

Oliver

von spulenkern (Gast)


Lesenswert?

>>wenn ein Interrupt das laufende Programm unterbricht, kann dies auch
>>INNERHALB einer IF-Anweisung passieren?

Ja!
Für den Prozessor gibt es keine "IF-Anweisung". Diese wird vom Compiler 
in Maschinensprache umgesetzt woraus sich dann eine Sequenz von mehreren 
Anweisungen ergibt. In etwa so
 - vergleiche a mit b
 - wenn a größer als b, gehe zu anweisung xxx
 - vergeliche c mit d
 - ...

Diese einzelnen Anweisungen sind dann quasi elementar und nicht 
unterbrechbar. Es wird dann zwischen den Zeilen unterbrochen.

von Oliver (Gast)


Lesenswert?

spess53 schrieb:
> Wenn die Befehlszeile mehrere Assemblerbefehle repräsentiert kann die
> Unterbrechung vor oder auch nach dem eigentlichen Vergleich auftreten.

Wenn die zu vergleichenden Daten breiter sind, als die Registerbreite, 
kann ein Interrupt auch mitten im Vergleich auftreten.

Oliver

von Random .. (thorstendb) Benutzerseite


Lesenswert?

Dagegen macht man solche C Konstrukte 'atomic'.
Das bedeutet, man schaltet im schlimmsten Fall vor diesem Bereich alle 
Interrupts tot, und danach wieder an.

Beim Cortex-M3 z.B. hat der NVIC ein Register (BasePri), in welchem man 
Interrupts unterhalb einer programmierbaren Priorität disablen kann, so 
dass wichtige Interrupts, die diesen Programmbereich nciht gefährden 
(Stichwort 'shared memory') weiterhin aktiv und ausführbar bleiben.

Um beim CM3 zu bleiben, zwei weitere Register mit jeweils einem Bit 
(PriMask, Faultmask) schalten die externen oder externen + internen 
Interrupts lahm.


VG,
/th.

von Grrrr (Gast)


Lesenswert?

Ich möchte noch ergänzen, dass bei entsprechendem Design das sperren von 
Interrupts nicht nötig ist.

Ganz allgemein ist die Frage, ob in dem fraglichen Interrupt eine der 
Variablen geändert werden kann, die in der IF-Bedingung geprüft wird.

Als zweites ist die Frage notwendig, ob die Variable(n) in ein 
Maschinenwort passt oder nicht. Evtl. mit dem Zusatz ob dies evtl. für 
mehrere Variablen in der Bedingung unterschiedlich ist.

Als drittes die Frage ob es für die Funktion des Programmes in Ordnung 
ist, wenn die Reaktion auf den Interrupt in main verzögert ablaufen 
darf. Dabei wird das übliche Designmuster vorausgesetzt, dass Interrupts 
lediglich Flags setzen und diese in einer while-Schleife in main immer 
wieder getestet werden.

Falls die Antwort auf die erste Frage Nein lautet, sind keine besonderen 
Maßnahmen oder Überlegungen notwendig. Sonst:

Falls die Antwort auf die zweite Frage ergibt, das eine der im Interrupt 
gesetzten und in der Bedingung abgefragten Variablen mehr als ein 
Maschinenwort belegt, so sollte die Auswertung atomic sein (was heisst, 
das Interrupts gesperrt werden). Evtl. bietet es sich an, die Auswertung 
in eine Funktion auszulagern und nur den relevanten Vergleich atomic zu 
machen. Dazu müsste noch betrachtet werden ob alle Variablen (bis auf 
die Ausführungszeit) synchron verändert werden.
Falls nicht:

Falls die Antwort auf die dritte Frage ergibt, das eine Verzögerung 
zulässig ist, dann brauchen keine besonderen Maßnahmen ergriffen werden. 
Als erstes sollte geprüft werden wie oft der Interrupt ausgelöst wird 
und wie oft im Vergleich dazu die Schleife mit der Bedingung durchlaufen 
wird.

Evtl. hast Du ein anderes Designpattern. In diesen Fall poste mal den 
Code.

von Torsten S. (grizu)


Lesenswert?

Vielen Dank für die vielen guten Antworten!

@Grrrr:

1. Frage: ja, eine der Variablen wird durch den Interrupt verändert.
2. Frage: alle Variablen passen in ein Maschinenwort 
(16-Bit-Mikrocontroller, die Variable ist ebenfalls 16 Bit lang).
3. Die Reaktion auf den Interrupt darf in der "main" verzögert ablaufen.

Ich will mein Problem mal konkretisieren. Bitte vergesst dazu die im 
ersten Posting genannte IF-Abfrage, da diese ein Teil einer von mir 
ausgedachten Lösung ist, aber eventuell braucht man diese IF-Abfrage gar 
nicht.

Ich möchte die zeitlichen Abstände zwischen Ereignissen A1, A2, A3, ... 
An einerseits und die zeitlichen Abstände zwischen Ereignissen B1, B2, 
B3, ... Bn andererseits messen. Für beide Ereignis-Reihen benutze ich 
den gleichen Input Capture. Der im Input Capture benutzte Base Timer hat 
16 Bit, was nicht ausreichend ist, um die Zeiten mit entsprechender 
Genauigkeit und Länge zu messen. Daher habe ich mir einen 32-Bit-Zähler 
erzeugt, indem ich einen High-Word-Zähler hochzähle, wenn der Base Timer 
überläuft und dadurch einen "Überlauf-Interrupt" auslöst.

Der Überlauf-Interrupt muss eine höhere Priorität haben als der 
Input-Capture-Interrupt, da ich sonst einen Überlauf verpassen würde, 
wenn dieser während der Ausführung des Input-Capture-Interrupts für An 
passiert und gleichzeitig das zweite Ereignis Bn passiert. Dann würde 
nämlich der Input-Capture-Interrupt gar nicht beendet (while-Schleife 
bis alle Ereignisse abgearbeitet sind) und daher würde die Abarbeitung 
des zweiten Input-Capture-Ereignisses (Bn) den Überlauf nicht 
mitbekommen. Also muss der Überlauf eine höhere Priorität haben.
Andererseits darf der High-Word-Zähler nicht hochzählen, wenn der 
Überlauf nach dem Ereignis An geschieht, aber noch vor dem Verarbeiten 
des Zählers im Input-Capture-Interrupt.

Genau das war am Anfang nämlich mein Problem: Ereignis An hat den 
Input-Capture-Interrupt ausgelöst, ganz kurz danach gab es den Überlauf, 
so dass mein 32-Bit-Zähler um einen Überlauf (also um 0x10000) zu hoch 
war.

Eventuell wäre die Lösung, die Priorität des Überlauf-Interrupts am 
Anfang des Input-Capture-Interrupts kurzzeitig niedriger zu setzen - 
eben so lange, wie der 32-Bit-Zähler nicht berechnet ist. Wenn man also 
gleich im ersten Befehl des Input-Capture-Interrupts die Priorität des 
Überlauf-Interrupts verringert. Meine Frage wäre dazu: kann es 
passieren, dass zwischen dem Aufruf des Input-Capture-Interrupts und der 
Ausführung des ersten Befehls (dem Verringern der Priorität des 
Überlauf-Interrupts) der Überlauf-Interrupt zuschlägt? Dies wäre ein 
seltener Fall, aber alles was passieren kann, wird früher oder später 
passieren und dann hätte ich mit dieser Lösung nichts gewonnen.

Die gleiche Frage/das gleiche Problem hat die Idee, in der ersten Zeile 
des Input-Capture-Interrupts den High-Word-Zähler in eine lokale 
Variable zu speichern, um nachfolgende Änderungen zu verhindern.

Danke im voraus für die Hilfe!

Torsten

von Andreas S. (Firma: Schweigstill IT) (schweigstill) Benutzerseite


Lesenswert?

spess53 schrieb:
> Wenn die Befehlszeile mehrere Assemblerbefehle repräsentiert kann die
> Unterbrechung vor oder auch nach dem eigentlichen Vergleich auftreten.

Und auch manche Assembler-Befehle können nicht-atomar sein, wenn sie auf 
mehrere Prozessor-Befehle abgebildet werden, z.B. bei ARM-Prozessoren 
die Befehle MOV32, ADRL. Folglich kann auch ihre Ausführung mittendrin 
unterbrochen werden.

von (prx) A. K. (prx)


Lesenswert?

Wenn es zulässig ist, fragwürdige Messergebnisse zu ignorieren, dann 
sollte es ausreichen, alle Capture-Werte dicht am oberen Grenzbereich 
des Zählers (z.B. 0xFF00-0xFFFF) zu ignorieren. Denn das sind jene 
Werte, bei denen es möglich ist, dass der Overflow-Interrupt nach dem 
Capture-Event aber vor dem Auslesen des Overflow-Counters zuschlug.

von (prx) A. K. (prx)


Lesenswert?

Wenn im Capture-Handler der aktuelle Wert des Timers kleiner ist als der 
Capture-Wert, dann war dazwischen ein Overflow und der kann entsprechend 
rausgerechnet werden. Natürlich muss man den Wert des Timers dabei 
zusammen mit dem Overflow-Counter konsistent kriegen, aber anders als 
beim Capture ist dies problemlos möglich.
1
volatile uint16_t overflow_counter; // zählt die Overflows
2
3
// Timer-Wert konsistent auslesen
4
uint16_t hi, lo;
5
do {
6
  hi = overflow_counter;
7
  lo = CurrentTimerValue;
8
} while (OverflowInterruptPending || hi != overflow_counter);
9
10
// Overflow-Wert für Capture korrigieren
11
int16_t diff = lo - CaptureValue; // signed!
12
if (diff < 0)
13
  hi -= 1;
14
15
uint32_t capture = (uint32_t)hi << 16 | CaptureValue;

Mit "OverflowInterruptPending" ist das entsprechende Bit vom Timer 
gemeint. Eine Absicherung für den Fall, dass der Overflow-Interrupt zwar 
vor/mit dem Auslesen des Timers ausgelöst wird, der Handler aber aus 
irgendwelchen Controller-internen Gründen erst nach dem Auslesen des 
overflow_counters (im while) läuft.

von (prx) A. K. (prx)


Lesenswert?

Korrektur:
1
volatile uint16_t overflow_counter; // zählt die Overflows
2
3
// Timer-Wert konsistent auslesen
4
uint16_t hi, lo;
5
do {
6
  hi = overflow_counter;
7
  lo = CurrentTimerValue;
8
} while (OverflowInterruptPending || hi != overflow_counter);
9
10
// Overflow-Wert für Capture korrigieren
11
if (lo < CaptureValue)
12
  hi -= 1;
13
14
uint32_t capture = (uint32_t)hi << 16 | CaptureValue;

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.