Hi Leute, ich habe leider nicht die Möglichkeit JTag zu verwenden. Habe nur einen einfachen ISP Programmer (mkII) Habe mein Programm um Source Code für die tm1637 7 Segment Anzeige erweitert. Funktioniert alles auch soweit. Zusätzlich hatte ich auch schon ein Modul geschrieben, mit dem ich Buttons einlese. Dieses Button Modul habe ich x Fach in Verwendung und bisher nie Probleme gehabt und auch nichts geändert. Drückt man nun einen solchen Button, hängt sich mein Programm auf. Ich kann den Fehlerfall also auslösen. Ich kann die Stelle die das Problem verursacht dennoch nicht lokalisieren. Ich kann an verschiedensten Stellen Code auskommentieren, der an sich nichts direkt mit den Funktionen an sich zu tun hat und das Problem ist weg. Deshalb gehe ich davon aus, es hat irgendwas mit dem Speichermapping oder Linking zu tun. Also abhängig davon wie das Programm zusammengebaut wird und welche Adressen verwendet werden tritt das Problem auf oder nicht. Speicher ist auch nicht voll: Program: 16164 bytes (49.3% Full) (.text + .data + .bootloader) Data: 738 bytes (36.0% Full) (.data + .bss + .noinit) Ich habe einen Scheduler, der durch eine ISR synchronisiert wird. Ich konnte soweit bisher herausfinden, dass wenn sich das Programm "aufgehängt" hat diese ISR immer noch aufgerufen wird. Es wird kein Reset durchgeführt und alle nicht benötigten anderen ISRs fange ich auch ab. Die nicht benötigten ISRs werden nicht angesprungen. Ich benutze keinen Watchdog. Was seltsam ist, ist dass im MAP file mein neues Modul als einziges unter .progmem.gcc_sw_table zu finden ist. *(.progmem.gcc*) .progmem.gcc_sw_table 0x00000054 0x2c obj/tm1637.o und dann nochmal unter .text für was ist der Bereich .progmem.gcc_sw_table und was ist der Auslöser, dass etwas in diesem Bereich landet? Habt ihr irgendwelche Tipps, wie ich weiter Debuggen kann um das Problem einzugrenzen?
Meine Glaskugel lokalisiert den Fehler in Zeile 42. Ohne Glaskugel würde ich auf ein vollgelaufenes Ram/Stack tippen.
Thomas Frosch schrieb: > dass wenn sich das Programm "aufgehängt" hat Das Programm bzw. der µC "hängt" sich nicht auf, sondern vermutlich wartet er verbittert mit seiner gesamten Rechenleistung in einer Mikroschleife auf irgendwas, das nie wieder kommen wird. > Drückt man nun einen solchen Button, hängt sich mein Programm auf. > Ich kann den Fehlerfall also auslösen. Sprich: der Fehler ist in deinem Programm. Das kommt mit irgendeiner Reaktion aus der Umwelt nicht zurecht. > Deshalb gehe ich davon aus, es hat irgendwas mit dem > Speichermapping oder Linking zu tun. Ich tippe auf den üblichen amoklaufenden Pointer... > Ich kann die Stelle die das Problem verursacht dennoch nicht > lokalisieren. Dann gibt doch einfach uns die Chance, indem du deinen Code hier anhängst.
:
Bearbeitet durch Moderator
Sorry, wollte euch nicht gleich den ganzen Source Code hochladen. hab mal im Anhang eine der Stellen, die ich hinzugefügt habe, dann geht es. links geht nicht rechts geht Sehe im Map file zwei Auffälligkeiten. links Zeile 1095 ist unter __do_copy_data das Object File genannt. rechts nicht. links Zeile 576 steht 0x21 für den Fall, dass es nicht geht. Im Fall das es geht steht dort 0x0. Im File tm1637.c.png seht ihr was ich einkommentiert habe, damit es geht. Kenn mich mit map files nicht wirklich aus.
Thomas Frosch schrieb: > Sorry, wollte euch nicht gleich den ganzen Source Code hochladen. Mache das, oder du bekommst keine Hilfe. Und zwar inklusive der Linker Konfiguration, falls vorhanden.
Thomas Frosch schrieb: > Habt ihr irgendwelche Tipps, wie ich weiter Debuggen kann um das Problem > einzugrenzen? In dem von dir verdächtigen Code-Abschnitt eine Ausgabe auf die 7-Segment Anzeige schreiben. Nach Code-Zeile .. Ausgabe "1" Nach Code-Zeile .. Ausgabe "2" Dann weißt du wie weit das Programm gelaufen ist.
:
Bearbeitet durch User
Was soll der PNG-Mist. Stell das vollständige ZIP rein, was ohne Error / Warnings compiliert.
:
Bearbeitet durch User
Jetzt aber weg mit dem Troll. Wenn man sich schon derart zersplitterten Code antut sollte man etwas weiter mir Debugstrategien sein.
Wie viele Unterprogramme samt IRQs können denn da maximal aufgerufen sein? Mfg
Also mir ging es ja auch ein bisschen darum, wie man einen solchen Fehler systematisch findet. Folgendes habe ich getan: === Schritt #1: Stackoverflow === Zunächst habe ich anhand dieser Seite: https://rn-wissen.de/wiki/index.php/Speicherverbrauch_bestimmen_mit_avr-gcc den Stack Verbrauch ermittelt. Ergebnis: Es sind noch über 1000byte frei. Also Stackoverflow kann ich ausschließen === Schritt #2: Softwarewatchdog implementiert === In der main while wird eine Zählvariable auf 0 gesetzt. In meiner Scheduler ISR die ja auch im Fehlerfall weiterhin aufgerufen wird, inkrementiere ich die Variable und prüfe sie auf einen Schwellwert ab. So konnte ich erkennen, ab wann sich die SW "aufhängt" und auch hier per SW Bitbang Uart zu diesem Zeitpunkt den zuletzt verbrauchten Stack ausgeben. Auch hier kein Stackoverflow. === Schritt #3: Rücksprungadresse aus ISR herausfinden === Versuch den Stackframe zu interpretieren um die Rücksprungadresse und damit die Fehlerhafte Stelle (Hängende Schleife) zu finden. Leider bin ich daran gescheitert. Hab Teile vom Stack via SW Bitbang Uart herausgeschrieben und im lss file geprüft wie viele push und pops gemacht werden um die Position der Rücksprungadresse im Stackframe zu finden. Jedoch habe ich keine sinnvolle Adresse gefunden. Wahrscheinlich habe ich die Struktur des Stackframes noch nicht richtig verstanden oder es muss noch ein Offset draufgerechnet werden. Hier muss ich nochmal ansetzen. Will das für die Zukunft verstehen. Wenn hier jemand gute Links zur Erklärung für AVR hat, dann gerne her damit! === Schritt #4: Ab da an wo ISR Fehler festgestellt hat, Software zurückverfolgen === Ich habe mir in der ISR beim Fehlerfall eine Variable gesetzt und dann in der Software an den verschiedensten Stellen diese abgefragt. Wenn die Variable gesetzt war, hab ich mir eine Marke via SW Bitbang Uart rausgeschrieben und konnte nach mehreren Implementierungen von Marken die Stelle finden. War tatsächlich eine FOR Schleife. Hatte eine const Variable definiert und zwar so: const uint8_t C_U16_BLABLA und dann in dem File in dem die Schleife verwendet wird so darauf zugegriffen extern const uint16_t C_U16_BLABLA das ganze war einer Konfigurationsanpassung geschuldet, die ich vor einem Jahr durchgeführt hatte und leider wie man oben sieht nicht ganz durchgezogen hab. uint8_t statt uint16_t verwendet. Im Namen aber noch brav angepasst. Hatte also überhaupt nichts mit meiner neuen TM1637 Implementierung zu tun sondern einfach nur damit, dass an das higher Byte dieser Konstanten was anderes hingemappt wurde wenn ich dieses Modul genutzt habe. Dadurch lief die Schleife statt bis 4 bis über 16000 und hat dadurch noch weiteren Schaden angerichtet. Jetzt noch folgende Fragen: - Wie kann man solche Fehler schneller finden? - Wie hättet ihr das gemacht? (ohne Debugger) - Hat jemand eine gut erklärte Seite über Stackframe bei ISR? Sodass ich Stack auslesen kann und dann im lss file nach der Adresse suchen kann? Würde mich über Antworten freuen. Auch wenn ich mir zu Weihnachten wahrscheinlich langsam mal einen JTAG Debugger schenken werde :-)
Thomas Frosch schrieb: > - Hat jemand eine gut erklärte Seite über Stackframe bei ISR? Sodass ich > Stack auslesen kann und dann im lss file nach der Adresse suchen kann? Welche Adresse willst du denn da im lss File wann suchen? Der Prozessor schreibt bei einem Interrupt die Rücksprungadresse auf den Stack. Danach kommt der vom Compiler erzeugte Prolog mit den zu sichernden Registern, und dann kommt eventuell noch ein Frame für lokale Variablen, falls erforderlich. Wie das genaus aussieht, verrät dir der Assemblercode der ISR. Das einzige, was du da vom Stack holen kannst, ist die Rücksprungadresse. Thomas Frosch schrieb: > - Wie hättet ihr das gemacht? (ohne Debugger) Mit Debugger, in der Simulation. Oliver
:
Bearbeitet durch User
Thomas Frosch schrieb: > const uint8_t C_U16_BLABLA Ist das die Ungarische Notation? Dann ist das eins der besten Bespiele dafür, wie bescheiden die doch ist. Thomas Frosch schrieb: > Jetzt noch folgende Fragen: > - Wie kann man solche Fehler schneller finden? > - Wie hättet ihr das gemacht? (ohne Debugger) Eine tool chain verwenden, welcher den dummen Fehler auch bemerkt und meldet.
1 | E:\Programme\arduino\portable\sketchbook\sketch_dec14b\sketch_dec14b.ino:4:27: warning: type of 'test' does not match original declaration [-Wlto-type-mismatch] |
2 | 4 | extern const unsigned int test; |
3 | | ^ |
4 | E:\Programme\arduino\portable\sketchbook\sketch_dec14b\test.c:2:21: note: type 'const unsigned char' should match type 'const unsigned int' |
5 | 2 | const unsigned char test = 34; |
6 | | ^ |
Thomas Frosch schrieb: > Wie kann man solche Fehler schneller finden? Probiere mal Qt Creator als IDE aus. Sie erkennt viele potentielle Fehler und zeigt sie schon beim Tippen an. Ich habe auch keine Ahnung wie man den Stack untersucht. Der Debugger nimmt mir diese Arbeit ab. Wenn ich den Debugger nicht verwenden kann (passiert bei AVR oft) gebe ich den Inhalt von Variablen und Auführungs-Markern seriell aus und schreibe sie mit dem PC in eine Datei. Wenn der serielle Port belegt ist, benutze ich einen I/O Pin mit Soft-UART (nur Ausgabe ist einfach). Wie du gesehen hast kann es sogar hilfreich sein, die Zählvariablen von Schleife auszugeben. Leider verändert sich dadurch das Timing der Anwendung erheblich, deswegen kann man auch das nicht immer machen. Der letzte Notnagel ist, Zustände auf I/O Pins zu signalisieren. Wenn der hängt kann man dann an den I/O Pins ablesen, wo er hängt. Früher als ich noch Mikroprozessoren verwendete, konnte man die Adresse des Programmzählers direkt an den entsprechenden Pins ablesen. Ich benutze gerne Git (oder Subversion) um Änderungen schrittweise zu protokollieren. Wenn dann später ein Fehler auftritt, kann ich das Programm schrittweise wieder zurück bauen, bis der Fehler verschwindet. Dann wiederhole ich meine Änderungen (anhand des Protokolls) in kleinen Schritten und achte ganz genau darauf, ab wann der Fehler wieder auftritt. Das gibt mir meistens einen Hinweis, woran es liegen könnte.
Stefan ⛄ F. schrieb: > Probiere mal Qt Creator als IDE aus. Sie erkennt viele potentielle > Fehler und zeigt sie schon beim Tippen an. Den Fall nicht. Oliver
Screenshot von Qt Creator Oliver S. schrieb: > Den Fall nicht. Datei-Übergreifend erkennt sie es leider nicht. Schade. Abgesehen davon meldet die IDE mehr Fehler, als viele andere (Netbeans, Eclipse, Visual Studio Code, AVR Studio, Atmel Studio). Zumindest war das vor 3 Jahren noch so. Vielleicht wurden die anderen IDE inzwischen auch verbessert. Auf jeden Fall kann es nicht schaden, fast alle Warnungen des Compilers mit dem Parameter -Wall zu aktivieren und das Programm zu zu bereinigen, dass er keine Warnungen ausgibt - auch wenn da ab und zu mal ein "Fehler" angemeckert wird, der keiner ist. Ich verstehe nicht, warum die das in der Arduino IDE nicht zur Standard-Vorgabe gemacht haben. Auf der Arbeit programmiere ich in Java, da nutzen zur zusätzlich zum Compiler das Programm Spotbugs (entsprechend Lint bei C). Dort haben wir die Regel, dass unsere Programme ohne Warnungen gebaut werden müssen. Wir haben die Möglichkeit, einzelne Checks an gewünschten Code-Stellen zu deaktivieren, aber dann auch die Pflicht zu kommentieren, warum wir uns da 100% sicher sind. In 1.500.000 Zeilen Quelltext kommt das vielleicht 20 mal vor. Nicht öfter.
Stefan ⛄ F. schrieb: > Datei-Übergreifend erkennt sie es leider nicht. Schade. Das schafft auch gcc nur mit lto, mit der aktivierten Warnung. Eine statische Codeanalyse sollte sowas auch finden, aber wer nutzt sowas privat schon. Oliver
Stefan ⛄ F. schrieb: > Datei-Übergreifend erkennt sie es leider nicht. Schade. Nur wenn man sich blöd anstellt. Wer eingetretene Pfade verlassen will, muss halt mit solchen Problemen rechnen. Der übliche Weg wäre: In irgendeiner Datei, die Variable definieren In einer *.h Datei die Variable deklarieren. Diese *.h Datei in alle Dateien einbinden, wo die Variable auftaucht.
1 | test.c:2:21: error: conflicting types for 'test'; have 'unsigned char' |
2 | 2 | const unsigned char test = 34; |
3 | | ^~~~ |
4 | In file included from E:\Programme\arduino\portable\sketchbook\sketch_dec14b\test.c:1: |
5 | E:\Programme\arduino\portable\sketchbook\sketch_dec14b\test.h:2:27: note: previous declaration of 'test' with type 'unsigned int' |
6 | 2 | extern const unsigned int test; |
7 | | ^~~~ |
Hier mal im konkreten Beispiel, wie sowas aussehen könnte/sollte. Geht natürlich mit JEDER IDE so, oder so ähnlich. Und auch ohne IDE
EAF schrieb: > er übliche Weg wäre: > In irgendeiner Datei, die Variable definieren > In einer *.h Datei die Variable deklarieren. > > Diese *.h Datei in alle Dateien einbinden, wo die Variable auftaucht. Der richtige Weg wäre: gar keine globalen Variablen benutzen. Oliver
Oliver S. schrieb: > Der richtige Weg wäre: gar keine globalen Variablen benutzen. Wenn man denn Bäcker ist, ist das im Bereich der Möglichkeiten. Kann es sein, dass du etwas angepisst bist, weil dir ein Arduino User das Zeugs unter die Nase reibt?
Deshalb ist es auch in der heutigen Zeit gut, einen gut zugänglichen Reset-Knopf aufs Board zu setzen. Damit man nicht jedes mal die Spannungsversorgung abschalten muss, nur weil der Prozessor sich aufgehängt hat.
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.