Forum: Mikrocontroller und Digitale Elektronik AVR: Endlosschleife erkennen


von Oliver L. (ollil)


Lesenswert?

Hallo,

ich habe ein Problem mit der Kommunikation meines ATMega mit einer 
Z80-PIO. Aus irgendwelchen Gründen komme ich in der Kommunikation mit 
der PIO aus dem Trab und bleibe unter gewissen Bedingungen in einer 
Endlosschleife hängen in der Hoffnung ein Signalwechsel der PIO zu 
erleben welcher aber nie eintritt.

Mein Problem ist nun, das ich gerne die Endlosschleife finden würde in 
der er hängen bleibt das genaue Signal zu erfahren auf was er wartet und 
wo er
genau im Programmablauf stecken bleibt.

Leider ist die Kommunikation mit der PIO vom Timing her so das ich 
extrem limitiert bin mit dem was ich so "dazwischen" noch machen kann. 
Ausgaben über UART fallen z.B. komplett flach.

Da auch alle Ports belegt sind, habe ich kein JTAG o.ä. zur Verfügung 
sondern ausschliesslich ISP und mein AVR Dragon.

Ich habe in meiner Signal-Warteschleife einen counter eingebaut und bei 
Überlauf des Counters gebe ich den letzten Wert einer Variable aus dem 
"Hauptprogramm" aus - in der Hoffnung zu erkennen an welcher Stelle er 
auf das Signal wartet.

Problem ist nun, das uint8_t zu wenig Wartezeit bietet. uint16_t auch, 
und uint32_t scheint irgendwie nie in den Überlauffall reinzulaufen 
(kann ich mir eigentlich nicht vorstellen). Konkrete Überprüfungen der 
Werte der Überlaufvariablen sind zeitlich auch nicht möglich (ich kann 
also nicht checken ob uint32_t i > 20.000.000 ist). Ich kann lediglich 
darauf prüfen ob der Counter 0 ist. Ich habe mir also mit einem 
Überlaufzähler beholfen. Leider ist die dadurch zur Verfügung stehende 
Zeit auch nicht ausreichen. Ein weiterer Überlaufzähler für den 
Überlaufzähler ist zeitlich schon wieder nicht möglich....

Hier mal der aktuelle Stand meiner "Warteroutine mit 
Endlosschleifenerkennung"
1
static inline  void wait_until_ardy_asserted (uint16_t err)
2
{
3
  uint8_t i,a;
4
  
5
  i=0;a=1;
6
  do {
7
    if ( !info_wardy_is_high() && !info_wardy_is_high() ) {
8
      return;
9
    }
10
    i++;
11
    if(i == 0) a++;
12
    if(a == 0) {
13
      uart_putstring(PSTR("ardy assert fehler: "),false);
14
      uart_putw_dec(err);
15
    }
16
  } while ( true );
17
}

"err" wird vom Hauptprogramm mitgegeben und gibt mir einen Hinweis auf 
die Codestelle im Hauptprogramm. Noch ein uint8_t geht nicht. 
JTAG-Debugging geht nicht (hab ich auch noch nie gemacht). Wie gesagt - 
irgendwelche uart-Ausgaben ohne das der Fehlerfall eingetreten ist geht 
auch nicht.

Hat noch einer eine Idee wie ich rausfinden kann in welcher Zeile des 
generierten ASM Codes der AVR bis zum Sankt Nimmerleinstag wartet (dank 
inline habe ich dann die Stelle des Hauptprogramms...)

Ich kann ca. sagen was eine einzelne Datenkommunikation an Zeit dauern 
müsste. Kann ich vor jeder Kommunikation einen Timer konfigurieren und 
diesen einen Interrupt auslösen lassen? Wenn ich dann die notwendigen 
Variablen um die Codestelle zu identifizieren global mache kann ich dann 
auf diese in der Interruptroutine zugreifen und diese dann dort 
ausgeben. Wenn die Datenkommunikation erfolgreich durchläuft, stoppe ich 
den Timer einfach (damit kein IRQ ausgelöst wird).... Klingt irgendwie 
durchs Knie ins Auge? Ist das so  möglich oder gibt es noch bessere 
Wege?

