www.mikrocontroller.net

Forum: Compiler & IDEs Mikrocontroller startet neu + komische Ausgabe über USART


Autor: Johannes Kugele (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Hi,

Ich bin gerade bei meinem 1. µC-Projekt, nämlich ein GrafikLCD mit einem 
myAVR Board anzusteuern. Mein Problem ist, dass der Controller (ATMega8) 
an einer bestimmten Stelle immer neustartet. Kurz vor dem Neustart wird 
zusätzlich über den USART etwas komisches ausgegeben. Normalerweise 
sollte etwas wie
CMDPUT
10000001
ausgegeben werden am Ende steht aber immer
CMDPUT
1111111110000001…á
im Terminal. Danach startet das Program neu (erkennbar daran das 
"USART_INIT" dahintersteht).

Ich weiß weder warum der Controller neustartet, noch warum der USART 
kurz vorher diese komische Zeichenkette ausgibt. Zuerst dachte ich an 
den Watchdog, aber auch ein eingefügtes wdt_disable() am Anfang hat 
nichts gebracht. Von der Stromzufuhr her müsste die Versorgung des 
AVR-Boards locker auch zum betreiben des LCDs reichen.

Hat sonst jemand eine Idee, woran das liegen könnte?

Ich habe übrigens den Sourcecode und den USART-Output angehängt. Wenn 
man in den Quellcode schaut werdet ihr sehen dass man am Anfang den 
Taster1 drücken muss damit das Program weiterläuft. Ich habe also, wie 
man in "AVR Output.txt" sieht, zweimal den Taster gedrückt. Danach 
wartet das Program wieder auf einen Tastendruck (was eigentlich nicht 
passieren darf).

Autor: Tim (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Lies doch mal mach dem Reset das MCUCSR Register aus
und sende es per UART an den PC.

Dann weist du wenigsten mal was den Reset verursacht hat....

bsp:
  // save && reset MCUCSR!
  char mcucsr_boot;
  mcucsr_boot = MCUCSR;
  MCUCSR = 0x00;

PS: Ich tippe mal auf die Watchdog.

Autor: Lothar Miller (lkmiller) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Mein Problem ist, dass der Controller (ATMega8)
> an einer bestimmten Stelle immer neustartet.
Amoklaufender Pointer?

Autor: Peter Diener (pdiener) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Du hast in jeder Funktion, deren Rückgabetyp void ist, das return 
vergessen, damit wird nicht in die aufrufende Funktion zurückgesprungen, 
sondern die im Quellcode folgende Funktion ausgeführt.

Auch eine Funktion, die nichts zurückgibt muss so aussehen:

void functName()
{
  //mache hier etwas

  return;  // <- das ist wichtig.
}

Grüße,

Peter

Autor: Peter (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Peter Diener (pdiener)
> Auch eine Funktion, die nichts zurückgibt muss so aussehen:
das ist doch unsinn:

If no return statement appears in a function definition, control 
automatically returns to the calling function after the last statement 
of the called function is executed. In this case, the return value of 
the called function is undefined. If a return value is not required, 
declare the function to have void return type; otherwise, the default 
return type is int.


Das Return kann man weglassen, das ist also nicht der Fehler.

Autor: Johannes M. (johnny-m)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das return zum Zurückkehren an die aufrufende Stelle steckt im Prinzip 
in der schließenden Klammer } drin.

Ein return braucht's nur dann, wenn die Funktion vorzeitig (also vor 
erreichen von }) verlassen oder wenn ein Wert zurückgegeben werden soll 
(also wenn der Typ der Funktion nicht "void" ist).

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> void uart_puti( uint16_t value )
> {
>   char str[10];
>
>    utoa( value, str, 2 );
>    uart_puts( str );
>  uart_puts("\r\n");
>
> }

Und wie denkst du passen 16 Bit in Binärdarstellung in einen maximal 9 
Zeichen langen String?


