Forum: Mikrocontroller und Digitale Elektronik Unterbrechen eines Befehls durch ISR


von Stefan P. (speedo)


Lesenswert?

Hallo,
ich frage mich wie das Unterbrechen eines Befehls durch eine ISR 
abläuft.

Wenn ich folgenden Befehle benutze um den Pin2 des Ports B zu setzen:
(I) PORTB |= _BV(PB2); // 2 Takte

Und ich habe eine ISR die am gleichen Port B aber einen anderen Pin 
(z.B.: PB3) setzen möchte, dann kann es doch passieren, das der Befehl 
(I) durch ein Befehl in der ISR nicht erkannt wird, da die ISR eine 
höhere Priorität hat und der Befehl(I) 2 Takte benötigt?


Ich habe mir das mal so dargestellt, wenn ich die Reihenfolge der von 
der CPU ausgeführten Schreib- und Leseanweisungen betrachte:

[ PORTB |= _BV(PB2) <=> laden und setzen <=> LDI R1,x + MOV R2,R1 ]

Also es wird erst geladen:
LDI R1, x
Dann tritt die ISR auf...
Die ISR verdrängt erstmal den MOV Befehl für eine Zeitdauer und in 
dieser Zeit wird das Register R1 überschrieben.
Nachdem die ISR fertig ist, wird der MOV Befehl ausgeführt.
Damit wurde der LDI Befehl nicht erkannt! Oder?
Gruß speedo

von michi (Gast)


Lesenswert?

avr-tutorial
-> Kapitel: Interrupts
-> Besonderheiten des Interrupthandlers

wer lesen kann ist klar im vorteil

von Skua C. (skua)


Lesenswert?

"erkannt" ist hier der falsche Ausdruck.
Aber du hast das Problem im Kern richtig erkannt.
Wenns geht in der ISR ein Flag setzen und Arbeit in Mainloop machen.

von Stefan P. (form)


Lesenswert?

Stefan P. wrote:

> Nachdem die ISR fertig ist, wird der MOV Befehl ausgeführt.
> Damit wurde der LDI Befehl nicht erkannt! Oder?

Hallo Namensvetter ;-)
Da man in der Regel vor/nach der Bearbeitung eines IRQ alle Register 
pusht/popt (sichert/wiederherstellt) geht dabei kein Befehl "verloren". 
Der MOV macht dann genau das was er auch anfangs machen sollte.

von Karl-heinz S. (cletus)


Lesenswert?

Stefan P. wrote:
> Stefan P. wrote:
>
>> Nachdem die ISR fertig ist, wird der MOV Befehl ausgeführt.
>> Damit wurde der LDI Befehl nicht erkannt! Oder?
>
> Hallo Namensvetter ;-)
> Da man in der Regel vor/nach der Bearbeitung eines IRQ alle Register
> pusht/popt (sichert/wiederherstellt) geht dabei kein Befehl "verloren".
> Der MOV macht dann genau das was er auch anfangs machen sollte.

Ja, aber das ist das Problem, welches der Threadsteller meint:

Hauptroutine:

Variable = PortB;
Variable |= 0x00010000;

<- Jetzt schlägt der Interrupt zu, der ein Flag setzt.

TMP = PortB;
TMP |= 0x10000000;
PortB = TMP;

<- Zurück ins normale Programm

PortB = Variable;


Und Schwupps - ist das Flag, das in der Interruptroutine gesetzt werden 
sollte, verloren.

(Die hier eingesetzte Sprache ist übelster Pseudocode)

von yalu (Gast)


Lesenswert?

Nur so am Rande: Das konkrete Beispiel
1
PORTB |= _BV(PB2);

wird vom GCC in einen einzelnen sbi-Befehl übersetzt, der nicht
unterbrochen werden kann.

Ähnliches gilt für
1
PORTB &= ~_BV(PB2);

von Sinusgeek (Gast)


Lesenswert?

Na und???

