Forum: Mikrocontroller und Digitale Elektronik USART-Interrupts auf Atmega lösen manchmal nicht aus


von Florian H. (flowbi)


Lesenswert?

Hallo ihr Lieben,
Folgendes Problem:
Ich arbeite mit einem ATMega 2560, einen Atmel-Ice Debugger und Atmel 
Studio 6.2 auf Windows 10.
Im Allgemeinen funktioniert die Arbeit auch. Allerdings kommt es 
scheinbar zufällig vor, dass die USART-Interrupts (Receive Complete) 
nicht mehr funktionieren. Das ist bisher nach kleinen Code-Änderungen 
vorgekommen (Bei vielen anderen ist nichts passiert). Nach Rückbau der 
Änderungen ändert sich aber nichts am Fehlverhalten. Die Änderungen 
haben nichts mit dem USART-Teil zu tun. Alle Interrupts sind enabled und 
das Flag wird gesetzt. Wenn ich im Debug Modus pausiere, kann ich das 
auch wunderbar im IO View einsehen. Faktisch wird aber das Interrupt 
nicht ausgelöst, während ein normales Timer-Interrupt noch funktioniert.
Das Nichtauslösen der Interrupts mache ich an nicht ausgeführten 
Anzeigen und Breakpoints aus. (Beschränkt sich alles nicht auf den Debug 
Modus, abgesehen von den Breakpoints natürlich)
Beim ersten Auftreten hat sich das Ganze scheinbar zufällig nach einiger 
Zeit und einigen Nerven von alleine gelöst. Jetzt ist das Problem 
allerdings wieder aufgetreten. Ich habe keine Ahnung woran es liegt.
Hat schon mal jemand ähnliche Erfahrungen gemacht oder ansonsten eine 
Ahnung woran es liegen kann?

Weitere Infos:
Signal kommt am pin an, hab ich mit Oszi überprüft.
Speicher ist noch lange nicht voll.
uC hängt sich nicht auf, normale Routinen und Timer-Interrupts 
funktionieren weiterhin.
Genutzte USART-Interrupts sind die Receive Complete Interrupts von 
USART0 und USART1. Beide sind betroffen.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Der Fehler liegt im nicht geposteten Programmcode.
Du kannst bei Interesse auch mehrere C-Files anhängen...

von Florian H. (flowbi)


Lesenswert?

Den Code habe ich bewusst weg gelassen, da es sich ja um Code unabhänige 
Probleme handelt. Wie beschrieben ändert ein Code Rückbau nichts an der 
Fehlfunktion.
Falls hier dennoch begründeter Verdacht besteht, reiche ich das gerne 
nach, sehe ich aktuell aber nicht als zielführend. Außerdem ist das 
nicht sehr schön ;)

von Ulrich F. (Gast)


Lesenswert?

Meine Glaskugel sagt:
Die gesendeten Daten sind Schrott.
Bei Frame Errors werden keine Interrupts ausgelöst.
ohne Gewähr

von Karl H. (kbuchegg)


Lesenswert?

Florian H. schrieb:
> Den Code habe ich bewusst weg gelassen, da es sich ja um Code unabhänige
> Probleme handelt.

Mit einiger Sicherheit nicht.

Du hast einen Fehler im Programm, der dir vorher nicht aufgefallen ist.

von Karl H. (kbuchegg)


Lesenswert?

Florian H. schrieb:

> Beim ersten Auftreten hat sich das Ganze scheinbar zufällig nach einiger
> Zeit und einigen Nerven von alleine gelöst.

Programmfehler lösen sich nicht von alleine in Luft und Liebe auf.
Ein Problem, welches scheinbar von alleine verschwindet ohne dass man 
den Hauch einer Idee hätte warum das so ist, das ist ein Alarmsignal und 
keineswegs etwas Beruhigendes.
So etwas zu ignorieren, verschlimmert das Problem im Endeffekt meistens.

: Bearbeitet durch User
von Florian H. (flowbi)


Angehängte Dateien:

Lesenswert?

Hab hier mal die main angehangen. Ist alles sehr unübersichtlich, da 
muss ausgemistet werden. Aber dann brauch man hier keine Glaskugeln 
mehr.
Die Daten sind nicht schrott, im Normalfall funktioniert ja alles. Ist 
es denn so, dass beim Frame Error das Flag nicht gesetzt wird, oder das 
Interrupt zum Flag nicht ausgeführt wird? Das Data Receive Complete Flag 
wird nämlich gesetzt.

von Karl H. (kbuchegg)


Lesenswert?

Ulrich F. schrieb:
> Meine Glaskugel sagt:
> Die gesendeten Daten sind Schrott.
> Bei Frame Errors werden keine Interrupts ausgelöst.
> *ohne Gewähr*