UNd hier nochmal
void uart_putPORT(char port, uint8_t value) {
  uart_puts("PORT");
  uart_puts(""+port);
  
  char s[8];
  utoa( value, s, 2 );
Für 8 lesbare Zeichen brauchst du ein Array der Länge 9!

An diesen Stelle ist kleckern absolut fehl am Platz. Ob der µC am Stack 
Platz für 8 oder 18 chars bereitstellt ist 'ghupft wie gesprungen'. 
(Solange du die paar Bytes im SRAM nicht schmerzlich an anderer Stelle 
vermist). Wenn du aber zuwenig hast, überschreibst du dir die 
Returnadresse am Stack und dann kann alles mögliche passieren.
Ausgabebuffer lieber etwas zu gross definieren, denn manchmal stellt man 
die Ausgaben ein klein wenig um und vergisst den Buffer anzupassen.

Was das hier sein soll
  uart_puts(""+port);
weißt du wohl nur selber. Es macht in den wenigsten Fällen Sinn einen 
Pointer mit einem char zu addieren. Es sei denn, du ersetzt damit einen 
Arrayzugriff (und selbst dann ist das meistens fragwürdig), was du hier 
aber nicht tust. Benutz ganz einfach deine uart_putc Methode, wenn du 
einen einzelnen Character ausgeben willst. Dafür ist sie da.


Noch ein Tip:
packe dein einziges Source File nicht in eine RAR Datei ein.
Die Chancen, dass jemand sich bereit findet einen Code zu studieren 
steigt proportional damit, wie einfach du es für ihn machst, den Code zu 
lesen. Häng einfach das Source Code File als Attachment an. Die 
Forensoftware kann ihn dann schön aufbereitet präsentieren.

Autor: Peter Diener (pdiener) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>If no return statement appears in a function definition...

Das kenne ich schon, leider habe ich mit dem gcc die Erfahrung gemacht, 
dass das nicht immer korrekt umgesetzt wird, deitdem schreibe ich immer 
ausdrücklich return; hin.

Peter

Autor: Peter (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Peter Diener
und du bist sicher, das es am gcc und nicht an etwas anderem lag? Dann 
wenn solche Fehler drin sind würden sehr viele Programme überhaupt nicht 
mehr laufen.
Bei c++ wird sehr viel mit void gearbeitet und das ohne return.

Autor: Stefan B. (stefan) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Peter Diener wrote:

> Das kenne ich schon, leider habe ich mit dem gcc die Erfahrung gemacht,
> dass das nicht immer korrekt umgesetzt wird, deitdem schreibe ich immer
> ausdrücklich return; hin.

Schaden tut das return; nicht. Ich halte es aber nicht für notwendig 
bzw. hat mir ein fehlendes return; an der Stelle noch nie Probleme 
bereitet.

So einen Fehlerfall würde ich archivieren/dokumentieren und den 
GCC-Entwicklern schicken. Nur was bekannt ist, kann gefixt werden.

Autor: wope (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

Hab auch kein rar im System (Stefans Aussage kann ich voll unterstützen) 
und werde mir dafür auch keins installieren und ist auch nicht nötig... 
:-)

Wenn ich mir zumindest die Ausgabe ansehe, denke ich, dass irgendwo eine 
Zuweisung (wahrscheinlich implizit vom Compiler bei irgendeinem 
Funktionsaufruf) mit sign-extension passiert. Dadurch wird aus 8bit mit 
MSB=1 ein 16bit Wert mit dem oberen byte "11111111"... Wo auch immer.

Daher würde ich sagen: Code Zeile für Zeile checken was passiert - am 
besten mit einem Debugger, dann ist das Problem sicher schnell gefunden.

Die Sache mit dem return ist tatächlich 100% unnötig. Der Lesbarkeit 
wegen sollte man überflüssige "Angstzeilen" nicht unbedingt einbauen - 
das sagen so manche coding guidelines - aber das ist Geschmacksfrage...

Statt dessen lieber auf Datentypen/Größen und auf saubere 
Zuweisungen/Parametertypen achten - die sind aus meiner Erfahrung zu 99% 
schuld an fehlerhafter Programmausführung und zu 99.999% 
wahrscheinlicher als ein Bug im Compiler bzgl. Funktionshandling :-)

Aber da hat Karl Heinz auch schon Tipps gegeben.

Sicherheitshalber (wenn nicht schon vorhanden) -Wall beim gcc verwenden, 
dann gibt es ev. ein paar Warnings mehr - die können ev. auch den 
richtigen "hint" geben.

Liebe Grüße,
Wope

Autor: Lothar Miller (lkmiller) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Mein Problem ist, dass der Controller (ATMega8)
> an einer bestimmten Stelle immer neustartet.
Sag ichs doch: Amoklaufender Pointer!

>... hat mir ein fehlendes return; noch nie Probleme bereitet.
Wenn nichts zurückgegeben wird brauchts das Ding nicht. Sonst könnte ich 
den kernigen Ritschie auch gleich wegschmeißen. Dort ist das return; 
auch nicht explizit erwähnt, wenns nichts zu returnen gibt.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Lothar Miller wrote:
>> Mein Problem ist, dass der Controller (ATMega8)
>> an einer bestimmten Stelle immer neustartet.
> Sag ichs doch: Amoklaufender Pointer!

:-)
In dem Fall eher 'Array beschreiben out of bounds'
obwohl ... die beiden Dinge sind im Grunde eh fast dasselbe