Der ASM-Programmierer sichert in der ISR das SREG in einem 
Exklusivregister oder notfalls auch auf dem Stack..
Er nutzt in der ISR auch Exklusiv-Register oder sichert die benutzten 
Register auf dem Stack.

Der Hochsprachenbenutzer überlässt dies dem Compiler, der weiß schon, 
was er tut....

Wo also ist das Problem?

Mit setzt ein Flag ist übrigens nicht ein Flag des SREG gemeint, 
sondern eine globale Boolsche Variable im Verantwortungsbereich des 
Programmierers.

von Karl-heinz S. (cletus)


Lesenswert?

Sinusgeek wrote:
>
> Wo also ist das Problem?

Vielleicht verstehe ich es nicht.

Aber es gibt Situationen, die funktionieren ohne eine mutual exclusion 
nicht...


> Der Hochsprachenbenutzer überlässt dies dem Compiler, der weiß schon,
> was er tut....

Oh man, der ist gut. Deshalb sind Race Conditions ja auch überhaupt 
keine Problem! lach

von Sinusgeek (Gast)


Lesenswert?

Noch vergessen:

Es gibt allerdings Fälle, in denen Unterbrechungen durch Interrupt 
vermieden werden müssen. Z.B. beim EEP-Schreiben zwischen Aufheben des 
Schreibschutzes und Schreibbefehl, aber auch bei RAM- oder 
Portzugriffen, wenn auch die ISR auf dieselben Adressen zugreifen kann. 
Diskussionen darüber wirst Du in diesem Forum vermutlich unter dem 
Suchbegriff "atomar" finden können.

von Karl-heinz S. (cletus)


Lesenswert?

Sinusgeek wrote:

> Es gibt allerdings Fälle, in denen Unterbrechungen durch Interrupt
> vermieden werden müssen. Z.B. beim EEP-Schreiben zwischen Aufheben des
> Schreibschutzes und Schreibbefehl, aber auch bei RAM- oder
> Portzugriffen, wenn auch die ISR auf dieselben Adressen zugreifen kann.

Exakt um einen solchen Port-Zugriff geht es doch, oder?

von Karl-heinz S. (cletus)


Lesenswert?

Ach, verdammt. Ich habe die Frage falsch verstanden. Sorry.

Aber das mit dem "Der PortB hat am Ende einen falschen Wert" liegt nicht 
daran, dass ein Befehl übersehen wurde ;-)

von Sinusgeek (Gast)


Lesenswert?

> Exakt um einen solchen Port-Zugriff geht es doch, oder?

Das wird doch aber nur dann zum Problem, wenn dieser Port (bzw. diese 
Speicherzelle(n)) von Hauptprogramm und ISR bedient werden. Dann sind 
im Hauptprogramm die zusammengehörenden (ASM-)Befehle durch kurzzeitige 
Sperrung der Interruptfreigabe atomar (ununterbrechbar) zu machen.

von Karl-heinz S. (cletus)


Lesenswert?

Sinusgeek wrote:
>> Exakt um einen solchen Port-Zugriff geht es doch, oder?
>
> Das wird doch aber nur dann zum Problem, wenn dieser Port (bzw. diese
> Speicherzelle(n)) von Hauptprogramm und ISR bedient werden. Dann sind
> im Hauptprogramm die zusammengehörenden (ASM-)Befehle durch kurzzeitige
> Sperrung der Interruptfreigabe atomar (ununterbrechbar) zu machen.

Jo, genau das meinte ich. Siehe Mutual Exclusion (kurz: MutEx ;-) )

von Stefan P. (speedo)


Lesenswert?

Vielen Dank für die reichhaltige Beteiligung :)

Ich meine natürlich nicht das ein Befehl ausgelassen wird, sondern das 
der Wert der in R1 geladen wird nicht registriert wird, da in der ISR, 
R1 wieder überschrieben wird und erst nach der Beendigung der ISR der 
MOV Befehl ausgeführt wird.

"sbi"-Befehl setzt sich aus einem "load" und einem "set" Befehl 
zusammen.

