Forum: Mikrocontroller und Digitale Elektronik Atmega stürzt ab, bleibt hängen


von kevin m. (kevinm)


Angehängte Dateien:

Lesenswert?

Guten Abend,

ich habe schon länger ein Problem mit meiner Software auf dem Atmega. 
Hardware fehler schließ ich aus.

Info zur Hardware/Software. Es handelt sich um einen Atmega 644 der mit 
16 Mhz läuft. Der Atmega ist mit einem MCP2515 und MCP2551 an der Can 
Bus Seite verbunden. Zum PC wird mit einem FTDI treiber über UART 
gesendet.

Das Programm funktioniert ohne Probleme, wenn nur CanBus Nachrichten 
empfangen werden und diese zum PC gesendet werden.

Werden gleichzeitig von PC auf den CANBus nachrichten gesendet, stürzt 
das Programm ab und startet neu und stürzt wieder ab und startet wieder 
neu solange ich nicht den ext. Reset drücke. Nicht Sofort sondern 
meistens nach ein paar 100 Nachrichten. Kann aber auch vorkommen das ein 
paar 1000 Nachrichten hin und herschickt und erst dann abstürzt. 
Manchmal bleibt das Programm aber einfach hängen und der Watchdog 
schaltet sich ein.

Der Programmcode ist auf ein minimum reduziert, der Fehler aber noch 
immer vorhanden. Ich habe im Programm so eine Art Zeilenmarke eingebaut, 
diese wird dann vom Watchdog angezeigt. gebracht hat mir das aber 
nichts. Anscheinend bleibt das Programm hängen, wenn es den UART 
Interrupt beendet. Aber wo er dannach genau hinspringt kann ich trotzdem 
nicht sagen. Meistens stürzt der Atmega aber sofort ab ohne das der 
Watchdog eingeschaltet wird.

Ich finde den Fehler einfach nicht, es sind schon soviele Tage vergangen 
und das Programm stürzt noch immer ab.

von Ben (Gast)


Lesenswert?

Hallo Kevin,

Ohne Quellcode bzw. Listing ist das schwer zu sagen. Klingt nach 
interrupt gesteuerten Prozessen, die sich globale Variablen teilen und 
das nicht ordentlich synchronisiert ist.

Grüße aus Bayern

Bernhard

von Conny G. (conny_g)


Lesenswert?

Auch Stack-Probleme haben solche Effekte. Also Stack zu klein bzw 
zuviele Stackvariablen. Oder der Stack wird überschrieben.

von kevin m. (kevinm)


Lesenswert?

Hallo Ben,

Habe das Projekt im ersten Beitrag angehängt. Ist ein komplettes AVR 
Studio 4 Projekt gepackt als rar. Soll ich es anders Abspeichern?

Hab soviel schon überlegt an was es liegen kann und komm aber nicht 
drauf bzw. fehlen mir wahrscheinlich kenntnisse dafür. Aber das mit 
einer Variable was nicht stimmt, klingt für mich auch plausibel.

Ich habe ein Interrupt BADISR, diese wird nicht ausgeführt und auch hab 
ich geprüft ob einer der beiden FIFO voll wird, aber auch dass konnte 
ich nicht feststellen.

von katastrophenheinz (Gast)


Lesenswert?

Conny G. schrieb:
> Auch Stack-Probleme haben solche Effekte. Also Stack zu klein bzw
> zuviele Stackvariablen. Oder der Stack wird überschrieben.

Würde ich auch drauf tippen.

von Ben (Gast)


Lesenswert?

Hallo Kevin,

Sorry, hab ich nicht gesehen, dass Du das Programm angehängt hast.

Schau ich mir mal an.

von katastrophenheinz (Gast)


Lesenswert?

Narbend,

was auf den ersten Blick unglücklich gelöst ist:
Du rufst aus deinem Uart-Receive-Interrupt heraus "write_string_dogm" 
auf.
Unglücklich in zweierlei Hinsicht:
- Durch die Aufrufkette hinter "write_string_dogm" packst du dir nochmal 
einen ganzen haufen Worte auf den ohnehin schon vollen Stack
- write_string_dogm ruft indirekt wieder write_dogm auf, wo sich ein 
Haufen Delay's tummeln. D.h. blockst du damit weitere Interrupts, die du 
dann verpennst.