Bevor jemand auf die Idee kommt - ja ich habe einen LA, aber der kann 
weder Life-View noch hat er Pre-Trigger-Möglichkeiten - und der Fehler 
tritt teilweise erst nach einer Stunde permanenter Kommunikation auf... 
Keine Chance mit meinem LA da per Zufall genau diesen Mißstand zu 
erwischen.

: Bearbeitet durch User
von S. R. (svenska)


Lesenswert?

Du kannst vor der Kommunikation den Timer starten und - wenn er feuert - 
den Program Counter vom Stack pulen. Den kannst du auf dein ELF werfen 
(addr2line) und bekommst dann genau die Codezeile, wo er war, als der 
Timer feuerte.

Je nachdem, wie viel Platz du noch im Flash hast, kannst du auch den 
gesamten Stack entlanglaufen und dir den Callstack zusammenbauen. In der 
Interrupt-Routine bist du ja ganz allein im System, und da es eh schon 
abgestuerzt ist, hast du auch nichts mehr zu verlieren.

: Bearbeitet durch User
von Uwe (Gast)


Lesenswert?

Hi,
>Hat noch einer eine Idee wie ich rausfinden kann in welcher Zeile des
>generierten ASM Codes der AVR bis zum Sankt Nimmerleinstag wartet

was macht dein Wachhund derweil?
>gebe ich den letzten Wert einer Variable aus dem
>"Hauptprogramm" aus -

Der Gedanke ist doch gut, du must ihn nur weiterspinnen.
Nenne die Variable Durchlaufzahl und lade sie in jedem wichtigen 
Programmabschnitt mit einer def. Zahl. Mit etwas Hirnschmalz kann man 
das sehr fein auflösen. Schnappt dann der Wachhund zu, wird bei 
Programmstart einfach die Durchlaufzahl ausgegeben und du kannst sagen 
wo er vorher war.
Ich mache das jedenfalls so und habe immer gefunden wo es klemmt.

Viel Erfolg, Uwe

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Oliver L. schrieb:
> Hat noch einer eine Idee wie ich rausfinden kann in welcher Zeile des
> generierten ASM Codes der AVR bis zum Sankt Nimmerleinstag wartet (dank
> inline habe ich dann die Stelle des Hauptprogramms...)

 Mach doch mal einen 8-bit SysTimer mit 1ms Auflösung.
 Vor jeder Kommunikation setzt du einen Flag auf 1 und Counter auf 0.
 Nach der Kommunikation (falls erfolgreich) wieder zurücksetzen - am
 besten alles in der Kommunikations-Routine selbst.

 Wenn in der ISR der Flag gesetzt ist, wird der Counter hochgezählt.
 Wenn der Counter überläuft ( > 255ms) wird der Stack uber UART
 ausgegeben - wenn man die PUSH-Befehle in der ISR abzieht, hat man
 die Adresse (+1 oder +2) wo es klemmt.

 So etwas ähnliches hatte ich früher, bei OBD-Testern, es hat immer
 funktioniert.

 EDIT
 Und hab es immer noch - bei I2C.

: Bearbeitet durch User
von Rolf M. (rmagnus)


Lesenswert?

Oliver L. schrieb:
> Da auch alle Ports belegt sind, habe ich kein JTAG o.ä. zur Verfügung
> sondern ausschliesslich ISP und mein AVR Dragon.

Hat dein geheimer ATmega evtl. DebugWire? Dann kannst du über den 
RESET-Pin debuggen.

von A. H. (ah8)


Lesenswert?