mag sein, dass ich mich irre.
Aber meiner Erinnerung noch, wird auch in so einem Fall ein Receive 
Complete Interrupt ausgelöst. Alles andere wäre auch dämlich.

von Peter II (Gast)


Lesenswert?

Da hat sich ja jemand die finger wund getippt:
1
void trans_stop(void)
2
{
3
  while(!(UCSR0A & (1<<UDRE0)));
4
  UDR0 = 's';
5
  while(!(UCSR0A & (1<<UDRE0)));
6
  UDR0 = 't';
7
  while(!(UCSR0A & (1<<UDRE0)));
8
  UDR0 = 'o';
9
  while(!(UCSR0A & (1<<UDRE0)));
10
  UDR0 = 'p';
11
}

warum nicht einfach eine Funktion schreiben, die strings ausgibt. Dann 
könnte der Code sogar lesbar werden.
1
void trans_stop(void)
2
{
3
  SendStr("stop");
4
}

von Florian H. (flowbi)


Lesenswert?

Peter II schrieb:
> Da hat sich ja jemand die finger wund getippt:
>
>
1
> void trans_stop(void)
2
> {
3
>   while(!(UCSR0A & (1<<UDRE0)));
4
>   UDR0 = 's';
5
>   while(!(UCSR0A & (1<<UDRE0)));
6
>   UDR0 = 't';
7
>   while(!(UCSR0A & (1<<UDRE0)));
8
>   UDR0 = 'o';
9
>   while(!(UCSR0A & (1<<UDRE0)));
10
>   UDR0 = 'p';
11
> }
12
>
>
> warum nicht einfach eine Funktion schreiben, die strings ausgibt. Dann
> könnte der Code sogar lesbar werden.
>
>
1
> void trans_stop(void)
2
> {
3
>   SendStr("stop");
4
> }
5
>

Und genau deswegen wollte ich den Code nicht posten.
Ich kommuniziere mit ner Matlab Gui, die funktion trans_stop soll 
einfach nur die vier chars senden. Wider besseren Wissens bin ich davon 
ausgegangen, dass die Funktion genau das tut was sie soll.
Das ganze hat aber überhaupt nix mit der Fragestellung zu tun...

Frame Error Flag wird übrigens nicht gesetzt. Nur Receive Complete und 
Data Register Empty. Also eigentlich alles paletti.

von Rainer B. (katastrophenheinz)


Lesenswert?

Eine offensichtliche Fehlerursache liegt darin, dass du aus der 
Interrupt-Service-Routine heraus "trans_val()" aufrufst, die ewig lange 
benötigt. Alle RXC-Interrupts, die in dieser Zeit auflaufen werden bis 
auf den letzten "vergessen".

von Peter II (Gast)


Lesenswert?

Florian H. schrieb:
> Ich kommuniziere mit ner Matlab Gui, die funktion trans_stop soll
> einfach nur die vier chars senden. Wider besseren Wissens bin ich davon
> ausgegangen, dass die Funktion genau das tut was sie soll.
> Das ganze hat aber überhaupt nix mit der Fragestellung zu tun...

war ja nur als netter Hinweis gemeint, je lesbarer ein Code ist, desto 
weniger können sich darin Fehler verstecken.

von Karl H. (kbuchegg)


Lesenswert?

Florian H. schrieb:
> Hab hier mal die main angehangen. Ist alles sehr unübersichtlich, da
> muss ausgemistet werden.

Dann mach das mal.
Der ganze Code kann um einiges eingedampft werden. Die Übersichtlichkeit 
würde dadurch nicht leiden. Ganz im Gegenteil.
1
....
2
    else
3
    {
4
      samples[i]=buffer;
5
      i++;
6
    }

gefährlich, gefährlich. Bei einem Array der Länge 4.
Und vor allen Dingen: so überflüssig wie ein Kropf.

Warum setzt ihr denn die Zahl nicht gleich während des Empfangens 
zusammen? (d.h. wenn das empfangene Zeichen ein digit ist). Beim '.' 
wird dann nur noch entschieden, in welche Variable (je nach tf bzw. dem 
anderen Flag) die Zahl kommen muss. Dieses Zwischenspeichern in einem 
char Array braucht kein Mensch. Nur läuft ihr dann eben Gefahr, dass 
Array zu überlaufen, wenn der Input mal nicht so ist, wie ihr erwartet 
habt.

: Bearbeitet durch User
von Florian H. (flowbi)


Lesenswert?

