Forum: Compiler & IDEs Funktionsaufruf aus ISR


von micro1 (Gast)


Lesenswert?

Hallo,

ich habe folgendes Programmiert.

SIGNAL(USART_RECV)
{

chr c=UDR;
xyz.adc0=get_adc(0)
xyz.adc1=get_adc(1);
xyz.adc2=get_adc(2);
xyz.abc3=get_adc(3);
xyz.abc4=get_adc(4);
xyz.abc5=get_adc(5);
xyz.adc6=get_adc(6);

xyz.abc=get_adc(7);// get_adc(7) ist funktion für adc

}
Immer wenn ich in einer ISR Routine einen Funktionsaufruf mit
Rückgabewert mache stürzt der Prozessor ab(ATMega32).
Wieso?

von peter dannegger (Gast)


Lesenswert?

Mit diesem herausgerissenen Codefragmentschnipselchen kann man dazu
überhaupt nichts sagen.


Peter

von johnny.m (Gast)


Lesenswert?

OK, eigentlich kann man sagen, dass Funktionsaufrufe in ISRs
grundsätzlich zu vermeiden sind. Ich weiß nicht, wie die Funktion
get_adc aussieht, aber ich vermute mal, dass da eine AD-Wandlung
durchgeführt werden soll, was bei 8 Aufrufen sicher eine ganze Weile
dauert und den Prozessor komplett blockiert.

Lösung: In der ISR nur ein Flag setzen (Achtung: volatile deklarieren)
und das Einlesen der Werte im Hauptprogramm erledigen.

Gruß

Johnny

von micro1 (Gast)


Lesenswert?

Hallo,

die Funktion get_adc sieht so aus:

