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