Was ich da lese klingt für mich nach einer typischen Race-Condition: 
„Unter bestimmten Umständen“ wir Deine Warteschleife das Signal einfach 
verpassen und dann bis in alle Ewigkeit auf das bereits vergangene 
Signal warten. Um das genau beurteilen zu können müsste man wissen, was 
genau Du machst. (Warum z.B. rufst Du 2x info_wardy_is_high() auf?) Wenn 
ich mit meiner Vermutung richtig liege wird ein Debugging mit üblichen 
Mitteln schwierig, da es schwierig ist, die Fehlerbedingung zu 
reproduzieren. Schon kleinste Änderungen am Code können das Timing so 
verändern, dass das Programm nicht mehr in den Fehler hineinläuft ohne 
ihn natürlich wirklich zu beheben.

Klopfe Deine Signalkette doch mal nach Stellen ab, an denen das Signal 
verloren gehen könnte.

Eine gute Einführung in das Thema findet sich, wenn ich mich richtig 
erinnere, bei ISBN 0596005903 im Kapitel 5. Das Buch ist auch online 
verfügbar: http://www.oreilly.com/openbook/linuxdrive3/book/

von Oliver L. (ollil)


Lesenswert?

Ich habe einen "Festplattenemulator" der als Speichermedium einersiets 
SD-Card sowie auch eine PATA-Festplatte anbietet.

Auf der anderen Seite sitzt eine Z80-PIO die nicht so schön designed 
einfach Ihre PIO-Ports nach aussen über ein DB25-Kabel zur Verfügung 
stellt (Legacy System, keine Änderung möglich). Die Signalqualität ist 
daher nicht wirklich optimal. Daher sind teilweise Abfragen doppelt drin 
um "Noise" zu umschiffen.

Mit der Festplatte als Speichermedium habe ich scheinbar keinerlei 
Probleme. Ich kann das System stundenlang laufen lassen mit permanenter 
Datenbefeuerung.

Sobald ich die Festplatte abklemme und jedoch mit der SD-Card arbeite 
verhängt sich das System in der Host-Kommunikation. Es verhängt sich 
nicht, wenn ich die Bustreiber auf meinem System mit ganz bestimmten 
HCT245 bestücke. Andere HCT245, oder LS245 führen zu diesem 
"Hängereffekt".....

Irgendwas in der Schaltung wird ein Problem haben was zu ggf. mehr Noise 
führt oder was weiss ich.... Will jetzt auch gar nicht zu sehr ausholen, 
da ich das Hardwareproblem schon an anderer Stelle diskutiere.

Jedenfalls interessiert mich erstmal wo aktuell die Kommunikation hängen 
bleibt.

http://www.pofo.de/P8000/wdcemu.php

ATMega ist ein 1284P - hat der DebugWire? Habe in den Specs nach 
"DebugWire" gesucht und nix gefunden.

: Bearbeitet durch User
von Oliver L. (ollil)


Lesenswert?

Edit: Ok - mit "noinit" lasse ich meine Zählervariable den 
Watchdog-Reset überleben...



Originaler Beitrag:

Ist denn nach einem Reset via Watchdog Timer sichergestellt, das ich den 
Inhalt einer globalen Variable noch lesen kann? Ich habe mal testweise 
eine variable global angelegt, und dann auf 1 gesetzt - danach den 
Watchdog timer auf 15ms gestellt. (über eine .init3 Funktion dann beim 
Bootup den WDT auch wieder deaktiviert).

Die Variable bleibt 0 beim bootup.

Wird der SRAM nicht abgeräumt beim bootup?

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


Lesenswert?

> Ist denn nach einem Reset via Watchdog Timer sichergestellt, das ich den
> Inhalt einer globalen Variable noch lesen kann?

Nur wenn du die Variable in einer nicht initialisierten Speicher-Sektion 
platzierst: http://www.nongnu.org/avr-libc/user-manual/mem_sections.html

: Bearbeitet durch User
von Oliver L. (ollil)


Lesenswert?

Jo, genau das habe ich nun getan.

Habe eine Variable in der .noinit Sektion
Meinen Watchdog Timer so aufgesetzt, das er eine ISR auslöst
Und in der ISR sorge ich dann dafür das ich insgesamt erst nach einer 
Minute einen Reset auslöse.

Ich hoffe, das ich nun so weiterkomme.... Vielen Dank erstmal soweit!!

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.