Forum: Compiler & IDEs Kommandointerface - ich hab nen Knoten im Gehirn


von Alter F. (alter_falter)


Lesenswert?

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

von Karl H. (kbuchegg)


Lesenswert?

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.

von Alter F. (alter_falter)


Lesenswert?

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.

von Bartli (Gast)


Lesenswert?

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?

von cskulkw (Gast)


Lesenswert?

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.

von cskulkw (Gast)


Lesenswert?

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

von Alter F. (alter_falter)


Lesenswert?

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.

von cskulkw (Gast)


Lesenswert?

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
Noch kein Account? Hier anmelden.