Besser isses, die ISR kurz und knackig zu machen und keine weiteren 
Funktionen aufzurufen, sondern dort lediglich ein Flag zu setzen, 
welches du dann in einer Endlosschleife in "main" auswertest.

Ich denke nach wie vor Absturzursache ist Stacküberlauf.

Gruss, Heinz

von kevin m. (kevinm)


Lesenswert?

Hallo Heinz,

du hast recht. Der Aufruf zur Anzeige im Display ist im Interrupt 
falsch.
Hab es allerdings mit einer if abfrage eingebaut um festzustellen, ob 
der FIFO Voll ist oder ziemlich stark befüllt ist.

    if(freeIndex == PUFFER_UART) write_string_dogm("Fehler 
UART_FIFO_OVERFLOW");//Puffer Overflow
    if(freeIndex == 10) write_string_dogm("[10in]");//Puffer Overflow

Das Display bleibt allerdings leer und der Atmega bleibt trotzdem hängen 
oder stürzt ab.

Die Funktionen sind nur zum testen in der ISR, werden wieder entfernt 
sobald es irgendwann mal funktionieren sollte.


EDIT:

Muss mich nochmal korrigieren. Ich habe gerade nochmal getestet und 
dabei festgestellt, dass ich nach einer gewissen zeit ( eben immer 
unterschiedlich ) keine Nachrichten mehr zum PC gesendet bekomme. Daraus 
schließe ich, dass das Programm hängen geblieben ist. Aber nach kurzer 
Zeit darauf wird mir im Display Fehler UART FIFO OVerflow angezeigt. 
Nachrichten erhalte ich trotzdem keine mehr. Nach Reset funktionierts 
wieder

: Bearbeitet durch User
von katastrophenheinz (Gast)


Lesenswert?

Wirf mal einen Blick ins map-file.
Du belegst allein schon ca 3 kb mit statischen Objekten im RAM.
Der Killer sind die 15 UART_FIFO-dinger mit jeweils 155 Bytes..
D.h. allein damit belegst du schon grob gerechnet 2,5k. Dann kommt noch 
Kleinkram dazu.

Und in main holst du dir dann nochmal 500 Bytes vom Stack.
D.h. da bleibt nicht nicht mehr viel übrig für weitere lokale Vars., 
Rücksprungadressen und gepushte Registerinhalte.

-> Quick Fix:weniger UART_FIFO-Dinger oder die struct UART_FIFOs kleiner 
machen.
-> Besser: Programmdesign überdenken

Ich poste dir gleich noch mal ein Stück programmcode, mit dem du zur 
Laufzeit gucken kannst, wieviel Stack/Heap du noch frei hast.

Gruss, Heinz

von katastrophenheinz (Gast)


Angehängte Dateien:

Lesenswert?

get_mem_unused() liefert die anzahl Bytes zwischen Heap und Stack, die 
noch nie genutzt wurden. D.h. nicht den aktuellen Stand, sondern den 
Minimalwert. Das Ganze funktioniert so, daß zu Beginn der gesamte RAM 
mit einem Muster beschrieben wird. get_mem_unused() guckt dann, wieviel 
davon noch vorhanden ist.

Hope that helps, Heinz

von kevin m. (kevinm)


Lesenswert?

Jetzt is schon recht spät.. ich habe das probiert und mir im laufendem 
Betrieb deine funktion ausgegeben. Daraufhin hab ich die Variabel größen 
in meinem Programm verkleinert. Deine Funktion zeigt mir jetzt ca 3000 
byte an.

Es ist einmal vorgekommen beim testen: Programm hängt, deine funktion 
zeigt mir 0 Byten.

Ansonsten hängt mein Programm obwohl platz vorhanden ist. Es läuft aber 
besser wie vorher.

von Purzel H. (hacky)


Lesenswert?

dynamischen Speicher ... sollte man strikt vermeiden. sonst ist es gar 
nicht moeglich soviel platz zu verbrauchen. bei ausschlisslich 
statischen variablen weiss man fast ganz genau wieviel platz man braucht

von katastrophenheinz (Gast)


Lesenswert?

Ich vermute immer noch, dass du dir da irgendwo verschachtelte 
Interrupts zusammenbaust. Anders kann ich mir nicht erklären, daß der 
Stack überläuft. Eine offensichtliche endlose Rekursion sehe ich auf den 
ersten Blick nicht.

Für mich ist nach wie vor die Implementierung von "write_dogm" das rote 
Tuch. Du verballerst darin für jedes einzelne Zeichen 4ms mit aktivem 
Warten. Zudem rufst du das mal aus dem Hauptprogrammpfad auf, mal aus 
dem Interrupt. D.h. ein gerade aktives "write_dogm_string", das aus dem 
Hauptprogrammpfad aufgerufen wurde, kann erneut durch ein 
"write_dogm_string", das aus einem Interrupt heraus aufgerufen wurde, 
unterbrochen werden. Das gibt im mindesten merkwürdige Effekte auf dem 
Display.

Zum anderen dauert jede Ausgabe von Zeichenketten aus Prozessorsicht 
Ewigkeiten. Gerade wenn du diese Ausgabe aus einem Interrupt heraus 
machst, sind weitere Interrupts für Ewigkeiten gesperrt und du verpasst 
möglichweise weitere Interrupts.

Ich würde wie oben schon gesagt vorgehen:

a)Interrupts kurz und knackig und keinen zeitintensiven Dinge aus 
Interrupts heraus. D.h. Im Interrupt selbst nur Flags oder 
Zählervariablen setzen und die dann in "main" abarbeien

