Die Problembeschreibung war von gestern (habe sie lediglich in diesen neuen Thread kopiert), heute habe ich die Lösung gefunden. Durch lesen des Souce Codes von scanf bin ich auf folgendem Haken gestoßen: scanf liest von stdin solange, bis es ein falsches (in meinem Fall nicht numerisches) Zeichen findet. Dann bricht scanf ab und schreibt das falsche Zeichen wieder zurück in den Stream. Da ich stdin mit RXD vom seriellen Port verbunden habe, ist dies ein read-only stream. Es gibt nur eine getc() Methode, und keine putc() Methode. An dieser Stelle ruft scanf einen null-pointer auf. Und genau dieser Aufruf führt zum Programmabsturz. Daraus habe ich gelernt: scanf kann man nur auf Streams anwenden, die das "zurücklegen" von Zeichen unterstützen. Diese Anforderung wird in der Library Doku nicht erwähnt. Ich dachte mir, dass andere Leute (z.B. Max, falls der hier noch mitliest) diese Erkenntnis ebenfalls gebrauchen können.
Wenn das wirklich nicht erwähnt wird, solltest du vlt. einen bugreport eröffnen. Nur so kann die Doku verbessert werden.
Stefan Frings wrote: > Da ich stdin mit RXD vom seriellen Port verbunden habe, ist dies ein > read-only stream. Es gibt nur eine getc() Methode, und keine putc() > Methode. > > An dieser Stelle ruft scanf einen null-pointer auf. Und genau dieser > Aufruf führt zum Programmabsturz. > > Daraus habe ich gelernt: scanf kann man nur auf Streams anwenden, die > das "zurücklegen" von Zeichen unterstützen. Diese Anforderung wird in > der Library Doku nicht erwähnt. Hallo, ich denke, Deine Darstellung ist nicht korrekt: es ist ungetc(), welches 1 einzelnens Zeichen zurück in das unsigned char __file->unget legt, und die steht in det stdio.lib zur Verfügung. Wichtig ist, daß selbstgeschriebene getc() zuerst dieses Zeichen lesen und das zugehörige Statusbit im stream löschen. Das alles geht auch mit Readonly-streams. Gruß, Michael
Stefan Frings wrote: > Die Problembeschreibung war von gestern (habe sie lediglich in diesen > neuen Thread kopiert), heute habe ich die Lösung gefunden. Ist es wirklich so schwer, einfach im alten Thread zu antworten? Wenn in zwei Monaten jemand hier drauf guckt, steht der Thread komplett zusammenhanglos da. xref: Beitrag "scanf stürzt bei seriellem Port ab" > scanf liest von stdin solange, bis es ein falsches (in meinem Fall nicht > numerisches) Zeichen findet. Dann bricht scanf ab und schreibt das > falsche Zeichen wieder zurück in den Stream. Das liegt in der Natur von scanf(), der C-Standard hat es explizit so definiert, dass man zumindest 1 Zeichen wieder in den Stream zurück schieben können muss. > Da ich stdin mit RXD vom seriellen Port verbunden habe, ist dies ein > read-only stream. Es gibt nur eine getc() Methode, und keine putc() > Methode. Das hat damit nichts, aber rein gar nichts, zu tun. Dafür wird ungetc() benutzt (ich entnehmen deinem Codeschnipsel, dass du mit einem AVR und damit wohl mit einer avr-libc arbeitest). Die Struktur für den Typ FILE besitzt extra ein eigenes Feld, um dieses eine Zeichen für ein einfaches ungetc() aufzunehmen. > An dieser Stelle ruft scanf einen null-pointer auf. Sourcecode, welche Datei, welche Zeile? Ich sehe dafür nichts in der avr-libc.
Gootverdammich, mit welcher Bibliothek arbeitest du denn nun
eigentlich? Warum lässt du uns das raten, statt das mal zu
schreiben? (Sowas gehört eigentlich mit ins Ursprungsposting.)
Außerdem darf ich an meine Frage erinnern:
> Sourcecode, welche Datei, welche Zeile?
Hi, ich habe nochmal in den Source geschaut, und konnte den putc() Aufruf nun nicht mehr finden. Da habe ich wohl in die Falsche Datei geschaut - so ein Scheiß. PS: ich habe einen neuen Thread aufgemacht, weil Du mich genau darum gebeten hast. Ich verstehe die Beschwerde nicht. Jedenfalls ist jetzt wohl klar, warum mein \n Zeichen verschwindet (denn ungetc() unterstüzt meine serielle Routine tatsächlich nicht. War in den Beispielen auch nicht drin :-) Offen ist dann wohl noch die Frage, warum mein Programm abstürzt und sich selbst löscht. Aber das bekomme ich sicher auch noch heraus. Danke für den Hinweis, dass meine Schlußfolgerung falsch war.
Stefan Frings wrote: > PS: ich habe einen neuen Thread aufgemacht, weil Du mich genau darum > gebeten hast. Ich verstehe die Beschwerde nicht. Sorry, dann habe ich die nun in der Folge falsch zusammen gebracht. Ich bitte vielmals um Entschuldigung. > Jedenfalls ist jetzt wohl klar, warum mein \n Zeichen verschwindet (denn > ungetc() unterstüzt meine serielle Routine tatsächlich nicht. War in den > Beispielen auch nicht drin :-) Das mit dem ungetc() sollte doch Sache der Bibliothek sein (wobei du halt immer noch nicht sagst, welche du denn nimmst), denn die muss ja den Stream fürs stdio abstrahieren. Dass man ein Zeichen nicht in den FIFO der UART zurückstopfen kann, ist ja normal. ;-) ungetc() sollte das Zeichen also irgendwie ,,beiseite'' legen, damit es später erneut lesbar ist. Das mit dem \n-Zeichen ist eine andere Sache. Whitespace wird per definitionem von scanf() überlesen, damit wird es u. U. bei der Suche nach dem Ende des aktuellen Elements dein \n lesen, sollte es aber mit ungetc() zurück stecken, sodass das nächste getc() (oder was auch immer, jedenfalls irgendeine Funktion von stdio muss es sein!) es von dort wieder lesen kann. Allerdings fand ich scanf() noch nie wirklich einfach zu benutzen. Meiner Meinung nach sinnvoller ist es, mit fgets() eine ganze Zeile in einem Puffer zu lesen (dann weiß man ganz genau, dass man alles bis zum nächsten \n da drin hat) und diesen dann mit sscanf() zu bearbeiten. Reines scanf() macht selbst auf PCs nicht richtig viel Spaß.
Ich nutze avr-libc und gcc. Meine Anwendung habe ich auch schon auf die Kombination fgets+char[] buffer+sscanf() umgestellt. Ist wirklich besser zu handhaben. Aber dieses Abstürze gehen mir nicht aus dem Kopf, deren Ursache möchte ich schon herausfinden. Ich kann das Problem zwar locker umgehen, doch Probleme zu lösen oder zumindest vollständig zu verstehen ist schon interessanter. Besonder coll finde ich auch den Effekt, daß beim Absturz mein flash verändert wird, obwohl ich in meinem Source Code keinen Einzigen schreibenden Zugriff vorgesehen habe und auch auch keinen Bootloader einsetze. Immerhin müssen ja eine ganze Reihe vopn Bedingungen erfüllt sein, um überhaupt in den flash schreiben zu können - das ist schon ein interessanter Zufall. Oder vieleicht ist es doch kein Zufall? Das möchte ich noch weiter untersuchen - die Zeit habe ich. Elektronik ist schliesslich mein Hobby, nicht Beruf. Ich werde mich erstmal mit ungetc() auseinander setzen, und dann mal genau untersuchen, was beim Absturz passiert. Vieleicht kann ich dieses Verhalten mit einem Simulator reproduzieren - das wäre am einfachsten.
Stefan Frings wrote: > Besonder coll finde ich auch den Effekt, daß beim Absturz mein flash > verändert wird, obwohl ich in meinem Source Code keinen Einzigen > schreibenden Zugriff vorgesehen habe und auch auch keinen Bootloader > einsetze. Immerhin müssen ja eine ganze Reihe vopn Bedingungen erfüllt > sein, um überhaupt in den flash schreiben zu können - das ist schon ein > interessanter Zufall. Oder vieleicht ist es doch kein Zufall? Oder vielleicht passiert es in Wirklichkeit auch gar nicht. Und das halte ich für wesentlich wahrscheinlicher, als das das Flash hops gegangen ist. Aber probiers doch einfach aus: Wen der µC streikt, lies das Flash aus und vergleich mit dem was du vorher reingeschrieben hast als es noch ging. Würde mich wundern, wenns da einen Unterschied gäbe.
Stefan Frings wrote:
> Ich werde mich erstmal mit ungetc() auseinander setzen, ...
Das sollte für dich komplett transparent sein, solange du danach
wieder mit stdio-Mitteln auf die Daten zugreifst. Das nächste
fgetc() (auf das greifen alle letztendlich zu) stellt dann fest,
dass im unget-Puffer ein Zeichen liegt und liest dieses, statt
die Backend-Funktion zum Lesen von der Hardware neu anzuwerfen.
Stefan Frings wrote: > Besonder coll finde ich auch den Effekt, daß beim Absturz mein flash > verändert wird, obwohl ich in meinem Source Code keinen Einzigen > schreibenden Zugriff vorgesehen habe und auch auch keinen Bootloader > einsetze. Dann verrate doch erstmal, um welche Architektur und um welches Derivat es überhaupt geht. Falls es um einen AVR geht, könnte Brownout-Reset nicht schaden. Und falls AVR mit externem Quarz ist full-swing Oszillator ne gute Idee. Peter
Ich nutze ein industriell gefertigtes Modul mit AVR ATmega168, 20Mhz Quartz, Brown-Out ist auf 2,7V eingestellt. Und ja: Nach dem Absturz stimmt der Flash Speicher tatsächlich nicht mehr mit dem Ursprünglichen Inhalt überein. Sobald ich scanf("%d",&i); durch dies ersetzte, tritt der Fehler nicht auf: int i; char buffer[20]; fgets(buffer,sizeof(buffer),stdin); sscanf(buffer,"%d",&i); Ich habe auch ein zweites Modul probiert - gleiches Ergebnis.
Befindet sich auf dem Modul zufällig ein Bootloader? Ein Sprung in diesen durch einen nicht initialisierten Funktionspointer gefolgt von Daten über den UART könnte den Flash überschreiben.
Stefan Frings wrote: > Ich nutze ein industriell gefertigtes Modul mit AVR ATmega168, 20Mhz > Quartz, Brown-Out ist auf 2,7V eingestellt. 20 MHz sind aber nur bei 5 V (-10 % Toleranz) definiert, du müsstest den Brownout also auf 4,3 V stellen. Aber das sollte natürlich trotzdem keine flash corruption bringen. > Und ja: Nach dem Absturz stimmt der Flash Speicher tatsächlich nicht > mehr mit dem Ursprünglichen Inhalt überein. Was genau wird denn geändert?
Stefan Frings wrote: > Und ja: Nach dem Absturz stimmt der Flash Speicher tatsächlich nicht > mehr mit dem Ursprünglichen Inhalt überein. Dem sollte man auf den Grund gehen, wenn es sogar auf beiden AVRs passiert. Schick dochmal einen kompletten Hex-Dump (alle 16kB) vom AVR vor und nach der Selbstzerstörung. Peter
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.