Tag zusammen! Ich habe mir eine Verbindung PC <-> uC gebastelt und möchte nun gerne Kommandos vom PC im Microcontroller auswerten und bestimmte Aktionen starten. Daher hätte ich mal vorab, um nicht jetzt gleich falsch anzufangen, eure Erfahrungen damit - also genauer gesagt suche ich ein Beispiel, wie ihr das gelöst habt. Meine Vorgehensweise wäre nun folgende: Ich habe einen Empfangspuffer, welcher die ankommenden Zeichen speichert und jedes Zeichen direkt auf ein '\r' (RETURN) abfragt. Gleichzeitig läuft ein Zeichenzähler mit - wird ein RETURN erkannt, so wird erst auf den Zeichenzähler geguckt, ob eine Mindestmenge eingegangen ist - das wäre dann mehr dafür da, dass nicht jedesmal der Textstring durchsucht werden muss. Sind theoretisch genug Zeichen da, so wird der String von Anfang an mit einem vorgegebenen String verglichen und auf Gleichheit überprüft. Liegt Gleichheit vor, so wird etwas ausgeführt. Mit jedem Tastendruck wird zugleich ein Timer gestartet, welcher die Eingabe nach einer Zeit verfallen lässt, sollte kein ENTER gedrückt werden. Ist mein Vorhaben so in Ordnung? Hat jemand evtl. ein Code-Beispiel oder ein paar Erläuterungen dazu, wie man es ggf. besser machen kann?
Ferdinand schrieb: > Ich habe einen Empfangspuffer, welcher die ankommenden Zeichen speichert > und jedes Zeichen direkt auf ein '\r' (RETURN) abfragt. Gut > Gleichzeitig > läuft ein Zeichenzähler mit - wird ein RETURN erkannt, so wird erst auf > den Zeichenzähler geguckt, ob eine Mindestmenge eingegangen ist - das > wäre dann mehr dafür da, dass nicht jedesmal der Textstring durchsucht > werden muss. Hast du mehr als 1000 Kommandos? Der µC ist schneller als du denkst. Da ich ein Freund von kurzen Kommandos bin (s = Status, w = Write, r = read etc.) finde ich die Einschränkung eher störend. Kurze Kommandos beschleunigen nicht nur das Eintippen, die Auswertung, sondern auch die Übertragung. Eine Überprüfung ob nicht mehr Zeichen angekommen sind, als der Puffer groß ist, hast Du vergessen. Dies ist ein muß. > Sind theoretisch genug Zeichen da, so wird der String von Anfang an mit > einem vorgegebenen String verglichen und auf Gleichheit überprüft. Liegt > Gleichheit vor, so wird etwas ausgeführt. Ist so Üblich. > Mit jedem Tastendruck wird zugleich ein Timer gestartet, welcher die > Eingabe nach einer Zeit verfallen lässt, sollte kein ENTER gedrückt > werden. Soll es ein Maschinen-Maschinen-Protokoll werden so ist das sinnvol. Eine Prüfsumme ist aber mindestens genauso wichtig. Ist es eine Mensch-Maschinen-Schnittstelle (Terminal-Programm) so ist beides nicht nötig. Hier reicht ein Befehlsinterpreter der falsche Kommandos mit einer Fehlermeldung quittiert. Volker
Du findest CLI Beispiele zu Hauf im Web, hast du schon in die Codesammlung hier geschaut? Habe kürzlich eine CLI implementiert, die Ein-Zeichen-Befehle erkennt und ausführt. Dazu verwende ich zwei Arrays:
1 | char cli_cmds[] = {'t' ,'c' ,'u' ,'d' ,'p' ,'\0' }; |
2 | int8_t (*cli_funcs[])(char*) = { cmd_toggle, cmd_clear, cmd_upload, cmd_debug, cmd_print }; |
Die cmd_* Funktionen liefern int8_t zurück (error code), der übergebene Pointer zeigt jeweils auf die Eingabezeile. So kann die cmd-Funktion allfällige Argumente auswerten.
1 | int8_t cmd_toggle(char *cmdline); |
Für komplexere Kommandos könntest du einen Parser schreiben, Mustererkennung, strtok(), eine FSM etc. verwenden. Für ein "Taschenrechner"-Projekt würde der Ansatz sicher ganz anders aussehen... :)
Habe das schon mal gemacht und auch hier nach Tipps gefragt. Der Thread, der daraus entstand, könnte interessant für dich sein: Beitrag "Terminal-Befehle aus mehreren Zeichen im Controller erkennen, wie am besten?"
Hi Da ich mit Assembler arbeite, hier mal ein Tip, wie ich vorgehe ohne dich jetzt groß mit Code zu quälen: Die Empfangsroutine im Controller ist eine ISR, also Interrupt. Das empfangene Zeichen geht auf einen Ringspeicher, das ist einfach ein Speicherbereich von ca. 10- 20 Bytes. Mit einem Doppelregister wird der Anfang des Bereiches fixiert und ein Schreibzeiger zuaddiert. Dort wird das empfangene Zeichen eingetragen. Danach wird der Schreibzeiger erhöht, die Grenze des Puffers geprüft und der Schreibzeiger evtl. zurückgesetzt. Nun ist zwischen Schreibzeiger und einem Lesezeiger ein Unterschied, der im Hauptprogramm erkannt wird. Es folgt das Lesen, auch mit einem Doppelregister und einem Lesezeiger, der auf die Adresse zuaddiert wird. So wird das aktuelle Zeichen gelesen und anschließend der Lesezeiger, wie auch der Schreibzeiger vorher, nachgeführt. Nun zur Auswertung der Zeichen: Klar kann man ganze Strings schicken, aber es gibt ja auch die Möglichkeit der Groß- und Kleinschrift. So kann "A" bedeuten "Relais A ein und "a" Relais A aus. Willst du das Absichern, also Fehler erkennen und evtl. unerwünschte Schaltvorgänge verhindern, so kannst du auch einen Rahmen verwenden und eine Chcksumme bilden, die als letztes Byte mitgeschickt wird. Also angenommen, ein Befehl für den µC sind 4 Bytes. 1.Zeichen : Anfangserkennung (z.B."/", "-", etc) 2.+3. sind Nutzdaten ( z.B. "A1", "b1" ) das 4. ist eine Checksumme nach einer math. Regel. Einfach ist z.B. die 3 Nutzzeichen XOR zu verknüpfen und das Ergebnis als 4. Byte mitschicken. Beispiel: 1. Zeichen 11100111 xor 2.Zeichen 01101101 -------- 10001010 xor 3. Zeichen 00111101 -------- Checksumme: 10110111 Nu kannst du im Controller gleiches ablaufen lassen und wenn das Ergenis der Checksumme passt, sollte die Übertragung fehlerfrei sein. Auch bei unterschiedlich langen Strings geht das. du mußt dann allerdings die Länge mitsenden, logischerweise im ersten Byte. Aber das ist dann schon ziemlich kompliziert.... Gruß oldmax
Aus dem von noips genannten Thread unbedingt beachten: > Autor: Karl Heinz Buchegger (kbuchegg) (Moderator) > Datum: 24.02.2010 14:30 > Jeder String in C hat IMMER ein \0 Zeichen am Ende! Daran wird das Ende > eines Strings erkannt. Das ist C-Konvention und du solltest dich > unbedingt daran halten und nicht dein eigenes Süppchen kochen. Also ich würde das '\r' nicht senden, sondern stattdessen gleich '\0'. Spart ein Byte und du kannst die Stringfunktionen aus der C-Bibliothek zum vergleichen nutzen.
Hallo Ferninand, was Du programmieren willst, ist bei mir der Befehlsinterpreter geworden. Kurze Befehle sind in Ordnung solange man nur eine handvoll benötigt. Du solltest Dir noch Gedanken machen, wie Du vom PC zum Controler mitzuteilende Parameter codieren willst. Ich habe mich bei meinem Vorhaben grob an den AT(+C)-Befehlen für Modem und GSM-Modulen orientiert. Die zu erkennenenden Strings habe ich in den Flash abgelegt, damit nicht unnötigt viel RAM verbraucht wird. Solltest Du den GNU-Compiler für den AVR benutzen, dann solltest Du Dir die pgmspace.h anschauen und Dich mit diesen Funktionen vertraut machen. Bei den meisten anderen Compilern reicht ein const vor der Definition. Dann legt der Compiler die Strings im Flash ab. Im ersten Schritt habe ich die Befehlsposition ermittelt. Im nachfolgenden einen möglichen Parameter separiert. Beispiel: SetDate = 01.01.2011\r ^ ^ ^ | | | | | zu interpretierende Daten. | Parameterankündigung Kommando Sobald die Identifizierung abgeschlossen ist, folgt die Frage, was man damit macht. Im ersten Anlauf habe in im Befehlsinterpreter (switch-case-Konstrukt) die Funktionen der anderen Komponenten aufgerufen. Das hat funktioniert. Jedoch beim Portieren in anderen Projekte habe ich viel Zeit verloren, bis ich alles Notwendige angepaßt hatte, weil meist hier viel zu ändern war. (Frust vorprogrammiert) Mittlerweile bin ich dazu übergegangen die sogenannte späte Bindung zu benutzen. Ich habe im Befehlsinterpreter (Komponente in Form eines C und H- Files) keine Strings mehr gespeichert, die eigentlich Teil anderer Komponenten - meinetwegen kannst Du sie auch Module nennen - sind. Das oben beschriebene SetDate gehört bei mir zur Komponente Realtime-Clock. Diese wiederum enthält nun in ihrer Datei den String "SetDate". Bei der Initialisierung registriere ich diesen String im Befehlsinterpreter. D. h. ich übergebe einen Pointer (also die Adresse) im Flash, wo dieser String steht. Im Befehlsinterpreter (nachfolgend BI genannt) habe ich nun ein Array von Pointer (eine RAM-Variable - sonst kann sie zur Laufzeit nicht verändert werden) auf Flash-Konstanten. Im gleichen Zug wird in einem weiteren Array (ebenfalls RAM) des BIs die Funktionsadresse gespeichert, die im Fall der erfolgreichen Identifikation ausgeführt werden soll. Wenn der String SetDate erkannt (simple for-schleife) wird, dann habe ich eine Position im String-Pointer-Array, die mit der Postion des Funktionspointerarray übereinstimmt. #define TABMAX 21 /* Instructiontable for Communication issues */ PGM_P InstTab[TABMAX]= {0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0}; /* list of Callbackfcts of componments with reaction. */ void (*ptr_CallBack_FctList[TABMAX]) (int param_Item); So wird ein Funktionspointer aufgerufen. (*ptrISR_CallBack_FctList[ Iterator ]) (Parameter); Der Vorteil ist, wenn der BI einmal fertig programmiert ist, er nicht wieder angefasst werden muß. Falls die Anzahl der Befehle erhöht werden muß, verändert man nur das Define. Der Rest funktioniert, wie erwartet. Außerdem könnte während der Laufzeit durch Pointermanipulation der Programmlauf verändert werden. Das braucht nicht jeder. Aber es funktioniert. Diese Technik verwende ich nun auch, wenn eine UART einen Empfangsinterrupt auslöst. Ich betreibe eine grafischen Display von EA über UART. Früher habe ich im UART-Treiber die Daten zwischengespeichert und dann (später) umkopiert. Das kostete unnötig RAM und war wenig performant. Jetzt ruft die ISR über einen Funktionspointer direkt die ihr zugewiesene Funktion auf und hängt als Parameter den Inhalt der UDR an. Der UART-Komponente ist es vollkommen gleichgültig, was mit dem empfangenen Daten passiert, darum kümmert sich jetzt die über einen Funktionspointer aufgerufene Funktion des Displays. Mittlerweile habe ich durch dieser strukturelle Methode wieder mehr Überblick, wer wann was bearbeitet. Meine wesentliche Motivation, das derart kompliziert zu lösen, war, die Anforderung, so viele Aufgaben wie möglich quasi parallel im Controler zu bearbeiten. Ich betreibe einen CAN-Controler, eine grafischer Dispay, diverse Sensoren, die PC-Kommunikation, etc. parallel. Das geht nur mit strikt aufgeräumten Komponenten. Vielleicht kommt mein Vorschlag als dickes Brett rüber. Aber das sind meine Erfahrungen. Sie zu nutzen, bleibt jedem selbst überlassen. Viel Spaß und Erfolg beim Programmieren.
Volker Zabe schrieb: > Eine Überprüfung ob nicht mehr Zeichen angekommen sind, als der Puffer > groß ist, hast Du vergessen. Dies ist ein muß. Oh...vergessen zu schreiben - die Abfrage ist auch drin, klar! Danke euch erstmal für die Antworten - es ist eine Mensch-Maschine-Schnittstelle: Hyper-Terminal -> uC noips schrieb: > Habe das schon mal gemacht und auch hier nach Tipps gefragt. Der Thread, > der daraus entstand, könnte interessant für dich sein: Den werde ich mir jetzt erstmal durchgucken.
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.