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?
Mit diesem herausgerissenen Codefragmentschnipselchen kann man dazu überhaupt nichts sagen. Peter
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
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.
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
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?
> 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.
> 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.
>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.
@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?
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
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
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
@Peter: öfters muß man eben mit nem Holzhammer darauf hinweisen. Möchte nicht wissen wieviele Leute das Wort "reentrant" erstmal nachschlagen mussten ;) Gruß Hagen
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.