unsigned int get_adc(int adcchannel)
   {
   ADMUX=adcchannel;
   ADCSRA|=(1<<ADSC)
   while((!(ADCSRA & (1<<ADIF)))
   ADCSRA |= (1<<ADIF);
   return ADC;
   }
In meiner ISr die ich oben schon gepostet habe erfolgt durch diesen
Aufruf ein Absturz der sich folgendermasen auswirkt.
Und zwar erfolgt der Absturz nur wenn ich aus der ISR die Funktion
get_adc() aufrufe. Das macht sich so bemerkbar. Ist der Watchdog
eingeschaltet gibt es ein reset. Ohne watchdog gibt es kein Reset
aber der AVR geht nicht mehr zurück in Hauptprogramm.
Die ISR wird aber vollständig abgearbeitet. Alle anderen ISR´s sind
auch noch aktiv. Er springt nur nicht mehr zurück ins Hauptprogramm.
Irgendwie total seltsam.

von peter dannegger (Gast)


Lesenswert?

Glaskugelbefrag:

1.
Es könnte sein, daß du zuviele globale Variablen hast und die dann mit
dem Stack kollidieren.

Durch einen Unterfunktionsaufruf im Interrupt erhöht sich nochmal
drastisch der Stackbedarf (alle Register sichern), daher kann es ohne
den Aufruf gerade noch ausreichen.

Guck mal ins Map-File.

2.
Wenn Hardware-Unterfunktion sowohl im Main als auch in Interrupts
aufgerufen werden, ist das ober-Bähh.
Hardware ist im allgemeinen nicht reentrant.



Peter

von micro1 (Gast)


Lesenswert?

Ja aber bekomme ich dann kein Compiler Warnings wenn der Stack
überläuft?
Weil das Programm wird ohne Fehler und Warnings übersetzt.
Und GCC sagt ich hätte 80% meines Datenspeichers belegt und 70%
meines Flash speichers das sollte doch funktionieren oder?

Wie kann ich sehen ob ich einen Stack überlauf habe?
Nur im map file? Oder geht das im AVR Studio wenn ich mir den Assembler
Code anschaue?

von Karl H. (kbuchegg)


Lesenswert?

> Ja aber bekomme ich dann kein Compiler Warnings wenn der Stack
> überläuft?

Nein. Wie soll der Compiler das denn machen?
Der Stack-Überlauf tritt erst zur Laufzeit auf und ist
abhängig davon, ob und wieviele Funktionen aufgerufen
werden, bzw. grade im Aufruf sind wenn der Interrupt
zuschlägt.
Das ist alles hochgradig dynamisch und mit statischer
Code-Analyse nicht zu sehen.

Wenn du dich an den Standard Code Aufbau hältst, ist das
alles kein Problem:

die main() initialisiert alles und geht dann in eine
Endlosschleife über. In dieser Endlosschleife werden
ständig ein paar globale Variablen (die als Flags fungieren)
abgefragt. Wenn diese Flags anzeigen, dass es etwas zu tun
gibt, dann wird in der main-Schleife die entsprechende
Arbeit erledigt. Die Interrupt-Funktion hat dann nur noch
die Aufgabe, die entsprechenden Flags zu setzen.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

> Ja aber bekomme ich dann kein Compiler Warnings wenn der Stack
> überläuft?

Nein, der Compiler kann das nicht wissen, da der Stackverbrauch erst
zur Laufzeit feststeht.  Extremfall: du hast eine Funktion, die sich
endlos rekursiv aufruft, dann hast du auch einen unendlich hohen
Stackverbrauch.

> Und GCC sagt ich hätte 80% meines Datenspeichers belegt

Der ATmega32 hat 2 KB RAM, wäre noch 400 Byte frei für den Stack.  Das
sollte gut ausreichen, sofern du keine anderen Schweinereien mit dem
Stack treibst, die du uns jetzt nicht erzählt hast.

> Wie kann ich sehen ob ich einen Stack überlauf habe?

Vor dem Programmstart den RAM mit einem bekannten Muster füllen,
Programm laufen lassen, zu beliebigen Zeiten nachsehen (im Debugger
oder über eine selbstgeschriebene Monitorfunktion), wie weit dieses
Muster ,,nach unten zu'' bereits überschrieben worden ist.

von Wolfram (Gast)


Lesenswert?

>Ja aber bekomme ich dann kein Compiler Warnings wenn der Stack
>überläuft
nein du bist in C da macht der Compiler was du sagst und geht davon aus
das du weisst was du willst.

Wenn du einen Funktionsaufruf in einer ISR machst so werden als erstes
sämtliche Register gesichert. (Stack + 32 Byte) Zusätzlich kommt noch
der Stackbedarf den die Funktion und evt. Unterfkt. haben.
Sollten deine anderen ISR ähnlich aussehen. müsstest du für jede ISR
die zwischenzeitlich kommt auch noch den Stackbedarf mit draufrechnen.
Sollte die ISR unterbrechbar sein (keine Ahnung ob das bei Signal so
ist) kämen noch jeweils das gleiche hinzu und es wird interessant ob
deine ISR reentrant ist.
Ich würde es vermeiden in der UART Empfangsroutine ein (oder 8)
ADCwandlungen zu machen.
Sagen wir du hast eine Empfangsrate von 9600Baud also zwischen den
Zeichen jeweils 1ms. Da kommst du mit den ADCwandlungen in die selbe
Größenordnung.

von micro1 (Gast)


Lesenswert?

@Jörg Wunsch
>Wie kann ich sehen ob ich einen Stack überlauf habe?

Kannst du mir das genauer erklären?
Oder gibt es einen Link wo man das nachlesen kann?

von Hagen R. (hagen)


Lesenswert?

Kann es sein das du get_adc() in der ISR und deiner Mainloop aufrufst ?
Wenn ja dann entsteht ein Deadlock durch die while Schleife in
get_adc(), bzw. genauer durch die Abfrage des ADIF Flags.

Gruß Hagen

von micro1 (Gast)


Lesenswert?

Ja mach ich.
Wieso entsteht da ein Deadlock

von Hagen R. (hagen)


Lesenswert?

while((!(ADCSRA & (1<<ADIF)))
   ADCSRA |= (1<<ADIF);

Die Schleife wartet so lange wie das ADIF flag nicht gesetzt ist.
Im nächsten Schritt löscht du explizit dieses Flag.

Nun, angenommen deine Mainloop ruft diese Funktion auf und startet den
ADC. Daraufhin wartest du zb. 184 CPU Takte. Aber in dieser Zeit wird
deine ISR aufgerufen die somit

1.) diese Warteschleife unterbricht
2.) selber diese Funktion aufruft
3.) quasi nun den ADC Wert der in der Mainloop gestartet wurde
übernimmt
4.) danach gleich das ADIF Flag löscht
5.) die ISR beendet und Kontrolle an die Mainloop -> Warteschleif im
ADC zurück gibt
6.) und diese Schleife nun lange warten kann auf ADIF = 1 da ja
garkeine ADC Konversion mehr läuft

