Hallo!
Ich bin Anfänger und habe eine Frage zu Interrupt Service Routinen:
Ich programmiere einen 32Bit ATmel in AVR Studio. In Beispielprogrammen
sehe ich immer wieder den Codeschnipsel "__attribute__((_interrupt_))"
vor Interrupt Service Routinen. Also z.B. in der Form:
1
__attribute__((__interrupt__))
2
voidExampleISR()
3
{
4
...
5
}
Was genau hat es damit auf sich?
Muss man das vor JEDE Interrupt Servis Routine setzen oder gibt es da
Ausnahmen?
Gilt dies nur für 32Bit µCs oder für alle?
Danke für Eure Hilfe!
lG
Das macht man, um dem Compiler mitzuteilen, dass es sich bei der Routine
um einer Interruptroutine handelt. Der Compiler erzeugt dann unter
Umständen geringfügig anderen Code, je nach Prozessorarchitektur. Beim
x86 würde er z.B. den Assemblerbefehl iret statt ret zum Verlassen der
Routine nutzen. Bei anderen Architekturen macht er das, was für
Interruptroutinen eben nötig ist.
Hans Peter schrieb:> Muss man das vor JEDE Interrupt Servis Routine setzen oder gibt es da> Ausnahmen?
Ja. Machst du es nicht, droht großes Unheil.
> Gilt dies nur für 32Bit µCs oder für alle?
Für alle.
Eine Interrupt Routine kann an (fast) beliebigen Stellen im Programm
aufgerufen werden. Also muss sie am Anfang erst einmal alle Sachen
sichern, die zu der Arbeit gehören, die gerade unterbrochen wurde (Akku,
Status, Register). Und nach getaner Arbeit muss sie den alten Zustand
wieder herstellen. Und dann schlussendlich der Hardware sagen "Interrupt
ist erledigt". All dieses macht der Compiler, wenn er am Anfang der
Routine das Interrupt Attribut sieht.
Hallo,
man muss dem Compiler mitteilen, dass er am Ende des Handlers mit "rete"
die Interruptbearbeitung beendet und ggf. noch gewisse Register pop-t,
je nach Architektur.
VG
David
PS: Wenn du das FW benutzt, dann schau dir die exception.s/exception.x
an. Danach sollte alles klar sein oder frag einfach wieder nach.
Hans Peter schrieb:> Ich bin Anfänger und habe eine Frage zu Interrupt Service Routinen:>> Ich programmiere einen 32Bit ATmel in AVR Studio. In Beispielprogrammen> sehe ich immer wieder den Codeschnipsel "__attribute__((_interrupt_))"> vor Interrupt Service Routinen.> Was genau hat es damit auf sich?> Muss man das vor JEDE Interrupt Servis Routine setzen oder gibt es da> Ausnahmen?> Gilt dies nur für 32Bit µCs oder für alle?
Aus Sicht des Compilers ist eine ISR (Interrupt Service Routine) eine
spezielle Funktion:
1. sie wird niemals aus dem Code aufgerufen, sondern von der Hardware
getriggert. Sie muß also auch dann compiliert und gelinkt werden, wenn
sie anscheinend nirgendwo aufgerufen wird.
2. sie muß an eine feste Adresse gelinkt werden bzw. es muß in eine
Tabelle ein Eintrag für die ISR gemacht werden (Interrupt-Vektoren).
3. vor Ausführung der ISR müssen alle Prozessorregister gesichert
werden.
4. die ISR wird mit RETI statt mit RET beendet.
5. die Signatur einer ISR ist immer void ISR(void).
Bis auf Details ist das für alle µC und alle ISR so. Allerdings ist
__attribute__((_ interrupt _) schon so ein Detail. In vielen
Programmierumgebungen - typischerweise als Teil der C-Library - werden C
Makros verwendet, um die Details etwas schöner zu verpacken.
Beispielsweise die AVR-Libc für 8-Bit AVRs verpackt das in ein Makro
ISR():
http://www.nongnu.org/avr-libc/user-manual/group__avr__interrupts.html
In deinem Fall sollte die Hilfe des AVR-Studio die entsprechenden
Hinweise enthalten.
HTH, XL
Vielen Dank für die ausführlichen Informationen!
Stimmt, ich hatte bei einem 8-Bitter die Interrupt Service Routinen mit
dem Makro ISR() definiert...
Un dbei den 32 Bittern eben mit dem besprochenen Attribut!
Ich werde es nun einfach vor jeder ISR einfügen, vielen Dank!
Hallo!
Ich habe nun einen GPIO Pin so konfiguriert, dass ein Interrupt
ausgelöst wird, wenn eine steigende Flanke kommt.
Wenn ich die InterruptServiceRoutuine so schreibe:
1
__attribute__((__interrupt__))
2
voidHomeSwitchInterruptHandler_0()
3
{
4
// ........
5
// to implement
6
7
gpio_clear_pin_interrupt_flag(PIN_XY);
8
9
}
wird der interrupt einmal ausgelöst, danach nicht mehr. Wenn ich im
Debug Modus dann auf Pause gehe, steht der Programmablauf im Disassambly
immer bei der gleichen Adresse.
Wenn ich die ISR so schreibe:
1
voidHomeSwitchInterruptHandler_0()
2
{
3
// ........
4
// to implement
5
6
gpio_clear_pin_interrupt_flag(PIN_XY);
7
8
}
also ohne dem attribut davor, funktioniert es (der Interrupt wird
jedsmal bei einer steigenden Flanke ausgelöst.
Kann mir jemand erklären warum? Ich dachte, dieses Attribut sei vor
jeder ISR notwendig?
Weiters ist mir noch aufgefallen, dass das InterruptFlag nicht gesetzt
ist. wenn ich vor gpio_clear_pin_int.... einen Breakpoint in der ISR
setze und dann das Register im Debug Modus betrachte, ist das Interrupt
Flag des entsprechenden Pins nicht gesetzt, warum??
Axel Schwenke schrieb:> 3. vor Ausführung der ISR müssen alle Prozessorregister gesichert> werden.
<pedanterie> Alle in der ISR verwendeten. </pedanterie>
@Rufus:
Was meinst du mit
Rufus Τ. Firefly schrieb:> Axel Schwenke schrieb:>> 3. vor Ausführung der ISR müssen alle Prozessorregister gesichert>> werden.>> <pedanterie> Alle in der ISR verwendeten. </pedanterie>
?
@Walter:
Warum steht dann im Datenblatt, dass man sich um das Löschen des IF
selbst kümmern muss? Auf seite 495 Kap 23.6.2.5 vorletzter absatz...
LG und DANKE!
Na, wenn in einer ISR nur die Register x, y und z verändert werden, ist
es nicht erforderlich, die Register a-w zu sichern.
Das sind Dinge, die der Compiler entscheiden kann, der weiß, welche
Register in der ISR verwendet werden und kann im Prolog/Epilog der
Funktion entsprechende Sicherungs- und Wiederherstellungsmaßnahmen
ergreifen.
Was im einzelnen geschieht, hängt ganz stark von der jeweiligen
Prozessorarchitektur ab, davon, wieviele Register es gibt, und was es
"kostet", die Register zu sichern.
> Warum steht dann im Datenblatt, dass man sich um das Löschen des IF> selbst kümmern muss?
Das Datenblatt beschreibt nicht die Programmierung in C.
Also erstmal eines: Dieser unsägliche Ausdruck ist schlicht und einfach
GCC-spezifisch. Andere Toolchaines machen das viel eleganter, z.B. mit
nem Attribut __irq o.ä.
Ich würde mich jedenfalls strikt weigern, sowas wie
"__attribute__((_interrupt_))"
auswendig lernen zu wollen. Dann schon lieber in irgendeiner von allen
Moduln benutzten Headerdatei ein
#define __irq __attribute__((_interrupt_))
einfügen und benutzen.
Aber im Grunde ist es schon notwendig, ALLE Interrupt-Handler mit diesem
Attribut auszustatten. Die Gründe sind vielschichtig.
Erstens ist das Startup- und Ende-Verhalten je nach Prozessorart
zwischen gewöhnlichen Funktionen und Interrupt-Handlern verschieden.
Zweitens gibt es oft gewisse Festlegungen über die Registerbenutzung
derart, daß manche Register zur freien Benutzung freigegeben sind, also
weder gerettet werden müssen, noch nach UP-Aufruf als valid angesehen
werden dürfen - was bei einem Interrupt-Handler natürlich NICHT gilt.
Drittens ist es so, daß bei manchen Prozessoren ein Interrupt-Service in
einem ganz anderen Modus gefahren wird, also nicht im User-Mode, sondern
in einem privilegierten Modus und daß es dabei (ARM7 z.B.) auch einen
ganz anderen Stack hat.
Viertens ist zumindest bei einer Reihe von C-Implementierungen die
Stack-Verwaltung derart, daß sich (eigentlich) das aufrufende Programm
um die Bereinigung des Stacks zu kümmern hat. Ist aber Prozessor- und
Toolchain-abhängig.
W.S.
Hallo,
danke für die ausführliche Antwort...
ich hab mich etwas schlecht ausgedrückt bzw. es ist aus den vorigen
posts nicht ganz rüber gekommen:
Es geht in erster linie nicht darum, wie dieses Attribut heißt (nennen
wir es jetzt einfach mal __irq, wie du es definiert hast), sondern
darum, ob dieses Attribut bei jeder ISR verwendet werden soll.
Deine Antowrt ist sehr ausführlich, aber kannst du dir erklären, warum
es (wie oben beschrieben) mit dem Attribut nicht funktioniert und ohne
dem attribut schon?
LG
Hans Peter schrieb:> Es geht in erster linie nicht darum, wie dieses Attribut heißt (nennen> wir es jetzt einfach mal __irq, wie du es definiert hast), sondern> darum, ob dieses Attribut bei jeder ISR verwendet werden soll.
Es soll nicht nur, es muß. Und wenn dein Code nicht tut was du glaubst
daß er tun müßte, dann liegt das ziemlich sicher nicht an diesem
Attribut. ich meine wir wissen ja noch nicht mal, ob deine ISR überhaupt
aufgerufen wird.
XL
Ja sie wird aufgerufen!
Und zwar mit dem Attribut NUR EINMAL (dann hängt das Programm an einer
bestimmten Adresse). Möglicherweise wird in dem Fall das IF nicht
automatisch gelöscht? Aber das müsste doch spätestens mit
gpio_clear_pin_interrupt_flag(PIN_XY); passiert sein... :(
Ohne Attribut immer!
SIehe Post von 12:37...
Hans Peter schrieb:> Ja sie wird aufgerufen!>> Und zwar mit dem Attribut NUR EINMAL (dann hängt das Programm an einer> bestimmten Adresse).
Dann stimmt noch irgendwas anderes noch nicht.
> Ohne Attribut immer!
Das bedeutet, dass du den entsprechenden Seiteneffekt, der dir die Tour
vermasseln wird, nur noch nicht gesehen hast.
Du kannst die Routine auch mit
_attribute_ ((naked))
dekorieren. Dann musst Du aber zu Fuss alle Register pushen und popen
und auch den iret oder wie immer der Return heisst absetzen.
Ich verwende das im Moment gerade bei einem Arm in einer FIQ Routine. Da
baut der GCC etwas Mist mit der IRQ Dekoration
Axel Schwenke schrieb:> Bis auf Details ist das für alle µC und alle ISR so.
Nicht bei den µCs mit Cortex-M Core - als Ausnahme von der Regel. Deren
ISR ist eine normale Funktion, ohne speziellem Frame und ohne speziellem
Return-Befehl.
Hans Peter schrieb:> Ohne Attribut immer!
Hallo Hans-Peter,
poste doch mal den Assemblercode den der Compiler generiert, einmal mit
und einmal ohne das Attribut.
Es ist in solchen Fällen immer hilfreich, den generierten Assemblercode
selbst zu untersuchen. Meistens werden dir die Zusammenhänge dann sofort
klarer.
LG, Sebastian
A. K. schrieb:> Axel Schwenke schrieb:>> Bis auf Details ist das für alle µC und alle ISR so.>> Nicht bei den µCs mit Cortex-M Core - als Ausnahme von der Regel. Deren> ISR ist eine normale Funktion, ohne speziellem Frame und ohne speziellem> Return-Befehl.
Und wie kommt das? Installiert das Laufzeitsystem da einen ISR-Stub und
springt aus dem in die User-ISR-Funktionen? Denn wenn das so wäre, dann
wäre das was man als Nutzer schreibt, gar nicht die eigentliche ISR. Und
dann bräuchte man da tatsächlich keine extra Attribute.
Disclaimer: ich hab noch nie was mit 32-bittigen AVRs gemacht. Oder mit
ARMs generell. Aber wenn ich es machen würde, dann würde ich auch die
verd^Wtolle Dokumentation dazu lesen. Und wüßte dann später auch genau,
welche Attribute oder Pragmas ich an meine ISR hängen müßte. Vielleicht
denke ich da ja zu sehr geradeaus?
XL
Axel Schwenke schrieb:> Und wie kommt das? Installiert das Laufzeitsystem da einen ISR-Stub und> springt aus dem in die User-ISR-Funktionen?
Nein. Die Corex-M Cores wurden gezielt auf effiziente Ausführung von
ISRs in Hochsprache optimiert.
- Der Core sichert beim Aufruf einer ISR jene Register bereits selbst
auf den Stack, die eine normale Funktion nicht ohnehin schon von Haus
aus sichert. Durch getrennte Busse können sich diese Push Operationen
ins RAM mit den Fetch des Vektors aus dem ROM überlappen. Folglich ist
jener Teil des ABI bereits von per Hardware vorgegeben, der die Rolle
der Register beim Funktionsaufruf definiert.
- Der im Link-Register hinterlassene Inhalt ist keine Return-Adresse,
sondern ein spezieller Wert, der dem Sprungbefehl signalisiert, dass es
sich um einen Return-from-Interrupt handelt. Die eigentliche
Return-Adresse steht auf dem Stack.
Als Ergebnis davon sind ISRs wirklich ganz normale C Funktionen. Ohne
Laufzeit-Stub. Der tatsächlich zur Laufzeit auftretende Unterschied wird
von der Hardware bereits selbst implementiert.
Axel Schwenke schrieb:> Disclaimer: ich hab noch nie was mit 32-bittigen AVRs gemacht.
Ich beschrieb die Controller mit einem Cortex-Mx Core von ARM. Mit den
AVR32 hat das nichts zu tun. Das ist eine andere von Atmel selbst
definierte Architektur und wie das bei denen aussieht habe ich nicht
parat.