@Peter:
darum ist es ja in eine Funktion verpackt. Strings senden ist ansonsten 
nicht interessant für mich.
@Rainer:
Danke für den Hinweis. Es löst allerdings kein einziges Interrupt aus 
und ich komme gar nicht zu diesem Punkt. Im Normalfall funktioniert es 
aber auch mit diesem sehr unschönen Aufbau Fehlerlos.

von Karl H. (kbuchegg)


Lesenswert?

Florian H. schrieb:
> @Peter:
> darum ist es ja in eine Funktion verpackt. Strings senden ist ansonsten
> nicht interessant für mich.

Statt dessen schreibst du lieber die Litanei da runter?

Deine trans_val könnte so einfach sein
1
void trans_val(void)
2
{
3
  char tmp[10];
4
5
  sprintf( tmp, "%d\n", T );
6
  uart_puts( tmp );
7
8
  sprintf( tmp, "%d\n", output );
9
  uart_puts( tmp );
10
11
  sprintf( tmp, "%d\n", samp + 1);
12
  uart_puts( tmp );
13
}

(oder so ähnlich)
> @Rainer:
> Danke für den Hinweis.

Die andere ISR hat ein ähnliches Problem, wenn ich den Funktionsnamen 
DISP_irgendwas richtig deute.

Man macht in einer ISR keine Ausgaben! Ausser vielleicht wenn sie 
interrupt getrieben gebuffert sind.

Ausgaben kommen in die Hauptschleife, die bei dir leer ist.

> Es löst allerdings kein einziges Interrupt aus
> und ich komme gar nicht zu diesem Punkt. Im Normalfall funktioniert es
> aber auch mit diesem sehr unschönen Aufbau Fehlerlos.

Bei diesem Aufbau weiss man doch gar nicht mehr, wo man mit der 
Fehlersuche anfangen soll.

: Bearbeitet durch User
von Florian H. (flowbi)


Lesenswert?

Karl H. schrieb:

> gefährlich, gefährlich. Bei einem Array der Länge 4.
> Und vor allen Dingen: so überflüssig wie ein Kropf.
>
> Warum setzt ihr denn die Zahl nicht gleich während des Empfangens
> zusammen? (d.h. wenn das empfangene Zeichen ein digit ist). Beim '.'
> wird dann nur noch entschieden, in welche Variable (je nach tf bzw. dem
> anderen Flag) die Zahl kommen muss. Dieses Zwischenspeichern in einem
> char Array braucht kein Mensch. Nur läuft ihr dann eben Gefahr, dass
> Array zu überlaufen, wenn der Input mal nicht so ist, wie ihr erwartet
> habt.

Hast du vollkommen recht mit. Kommt auf die TODO-Liste. Allerdings ist 
der Anwendungsbereich auf die Kommunikation mit der Matlab-Gui 
beschränkt, welche ich ja entsprechend aufgebaut habe. Insofern in 
meinen Augen aktuell nicht so dringend. Weiterhin suche ich ja primär 
nach Ideen zu Gründen für das beschriebene Verhalten.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Zusätzlich zum bereits erwähnten "tans_val()":
in einem Interrupt bedient man (oder zumindest ich) kein Display...
Im Gegenteil: ich versuche so schnell wie irgend möglich den Interrupt 
wieder zu verlassen, damit ein anderer Interrupt nicht verschlampt wird. 
Denn es gibt nur 1 einziges Interruptflag, das anzeigt, ob etwas 
passiert ist. Wenn das "etwas" 4x passiert, dann habe ich 3x "verloren", 
weil das eine einzige Flag ja nur 1x merken kann...

von Karl H. (kbuchegg)


Lesenswert?

Florian H. schrieb:

> Hast du vollkommen recht mit. Kommt auf die TODO-Liste.


Bei dir kommt immer alles auf die TODO Liste.

Die Dinge, von denen die Profis wissen, dass sie vital sind, um blöde 
Fehler erst gar nicht zu machen, die sind bei dir auf der TODO Liste.

von Karl H. (kbuchegg)


Lesenswert?

Um es klar zu sagen.
Irgendwo in deinem Programm sitzt ein Fehler. Ich tippe mal auf einen 
Array-Überlauf.

Aber dazu müsste man wissen, was über die UART reinkommt und vor allen 
Dingen müsste man erst mal das Programm komplett auseinandernehmen und 
vernünftig wieder neu zusammensetzen.

Was du gerade lernst, das man mit Schlamperei bei kleinen Programmen 
noch durchkommt. Bei größeren Programmen geht das aber ins Auge. Und das 
so sicher, wie das Amen im Gebet.

: Bearbeitet durch User
von Florian H. (flowbi)


Lesenswert?

