www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Unterbrechen eines Befehls durch ISR


Autor: Stefan P. (speedo)
Datum:

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

Autor: michi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
avr-tutorial
-> Kapitel: Interrupts
-> Besonderheiten des Interrupthandlers

wer lesen kann ist klar im vorteil

Autor: Skua C:\> (skua)
Datum:

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

Autor: Stefan P. (form)
Datum:

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

Autor: Karl-heinz Strunk (cletus)
Datum:

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

Autor: yalu (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Nur so am Rande: Das konkrete Beispiel
PORTB |= _BV(PB2);

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

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

Autor: Sinusgeek (Gast)
Datum:

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

Autor: Karl-heinz Strunk (cletus)
Datum:

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

Autor: Sinusgeek (Gast)
Datum:

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

Autor: Karl-heinz Strunk (cletus)
Datum:

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

Autor: Karl-heinz Strunk (cletus)
Datum:

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

Autor: Sinusgeek (Gast)
Datum:

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

Autor: Karl-heinz Strunk (cletus)
Datum:

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

Autor: Stefan P. (speedo)
Datum:

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

Autor: Karl-heinz Strunk (cletus)
Datum:

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

Autor: Sinusgeek (Gast)
Datum:

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

Autor: Sven (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Also so:
cli  ;Interrupt aus
in TEMP1, PORTD  ;2 Takte
ori TEMP1, 1<<PD7  ;Pin 7 setzen, 1 Takt
out PORTD, TEMP1  ;2 Takte
sei  ;Interrupt ein

bei:
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...

Autor: Sven (Gast)
Datum:

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

Autor: Karl-heinz Strunk (cletus)
Datum:

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

Autor: Sinusgeek (Gast)
Datum:

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

Autor: Stefan P. (speedo)
Datum:

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

Autor: Sinusgeek (Gast)
Datum:

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

Autor: Michael Appelt (micha54)
Datum:

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

Autor: Sven (Gast)
Datum:

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

Autor: Martin Schneider (Gast)
Datum:

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

Ahoi, Martin

Autor: Sven (Gast)
Datum:

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

Autor: Sinusgeek (Gast)
Datum:

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

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.