Liebe Gemeinde, nachdem sich der Knoten einfach nicht auflösen möchte, wende ich mich mal an Euch. Die Aufgabenstellung ist recht einfach: Es gilt mit einem Messgerät zu kommunizieren. Dieses Gerät kennt verschiedene Befehle. Die Befehle (1Byte) an sich haben keine,einen oder mehrere Übergabeparameter. Das Messgerät antwortet immer mit 1-4 Byte. Abhängig vom Kommando. Beispiel: Gib mal deine Seriennummer her: Befehl: 0x0E - ohne Übergabeparameter Antwort: 3 Byte Setze die Seriennummer: Befehl: 0x8E mit 3Byte Übergabeparameter Antwort: 3 Byte Wie kriegt man denn sowas in eine Funktion gegossen, damit man verschiedene Befehle benutzen kann? Die Sende/Empfangsfunktion muss ja wissen, wieviele Byte gesendet werden müssen und wieviele empfangen. Es bedarf also irgendeiner Tabelle in der diese Werte pro Befehl abgelegt werden. Irgendwie will sich die Wolke in meinem Hirn nicht verziehen. Vielleicht hat ja von Euch jemand den rettenden Tip. Bye alter_falter
Im einfachsten Fall wird einfach für jedes vom Gerät verstandene Kommando eine Funktion gemacht, die genau diese Details weiß. Denn: man muss ja auch wissen, was mit dem vom Gerät zurückgesendeten passieren soll. Zu wissen, dass vom gerät 3 Bytes zurückkommen, ist nett, aber: was tun damit? Und dieses Wissen steckt dann in den Kommandofunktionen. (Diese Kommandofunktionen stützen sich dann natürlich wieder auf einfachere Funktionen, wie man sie zb zum versenden von Bytes oder Bytesequenzen definierter Länge braucht) Solange das alles nur für ein bestimmtes Gerät oder eine Gerätefamilie mit gleichen Kommandos gebraucht wird, ist das völlig ausreichend. Schwieriger wirds, wenn man unterschiedliche Geräte bedienen können muss, die dann auch noch ohne Programmänderung wartbar sein müssen. Dann muss man sich tatsächlich etwas komplett datengetriebenes überlegen.
Vielen Dank für Deine Antwort Karl Heinz. Dass es einzelne Funktionen werden war schon klar. Ich habe eher auf die niedrigere UART Funktion abgezielt. So nach dem Motto: uart_puts(längeSenden,längeAntwort,Payload Senden)... Das Problem hier ist, dass alles in Hex abläuft und normal NULL terminierte Strings nicht zu gebrauchen sind. Ist wohl eher unhandlich das alles zusammenzufassen.
Na, angenommen du willst das tatsächlich so machen (uart_puts sendet ne Handvoll Bytes und wartet danach auf 0 oder mehrere Antwortbytes), dann irgendwie so:
1 | void uart_puts(size_t laenge_senden, size_t laenge_antwort, const unsigned char *payload, unsigned char *antwort) |
2 | {
|
3 | // Daten senden und auf Antwort warten |
4 | } |
5 | |
6 | void get_serial_number(void) |
7 | {
|
8 | unsigned char command = 0x0e; |
9 | unsigned char antwort[3]; |
10 | uart_puts(1, 3, &command, serial_number, antwort); |
11 | } |
Wo ist das Problem?
Vielleicht ist mein Vorschalg oversized, aber er funktioniert gut und ist zuverlässig. Vorweg möchte ich erwähnen, dass ich mit einem selbst erstellten Scheduler arbeite. In dem habe ich z. Z. 6 Tasklisten, die je nach Konfiguration unterschiedlich häufig aufgerufen werden. Die erste ist die IDLE-Task. Die zweite Liste würde bspw. alle 1 ms aufgerufen werden und so weiter. Die UART-daten empfange ich per Interrupt und speichere Sie in einem Empfangspuffer. Der Befehlsinterpreter wartet auf eine Semaphore vom Empfänger, die gesetzt wird, wenn entweder (konfigurationsabhängig) eine 0x0D und oder Ox0A empfangen wird. (Terminal Carrige Return und/oder Line Feed) Alternativ kann ich auch ein TimeOut definieren, weil ich über den Scheduler in einem relativ berechenbaren Raster zählen kann. Der TimeOut funktionert so, dass ich einen Zähler (global angelegte Variable) beim ersten Empfangen eines Zeichens starte und in einer anderen geschedulten Funktion inkrementiere. Diese heißt CheckTimeOut. Jedes über die UART empfangene Zeichen setzt den Zähler zurück. Wenn das letzte Zeichen über die UART empfangen worden ist, dann inkrementier der TimeOutzähler solange, bis die TimeOut-Schwelle erreicht/überschritten worden ist. Die Semaphore wird auf Befehl interpretieren gesetzt (nur ne boolsche Bitvariable innerhalb eines Array). Der Befehlsinterpreter führt dann einen String-Compare über alle bekannten Befehlsstrings aus (str'n'cmp - berücksichtig die unterschiedliche Kommandolänge !) und ruft bei einem Match eine Funktion auf, die die Zielbearbeitung durchführen kann. Nach der Bearbeitung setzt der Befehlsinterpreter die Semaphore wieder zurück und wartet auf das nächste eintreffende Kommando. Durch den Scheduler und die Semaphore sind beide Programmteile quasi parallel wirksam. Genau genommen arbeitet das System kooperativ. Da ich aus der OOP-Programmierung stamme, kapsele ich gern die Zuständigkeiten. Das geht unter C natürlich nur bedingt. Daraum habe ich es ein wenig weitergetrieben und verwende Funktionspointer. Damit kann ich dem Befehlsinterpreter während der Initialisierung fast beliebig viele Kommandos und Bearbeitungsfunktionen mitteilen, die er über zwei gleich indizierte Array synchron organisiert. Index Befehl Funktion 0 "SendTemp" SendTempDS1820() 1 "SendHumid" SendHumidSHT11() 2 "SendTemp2" SendTempSHT11() . . . Mein Ziel war es, eine Softwarekomponente Temperatur (DS1820) über UART abzufragen, ohne dabei im C-File des Befehlsinterpreters eine Funktion SendeTemperaturDS1820() fest in die C-Datei einzutragen. Der Clou, weil ich die Pointer ins RAM gelegt habe, kann ich zur Laufzeit die Funktionen beliebig austauschen oder deaktivieren, ohne jedes Mal eine Build mit Download zu starten. Geht natürlich nur, weil es eine Komponente gibt, die das leistet und ich dazu auch primitives Protokoll definiert habe. Wo braucht man so etwas? Ich benutze einen AT90CAN128 für eine kleine CAN-Restbussimulation. Über Kommandos veranlasse ich ihn diverse CAN-Botschaften zu erzeugen und mit bestimmten Daten zu senden. Wenn eine Botschaft oder eine bestimmte Funktion zur Erzeugung innerhalb der CAN-Daten nicht mehr gebraucht wird, erfolgt während ihre Laufzeit. Außerdem benutze ich diese Technik, um µCs mit Matlab/Simulink über UART zu verbinden.
cskulkw schrieb: > Erzeugung innerhalb der CAN-Daten nicht mehr gebraucht > > wird, erfolgt während ihre Laufzeit. Das ist unvollständig: Erzeugung innerhalb der CAN-Daten nicht mehr gebraucht wird, erfolgt ihre Löschung während der Laufzeit. Sorry
Alter Falter! Das ist jetzt aber mit Atombomben auf Spatzenküken geschossen. Ich hab es inzwischen auch hinbekommen. Zuerst die ganz normale Uart Funktion mit Ringbuffer (von Peter glaube ich). Kommandos werden über die jeweilige Funktion gesendet und gleichzeitig eine modifizierte Empfangsroutine angesprochen, die auf die richtige Anzahl Bytes wartet. Diese "breaked" nach 1000 leeren Abfragen und retourniert einen Fehler, wenn nichts oder nicht genug zurückkommt. Funzt soweit erstmal alles.
Na, wenn die Atombombe geholfen hat, den Knoten im Hirn zu lösen, dann hat sie ja ihren Zweck erfüllt, oder? Weiterhin viel Erfolg und Spaß natürlich ;-)
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.