>>... hat mir ein fehlendes return; noch nie Probleme bereitet.
> Wenn nichts zurückgegeben wird brauchts das Ding nicht. Sonst könnte ich
> den kernigen Ritschie auch gleich wegschmeißen. Dort ist das return;
> auch nicht explizit erwähnt, wenns nichts zu returnen gibt.

Richtig. Das return ist hier völlig unnütz.

Wenn ein Programm seltsames Verhalten zeigt, das auf den ersten Blick 
unerklärbar scheint, dann liegt es zu 70% an Array-Overflows und zu 28% 
an Pointern, die in den Wald zeigen. 70 zu 28 deswegen, weil die 
Mehrheit lieber mit Array-Syntax als mit Pointern arbeitet :-) Allen 
gemeinsam ist aber, dass auf Speicher geschrieben wird, der nicht zum 
Programm gehört und das Programm sich damit selbst die Arbeitsgrundlage 
zerstört. In diesem Fall heißt das dann eben: die Returnadresse am Stack 
zerschiesst.

Autor: wope (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Würde es nicht auf Arrays reduzieren. Alle Arten von Datentypen können 
Probleme machen, wenn man nicht aufpasst. Bei den regulären Typen hat 
aber zumeist der Compiler die Chance, richtig zu casten oder 
Fehler/Warnungen zu liefern.

Mein Tipp, gerade wenn man Sprachen wie C zum ersten Mal (?) im 
Selbststudium verwendet, ist ziemlich egal ob PC oder Micro. Vor allem 
ist der Lerneffekt viel höher, wenn man die Probleme selbst findet :-)

- Langsam und strukturiert vorgehen, jede (!) Codezeile verstehen lernen 
und erstmal "trocken" überlegen, was passieren soll (speziell bei allen 
Datenstrukturen: Variablen, Arrays, Structures, Pointer, .. u.s.w.). C 
schreibt man nicht "einfach so", gerade ohne Erfahrung geht das schon 
gar nicht gut. Und probieren mag zwar (temporär) das Problem lösen, wenn 
man es aber nicht verstanden und richtig gelöst hat, schlägt es später 
wieder doppelt durück :-)

- Dazu gehört auch das Grundverständnis der HW-Registerbits, die man 
modifiziert. Ist furchtbar trocken, aber ohne Basisarbeit gehts nunmal 
nicht, wenn was Gescheites herauskommen soll. So kann man z.B. 
feststellen, ob man z.B. fälschlicherweise den Watchdog oder einen 
Interrupt (ohne Handler dahinter) eingeschaltet hat.

