Forum: Mikrocontroller und Digitale Elektronik __attribute__((__interrupt__)) vor ISR


von Hans Peter (Gast)


Lesenswert?

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
void ExampleISR()
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

von Alexander B. (leuchte)


Lesenswert?

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.

von Georg G. (df2au)


Lesenswert?

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.

von Ing.David (Gast)


Lesenswert?

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.

von Axel S. (a-za-z0-9)


Lesenswert?

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

von Hans Peter (Gast)


Lesenswert?

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!

von Hans Peter (Gast)


Lesenswert?

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
void HomeSwitchInterruptHandler_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
void HomeSwitchInterruptHandler_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??

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Axel Schwenke schrieb:
> 3. vor Ausführung der ISR müssen alle Prozessorregister gesichert
> werden.

<pedanterie> Alle in der ISR verwendeten. </pedanterie>

von Walter (Gast)


Lesenswert?

Hans Peter schrieb:
> Weiters ist mir noch aufgefallen, dass das InterruptFlag nicht gesetzt

das wird beim Aufruf des Interupts automatisch gelöscht

von Hans Peter (Gast)


Lesenswert?

@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!

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

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.

von Hans Peter (Gast)


Lesenswert?

Danke für die Antwort!

Da heißt also, es ist doch nicht richtig, einfach vor jeder ISR dieses 
Attribut anzuführen?

von W.S. (Gast)


Lesenswert?

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.

von Hans Peter (Gast)


Lesenswert?

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

von Axel S. (a-za-z0-9)


Lesenswert?

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

von Hans Peter (Gast)


Lesenswert?

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

von Karl H. (kbuchegg)


Lesenswert?

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.

von heinz (Gast)


Lesenswert?

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

von (prx) A. K. (prx)


Lesenswert?

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.

: Bearbeitet durch User
von Sebastian W. (wangnick)


Lesenswert?

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

: Bearbeitet durch User
von Axel S. (a-za-z0-9)


Lesenswert?

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

von (prx) A. K. (prx)


Lesenswert?

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.

: Bearbeitet durch User
von (prx) A. K. (prx)


Lesenswert?

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.

: Bearbeitet durch User
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.