Ich hab doch gesagt, dass das sehr unschön, aber im Normalfall 
funktional ist. Diese unschöne Struktur ist durch den Entstehungsprozess 
bedingt, in dem in der Regel quick & dirty angesagt war. Fakt ist, ich 
weiß dass das ziemlich dirty ist aber eigentlich funktioniert. Die 
Interrupts fallen ca im Sekundentakt.
Seid ihr denn ernsthaft der Meinung, dass das Problem damit zu tun hat?

von Florian H. (flowbi)


Lesenswert?

Karl H. schrieb:
> Florian H. schrieb:
>
>> Hast du vollkommen recht mit. Kommt auf die TODO-Liste.
>
>
> Bei dir kommt immer alles auf die TODO Liste.
>
> Die Dinge, von denen die Profis wissen, dass sie vital sind, um blöde
> Fehler erst gar nicht zu machen, die sind bei dir auf der TODO Liste.

Ja danke, ich habe auch nie behauptet professionell zu sein.
Wenn ihr der Meinung seid, dass deswegen keine Interrupts fallen, weil 
die zu lange zum ausführen brauchen, dann finde ich das interessant 
werde das aber so mitnehmen. Wieso interessiert eigentlich niemanden, 
dass es im Problemfall gar nicht dazu kommt dass ich zu lange in den 
Interrupts hänge...???

von Karl H. (kbuchegg)


Lesenswert?

Wem nicht zu helfen ist ....


Die UART ist es jedenfalls nicht.
Millionen Programmierer haben absolut kein Problem mit der UART.
Du währst gut beraten, die Schuld nicht bei der Hardware, beim Compiler 
etc zu suchen, sondern bei dir und deinem Programm!
Natürlich gibt es in all diesen Bereichen Fehler. Aber in in mehr als 
99% aller Fälle ist es der Programmierer, der die Sache verbockt hat.

von Karl H. (kbuchegg)


Lesenswert?

Florian H. schrieb:

> Wieso interessiert eigentlich niemanden,
> dass es im Problemfall gar nicht dazu kommt dass ich zu lange in den
> Interrupts hänge...???

Weil in deinem Programm soviele potentielle Fehlermöglichkeiten stecken, 
dass man nicht mit dem Finger auf eine davon zeigen kann und sagen kann, 
die ist es.
In deinem Programm stimmt fast gar nichts. Das beginnt bereits beim 
globalen Programmdesign.

von Florian H. (flowbi)


Lesenswert?

Ich versuche eure Kritik auf zu nehmen und entsprechend nach zu bessern. 
Bzw. im Neuanfang zu berücksichtigen.
Die Frage war aber ernst gemeint. Kann die schlampige programmierung 
innerhalb der Interrupts dazu führen, dass die nicht ausgeführt werden?

Ich kann leider nicht direkt alles verbessern war hier angemerkt wird, 
werde das aber jetzt in Angriff nehmen. Ich will nur ne Einschätzung 
haben, ob das realistisch ist, dass das die Ursache für die 
beschriebenen Fehler ist.

Edit: Okay das scheint wohl so zu sein. Ich hoffe ihr habt recht, 
ansonsten melde ich mich dann wohl mit neuem Code zurück...

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Florian H. schrieb:

> Regel quick & dirty angesagt war.

Auch wenn man quick&dirty arbeitet, ist man in der Regel gut beraten, 
zwischendurch immer wieder mal den Code aufzuräumen, durch den Code zu 
scrollen und sich zu überlegen, wie man Dinge quick einigermassen 
bereinigen kann.

Sonst bleibt nämlich am Ende nur noch "dirty" übrig. "Quick" spielt es 
dann nicht mehr.

von Joachim B. (jar)


Lesenswert?

Florian H. schrieb:
> Kann die schlampige programmierung
> innerhalb der Interrupts dazu führen, dass die nicht ausgeführt werden?

klaro wenn am Anfang der IRQ if 1=2 steht gehts gleich wieder raus.
Eigentlich wird sie ja ausgeführt, oder man kommt nie mehr raus 
while(1);

Dauert die IRQ zu lange und bleiben die IRQ gesperrt kommen andere IRQ 
nie an die Reihe.

Ich hatte mir angwöhnt ein Port am Anfang der IRQ zu setzen und am Ende 
zu löschen und mir gerne mal die Verweilzeit im IRQ anzusehen per Oszi, 
da wird manche "Fehlfunktion" klarer.

von Rainer B. (katastrophenheinz)


Lesenswert?

Florian H. schrieb:
> Wieso interessiert eigentlich niemanden,
> dass es im Problemfall gar nicht dazu kommt dass ich zu lange in den
> Interrupts hänge...???