b) write_dogm nicht mit aktivem Warten implementieren, sondern dafür 
einen Ausgabepuffer und Timerinterrupt verwenden.

c) statt cli() ... sei() immer ATOMIC_BLOCK(RESTORESTATE) verwenden. 
Macht in deinem Code jetzt keinen Unterschied, aber das ist ein extrem 
guter Kandidat zum Selbstabschießen.

Gruss, Heinz

von der alte Hanns (Gast)


Lesenswert?

Warum nimmt man nicht einfach einen 1284 mit seinen 16 kiB, um die 
Speicherproblematik auszuschließen bzw. zu bestätigen? Danach kann man 
weitersuchen.

von Zuuz (Gast)


Lesenswert?

Warum immer selbst das RAD neu erfinden?

http://www.mhs-elektronik.de/index.php?module=content&action=show&page=tinycan_hardware

Und weitere dutzende !!!

von Conny G. (conny_g)


Lesenswert?

Zuuz schrieb:
> Warum immer selbst das RAD neu erfinden?
>
> 
http://www.mhs-elektronik.de/index.php?module=content&action=show&page=tinycan_hardware
>
> Und weitere dutzende !!!

Weil es beim Elektronikbasteln um den Spass an der Aufgabe geht. Sonst 
würde auch jeder einfach nur mit Arduino und Shields hantieren statt 
selber eine Schaltung machen.

von kevin m. (kevinm)


Angehängte Dateien:

Lesenswert?

Die letzten Tage hab ich damit verbracht das Design sprich den 
Programmcode zu ändern. Danke schonmal an die Denkanstösse bzw. Hilfe.

Die UART-FIFO Dinger hab ich entfernt und dafür einen Rinpuffer mit 
char[100] und 2 Zeigern verwendet. Die Struktur für CAN Nachrichten 
wurde jetzt ebenfalls als Ringpuffer mit Zeigern realisiert. Die 
Interrupts sind so kurz und schnell wie möglich, alle daten werden in 
der main() verarbeitet.
Data: 320 Bytes ( 7.8% Full ) zeigt mir der Compiler. Also hat der AVR 
jetzt reichlich Reserven.

Das Programm funktioniert soweit, bis jetzt kein Absturz mehr. Leider 
werden jetzt einige Interrupts und somit CAN Nachrichten "verschluckt". 
Gekennzeichnet hab ich es im Programm mit
if(MCPMessageReceived[1] == MCP_BUFFER_1) PIEP1(500);

Wenn diese Anweisung Wahr ergibt, heißt es, das Flag für den MCP2515 
Buffer 1 VOLL ist noch immer gesetzt. Da der MCP2515 nur 2 Buffer für 
Nachrichten enthält, überschreibt er eine vorhanden oder kann diese 
nicht mehr speichern.

Kann sich bitte einer mein Programmcode anschauen und mir vll zu dem 
Problem eine Antwort geben? Gibt es eine Funktion die sehr viel 
Geschwindigkeit kostet?