Zusammenfassend:
Eine versuchte Änderung an einem Port "PORTB |= _BV(PB2); // 2 Takte"
kann durch eine ISR überschrieben werden sodass die ursprüngliche 
Änderung nicht erkannt wird.

Beheben durch:
cli();
PORTB |= _BV(PB2);
sei();

oder
einer globalen Variable die eine Änderung schützt.

Frage nebenbei: Haben die 2 Takte für den Befehl jetzt irgendeine 
Auswirkung? :)

von Karl-heinz S. (cletus)


Lesenswert?

Stefan P. wrote:

> einer globalen Variable die eine Änderung schützt.

Nein. Dann passiert exakt das Gleiche.

Was soll die ISR machen, wenn der Port durch die globale Variable 
geschützt ist?

Beim anständigen Threading kannst du einfach mal kurz schlafen gehen und 
danach wieder überprüfen, den Luxus hast du in einer ISR aber nicht.

von Sinusgeek (Gast)


Lesenswert?

> Zusammenfassend:
> Eine versuchte Änderung an einem Port "PORTB |= _BV(PB2); // 2 Takte"
> kann durch eine ISR überschrieben werden sodass die ursprüngliche
> Änderung nicht erkannt wird.

Nein...

PortB liegt in dem I/O-Bereich (die unteren 32 I/O-Adressen), für den es 
Bit-Befehle gibt (SBI, CBI, SBIS, CBIS). Diese Befehle sind atomar, sie 
lassen sich nicht unterbrechen. Ein cleverer ASM-Programmierer oder 
Compiler nutzt diese Befehle.

Anders sieht es aus, wenn eine der oberen 32 I/O-Adressen angesprochen 
wird. Da muss dann programmiert werden:

 in r16,gimsk
 ori r16,1<<int0
 out gimsk,r16

Das sind also 3 unabhängige ASM-Befehle, wo ein Interrupt 
dazwischenfunken kann. Da es aber kaum vorkommt, dass eine ISR auch auf 
gimsk zugreift, braucht das im Normalfall nicht mit CLI/SEI atomar 
gemacht werden.

Wird aber vom Hauptprogramm und der ISR auf dieselbe Adresse 
zugegriffen, wobei das Hauptprogramm mehrere (ASM- bzw. 
Maschinensprache-)Befehle braucht, dann müssen diese mit CLI und SEI 
ununterbrechbar zusammengefasst werden.

Es schadet also einem Hochsprachenbenutzer nicht, etwas ASM zu 
verstehen, denn der Controller kann keine Hochsprache, er arbeitet in 
MC, was 1 zu 1 in ASM notierbar ist.

von Sven (Gast)


Lesenswert?

Also so:
1
cli  ;Interrupt aus
2
in TEMP1, PORTD  ;2 Takte
3
ori TEMP1, 1<<PD7  ;Pin 7 setzen, 1 Takt
4
out PORTD, TEMP1  ;2 Takte
5
sei  ;Interrupt ein

bei:
1
sbi PORTD, PD7  ;Pin 7 setzen, 2 Takte

muss der Int nicht disabled werden, da es ein Befehl ist, der zwar 2 
Takte benötigt, aber nicht unterbrochen wird.

Und woher weiss ich, welche Variante der Compiler umsetzt? Und ob er da 
CLI automatisch berücksichtigt?

Ich weiss schon, warum ich ASM programmiere...

von Sven (Gast)


Lesenswert?

>> PortB liegt in dem I/O-Bereich (die unteren 32 I/O-Adressen), für den es
>> Bit-Befehle gibt (SBI, CBI, SBIS, CBIS). Diese Befehle sind atomar, sie
>> lassen sich nicht unterbrechen. Ein cleverer ASM-Programmierer oder
>> Compiler nutzt diese Befehle.

Wie machst Du
1
in TEMP1, PORTD  ;2 Takte
2
eor TEMP1, 0b11001100  ;Port Pins toggeln
3
out PORTD, TEMP1  ;2 Takte

mit SBI und CBI? Also immer schauen, was effektiver ist.