Diese Randbedingung als gesetzt hinzunehmen ist aufgrund der 
Asynchronität des Auftretens von Interrupts gefährlich. Nach meiner 
Erfahrung sind sich blockierende Interrupts häufige und sehr schwer zu 
lokalisierende Fehlerursache.

> Kann die schlampige programmierung
> innerhalb der Interrupts dazu führen, dass die nicht ausgeführt werden?

Nur indirekt, wie oben angemerkt: Weil weitere Interrupts (auch von 
völlig anderen Interruptquellen) eintrudeln, während dein Programm noch 
in der Abarbeitung des ersten steckt.

von Florian H. (flowbi)


Lesenswert?

Karl H. schrieb:
> Florian H. schrieb:
>
>> Regel quick & dirty angesagt war.
>
> Auch wenn man quick&dirty arbeitet, ist man in der Regel gut beraten,
> zwischendurch immer wieder mal den Code aufzuräumen, durch den Code zu
> scrollen und sich zu überlegen, wie man Dinge quick einigermassen
> bereinigen kann.
>
> Sonst bleibt nämlich am Ende nur noch "dirty" übrig. "Quick" spielt es
> dann nicht mehr.

Meiner zugegebenermaßen wenig professionellen Einschätzung nach lief es 
halt bis jetzt. Und all zu lange sitze ich auch nicht dran. Aber 
offensichtlich ist es mehr als Zeit auf zu räumen.
Danke für die konstruktiven Kritiken und wer weiß vielleicht fällt einem 
ja noch eine andere Ursache ein.

von Uwe (de0508)


Angehängte Dateien:

Lesenswert?

Hallo Florian,

um dir einen Weg auf zuzeigen, versuche mal einen anderen Ansatz.

Implementiere für alle Sende- und Empfangsroutrinen einen "UART mit 
FIFO".

So können alle eingehenden und ausgehenden Zeichen in einem 
"asynchronen" Puffer "eingeliefert" werden und entsprechend beim Senden 
auch aus einem weiteren "abgeholt" werden.

Somit erfolgt dieser Aufgabe komplett transparent vom eigentlichen 
Hauptprogramm und man kann die Routinen auch immer wieder als 
Funktionsbibliothek einsetzen.

In nutzte die Implementierung von Peter Dannegger  (peda):
# Beitrag "AVR-GCC: UART mit FIFO"

Der kompakte Programmierstil von Peter muss ja nicht deiner sein, aber 
aus der Idee und deren Umsetzung könntest Du deinen eigenen Code 
implementieren.

: Bearbeitet durch User
von Peter D. (peda)


Lesenswert?

Florian H. schrieb:
> Allerdings kommt es
> scheinbar zufällig vor, dass die USART-Interrupts (Receive Complete)
> nicht mehr funktionieren.

Warum denkst Du das, d.h. wie genau hast Du das festgestellt?

Florian H. schrieb:
> Nach Rückbau der
> Änderungen ändert sich aber nichts am Fehlverhalten.

D.h. Du hast eine vorher laufende Version wieder aus der 
Versionsverwaltung ausgecheckt und sie verhält sich plötzlich anders?
Das glaub ich Dir nicht.

Florian H. schrieb:
> Ich kommuniziere mit ner Matlab Gui

Du must natürlich auch die dazugehörende Version des Matlab Programms 
mit auschecken.

von Florian H. (flowbi)


Lesenswert?

Peter D. schrieb:
> Florian H. schrieb:
>> Allerdings kommt es
>> scheinbar zufällig vor, dass die USART-Interrupts (Receive Complete)
>> nicht mehr funktionieren.
>
> Warum denkst Du das, d.h. wie genau hast Du das festgestellt?
>
> Florian H. schrieb:
>> Nach Rückbau der
>> Änderungen ändert sich aber nichts am Fehlverhalten.
>
> D.h. Du hast eine vorher laufende Version wieder aus der
> Versionsverwaltung ausgecheckt und sie verhält sich plötzlich anders?
> Das glaub ich Dir nicht.
>
> Florian H. schrieb:
>> Ich kommuniziere mit ner Matlab Gui
>
> Du must natürlich auch die dazugehörende Version des Matlab Programms
> mit auschecken.

Zu 1:
Die ausgaben aus dem Interrupt passierten nicht mehr, im Debug Modus 
lösten die Routinen trotz Flags nicht aus

Zu 2:
Das kann ich gut verstehen, aber genau das ist so geschehen.

Zu 3:
Matlab Teil ist aktuell statisch. Ändere ungern mehrere Dinge 
gleichzeitig.

Aktuell versuche ich den Code neu in besser zu schreiben.