Die einzige Funktion die mir ins Auge sticht ist:
          char bufferString[60];
          sprintf(bufferString,"%c%#3x>%#2.2x>%#2.2x>%#2.2x %#2.2x 
%#2.2x %#2.2x %#2.2x %#2.2x %#2.2x 
%#2.2x%c",2,LesezeigerCAN->id,LesezeigerCAN->rtr,LesezeigerCAN->length, 
LesezeigerCAN->data[0], LesezeigerCAN->data[1], LesezeigerCAN->data[2], 
LesezeigerCAN->data[3], LesezeigerCAN->data[4], LesezeigerCAN->data[5], 
LesezeigerCAN->data[6], LesezeigerCAN->data[7],3);
          uart_puts(bufferString);
Braucht der Prozessor viel zeit um diese Funktion abzuarbeiten? würde es 
die geschwindigkeit verbessern diese Funktion selber mir bit Shifting zu 
erstellen?


Ich werde vll versuchen als nächstes einige Programmabschnitte mit einem 
Timer zu analysieren. Ich weis nicht ob es der richtige weg ist oder ob 
es vll Physikalisch nicht möglich ist mit dieser Hardware die 
Nachrichten von einem 500kbit bus mit voller Geschwindigkeit echtzeit 
auf den PC zu übertragen.

von katastrophenheinz (Gast)


Lesenswert?

> Gibt es eine Funktion die sehr viel Geschwindigkeit kostet?
ja "uart_puts" bzw. "uart_putc" - hier vertrödelst du immer noch Zeit 
mit aktivem Warten. Dh. solange bis das letzte Byte im Sendepuffer 
steht, beschäftigt sich uart_puts mit nichts anderem als warten. Neben 
dem Empfang, den du ja schon auf Interrupt-Betrieb gebaut hast, solltest 
du auch das Senden auf Interrupt-Betrieb umstellen.
-> guck dir mal P. Fleury's UART-Lib an.

> Ich weis nicht ob es der richtige weg ist oder ob
> es vll Physikalisch nicht möglich ist mit dieser Hardware die
> Nachrichten von einem 500kbit bus mit voller Geschwindigkeit echtzeit
> auf den PC zu übertragen.

Hängt primär von der max. Busauslastung ab:
Rechne mal überschlagsmäßig:
500kbit -> ca. 50kByte pro Sekunde
-> alle 20 µS ein Byte bei 100% Buslast
-> Bei F_CPU=16MHz (stimmt das?) und platt gerechnet 2 Clock cycles / 
Instruction hast du dann gerade mal durchschnittlich 160 
Maschinenbefehle, um ein Byte zu empfangen, umzupacken, rauszusenden, 
und das nur in eine Richtung. D.h. bidirektional dann nur noch 80 
Maschinenbefehle -

Nun wirst du keine 100% Buslast haben, aber sagen wir mal 50%. D.h. ganz 
grob gepeilt: 160 Maschinenbefehle pro Byte.

Hängt aber primär von der tatsächlichen Busauslastung ab. Wenn du nur 
kurze Zeiten mit hoher Buslast hast, dann kannst du das über fettere 
Puffer abpuffern (Platz genug hast du jetzt ja ;-) ), wenns im Mittel 
50% Busauslastung sind, dann meine Baucheinschätzung: Funktioniert nicht 
aus der Tüte programmiert, sondern erfordert hardcore-Optimierung. Mit 
der Reaktivierung von "dogm_write_string" und dem damit verbundenen 
"warte 4 ms pro Zeichen" schon gar nicht.

>sprintf(bufferString, ...

Kannst du ja mal in einem anderen Projekt testen, ungefähr so:
- 16bit-Timer (Timer1) im NormalMode mit 1MHz hochzählen lassen
- Interrupts deaktivieren
- TCNT1 auslesen
- sprintf aufrufen
- TCNT1 auslesen
-> Differenz der beiden TCNT1-Werte = Ausführungszeit(sprintf) in µS.

Gruss, Heinz

von kevin m. (kevinm)


Lesenswert?

Wollte mich nochmal kurz melden.

Es funktioniert jetzt alles wunderbar.

Der letzte Fehler lag daran, dass ein Fehler im UART Ringpuffer 
vorhanden war und dieser deswegen immer in der Schleife blockierte.

Vielen Dank vor allem an katastrophenheinz der mir wirklich sehr gut 
weitergeholfen hat.

Programm stürzt jetzt nicht mehr ab, auch mit kompletten Programmunfang, 
und ist zudem super schnell gegenüber meiner vorgänger Version.

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.