von Karl-heinz S. (cletus)


Lesenswert?

Sven wrote:

> Und woher weiss ich, welche Variante der Compiler umsetzt? Und ob er da
> CLI automatisch berücksichtigt?

Da du dank Kapselung auch nie weißt, wo du deinen Code noch einsetzt und 
du nie alle Eigenheiten des Compilers kennst, lautet Sicherheit die 
Maxime.

Außer in den 2 % Code, von denen Echtzeitfähigkeiten gefordert werden.

von Sinusgeek (Gast)


Lesenswert?

> Ich weiss schon, warum ich ASM programmiere...

Ich auch... ;-)

Übrigens:
Im ersten Beitrag dieses Threads steht:

 LDI r1,x

LDI kenne ich nur von AVRs (andere ASM-Dialekte nennen das MOV), daher 
gehe ich davon aus, dass Du AVR-ASM meinst. R1 iat aber nicht mit LDI 
ansprechbar, das geht nur mit R16 bis R31. ;-(

von Stefan P. (speedo)


Lesenswert?

Vielen Dank euch Allen. Jetzt habe ich es verstanden. Was man so alles 
berücksichtigen muss :)

von Sinusgeek (Gast)


Lesenswert?

> Wie machst Du

> in TEMP1, PORTD  ;2 Takte
> eor TEMP1, 0b11001100  ;Port Pins toggeln
> out PORTD, TEMP1  ;2 Takte

> mit SBI und CBI? Also immer schauen, was effektiver ist.

Gar nicht, denn EOR geht nicht mit einer Konstante!!!

Außerdem kommt es auf den Controller an, bei den neuen AVRs mache ich es 
dann so:

 ldi r16,0b11001100
 out pind,r16

Sollte die ISR auch r16 nutzen, dann hat der Programmierer an anderer 
Stelle etwas Grundlegendes falsch gemacht. Der eigentliche Portzugriff 
ist aber atomar und braucht nicht gekapselt werden.

von Michael A. (micha54)


Lesenswert?

Hallo,

ganz richtig ist, daß man bei Zugriffen auf Ports oder Daten, die auch 
durch ISRs benutzt werden, eine Gleichzeitigkeit vermeiden muss.
In gcc z.B. so:

char tmp = SREG; // sichere I-Bit
cli();
unteilbar_teil1;
unteilbar_teil2;
SREG = tmp;      // restauriere I-Bit

Eine Kontrolle der Assemblerbefehle, die der GCC erzeugt, halte ich für 
Quatsch, dann bitte direkt ASM kodieren. Schliesslich weiss man nie, ob 
eine andere -ox das genauso macht.

Gruss,
Michael

von Sven (Gast)


Lesenswert?

>> Gar nicht, denn EOR geht nicht mit einer Konstante!!!


Oh, sorry, da stand vorher andi. ;-)

Außerdem kommt es auf den Controller an, bei den neuen AVRs mache ich es
dann so:

 ldi r16,0b11001100
 out pind,r16

Häh? Das ist aber nicht das gleiche. Wenn Du nur einige Pins ändern 
willst, musst Du trotzdem vorher den Port einlesen.

von Martin Schneider (Gast)


Lesenswert?

Dochdoch, das ist auch eine AVR-Spezialität,
Bit-Setzen im PIN-Register toggelt das entsprechende Ausgangspin

Ahoi, Martin

von Sven (Gast)


Lesenswert?

Achso, hab das pind für einen Schreibfehler gehalten. ;-) Ist ja witzig, 
welche AVRs können das denn, die Xmegas?

von Sinusgeek (Gast)


Lesenswert?

> Achso, hab das pind für einen Schreibfehler gehalten. ;-) Ist ja witzig,
> welche AVRs können das denn, die Xmegas?

Neee, sooooo neu müssen sie nun auch wieder nicht sein. Tiny13, 
Tiny24/44/84, Tiny25/45/85, Mega48/88/168 und Mega644 z.B. können das 
auch schon. Einfach mal die entsprechenden Datenblätter ansehen.

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.