Edit: zu 2 noch: Falls das ganze wie hier bereits mehrfach angedeutet an 
den zu langen Interrupts liegt kann ich mir vorstellen, dass das zu 
scheinbar nicht deterministischen Abläufen führen könnte, aber ist eine 
reine Vermutung.

Edit2: @Uwe: Danke für den Tipp. Werde ich mal rein schauen.

: Bearbeitet durch User
von Peter D. (peda)


Lesenswert?

Florian H. schrieb:
> Die ausgaben aus dem Interrupt passierten nicht mehr, im Debug Modus
> lösten die Routinen trotz Flags nicht aus

Die Ausgaben sind kein zuverlässiger Beweis, sie können ja selber 
fehlschlagen.
Setze direkt bei Eintritt eine LED (Portpin) und lösche sie bei Austritt 
aus dem Handler.

Ist das Flag gesetzt und der Interrupt wird aber nicht betreten, gibt es 
folgende Ursachen:
1. der Interrupt ist disabled
2. die Interrupts sind global disabled
3. Du bist bereits im Interrupthandler oder einer von ihm aufgerufenen 
Funktion.

von R. R. (elec-lisper)


Lesenswert?

Ich hab immer Bauchweh beim benutzen eines Debuggers.
Zu oft sehe ich Entwickler mitm Debugger rumhantieren und
zu keinem Ziel kommen. Mit Logging - oder bei MC via Pin-Togglen -
bin ich meist viel schneller und weiter gekommen.
Wenn man jetzt mehrere Kanäle gleichzeitig aufnehmen kann,
sieht man was genau passiert und kann damit argumentieren.
Debugger - ohne den hier verwendeten jetzt genau zu kennen -
zeigen nur selten was man sehen muss/will. Nämlich wie sich
Werte im Zusammenhang mit anderen Werten verändern,
insbes. zu welchem Zeitpunkt im Programm.

Ich suche und finde Fehler schon seit ich mit Softwareentwicklung
vor 15 Jahren anfing mit printf(). Selten haben mir Debugger wirklich
geholfen. Gut, was geholfen hat war ein analytisches denken.
Thesen aufstellen (was sollte ich gleich sehen? was für werte erwarte 
ich wo?), ein "Experiment" (Reproduktion des Bugs) und die Analyse des
Ergebnisses (was habe ich gesehen? wurde meine These belegt oder 
wiederlegt?
Fehlen noch informationen?).
Hier ists im Grunde egal ob Debugger oder printf(). Aber ein Logfile
(sei es jetzt auf der Festplatte oder im Speicher-Oszi) beinhaltet
meist noch weitere Kontext-Infos von vorherigen "Experimenten". Und
das hilft meist beim progressiven Arbeiten.

Kurz: Machs wie Peter Dannegger vorschlägt :-)

von Karl H. (kbuchegg)


Lesenswert?

Florian H. schrieb:

> Aktuell versuche ich den Code neu in besser zu schreiben.

Und ich hoffe, du fängst erst mal mit der UART, UND NUR mit der UART an 
(und noch die Anzeige mit dazu, damit du dir ausgeben lassen kannst, was 
über die UART rein kommt).

Nicht zu viel auf einmal. Immer in Schritten arbeiten. Jeden Schritt 
ausgiebig testen, eher der nächste Teilschritt dazu kommt.
Und wenn dir einem Teilschritt etwas unhandlich oder schlecht zu 
benutzen vorkommt, dann ändere das gleich. Es ist absolut normal, dass 
man während der Entwicklung drauf kommt, dass manche Dinge sich in der 
Praxis als weit weniger elegant darstellen als man ürsprünglich dachte. 
Es ist nicht schlimm seine Entscheidungen zu revidieren. Schlimm ist 
nur, mit dem selben Krampf wieder besseren Wissens immer weiter zu 
machen. Und ja, spätestens wenn du 'denselben' Code dem Prinzip nach 
immer wieder kopierst um nur ein paar Konstanten zu ändern, dann ist es 
Zeit dafür ein Funktion zu machen. Gerade für UART Funktionen hab ich 
überhaupt kein Verständnis dafür, warum man sich selbst das Leben schwer 
machen muss, indem man sich selbst keinerlei Hilsfunktionen schreibt. 
Die nächsten 3 Funktionen
1
void uart_putc( char c )
2
{
3
  while(!(UCSR0A & (1<<UDRE0)));
4
  UDR0 = c;
5
}
6
7
void uart_puts( const char* s )
8
{
9
  while( *s )
10
    uart_putc( *s++ );
11
}
12
13
void uart_puti( int i )
14
{
15
  char tmp[7];
16
17
  itoa( i, tmp, 10 );
18
  uart_puts( tmp );
19
}
sind das absolute Minimum, das jeder in seinem Vorrat an UART 
Hilfsfunktionen haben sollte.


