Alexander M. schrieb:> Ich bekomme dort keinen Output, warum?
Kann man so nicht beantworten.
Das Beispiel sieht korrekt aus.
Vielleicht mal mit vsprintf() probieren und dann im Debugger schauen,
was passiert?
Alexander M. schrieb:> Ich bekomme dort keinen Output, warum?
Bei mir funktioniert Dein Codebeispiel, wenn man noch stdarg.h mit
einbindet.
Das einzige, was mir auffällt: Du gibst in Deinem Format kein \n an.
printf (bzw. stdio) buffert im Allgemeinen, bis ein Newline ausgegeben
wird.
Ich weiß ja nicht, was sich hinter "...." in Deinem Code verbirgt. Kann
aber sein, dass der Output niemals geflusht wird, weil Du vielleicht
danach in einer Endlosschleife verweilst.
Probiere daher mal:
Vielen Dank :)
vsprintf hat alles prima funktioniert, dadurch habe ich den Fehler auch
gefunden:
Am Ende des Strings hat das "\n\r" gefehlt...
Ich dachte bei fehlendem "\n\r" verweilt der Prozessor, bis er diese
Eingabe bekommen hat, anscheinend "überspringt" er aber einfach diesen
String ohne eine Ausgabe...
Danke ;)
Alexander M. schrieb:> Am Ende des Strings hat das "\n\r" gefehlt...
Wenn, dann eher "\r\n".
Und meist wird '\n' allein schon auf \r\n gemappt. Von welchem System
sprechen wir denn? Probiere erstmal '\n'. Erst wenn das nicht reicht,
dann "\r\n".
> Ich dachte bei fehlendem "\n\r" verweilt der Prozessor, bis er diese> Eingabe bekommen hat, anscheinend "überspringt" er aber einfach diesen> String ohne eine Ausgabe...
Nein, er überspringt nicht die Ausgabe, sondern speichert sie zwischen,
bis:
- ein Newline ausgegeben wird
- das Programm beendet wird.
Du scheinst aber das Programm niemals zu beenden, das nahm ich oben
schon an, als ich "...." in Deinem Code las.
Sehr interessant, danke für die Informationen :)
Verwendeter uC: ATSAMG55
Wo kann ich denn nachlesen, ob automatisch schon ein "\r\n" gemappt
wird?
Exakt, nach der main kommt noch eine Endlosschleife... ;)
Alexander M. schrieb:> Wo kann ich denn nachlesen, ob automatisch schon ein "\r\n" gemappt> wird?
Ausprobieren. Wo gibt das printf() denn seine Ausgabe aus? Auf einem
UART? Dann wird wahrscheinlich nicht gemappt und Du solltest "\r\n"
schreiben.
Man kann es aber auch prüfen: Sollte es tatsächlich ein UART sein, dann
PuTTY starten und schauen, ob nach Ausgabe der Test-Zeichenkette gefolgt
von '\n' der Cursor
1) nur eine Zeile nach unten
2) eine Zeile nach unten und zusätzlich an den Anfang der Zeile
springt.
Wenn 1: Du solltest immer "\r\n" schicken
Ween 2: Es reicht, ein "\n" zu schicken, wird gemappt auf "\r\n"
Merke:
\r - Carriage Return = Wagenrücklauf -> Cursor an den Anfang der Zeile
\n - Newline - Neue Zeile -> Cursor eine Zeile tiefer
> Exakt, nach der main kommt noch eine Endlosschleife..
Wenn Du die Ausgabe ohne Newline erzwingen willst, kannst Du auch
1
fflush(stdout);
verwenden. Das forciert die Ausgabe aller zwischengespeicherten Zeichen.
Siehe auch oben, da hatte ich das schon mal erwähnt.
Frank M. schrieb:> Auf einem UART? Dann wird wahrscheinlich nicht gemappt und Du solltest> "\r\n" schreiben.
Alternative: im UART-Treiber selbst \n auf \r\n mappen. Machen wir hier
so, es sei denn, der Stream wird mit O_BINARY geöffnet ("wb" statt "w"
im fopen()).
Funktioniert prima, und man muss nicht quer durch den gesamten Code \r\n
schreiben. Entspricht letztlich dem ONLCR-Feature von Posix.
Jörg W. schrieb:> Alternative: im UART-Treiber selbst \n auf \r\n mappen. Machen wir hier> so, es sei denn, der Stream wird mit O_BINARY geöffnet ("wb" statt "w"> im fopen()).
Sehr gut gelöst.
> Funktioniert prima, und man muss nicht quer durch den gesamten Code \r\n> schreiben. Entspricht letztlich dem ONLCR-Feature von Posix.
Jepp. Absolut sinnvoll.
Frank M. schrieb:> 1) nur eine Zeile nach unten> 2) eine Zeile nach unten und zusätzlich an den Anfang der Zeile>> springt.>> Wenn 1: Du solltest immer "\r\n" schicken> Ween 2: Es reicht, ein "\n" zu schicken, wird gemappt auf "\r\n">> Merke:>> \r - Carriage Return = Wagenrücklauf -> Cursor an den Anfang der Zeile> \n - Newline - Neue Zeile -> Cursor eine Zeile tiefer
Das ist systemabhängig. Windows, Linux und Mac machen das alle drei
verschieden. Normalerweise wird das dann aber in den I/O-Funktionen
gemappt, so dass man bei printf() immer nur ein \n braucht. Genau das
macht ja auch die von Jörg genannte Variante.
Rolf M. schrieb:> Windows, Linux und Mac machen das alle drei verschieden.
Mac aber nur bis MacOS 9.
Seit OSX (und damit nun auch schon einige Jahre lang) sind sie da auch
unixoid.
Rolf M. schrieb:> Normalerweise wird das dann aber in den I/O-Funktionen gemappt, so dass> man bei printf() immer nur ein \n braucht.
Korrekt, das gilt allgemein für Betriebssysteme. Das war schon in den
80er Jahren unter VAX/VMS so. Aber hier geht es um einen µC. Da wird
normalerweise nix gemappt.
Du hast offenbar den letzten Satz meines Postings übersehen.
Frank M. schrieb:> Da wird normalerweise nix gemappt.
Je nachdem, denn:
Rolf M. schrieb:> Genau das macht ja auch die von Jörg genannte Variante.
Zwei Dinge:
1) Die CR/LF-Konvertierung sollte nicht im Mikrocontroller und erst
recht nicht im C-Lib gemacht werden. Das von C verlangte einzelne
Zeichen (\n) als Zeilentrenner, hat sich bewährt - auch als systemweite
Konvention. Warum sollte man den CR/LF-Quatsch (den ja sogar Microsoft
inzw bereut) auch auf dem Mikrocontroller einführen? Wenn konvertiert
werden muß, dann so spät wie möglich. Im typischen Mikrocontrollerfall
heißt das: im Terminalprogramm - stellt das passend ein und fertig.
2) Dass deine Ausgabe ohne \n nicht erscheint, liegt, wie oben schon
erwähnt, am Output-Buffering, das deine stdio-Routinen machen. Du
kannst das auf unbuffered umschalten (z.B. mittels setbuf(stdout,
NULL);). Allerdings solltest du überlegen, ob du in deinem Bootloader
wirklich full-featured stdio-Routinen mit Buffering brauchst[1]. Sie
fressen deutlich mehr RAM und ROM als triviale, unbuffered-Versionen
(wie z.B. AVR-libc, um 1.5kB ROM, ein paar Bytes RAM).
[1] Das Buffering ist bei Betriebssystemen, bei denen der Aufruf der
Ausgaberoutinen sehr teuer ist (Systemcall, viele 1000 Taktzyklen)
sinnvoll. Bei einem Mikrocontroller, bei dem das ein einfacher
Funktionsaufruf ist, ist es meist unnötig.
foobar schrieb:> 1) Die CR/LF-Konvertierung sollte nicht im Mikrocontroller und erst> recht nicht im C-Lib gemacht werden. Das von C verlangte einzelne> Zeichen (\n) als Zeilentrenner, hat sich bewährt
Hat es.
Aber wenn es sich sooo sehr "bewährt" hat – warum dann konvertieren
Unixe (von denen die Konvention mit \n ja stammt) dann eigentlich bei
der Terminal-Ein-/Ausgabe und nicht im Terminal selbst?
> [1] Das Buffering ist bei Betriebssystemen, bei denen der Aufruf der> Ausgaberoutinen sehr teuer ist (Systemcall, viele 1000 Taktzyklen)> sinnvoll. Bei einem Mikrocontroller, bei dem das ein einfacher> Funktionsaufruf ist, ist es meist unnötig.
Ob das Buffering sinnvoll ist oder nicht, hängt nicht von der
Anwesenheit eines Betriebssystems ab. Gerade erst wieder durch hier:
Ausgabenachrichten im Binärformat bestehen aus einer Sync-Sequenz, einem
Header, einer Nachricht und einer CRC. Jedes dieser Teile wird mehr oder
weniger separat erzeugt. In der ersten Version wurde jeweils danach
schnell noch ein fflush(stdout) gemacht. Solange das nur auf 'ne UART
ging, war das noch einigermaßen vertretbar, aber als stattdessen HS-USB
als Transport heran sollte, ist die Wirkung katastrophal. Da wurden dann
teilweise einzelne Zeichen in einem kompletten USB-Paket versendet.
Umbau auf fully buffered und fflush() erst am Ende der Nachricht: jetzt
kommen die ersten xxx KiB alle schön in 512-Byte-Paketen, nur das letzte
ist kürzer.
Auch alles ohne Betriebssystem … aber man sollte drüber nachdenken. Die
paar Byte für den stdio-Buffer stören auf einem größeren Mikrocontroller
kaum. Auf einem ATtiny13 würde ich das natürlich anders sehen.
foobar schrieb:> Zwei Dinge:>> 1) Die CR/LF-Konvertierung sollte nicht im Mikrocontroller und erst> recht nicht im C-Lib gemacht werden.
Solange sie nicht beim Aufruf von printf() gemacht wird. Da gehört es am
allerwenigsten hin.
> Das von C verlangte einzelne Zeichen (\n) als Zeilentrenner, hat sich> bewährt - auch als systemweite Konvention.
C verlangt es allerdings nur im Programm. In was das beim Rausschreiben
konvertiert wird, ist C egal.
> Warum sollte man den CR/LF-Quatsch (den ja sogar Microsoft inzw bereut)> auch auf dem Mikrocontroller einführen?
Es schadet auf anderen Systemen nicht.
Jörg W. schrieb:> Aber wenn es sich sooo sehr "bewährt" hat – warum dann konvertieren> Unixe (von denen die Konvention mit \n ja stammt) dann eigentlich bei> der Terminal-Ein-/Ausgabe und nicht im Terminal selbst?
Was meinst du damit? Unixe konvertieren da doch nix.
Rolf M. schrieb:> Unixe konvertieren da doch nix.
Selbstverständlich tun sie das.
stty onlcr icrnl
Sie erledigen das tief unten im seriellen Treiber, aber noch generisch
oberhalb der Hardware.
Bei der von mir beschriebenen Variante erfolgt es dagegen tatsächlich
erst im Hardware-Treiber (UART bzw. USB/CDC).
Jörg W. schrieb:> Aber wenn es sich sooo sehr "bewährt" hat – warum dann konvertieren> Unixe (von denen die Konvention mit \n ja stammt) dann eigentlich bei> der Terminal-Ein-/Ausgabe und nicht im Terminal selbst?
Das hat historische Gründe. Die damaligen Teletypes (TTYs,
schreibmaschinenähnlich) konnten schon immer zwischen Wagenrücklauf (CR)
und Zeilenvorschub (LF, auch NL) unterscheiden - was auch sinnvoll war.
So konnte man auch von der aktuellen Spaltenposition wesentlich
schneller eine Zeile tiefer springen, ohne den Wagenrücklauf zu
verwenden. Denn dann hätte man noch eventuell Dutzende von SPACEs
"drucken" müssen, um an dieselbe Spalte zu gelangen.
Die von DEC erfundenen nachfolgenden Terminals wie VT100 bzw. Varianten
haben diese Tradition fortgeführt. Das passte auch gut zu deren
Betriebssystemen RSX11 und VMS. Die damit ausgestatteten Rechner PDP11
bzw. VAX arbeiteten nicht nur auf TTY-Basis, sondern auch bei
Text-Dateien grundsätzlich mit CR+LF.
Erst UNIX hat das einfache Newline '\n' eingeführt. Daher das ONLCR, mit
dem man das Mapping für TTYs dann einstellen kann.
EDIT:
Für die nicht-unixoiden Betriebssysteme wurde übrigens erst nachträglich
das Binary-Flag für fopen() wie "rb" und "wb" eingeführt - um das
Mapping von '\n' für Binärdateien unterdrücken zu können. UNIX kennt
dieses Mapping sowieso nicht für Dateien, nur für TTYs. Später wurde das
Binary-Flag dann auch aus Kompatibilitätsgründen für UNIX/Linux
eingeführt, obwohl es da überhaupt keinen Effekt hat.
Jörg schrieb:
> Aber wenn es sich sooo sehr "bewährt" hat – warum dann konvertieren> Unixe (von denen die Konvention mit \n ja stammt) dann eigentlich bei> der Terminal-Ein-/Ausgabe und nicht im Terminal selbst?
Wegen der nicht perfekten Welt ;-) Nicht alle Geräte waren frei
konfigurierbar (einige hatten ja nicht mal Elektronik drin). Deshalb
hat man über dem Interface-Layer (UART-Treiber) einen weiteren Layer
eingeschoben (line-discipline), der einen Teil der gerätespezifischen
(nicht interfacespezifischen) Eigenschaften wegabstrahierte. Dazu
gehörte unter anderem die CR/LF-Konvertierung, aber auch z.B.
Verzögerungen nach einem
Wagenrücklauf/Zeilenvorschub/Tabulator/Backspace,
(XON/XOFF-)Flow-Control, oder der Line-Mode (Minizeileneditor) und
lokales Echo.
Von dem ganzen bekommt das Programm nichts mit. Es schickt seine \n
raus, und egal wo die nun hingehen (Datei, Pipe, Drucker, Terminal), es
geschieht das passende. Ähnlich beim Input (inkl eventuellen
Zeileneditor).
> Ob das Buffering sinnvoll ist oder nicht, hängt nicht von der> Anwesenheit eines Betriebssystems ab. Gerade erst wieder durch hier:> Ausgabenachrichten im Binärformat bestehen aus einer Sync-Sequenz, einem> Header, einer Nachricht und einer CRC.
Du weißt aber schon, dass das Verhalten von stdio bezgl des Bufferings
extrem implementationsabhängig ist; von gar kein Buffering über kleine,
große bis zu dynamischen Buffern oder gar ein Mix-Betrieb.
> Da wurden dann teilweise einzelne Zeichen in einem kompletten USB-Paket> versendet.
Dann fix den USB-Treiber: Daten sammeln bis ein Paket voll ist oder ein
Time-out eintritt. Der USB-Treiber weiß viel besser, was und wieviel er
wielange Buffern muß als der stdio-Layer.
Rolf schrieb:
>> Das von C verlangte einzelne Zeichen (\n) als Zeilentrenner, hat sich>> bewährt - auch als systemweite Konvention.>> C verlangt es allerdings nur im Programm. In was das beim Rausschreiben> konvertiert wird, ist C egal.
Schon klar, deshalb schrieb ich ja extra, dass das sich aber auch als
systemweite Konvention bewährt hat.
>> Warum sollte man den CR/LF-Quatsch (den ja sogar Microsoft inzw bereut)>> auch auf dem Mikrocontroller einführen?>> Es schadet auf anderen Systemen nicht.
Das muß von einem Windows-Nutzer sein ;-)
Was heißt schon schaden, es nervt, ist unnötig und an anderer Stelle
besser zu behandeln.
foobar schrieb:> Der USB-Treiber weiß viel besser, was und wieviel er> wielange Buffern muß als der stdio-Layer.
Der USB-Treiber weiß wesentlich schlechter, wie das zugrundeliegende
Protokoll funktioniert als der Protokoll-Treiber (der das via stdio
kommuniziert, weil er von USB nichts weiß).
Es ist nicht Aufgabe der unteren Schichten, mangelhafte Abstraktionen
zu "reparieren". Denn damit zieht man sich furchtbare
Kreuzabhängigkeiten in das gesamte System rein und kann sich die
Abstraktionen irgendwann ganz schenken.
S. R. schrieb:> foobar schrieb:> Der USB-Treiber weiß viel besser, was und wieviel er> wielange Buffern muß als der stdio-Layer.>> Der USB-Treiber weiß wesentlich schlechter, wie das zugrundeliegende> Protokoll funktioniert als der Protokoll-Treiber (der das via stdio> kommuniziert, weil er von USB nichts weiß).
So ist es.
stdio macht das Buffering in den verwendeten Bibliotheken (newlib auf
ARM) gut und erprobt genug, als dass ich mir nicht stattdessen woanders
neue Fehler einbauen muss.
Wenn ich deren dynamisch allozierten Puffer nicht mag, kann ich allemal
noch mit setvbuf() einen eigenen statischen hinterlegen.
Frank M. schrieb:> Du solltest immer "\r\n" schicken...
Dem schließe ich mich ausdrücklich an. Es ist die einzige wirklich
saubere Lösung - wenngleich auch etwas Disziplin erfordernd.
All das Gefasel von Zwischenpuffern in stdio und Konvertierungen in
Layern, die eigentlich nur übertragen sollen und gefälligst NICHTS
eigenmächtig an dem Datenstrom herumändern dürfen, mach die Dinge nur
noch komplizierter als sie tatsächlich sind.
S. R. schrieb:> Es ist nicht Aufgabe der unteren Schichten, mangelhafte Abstraktionen> zu "reparieren".
Darum geht es beim USB ja auch gar nicht. Der USB ist blockorientiert
und serielle Kanäle a la stdio sind zeichenorientiert.
Wenn nun ein USB-Treiber nach außen hin zeichenorientiert sich
darstellen soll (VCP), dann muß er die Umsetzung in den internen
Blockverkehr irgendwie organisieren.
Also selber zwischenpuffern und dann blockweise übertragen und das
Hängenbleiben von Zeichen im Zwischenpuffer in geeigneter Weise
verhindern.
foobar schrieb:>>> Warum sollte man den CR/LF-Quatsch (den ja sogar Microsoft inzw bereut)>>> auch auf dem Mikrocontroller einführen?>>>> Es schadet auf anderen Systemen nicht.>> Das muß von einem Windows-Nutzer sein ;-)>> Was heißt schon schaden, es nervt, ist unnötig und an anderer Stelle> besser zu behandeln.
Tja, frühere Fehler in den Fundamenten haben eben Spätfolgen. Ätsch.
Man hätte damals eben irgend ein anderes Zeichen als Zeilen-Ende sich
aussuchen sollen. RS ($1E) zum Beispiel.
Aber nein, man mußte ja die direkten Hardware-Codes zum Auslösen des
Wagenrücklaufs und des Walzendrehens verwenden.
Aber das sind 2 Zeichen - und jeder macht es anders, die einen nur CR,
die anderen nur LF und nur DOS/Windows macht es eigentlich richtig,
nämlich beides korrekterweise zu verwenden. CR würde ja nur auf den
Zeilenanfang setzen und LF würde die Spaltenposition beibehalten.
Es gab ja in den 80er Jahren mal den Versuch, RS als Zeilenende
einzuführen - aber die superklugen Programmierer haben es verschmäht.
Ganz schön blöd gewesen.
Und jetzt jammert ihr darüber.
Traurig, traurig, traurig.
W.S.
Jörg W. schrieb:> Alternative: im UART-Treiber selbst \n auf \r\n mappen. Machen wir hier> so, es sei denn, der Stream wird mit O_BINARY geöffnet ("wb" statt "w"> im fopen()).
oder es sei denn, die Anwendungsschicht will dediziert kein Editieren
des Ausgabe-Streams im Lowlevel-Treiber, oder.. oder.. oder..
Wenn ihr das bei euch so macht, dann ist das eure Sache, aber es ist
Pfusch. Ein UART-Treiber soll Zeichen transportieren und nicht
interpretieren und auch nicht editieren.
W.S.
W.S. schrieb:> Wenn ihr das bei euch so macht, dann ist das eure Sache, aber es ist> Pfusch. Ein UART-Treiber soll Zeichen transportieren und nicht> interpretieren und auch nicht editieren.
Wo ist das Problem?
1
Anwendung
2
|
3
v
4
IO-LIB
5
|
6
V
7
UART-Treiber
Die Anwendung schickt "\n", die I/O-Lib sieht: "Oh, das geht auf ein
TTY!" und macht "\r\n" draus. Der UART-Treiber bekommt "\r\n" und
schickt "\r\n".
Ich halte es für durchaus legitim, 2 Schichten in einem kleinen und
überschaubaren System auch innerhalb eines C-Moduls zu erschlagen. Wenn
die IO-Lib zum Beispiel sowieso nichts anderes zu tun hat, als
ausschließlich den UART-Treiber zu bedienen, kann man diese auch
zusammenfassen, ohne etwas an Funktionalität zu verlieren.
Von daher verstehe ich Deine Aufregung nicht.