- Das erste Projekt klein anfangen. Nicht zuviel auf einmal 
(UART+LCD+...), vor allem wenn man die ersten Gehversuche macht. So kann 
man Probleme separieren und eingrenzen - zumeist findet man das Problem 
dann auch selbst. Auch wenn das "fad" klingt, ewiges Fehlersuchen ist 
sicher auch nicht lustiger.

-Software-Debugger verwenden! Natürlich ist ein ICE super, aber auch 
Simulavr & Co können sehr viele Anfängerfehler aufdecken. Dazu ist es 
natürlich gut, wenn man Schritt für Schritt die Einzelfunktionen da drin 
aufrufen kann, also "bottom-up" das Projekt zusammenbaut. HW-IO muss man 
dafür manchmal durch geschickten Ersatz von Konstanten o.ä. ersetzen. 
Aber das o.g. Problem hat offensichtlich (noch) nichts mit HW-Problemen 
zu tun. Aber auch da kann man sich oft wundern, was man glaubt dass z.B. 
in ein Register geschrieben wird und dann tatsächlich dort landet!

- Und generell für die ersten Projekte: Ein bischen suchen und fertigen 
Code aus bestehenden Projekten verwenden - vor allem für die 
UART-Geschichten, da muss man das Rad nicht immer neu erfinden. Davon 
kann man auch viel lernen, dazu muss man sich (speziell am Anfang) nicht 
unbedingt (viele) Bücher kaufen. Dann diese Programme modifizieren, wenn 
man sich später besser auskennt, eigenen Code schreiben anfangen.

Autor: Johannes Kugele (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo und vielen Dank für die vielen Antworten :-)

@kbuchegg
Es tut mir Leid, dass ich meine Dateien als RAR angehängt habe, ich 
wollte sie eigentlich als ZIP anhängen. (ich habe sie gepackt da ich 
zwei Dateien anhängen wollte, den Output und die Source).

Das hier
void uart_putPORT(char port, uint8_t value) {
  uart_puts("PORT");
  uart_puts(""+port);
  
  char s[8];
  utoa( value, s, 2 );
war mein Versuch eine Methode zu schreiben, die den Inhalt des Ports 
ausgibt. Ich weiß dass sie nicht funktioniert und sie wird im Programm 
nicht aufgerufen.

>""+port
 Ich wusste nicht wie man in C ein Char in einen String verwandelt oder 
ein Char an einen String anhängt, deshalb habe ich einfach versucht an 
einen leeren String den Char anzuhängen... was nicht klappt. Evtl. muss 
man nur eine Null anhängen, um einen String daraus zu machen ??

Und noch was zum Debuggen: Ich benutze den integrierten Debugger des 
AVR-Studio, doch dieser funktioniert im Zusammenhang mit USART-Register 
writes nicht, sondern bleibt nach einer Weile einfach "stecken". Dieser 
Fehler ist auch schon anderen aufgefallen. Deshalb konnte ich das 
Programm leider nicht debuggen.

Es lag übrigens wirklich am zu kleinen Puffer in der Funktion uart_puti.
Vielen Dank für den Tipp kbuchegg  :D

Grüße Johannes Kugele

Autor: Lothar Miller (lkmiller) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> eine Methode
Nee, nee, das ist eine ganz ordinäre Funktion, nix mit C++

>   uart_puts(""+port);
Wie gesagt: wir sind hier nicht bei C++
In C wird die Zahl in ein char-Array (das ist der String) gewandelt über 
Funktionen wie itoa(), utoa() usw.
Danach wird mit strcat() der String an einen anderen angehängt.

Das: "" ist ein Zeiger auf eine 0 (= Stringende) im Speicher.
Und so: uart_puts("") wird der Funktion dieser Zeiger auf 0 übergeben.
Durch: (""+port)  wird dem Zeiger ein Offset aufaddiert. Der zeigt jetzt 
nicht mehr auf das Stringende, sondern irgendwohin. Und ab dort wird 
dann ein Speicherabbild ausgegeben (bis hoffentlich irgenwo eine 0 
auftaucht, die ja dann wieder ein Strindende markiert).

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.