Das zum Beispiel
1
  uint8_t S[4];
2
  sprintf(Temp, "%d", T);
3
  sprintf(Out, "%d", output);
4
  sprintf(S, "%d", samp+1);
5
  while(!(UCSR0A & (1<<UDRE0)));
6
  UDR0 = 2;  //start of text
7
  while(!(UCSR0A & (1<<UDRE0)));
8
  UDR0 = 'z';
9
  while(!(UCSR0A & (1<<UDRE0)));
10
  UDR0 = S[0];
11
  while(!(UCSR0A & (1<<UDRE0)));
12
  UDR0 = S[1];
13
  while(!(UCSR0A & (1<<UDRE0)));
14
  UDR0 = S[2];
15
  while(!(UCSR0A & (1<<UDRE0)));
16
  UDR0 = S[3];

woher weisst du eigentlich, dass in S[3] irgendwas sinnvolles steht?
weisst du doch gar nicht.
Solche Dinge sind extrem mühsam zu debuggen, auch wenn die jetzt 
wahrscheinlich nichts mit deinem Problem zu tun haben. Aber: Du sendest 
höchst wahrscheinlich etwas an dein Matlab Programm, was da eigentlich 
nichts zu suchen hat. Wie das Matlab Programm darauf reagiert, das steht 
in den Sternen.
Der springende Punkt ist: solche Dinge musst man suchen und wie die 
Auswirkungen sind, ist gar nicht so leicht vorherzusagen. Selbst wenn 
ich kein sprintf verwende (verwenden will), ist ein
1
  uart_putc( STX );
2
3
  uart_putc( 'z' );
4
  uart_puti( samp + 1 );

schon wesentlich leichter zu überblicken und zu verfolgen, als deine 
ganze UART Orgie. Und vor allen Dingen: Die Mechanik dahinter ist 
korrekt.


Und in der UART-ISR ungeprüft in ein Array schreiben, das ist sowieso 
ein NoGo. Wenn du dir den Speicher erst mal mit einem Array Überlauf 
zerschossen hast, werden keine Wetten mehr angenommen, was dein Programm 
eigentlich noch so alles macht bzw. nicht macht.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Karl H. schrieb:

> Und ich hoffe, du fängst erst mal mit der UART, UND NUR mit der UART an
> (und noch die Anzeige mit dazu, damit du dir ausgeben lassen kannst, was
> über die UART rein kommt).


Eine erste Version könnte zb so aussehen
1
#define F_CPU 16000000UL
2
3
4
#define BAUD2 38400L
5
#define UBRR_VAL2 ((F_CPU+BAUD2*8)/(BAUD2*16)-1)
6
#define BAUD_REAL2 (F_CPU/(16*(UBRR_VAL2+1)))
7
#define BAUD_ERROR2 ((BAUD_REAL2*1000)/BAUD2)
8
#if ((BAUD_ERROR2<990) || (BAUD_ERROR2>1010))
9
#error Baudratenfehler zu gross! (2)
10
#endif
11
12
#include <stdlib.h>
13
#include <ctype.h>
14
#include <avr/io.h>
15
#include <avr/interrupt.h>
16
#include <util/delay.h>
17
18
#include "Anzeige.c"
19
20
volatile uint16_t samp=0;
21
volatile int16_t temp_soll = 10000;
22
23
#define CMD_NONE         0
24
#define CMD_NEW_VALUE    1
25
#define CMD_READ         2
26
volatile uint8_t command = CMD_NONE;
27
28
#define STX    2
29
#define ETX    3
30
31
void init(void)
32
{
33
  //setting baud rate
34
  UBRR0H = UBRR_VAL2 >> 8;
35
  UBRR0L = UBRR_VAL2 & 0xFF;
36
  //enable receive and interrupt
37
  UCSR0B |= (1 << RXCIE0) | (1 << RXEN0) | (1<<TXEN0);
38
}
39
40
void uart_putc( char c )
41
{
42
  while(!(UCSR0A & (1<<UDRE0)));
43
  UDR0 = c;
44
}
45
46
void uart_puts( const char* s )
47
{
48
  while( *s )
49
  uart_putc( *s++ );
50
}
51
52
void uart_puti( int i )
53
{
54
  char tmp[7];
55
56
  itoa( i, tmp, 10 );
57
  uart_puts( tmp );
58
}
59
60
int main(void)
61
{
62
  init();
63
  
64
  sei();
65
  while(1)
66
  {
67
    if( command == CMD_NEW_VALUE )
68
    {
69
      command = CMD_NONE;
70
      .... temp_soll und samp auf der Anzeige ausgeben  
71
    }
72
73
    else if( command == CMD_READ )
74
    {
75
      command = CMD_NONE;
76
77
      uart_putc( STX );
78
79
      uart_putc( 'z' );
80
      uart_puti( samp + 1 );
81
82
      uart_putc( ETX );
83
    }
84
  }
85
}
86
87
ISR(USART0_RX_vect)
88
{
89
  static uint8_t target;
90
  static uint16_t number = 0;
91
  uint8_t c;
92
  
93
  c = UDR0;
94
  
95
  if( c == 't' )
96
  {
97
    number = 0;
98
    target = 't';
99
  }
100
101
  else if( c == 's' )
102
  {
103
    number = 0;
104
    target = 's';
105
  }
106
107
  else if( isdigit(c) )
108
    number = 10*number + ( c - '0' );
109
      
110
  else if( c == '.' )
111
  {
112
    if( target == 't' )
113
      temp_soll = number;
114
    else if( target == 's' )
115
      samp = number;
116
    
117
    number = 0;
118
    command = CMD_NEW_VALUE;
119
  }
120
121
  else if( c == 'r' )
122
    command = CMD_READ;
123
124
  else if( c == 'x' ) 
125
  {
126
    temp_soll = 0;
127
    samp = 0;
128
    command = CMD_NEW_VALUE;
129
  }
130
}

