Guten Abend, ich suche Lösungsansätze für fortgeschrittene Programmierung, da ich eine größere Applikation schreiben möchte. Mir geht es z.B. um die sinnvolle Bearbeitung empfangener Daten. Wie prüfe ich diese, wie springe ich die entsprechenden Routinen zu den empfangenen Befehlen an, wie behandle ich Fehler usw. Kurz: Ein Beispiel einer größeren Applikation, die ein Befehlsprotokoll enthält, mit Fehlerprüfung (Kommunikationsfehler als auch "Parameter"-Fehler), Fehlerbearbeitung und Rückmeldung an den User usw. Alles was mir mein Hirn gebracht hat, war irgendwie nur halbgar, vielleicht stell ichs mir zu kompliziert vor :-) Kann hier jemand so eine Applikation posten? Das wär echt super! Matze
Ich glaube eine richtig fertige App wirst du hier nicht unbedingt bekommen. Aber wenn du konkrete Fragen stellst, kann bestimmt jemand auch eine Antwort liefern, die dir hilft (im Gegensatz zu dieser hier ;-) Gast.
@Gast: Du hast bestimmt recht, im Prinzip wärs darauf hinausgelaufen, dass ich mir meine App aus verschiedenen fremden Apps zusammengestückelt hätte, und mich dann gefragt hätte, warums nicht tut... Also mal sehen, ich möchte dem Controller Befehle senden. Der Aufbau der Befehle sieht etwa so aus: BB[DD...DD]CR BB = Befehlsbyte DD = optionale Datenbytes CR = Carriage Return Das Befehlsbyte liegt im Bereich der darstellbaren Zeichen, also von 0x20 bis 0x7F. Meistens handelt es sich um Buchstaben. Die Datenbytes sind ausnahmslos ASCII-Hex-Zeichen. Der Controller soll auf jeden Befehl antworten, und zwar so, dass der User im Fehlerfall erkennt, was der Fehler war (Bufferüberlauf, falsche Werte eingegeben, falsche Zeichen, etc.) Ich habe eine Routine geschrieben, die die Daten empfängt, und in einem Buffer ablegt. Der Buffer ist gegen Überlauf geschützt, die Routine wird solange ausgeführt, bis das CR-Zeichen kommt, selbst wenn ein Überlauf auftrat. Ein Datenzähler enthält die Anzahl der empfangenen Daten. Das heisst, nach dieser Funktion habe ich einen Buffer, der mindestens ein Befehlszeichen plus optionaler Daten enthält. So und jetzt wirds für mich kribblig. Eine Wandlung der ASCII-Hex-Zeichen nach Hex bekomme ich noch hin, logischerweise sollte ich das machen, bevor ich einen Befehl ausführe, sonst kann ich in jeder Befehlsroutine konvertieren, das halte ich für ungünstig. Aber wie löse ich z.B. das Anspringen der entsprechenden Befehlsroutinen abhängig vom Befehlsbyte? Ein Switch-Case-Konstrukt wird da sicher unübersichtlich. Hm... vielleicht Funktionspointer? Mir bildet sich da grad ne Idee... Die muss ich mir mal durchn Kopp gehen lassen... Egal, was mich dann noch interessiert, wie behandle ich Fehler? Beispiel: Ich erkenne, dass die empfangenen Daten zwar ASCII-Hex-Zeichen sind, aber sie sind ausserhalb des Bereichs (also hat der Bediener gemurkst --> falschen Wert eingegeben). Wie löse ich es so, dass der User anhand der Antwort vom Controller den Fehler entdeckt, also ob er einen falschen, ungültigen Wert oder ob er ein "verbotenes" Zeichen gesendet hat? Oder ob der Buffer übergelaufen ist? Fragen über Fragen... Matze
Also, ich habe das mal mit einer State-Engine gelöst. Da wurde auch der ganze Text empfangen und dann bearbeitet.
1 | Do while |
2 | case State |
3 | State=0 ... warten auf Start-Code (Byte), dann State=1 |
4 | State=1 ... Byte hat das richtige Format für Cmd ? Nein-Error |
5 | Cmd=1 ? - State=$10 |
6 | Cmd=2 ? - State=$20 |
7 | State=10 oder $20 ... HEX-Zeichen ? nein-Error |
8 | Byte in Hex wandeln ... wenn x Bytes durch ... State=State+1 |
9 | State=11 oder $21 ... Byte=CR ? nein-Error |
10 | State=$11 ... Cmd 1 ausführen |
11 | State=$21 ... Cmd 2 ausführen |
12 | end case |
13 | weiterrücken |
14 | loop |
Da kann man dann eine Syntax prüfen und gleichzeitig einen Zustand $?1 für mehrere Befehle nutzen (versch. Datenbytes) und es trotzdem kompakt halten. Der Vorteil einer solchen State-Engine ist der starre und klare Aufbau (Übersicht). Man kann jeden Zustand fest einordnen und programmiert sich nicht zu Tode (Spagetti-Code). Aber man muß da mal etwas Gehirnschmalz einbringen. Ich arbeite gerne damit, weil es hilft, Programmierfehler (side-effects) zu vermeiden. Klare abgeschlossene Zustände mit festen Regeln für den Wechsel in einen anderen Zustand (Ausgänge schalten etc.) sind der Vorteil, den man mit etwas Codespeicher bedingt durch die Nutzung der Compiler-Direktiven CASE erkauft. Aber der Speicher sollte heute nicht mehr so sehr wichtig sein. Zur Not kann man am Ende des Projekts noch etwas Handarbeit nachlegen um Code zu sparen.
Matthias W. wrote: > Also mal sehen, ich möchte dem Controller Befehle senden. Der Aufbau der > Befehle sieht etwa so aus: > > BB[DD...DD]CR > > BB = Befehlsbyte > DD = optionale Datenbytes > CR = Carriage Return sieht doch schon mal gut aus. > > Das Befehlsbyte liegt im Bereich der darstellbaren Zeichen, also von > 0x20 bis 0x7F. Meistens handelt es sich um Buchstaben. > Die Datenbytes sind ausnahmslos ASCII-Hex-Zeichen. Gut > Ich habe eine Routine geschrieben, die die Daten empfängt, und in einem > Buffer ablegt. Der Buffer ist gegen Überlauf geschützt, die Routine wird > solange ausgeführt, bis das CR-Zeichen kommt, selbst wenn ein Überlauf > auftrat. Ein Datenzähler enthält die Anzahl der empfangenen Daten. OK > Das heisst, nach dieser Funktion habe ich einen Buffer, der mindestens > ein Befehlszeichen plus optionaler Daten enthält. OK > So und jetzt wirds für mich kribblig. Eine Wandlung der > ASCII-Hex-Zeichen nach Hex bekomme ich noch hin, logischerweise sollte > ich das machen, bevor ich einen Befehl ausführe, sonst kann ich in jeder > Befehlsroutine konvertieren, das halte ich für ungünstig. Warum soll das ungünstig sein? Letztendlich kann doch nur die Befehlsroutine wissen ob sie jetzt eine 2 Byte Zahl oder eine 4 Byte Zahl haben möchte oder ob die Zeichen vielleicht sogar direkt (also ohne Umwandlung) benutzt werden sollen. > > Aber wie löse ich z.B. das Anspringen der entsprechenden Befehlsroutinen > abhängig vom Befehlsbyte? Ein Switch-Case-Konstrukt wird da sicher > unübersichtlich. Hm... vielleicht Funktionspointer? Mir bildet sich da > grad ne Idee... Die muss ich mir mal durchn Kopp gehen lassen... Guckst du mal hier http://www.mikrocontroller.net/articles/FAQ#Men.C3.BCs_mit_Funktionszeigern anstelle des Menüs benutzt du das empfangene Befehlsbyte um den richtigen Eintrag zu finden. > > Egal, was mich dann noch interessiert, wie behandle ich Fehler? > Beispiel: Ich erkenne, dass die empfangenen Daten zwar ASCII-Hex-Zeichen > sind, aber sie sind ausserhalb des Bereichs (also hat der Bediener > gemurkst --> falschen Wert eingegeben). Wie löse ich es so, dass der > User anhand der Antwort vom Controller den Fehler entdeckt, also ob er > einen falschen, ungültigen Wert oder ob er ein "verbotenes" Zeichen > gesendet hat? Oder ob der Buffer übergelaufen ist? > Fragen über Fragen... Wie wäre es mit Einen Klartext per UART zurücksenden, der den Benutzer darüber aufklärt. Was auch immer gut kommt: Zumindest etwas von der Eingabe wieder ausgeben, damit der Benutzer erkennen kann, wo den der Fehler aufgetreten ist. Also etwas in der Form ** Fehler in B24FG76 ** * ** ungültiges Zeichen
Hallo Bernd, hallo Karl-Heinz, sorry für die späte Antwort. Danke euch beiden, ich werd das mal in aller Ruhe durchgehen. Mal sehen ob ich durch die State-Machine bzw. die geänderte Menü-Routine die App ans laufen bekomme. Nochmals besten Dank. Matze
Matthias, ein menschenlesbares Protokoll hat den Vorteil, dass man's an Hyperterm einhaemmern kann. Dann sollte man allerdings den CRC weglassen. Wenn das Protokoll mit einer Software abgearbeitet wird, macht man's anders. Dann muss es naemlich nicht lesbar sein, und auch nicht ASCII. Als Beispiel fuer ein Maschinenprotokol moege meine Seite dienen. http://www.ibrtses.com/embedded/shortmsgprotocol.html Rene
Hallo Rene, hab mir dein Protokoll mal angeguckt. Du arbeitest ohne Abschlusszeichen, soweit ich das sehen kann. Was passiert, wenn z.B. ein Zeichen verloren geht? Lässt du ein TimeOut laufen? Oder dient das Sync-Zeichen dazu, vorher unvollständig empfangene Daten zu verwerfen? Matze
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.