Gast
#5075190
Hallo, ich möchte Strings "hello123\n" bis zum Zeilenumbruch lesen. Wie stelle ich das an? Einzel char lese ich wie folgt:
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
Vielen Dank im Voraus.
|
Anzeige
|
STM32F1: Wie lese ich chars zeilenweise über UART?
Gast
#5075190
Hallo, ich möchte Strings "hello123\n" bis zum Zeilenumbruch lesen. Wie stelle ich das an? Einzel char lese ich wie folgt:
Vielen Dank im Voraus. 1. Du machst Dir Gedanken wie lang eine Zeile maximal sein darf. Z.B. 100 Zeichen. 2. Du allozierst für diese maximale Zeile Speicher 3. Du empfängst solange Zeichen und packst sie in Deinen Puffer, bis entweder die maximale Zeilenlänge (siehe oben) erreicht ist, oder eben ein Zeilenende empfangen wurde Leis mal https://www.mikrocontroller.net/articles/AVR-GCC-Tutorial/Der_UART#Empfang_von_Zeichenketten_.28Strings.29 durch, respektive den ganzen Artikel. Du kannst einfach auf das '\n' NewLine prüfen oder auf CarriageReturn, wenn du es über Tastatur eingibst und ENTER drücken möchtest.
Adam P. schrieb: > #define NL 0x10 > #define CR 0x0d Das
halte ich für lesbarer und bedeutet dasselbe. > if((ch == NL) || (ch == CR)) > { > // Zeileneingabe beendet > } > else > { > // mach etwas mit dem empfangenen Zeichen > } Kann man nicht unbedingt allgemein so formulieren. Mit Deiner Methode bekommt man bei einer Zeile, die mit CR+NL beendet wird, immer noch eine Leerzeile. Wenn gewährleistet ist, dass die Zeile tatsächlich mit NL beendet wird, dann wäre besser:
Wenn jedoch eine Tasteneingabe über eine Terminal-Emulation wie z.B. PuTTY zustandekommt, wäre es besser, folgendes zu schreiben:
Denn Terminal-Emulationen senden standardmäßig lediglich ein CR und kein NL, wenn man die Enter-Taste drückt - außer man stellt es in der Terminal-Emulation um.
Gast
#5075858
Vielen Dank!!
Gast
#5076210
Ich mache das meistens so: Eine Interruptroutine sammelt alle empfangnen Zeichen in einem Ringpuffer. Sie ersetzt dabei jede empfangene CR durch LF und ignoriert jede empfange LF wenn direkt davor ein CR empfangen wurde. So erreiche ich, daß das Programm mit allen Varianten von Zeilenumbrüchen gleich klar kommt: CR, CR-LF, und LF. Mein Programm checkt regelmäßig, ob im Puffer ein NL steht. Wenn ja, dann holt es alle Zeichen bis einschließlich dem NL heraus und verarbeitet die Zeile. Hier ein Auszug aus meiner ISR:
Stefan U. schrieb: > So erreiche ich, daß das Programm mit allen Varianten von > Zeilenumbrüchen gleich klar kommt: CR, CR-LF, und LF. Und keine Binärdaten mehr übertragen kann. Das ist der Nachteil dabei, daher überlasse ich diese Auswertung einer weiter oben stehenden Schicht. Frank M. schrieb: >> #define NL 0x10 >> #define CR 0x0d > > Das#define NL '\n' > #define CR '\r' > > halte ich für lesbarer und bedeutet dasselbe. Nö. '\n' ist nämlich das Zeichen 10, oder 0x0a. Damit wäre '\n' wenigstens korrekt, während 0x10 nicht klappt. ;-)
Gast
#5076312
Jörg W. schrieb: > Frank M. schrieb: >>> #define NL 0x10 >>> #define CR 0x0d >> >> Das#define NL '\n' >> #define CR '\r' >> >> halte ich für lesbarer und bedeutet dasselbe. > > Nö. '\n' ist nämlich das Zeichen 10, oder 0x0a. > > Damit wäre '\n' wenigstens korrekt, während 0x10 nicht klappt. ;-) Das alles geht im übrigen so lange gut, wie man keine Hex Values schickt. ;-) Da bin ich nämlich schon des öfteren drauf reingefallen... wenn man z.b. eine Variable, dessen Inhalt eben 0x0a ist, per UART rausschickt. Deshalb habe ich einen Grundsatz für mich gefasst: Sobald Variablen geschickt werden - immer ein String mit fester länger verwenden. ;-) Selbst eine Kombo von 0x0d und 0x0a hatte mich schon mal Stunden des Suchens gekostet. :'D Traurig, aber ist so.
Gast
#5076317
>durch, respektive den ganzen Artikel.
Warum benutzen eigentlich nur Informatiker das Wort "respektive"?
In 97,4% aller Fällen ist das so.
DraconiX schrieb: > Das alles geht im übrigen so lange gut, wie man keine Hex Values > schickt. ;-) > > Da bin ich nämlich schon des öfteren drauf reingefallen... wenn man z.b. > eine Variable, dessen Inhalt eben 0x0a ist, per UART rausschickt. > Deshalb habe ich einen Grundsatz für mich gefasst: Sobald Variablen > geschickt werden - immer ein String mit fester länger verwenden. ;-) > Selbst eine Kombo von 0x0d und 0x0a hatte mich schon mal Stunden des > Suchens gekostet. :'D Traurig, aber ist so. Das hat aber nicht direkt etwas mit Hex Values zu tun. Das Problem ensteht, wenn man seine übertragenen Werte nicht als ASCII codiert sondern versucht direkt Byte Werte zu übertragen. Bei so etwas sollte man sich überlegen, ob man nicht die Steuerzeichen aus dem ASCII Satz verwendet. Also: 0x02 als Start einer Nachricht 0x03 als Ende der Nachricht 0x10 als Escape Zeichen, wenn ein Steuerzeichen in der Nachricht auftritt Stefan U. schrieb: > Mein Programm checkt regelmäßig, ob im Puffer ein NL steht. Wenn ja, > dann holt es alle Zeichen bis einschließlich dem NL heraus und > verarbeitet die Zeile. Alternative Strategie wäre, in der ISR eine Flagge zu setzen, wenn eine der Zeilenendenvarianten erreicht ist. Das Hauptprogramm checkt die Flagge und zündet, wenn gesetzt, den Input Parser. Nach erfolgtem Parsing wird die Flagge gelöscht. Frank M. schrieb: > Kann man nicht unbedingt allgemein so formulieren. Natürlich hast du recht, dies sollte Ihm nur verdeutlichen, wie er ein Zeilenende (Umbruch) ansich erkennen kann. (Dies war nämlich seine Frage) Ansich gehört da noch viel mehr zu, wollte Ihm nur nicht meinen Hundertzeilen langen Code posten. Erkennen von: - Backspace - evtl. links/rechts Pfeiltasten - Steuerbefehle mittels STRG + Taste - usw. Kann beliebig viel werden, aber hast schon recht. Ausser er legt es per Protokollabsprache fest, dass immer ein \n das Ende markiert. Mike R. schrieb: > Das Problem ensteht, wenn man seine übertragenen Werte nicht als ASCII > codiert sondern versucht direkt Byte Werte zu übertragen. Dieses Problem ensteht jedoch auch nur, wenn du Rohdaten als Byte-Stream sendest sie aber als ASCII interpretierst. Da kann man sich lieber auf einen definierten Paketaufbau einigen, z.B.: --------------------------------- | CRC16 | valid_data_cnt | data | --------------------------------- ASCII Werte die über das Terminal gesendet werden, sind auch nur Bytes, ob nun Dezimal oder Hex - es liegt allein in der Interpretation der Daten. Jörg W. schrieb: > Nö. '\n' ist nämlich das Zeichen 10, oder 0x0a. :-D ...hast ja recht - da hab ich wohl an Dec. gedacht und in Hex geschrieben. Sorry. Adam P. schrieb: > Dieses Problem ensteht jedoch auch nur, wenn du Rohdaten als Byte-Stream > sendest sie aber als ASCII interpretierst. Nein. Wenn ich auf der untersten Ebene (also im UART-Treiber) schon bestimmte Bytes verarbeite, dann kann ich diese nicht mehr als Nutzdaten übertragen. Das hat nichts mit ASCII zu tun. Entweder, ein Übertragungskanal ist transparent (dann kann ich beliebige Daten übertragen), oder er ist es nicht (dann bin ich eingeschränkt in dem, was ich übertragen kann). Dein Beispiel mit "CRC, Länge, Daten" funktioniert mit den meisten hier genannten Codebeispielen nicht.
Gast
#5080331
S. R. schrieb: > Adam P. schrieb: >> Dieses Problem ensteht jedoch auch nur, wenn du Rohdaten als Byte-Stream >> sendest sie aber als ASCII interpretierst. > > Nein. Wenn ich auf der untersten Ebene (also im UART-Treiber) schon > bestimmte Bytes verarbeite, dann kann ich diese nicht mehr als Nutzdaten > übertragen. Natürlich kann man das. Man muss sich nur auf ein Format einigen, welches das entsprechend handhabt. Theoretisch ist das seit den Zeiten des OSI-Modells eigentlich komplett abgefrühstückt und praktisch beruht z.B. das ganze Internet-Zeugs darauf, das jeder Layer seinen Kram verarbeitet und die Nutzdaten kodiert/dekodiert wie es gebraucht wird. Eines der simpelsten Layer 2-Protokolle für serielle Schnittstellen ist SLIP, RFC1055 von 1988 - Steinzeit. Das eignet sich auch problemlos für die Übertragung von Datensätzen auf seriellen Schnittstellen wenn man kein IP oben drüber hat. Hier der Code aus dem RFC:
Und hier die Empfangsroutine aus dem RFC:
Das ist so simpel: 0xC0 im Datenstrom markiert das Ende eines Datensatzes. Eine Markierung für den Anfang hat man sich gespart. Wenn was kommt geht es los. Wer tricksen will kann den Anfang auch mit 0xC0 markieren, das gibt dann zwischen den Datensätzen leere Datensätze. 0xDB im Datenstrom ist das Escape-Zeichen. Wofür braucht man das? Um 0xC0 in den Daten kodieren zu können: Eine Sequenz von 0xDB, 0xDC kodiert ein 0xC0 im Nutzer-Datenssatz (die Escape-Sequenz für 0xC0 wurde bewusst nicht als 0xDB 0xC0 definiert). Jetzt muss man nur noch eins machen, das Escape-Zeichen escapen: 0xDB, 0xDD kodiert ein 0xDB im Nutzer-Datensatz (auch hier wurde bewusst nicht 0xDB 0xDB als Kodierung für 0xDB verwendet). 0xDC und 0xDD im Datensatz, ohne führendes 0xDB sind unkritisch.
Beitrag #5080396 wurde von einem Moderator gelöscht.
Gast
#5081120
Jack schrieb: >> Nein. Wenn ich auf der untersten Ebene (also im UART-Treiber) schon >> bestimmte Bytes verarbeite, dann kann ich diese nicht mehr als Nutzdaten >> übertragen. > > Natürlich kann man das. Man muss sich nur auf ein Format einigen, > welches das entsprechend handhabt. Theoretisch ist das seit den Zeiten > des OSI-Modells eigentlich komplett abgefrühstückt und praktisch beruht Moin, ich moechte an dieser Stelle nur nochmal darauf hinweisen, dass auch das OSI Modell und die zitierte RFC die Meinung stuetzt, dass ein Transportprotokoll eben nicht in den UART Treiber gehoert. Daher ist es ja ein Layer 2 Protokoll. Der gepostete Code verwendet denn auch ein recv_char(). Dahinter verbirgt sich irgendwann, mal der UART Treiber, falls dieses Protokoll ueber UART angewendet werden soll. Wenn man aber schon Zeichenfilter und Ersetzungen fest in den UART Treiber einbaut, kann man darueber keine anderen Protokolle fahren, die eben diese Zeichen benoetigen. Es ging bei dem Einwand genau darum, dass CR/LF ggf. schon im UART Treiber ausgefiltert bzw. Ersetzt wurden. Das waere dann fuer Binaerdaten ungeeignet, da man nicht wissen kann, ob das Ersetzungszeichen tatsaechlich im Datenstrom war oder eben die anderen beiden. Wenn man also zwischen ASCII Zeilenbasierte Kommandos und Datenuebertragung (bin) hin und herschalten will, was nun nichts ungewoehnliches war/ist. Dann muesste man dabei den Treiber wechseln. Das macht man eher nicht, sondern nutzt je nach Mode andere L2 Interfaces. Oder aber man baut alle Modi und Protokolle in den UART Treiber ein. Und wenn man dann das selbe Protokoll nicht ueber den echten UART, sondern Virtuellen Com Port ueber USB schieben will, dann baut man alle diese Protokolle und Modi nochmal schnell eben in den VCP Treiber ein. Den man ja auch immer im Source Code vorliegen hat. Ich halte das fuer keine gute Idee. Wenn man wild L2 Protokolle fest schachtelt, erhoeht man drastisch die Wahrscheinlichkeit, dass kein anderes Produkt mit "mir" reden mag/kann. Obwohl man doch nur standard Protokolle verwendet. Das kann natuerlich auch gewollt sein. Muss also jeder selber entscheiden.
Gast
#5081147
>> So erreiche ich, daß das Programm mit allen Varianten von >> Zeilenumbrüchen gleich klar kommt: CR, CR-LF, und LF. > Und keine Binärdaten mehr übertragen kann. Ja klar, das macht so nur bei textorientierter Kommunikation Sinn. > Da bin ich nämlich schon des öfteren drauf reingefallen... wenn man z.b. > eine Variable, dessen Inhalt eben 0x0a ist, per UART rausschickt. Man muss sich schon für eins entscheiden. Entweder überträgst du Text-Zeilen, oder Binärdaten. Die musst du ann aber logischerweise anders "framen", zum Beispiel indem du Pakete mit Längen-Angabe erstellst. Ich hatte früher viele Anwendungen gesehen, wo man das Ende eines Paketes durch eine zeitliche Pause dargestellt hat. Das ist aber sehr unzuverlässig, da PC Programme auch vom Betriebssystem mal pasiert werden können. Außerdem hat sich das Timing seit Einführung der USB-UART Adapter sehr verändert. > Alternative Strategie wäre, in der ISR eine Flagge zu setzen Finde ich sehr gut, dann muss nicht im Puffer suchen, um festzustellen, ob eine Zeile empfangen wurde. Ist bestimmt wesentlich effizienter. > ich moechte an dieser Stelle nur nochmal darauf hinweisen, dass auch > das OSI Modell und die zitierte RFC die Meinung stuetzt, dass ein > Transportprotokoll eben nicht in den UART Treiber gehoert. Mit OSI Schichten wurde schon so manches einfache Program kaputt designt, vor allem, was Performacne und Speicherbedarf angeht. Keine Frage, das ist ein klares Modell für saubere Strukturierung. Aber nicht in jedem Fall angemessen. Vor allem nicht auf Mikrocontrollern mit nur wenigen Bytes RAM. Darth Moan schrieb: > Dann muesste man dabei den Treiber wechseln. > Das macht man eher nicht Auf unixoiden Systemen war/ist das gang und gäbe: "raw" vs. "cooked" mode im Terminaltreiber umschalten. Man muss ja nicht unbedingt ein komplettes termios dafür implementieren, aber es spricht grundsätzlich nichts weiter dagegen, dass man das auch auf einem Controller ähnlich strukturiert. Darth Moan schrieb: > dass ein > Transportprotokoll eben nicht in den UART Treiber gehoert. Vielen dank, so sehe ich das auch. UART ist nur das Übertragungsmedium, Sie soll nur Daten senden/empfangen und nichts interpretieren.
Gast
#5081673
Adam P. schrieb: > Darth Moan schrieb: >> dass ein >> Transportprotokoll eben nicht in den UART Treiber gehoert. Erstens, Transport ist Layer 4, nicht das gezeigte SLIP. Das ist Layer 2. Also was soll die Aufregung? > > Vielen dank, so sehe ich das auch. > UART ist nur das Übertragungsmedium, Sie soll nur Daten senden/empfangen > und nichts interpretieren. Zweitens wird die Aussage nicht durch die Realität gestützt. Wer will kann sich mal RS232 im Detail ansehen, zum Beispiel das Senden von Breaks, das generieren von Parity-Bits oder das so geliebte Software-Flowcontrol. Dritten werden in der Diskussion die zur Ansteuerung des URAT benötigten drei Zeilen Assembler (variiert geringfügig je nach MCU) zum "Treiber" hochstilisiert und sich dann sinnlos an einem recv_char() Platzhalter in Beispielsourcecode aufzuhängen. "Treiber" brauchen Arduino-Bastler. Viertens, wenn sich schon bei der bloßen Erwähnung des OSI-Modells reflexartig in die Hose macht wird und bei 30 bis 40 Zeilen simpelsten C-Code was von >>> kaputt designt, vor allem, was Performacne und Speicherbedarf angeht.... geredet wird, dann ist das hier Zeitverschwendung. Für die anderen, die ein bisschen Mitdenken wollen ein Tipp. Man ersetze im Code
einfach durch
oder gar, unter Verlust einiger Protokolleigenschaften und der Notwendigkeit ein weitere Zeile zu ändern, durch
und überlege sich dann was bei diesem "ganz schlimmen" Layer 2 Protocol herauskommt.
Gast
#5081835
Jack schrieb: > Erstens, Transport ist Layer 4, nicht das gezeigte SLIP. Das ist Layer > 2. Also was soll die Aufregung? > Hoppala, stimmt auffallend. Wie peinlich. Aendert aber nichts daran, dass das "Protokoll" nicht in die unterste ISR des UART gehoert. > Dritten werden in der Diskussion die zur Ansteuerung des URAT benötigten > drei Zeilen Assembler (variiert geringfügig je nach MCU) zum "Treiber" > hochstilisiert und sich dann sinnlos an einem recv_char() Platzhalter in > Beispielsourcecode aufzuhängen. "Treiber" brauchen Arduino-Bastler. Kannst du mir schnell mal die 3 Zeilen Assembler, die den VCP ueber USB machen, zeigen? Ich nehme da recht fette (im Vergleich zu 3 Zeillen Assembler) Code Bloecke vom µC Hersteller (obwohl, vielleicht war auch mal 3P SW dabei, kann ich jetzt nicht beschwoeren). 3 Zeilen Assembler waeren da viel sparsamer. :-) Das recv_char() ist eben nur ein Platzhalter, den man nach belieben austauscht. Deshalb baut man das "Protokoll" eigentlich nicht in das recv_char() hinein sondern macht es ausserhalb. Das war alles, was gesagt wurde. Oh, nicht ganz! Es wurde ausserdem gesagt, das dann (ausserhalb) beliebig viele dieser "Protokolle" "gleichtzeitig das selbe recv_char() nutzen koennen, ohne die ISR dann zur Laufzeit aedern zu muessen bzw. die Umschaltung aller dieser "Protokolle" in die ISR einbauen zu muessen. Aber dieser gigantische Aufwand (das nicht gleich in die ISR zu haengen) scheint manchen Menschen auch irgendwie angst zu machen. ;-) Das es Rahmenbedingungen geben kann, die Jemanden dann doch dazu bewegen, Dinge zu tun, die der reinen Lehre nach nicht sauber sind, hat (glaube ich) niemend bestritten. Nicht böse sein, wir haben offensichtlich unterschiedliche Meinungen, was alles in die ISR gehoert und was nicht.
Gast
#5082037
Jörg W. schrieb: > Auf unixoiden Systemen war/ist das gang und gäbe: "raw" vs. "cooked" > mode im Terminaltreiber umschalten. was ist ein Terminaltreiber? Mal abgesehen davon, daß der TO schon am 13. 7. ausgestiegen ist, geht diese Diskussion in die falsche Richtung. Terminal-Zeugs gehört in's jeweilige Terminal, die schieren HW-Treiber gehören jedoch nicht dazu. Die sollen sich auch nicht um den Inhalt der gesendeten und empfangenen Zeichen kümmern, sondern nur darum, daß der jeweilige Datenfluß ungestört passiert. Da ist die ganze hitzige Diskussion um "recv_char()" überflüssig. Wenn man (was ja durchaus erwünscht ist) seine höheren Programmschichten weitgehend HW-unabhängig schreiben will, dann braucht es nur ein ganz billiges Zweischichten-Modell: unterste Schicht: die HW-Treiber für die seriellen Peripheriecores, einschließlich der virtuellen (USB-CDC). Die können und sollen alle das gleiche Interface nach 'oben' zeigen, also in so einem Sinn: - uart0_getchar(), uart1_getchar(),.., uart99_getchar(), usb_getchar(), spi1_getchar().. usw. darüberliegende Schicht: eine Durchreicher-Schicht, die einfach nur als Weiche dient: getchar(name) mit name= uart0,..99, usb, stdin und so. Auf so ein Konstrukt kann man ein beliebiges Programm draufsetzen, entweder ne simple Kommandozeile oder eine Terminal-Emulation oder was auch immer. Sowas hat dann nur noch mit
zu tun und das sollte eigentlich jeder µC-Eleve formulieren können - und wenn nicht, dann entweder Nase ins Buch stecken oder sich ein anderes Hobby suchen. W.S. W.S. schrieb: > was ist ein Terminaltreiber? Das Zeug, was einen Fernschreiber (tty) bedient. ;-)
Gast
#5082084
ich hätte lieber ne ernstgemeinte Antwort. Bei Linux und anderen ixe/uxen ist es nicht anders als bei Windows, wo serielle Ströme von den Anwendungen wie ne Datei geöffnet werden - und Terminalprogramme sind nix als Anwendungen. Und das, was nen Fernschreiber bedient, ist ein Kabel mit nem Stecker dran - der Rest im Fernschreiber ist Elektromechanik. ratter..ratter..ratter W.S. W.S. schrieb: > ich hätte lieber ne ernstgemeinte Antwort. Naja, der Terminaltreiber im Unix hat eben in der Tat einerseits zeilenweise gepuffert und Editiermöglichkeiten angeboten (back space, ganze Zeile löschen etc.). In diesem Modus schickt er die komplette Zeile erst weiter, wenn ein CR oder LF auftritt. Andererseits kann man ihn in den raw mode schalten, bei dem er einfach Zeichen für Zeichen nach oben durchreicht. Kein Echo, nichts. > Und das, was nen Fernschreiber bedient, ist ein Kabel mit nem Stecker > dran /dev/tty* kommt namensmäßig aber eben genau daher.
Gast
#5083000
Jörg W. schrieb: > Naja, der Terminaltreiber im Unix hat eben in der Tat einerseits > zeilenweise gepuffert und Editiermöglichkeiten angeboten Also das ist in meinem Sprachgebrauch "Die Konsole", also eine Anwendung, die man auch als Dienst laufen lassen kann und die in deinen Gefilden ne Shell als Tochter laufen läßt. Das ist dann alles vor den Toren des API des OS im Bereich der Apps. Aber das hat wohl alles nix mit dem kläglichen Versuch des TO zu tun, einen UART auf seinem µC benutzen zu wollen. Und der hat sich schon am 13.7. verabschiedet. W.S. W.S. schrieb: > Also das ist in meinem Sprachgebrauch "Die Konsole", also eine > Anwendung, die man auch als Dienst laufen lassen kann und die in deinen > Gefilden ne Shell als Tochter laufen läßt. Dann ist dein Sprachgebrauch unpräzise. Nicht jedes Terminal ist auch eine Konsole.
Gast
#5083153
S. R. schrieb: > Nicht jedes Terminal ist auch eine Konsole. Er hat auch nicht von Terminal geschrieben, sondern von terminal_TREIBER W.S. Antwort schreibenBitte melde dich an, um einen Beitrag zu schreiben. |
Anzeige
|