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
1
CMDPUT
2
10000001
ausgegeben werden am Ende steht aber immer
1
CMDPUT
2
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).
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:
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
@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.
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).
> 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
1
voiduart_putPORT(charport,uint8_tvalue){
2
uart_puts("PORT");
3
uart_puts(""+port);
4
5
chars[8];
6
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
1
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.
>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
@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.
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.
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
> 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.
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.
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.
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
1
voiduart_putPORT(charport,uint8_tvalue){
2
uart_puts("PORT");
3
uart_puts(""+port);
4
5
chars[8];
6
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
> 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).