Nach gewisser zeit kommt wieder deine ISR, fäng an den ADC zu samplen
löscht wieder das ADIF Flag, gibt Kontrolle an unterbrochene Main
zurück die widerum immer noch in ihrer Verbissenheit darauf wartet das
der ADC ihr einen Wert liefert und das mit ADIF signalisiert, aber
garnicht gestartet wurde !

Ergo: in ungünstigen Umständen wird deine MainLoop die ausserhalb der
ISR selbr auch get_ADC() aufruft für immer in einer Endlosschleife in
get_ADC() hängen bleiben.

Das ist zwar noch kein vollkommener Deaklock der kompletten MCU, da ja
ISRs noch abgeargeitet werden, aber die Mainloop() hängt auf alle Fälle
fest.

Gruß Hagen

von micro1 (Gast)


Lesenswert?

Ah. Alles klar vielen dank für die Info.
Jetzt ist mir das klar.

von Peter D. (peda)


Lesenswert?

Genau das meinte ich damit, daß Hardwareaufrufe nicht reentrant sind
(Eine Hardwaresequenz ist nicht von sich selber unterbrechbar).

Am lustigsten ist das bei ner UART, wenn man da von überall her sendet,
kriegt man den totalen Zeichensalat. Oder beim Schreiben auf ein LCD.


Peter

von Hagen R. (hagen)


Lesenswert?

@Peter: öfters muß man eben mit nem Holzhammer darauf hinweisen.
Möchte nicht wissen wieviele Leute das Wort "reentrant" erstmal
nachschlagen mussten ;)

Gruß Hagen

von micro1 (Gast)


Lesenswert?

Danke an alle!
Aber noch eine Frage wie kann man im AVR Studio einen Stacküberlauf
sehen?

von Hagen R. (hagen)


Lesenswert?

Hm, ich würde eine globale Variable deklarieren die aber am oberen Ende
aller Variablen im RAM liegen muß. Diese Variable initialisierst du zb.
auf 0xAAAA oder 0xFFFF oä.

Bei einem Stacküberlauf, der quasi eher ein Unterlauf im RAM ist, würde
der Inhalt dieser Variable dann nicht mehr deiner Vorinitialisierung
entsprechen.

Der RAM ist immer so aufgebaut

0x0000 -> Register File
0x1234 -> globale Variablen
0x5678 -> Stack
0xFFFF -> RAM End

von 0x0000 bis 0x1233 geht das Registerfile.
Ab  0x1234 bis 0x5678 deine globalen Variablen im RAM.
Und der Rest bis 0xFFFF der Stack.

Der Stack wird von 0xFFFF beginnend nach unten hin benutzt. Ein
Stack-"überlauf" würde also ein "Unterlauf" sein wenn er bei 0x5678
ankommt und beginnt deine globalen Variablen zu überschreiben. Nach
einiger Zeit würde der Stck so groß werden das er bei 0x1234 angekommen
ist und quasi in das Register File reinschreibt. Und bei 0x0000 entsünde
nun ein Unterlauf der dann den Stack wieder bei 0xFFFF weitermachen
würde.

Obige Addressbereiche sind natürlich reine Fantasiewerte, zur Erklärung
;)

Gruß Hagen

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.