das ist sicherlich noch nicht der Weisheit letzter Schluss und schon da 
gibt es Alternativen und Dinge, die man anders machen könnte. Aber es 
ist ein Anfang. Damit kannst du dein UART mal im Dauerbetrieb testen und 
es zeigt auch, wie eine sinnvolle Arbeitsteilung zwischen ISR und 
Hauptschleife aussehen könnte. Die ISR macht die Dinge die einfach gehen 
und die keine nennenswerte Zeit benötigen, wie zb das Zusammensetzen von 
Digits zu kompletten Zahlen. Für alles weitere hinterlässt sie in einer 
globalen Variablen einen Code, der dann von der Hauptschleife 
ausgewertet wird und der dann länger andauernde Aktionen anstösst.

: Bearbeitet durch User
von Florian H. (flowbi)


Lesenswert?

@Peter:
Dritteres ist/war wohl der Fall.
@Robin:
Ist jetzt auch nicht das erste Mal, dass ich was am uC mache, allerdings 
das erste Mal in größerem Umfang. Die genannten Methoden sind aber 
bekannt. Trotzdem danke.
@Karl Heinz: Genau da habe ich angefangen. UART habe ich die Basis 
Funktionen rein gepackt und es ist wesentlich einfacher und 
übersichtlicher.
Die nicht voll geschriebenen Arrays habe ich übrigens Matlabseitig 
abgefangen, Überläufe empfangsseitig genau so. Bin aber dabei die ganzen 
Vorschläge aus dem Thread ein zu bauen.

Stand bis jetzt ist, dass alle größeren Dinge, vor allem die 
Sendegeschichten nicht mehr in den Interrupts stehen. Ich habe jetzt 
wieder ein paar andere Dinge die nicht laufen aber da werde ich erst mal 
selber ein wenig testen bevor ich deswegen ein Fass auf mache.
Eindruck bis jetzt ist jedenfalls, dass die These mit den zugemüllten 
Interrupts als Quelle für das Fehlverhalten Stand jetzt schwer zu 
widerlegen sein wird ;)
Danke an alle Konstruktiven Beiträge und entschuldigung für den 
unschönen Code den ich euch aufgetischt habe.

von Der Andere (Gast)


Lesenswert?

Robin R. schrieb:
> Selten haben mir Debugger wirklich geholfen.
Kommt darauf an. Bei Fehlern in Berechnungen oder der Logik an sich 
hilft ein Debugger immens, bei Laufzeitproblemen wie konkurrierende IRQs 
hilft er nicht.

> Gut, was geholfen hat war ein analytisches denken.
Das unterschreibe ich zu 100%

> Thesen aufstellen (was sollte ich gleich sehen? was für werte erwarte
> ich wo?), ein "Experiment" (Reproduktion des Bugs) und die Analyse des
> Ergebnisses (was habe ich gesehen? wurde meine These belegt oder
> wiederlegt?

Ich hatte hier schon mehrere Male die Situation wo ich ein 
dokumentiertes Verhalten im Trace nicht verstanden habe / nicht 
nachvollziehen konnte. Da man nicht immer grenzenlos Zeit hat wurde der 
eigentliche Fehler gefixt, aber fast jedes Mal hat sich das "seltsame" 
Verhalten als weiterer Fehler oder als Teil des Fehlers herausgestellt 
und man nusste später noch mal ran.

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.