Hallo Ihr,
da ich aktuell etwas mit einem STM32F1 spiele, und leider nicht weiter
komme, hoffe ich, dass Ihr mir helfen könnt.
Ich möchte via UART einen Befehl, z.B. "motor 75" oder "motor 100" oder
"servo 45" an den STM32 senden, und so verarbeiten, dass das Motor PWM
dann 75 oder 100% ist.
Hierzu muss ich den empfangenen String spliten, vergleichen und und dann
die Zahl in einen anderen Datentyp umwandeln.
Das senden und empfangen klappt...aber mit der String verarbeitung
scheitert es...
hier mal mein Code:
für string operationen gibts im internet 1000nde Beispiele.
Nur mal nach
string split oder
string to int
etc. suchen.
Natürlich brauchst du auch noch eine Funktionstabelle, also bei welchem
Schlüsselwort wird welche Funktion ausgeführt. Das ist im einfachsten
Fall eine switch case
Wo ist denn der Teil deines Codes, der aus einem empfangenen Zeichen
(str) eine Zeichenkette incl. terminierendem \0 zusammenbastelt? Und das
dann an strtok übergibt? So wie du es jetzt machst, übergibst du nur
einen zeiger auf einen char, aber keine 0-terminierte Zeichenkette a
strtok.
Robin schrieb:> char str;> ...> str = USART_ReadByteSync(USART1);
Stell dich nicht so an.
Also:
Trenne die Peripheriebedienung von der Auswertung von Kommandos. Das
sind zwei völlig unterschiedliche Ebenen.
Also schreib dir einen ordentlichen LowLevel-Handler für deine diversen
USART's oder suche hier im Forum, den ich hatte sowas schon mehrfach
gepostet.
Sodann schreib dir ein Kommandoprogramm oder kopiere (und VERSTEHE) es
von irgendwo. Du kannst z.B. dir die Lernbetty hier herunterladen, dort
findest du das bereits vorgeturnt.
Beispiel:
if (match("MOTOR",&Cpt))
{ DoMotorOnOff(Long_In(&Cpt));
return;
}
Etwa so kann sowas gehen.
W.S.
Das hier verwende ich :
In der String receiveRx_2 steht dan eine name und = und sollwerte. Hier
nur fur positieve integers. Variabele printRx_2 gib an das ein komplette
string fertig in buffer steht. Include <string.h> wird verwendet.
1
//Nieuwe string ontvangen via USART1
2
if(printRx_2){
3
printf("Ontvangen %s",receiveRx_2);printRx_2=0;
4
chardelimiter[]="=";//op deze tekens wordt de string gesplitst
5
char*ptr1,*ptr2;uint32_ti;
6
// initialisieren en eerste deel tot aan delimiter
Jan H. schrieb:> In der String receiveRx_2 steht dan eine name und = und sollwerte. Hier> nur fur positieve integers. Variabele printRx_2 gib an das ein komplette> string fertig in buffer steht.
Also, so wie ich das verstehe:
"Amsterdam=128"
"Rotterdam=275"
und so weiter. OK?
Was machst du nun, wenn die Form dort vielleicht einmal etwas anders
steht? Also so zum Beispiel:
" Amsterdam= 92128"
"Rotterdam =14"
Das, was ich in der Lernbetty gepostet hatte, war formatfrei und die
Funktion 'match' hat das, was sie gefunden hat, sozusagen aufgefressen.
Deshalb war das Verhalten tolerant zu Leerzeichen und es konnte sehr
unterschiedliche Formate auf der Kommandozeile verarbeiten, so daß man
eben nicht an ein bestimmtes Format gebunden ist.
W.S.
In meinen Beispiel muss der String absolut passen, leerzeichen werden
auch interpretiert. Mit etwas mehr Programmier Aufwand ist das naturlich
auch zu vermeiden, aber für mich ist das brauchbar.
MFG, RP6conrad.
Felix F. schrieb:> Oder man vermeidet diese fehleranfälligen, unsicheren, langen Strings> komplett und macht gleich ein einfaches, kurzes, abgesichertes> Protokoll...
mit einem struct, der reine Binärdaten beinhaltet?
Finde ich sehr schlecht, da einerseits fehleranfällig und andererseits
extrem problematisch und unleserlich. Warum? Nun:
1. man braucht dafür etwas zum Synchronisieren, sonst kann man nicht
feststellen, an welcher Stelle seines struct's der Sender grad ist.
2. außer dem CRC hast du nichts, um die Fehlerfreiheit deines Paketes
festzustellen. Möglicherweise ist der Fehler aber in einem konkret
unbenutzen Bereich? Und wie sagt man es dem Sender, daß man seinen Kram
nicht angenommen hat?
3. du bist festgenagelt auf ein spezielles Format. Das ist
grottenschlecht, denn du kannst dir damit keinerlei Erweiterungen,
Rückmeldungen, Kommentare und sonstiges Zeugs leisten, ohne alles
nochmal neu zu definieren.
Ein anderes Beispiel:
Von der Sache her ist eine formatfreie Transmission in quasi UPN das
eleganteste, aber es fehlt en bissel an der Sicherheit. Das sähe dann so
aus:
Prinzip: dezimaler Parameter Kommandobuchstabe
Beispiel: 0M4711A1M0R5Q
und im Klartext:
schalte Motor aus (0M)
setze Variable A auf 4711 (4711A)
schalte Motor ein (1M)
schalte Relais aus (0R)
berichte den Status von Sensor 0 und Sensor 2 (5Q)
Bei sowas sind alle Textzeichen außer Ziffern und ausgewählte Buchstaben
wahlfrei zu senden und zu empfangen und alle Operationen sind atomar.
Sowas kann man leicht erweitern, Statusmeldungen und sonstiges Zeugs
sind problemlos in den übertragenen Stream einflechtbar ohne daß es zu
Kollisionen kommt. Beispiel: 0Motto ist da4711A1aber heinz nichtM0R5Q
Das ergibt die gleiche Kommandofolge wie oben.
Bei meinem ersten Beispiel sieht das ganz anders aus: Zuerst hat man mit
der zeilenweisen Kommandoabarbeitung eine sehr gute Synchronisation
zwischen TX und RX. Dann hat man durch die Länge der Kennwörter eine
gute Sicherheit gegen Übertragungsfehler. Weiters kann man das Ganze
beliebig mit anderen Kommandos anreichern, ohne am bisherigen Konzept
etwas ändern zu müssen. Und zu guter letzt hat man das Ganze auch noch
gut menschlich lesbar. Das ist in ganz vielen Fällen wertvoll - immerhin
handelt es sich um das Synchronisieren zwischen zwei verschiedenen
Gerätschaften und das Nachschauenkönnen bei Problemen (die es IMMER
gibt...)
W.S.
Okay...also das mit mit String zusammenbasteln...wie mach ich das am
besten??
Ich habs jetzt mal so gemacht...kanns leider aber erst kommende Woche
testen, hab mein Board leider am Sonntag in der Heimat liegen lassen :-(
also ich will fürs erste einfach, dass er beginnt einen String
zusammenzubauen wenn er ein Ausrufezeichen erkennt.
Zudem soll er bei einem linefeed (0x0A) damit aufhören.
passt das so? oder kann ich daran noch was optimieren?
Kann ich dann strtok(USART_ReadString(USART1,&str)?
W.S. schrieb:> Binärdaten> 3. du bist festgenagelt auf ein spezielles Format. Das ist> grottenschlecht, denn du kannst dir damit keinerlei Erweiterungen,> Rückmeldungen, Kommentare und sonstiges Zeugs leisten, ohne alles> nochmal neu zu definieren.
Was hat das eine mit dem anderen zu tun? Er könnte jederzeit falls nötig
neue Kommandos definieren deren Datenbereich dann eine ganz andere
Bedeutung oder gar Länge hat. Dabei müsste er bestehenden Code und die
Struktur bestehender Pakete nicht anfassen, nur neuen Code hinzufügen
für die neuen Befehle.
W.S. schrieb:> Beispiel: 0M4711A1M0R5Q
...
> Und zu guter letzt hat man das Ganze auch noch> gut menschlich lesbar.
Aber eine gewisse autistische Ader besitzt Du auch, oder?!
So ist meinen ISR für den UART Rx gemacht :
Jeden empfangen byte wird in ein String Puffer kopiert. Wen ein "\n"
oder "\r" empfangen wird, wird das ganse in eine Zweite Puffer kopiert,
die erste Puffer fangt wieder ab 0 an. Den Zweite puffer kan in Main()
weiter verarbeitet worden. In Fall keine \n oder \r empfangen wird, ist
nach 250 Zeichen wieder ab 0. (Ueberlauf Puffer vermeiden).
Der Synchronisation ist dan immer \n oder \r.
1
//Usart 2 = PC-connection
2
voidUSART2_IRQHandler(void){
3
/* Check if interrupt was because data is received */
4
if(USART2->SR&USART_SR_RXNE){
5
/* Put received data into internal buffer */
6
7
/* Read one byte from the receive data register */
Gerald schrieb:> Aber eine gewisse autistische Ader besitzt Du auch, oder?!
Du kannst erwiesenermaßen nicht gründlich lesen. Also hier mein Sermon
nochmal in Auszügen:
"Bei meinem ersten Beispiel sieht das ganz anders aus: ...
... Und zu guter letzt hat man das Ganze auch noch gut menschlich
lesbar."
und das erwähnte erste Beispiel:
"Beispiel:
if (match("MOTOR",&Cpt))
{ DoMotorOnOff(Long_In(&Cpt));
return;
}
Etwa so kann sowas gehen."
Auf der Seriellen sieht man also etwa sowas:
> MOTOR 1 (cr/lf)
Ist das nun klar genug?
W.S.
Robin schrieb:> also ich will fürs erste einfach, dass er beginnt einen String> zusammenzubauen wenn er ein Ausrufezeichen erkennt.> Zudem soll er bei einem linefeed (0x0A) damit aufhören.
Nein, nein, nein
.. genau SO solltest du nicht herangehen.
Sondern:
Du solltest zu allererst einen Treiber haben, der die zugrundeliegende
HW abstrahiert und dir ne Schnittstelle liefert, die sowohl von der HW
unabhängig ist als auch vom konkreten Inhalt unabhängig ist. Sinngemäß
etwa so:
extern dword InitSerial (long baudrate);
extern char Char_Out (char c);
extern bool RxAvail (void);
extern char GetChar (void);
Sicherlich wirst du den Treiber irgendwann so schreiben, daß er die ein-
und ausgehenden Datenströme sinnvoll zwischenpuffert und ansonsten
interruprgesteuert arbeitet, um den Rest der Firmware nicht zu
blockieren, aber wie nun genau, ist interne Angelegenkeit des Treibers
und geht den Rest der Welt nix an. Die soll sich mit den genannten
Funktionen begnügen. Ebenso soll der Treiber sich eben NICHT drum
kümmern, was für einen Inhalt die Zeichen haben, die er sendet und
empfängt. Das ist nicht sein Job.
Als nächstes solltest du dir ein allgemeines Kommandoprogramm schreiben,
was etwa folgendes enthält:
- einen Kommandoprompt ausgeben (oder nicht, falls du sowas nicht magst)
- per Polling eine Kommandozeile hereinholen, die empfangenen Zeichen
als Echo zurückschicken (damit man am PC im Terminalprogramm auch sieht,
was tatsächlich angekommen ist) und ggf. ein paar simple
Editierfunktionen dafür benutzen (Backspace zum Korrigieren oder sowas),
also etwa so:
in der Grundschleife in main:
if(RxAvail()) Kommandoprogramm();
Und wenn das Kommandoprogramm ein CR/LF gefunden hat oder die
Kommandozeile voll ist, dann soll es die Kommando-Auswertung aufrufen.
Diese nun muß ihrerseits die Kommandozeile auswerten und das Gewünschte
tun. Obendrein soll sie bei nichterkanten Kommandos dich anmeckern,
damit du weißt, daß du nicht verstanden wurdest. Schlußendlich soll sie
die Kommandozeile löschen, da abgearbeitet und ggf. einen neuen
Kommandoprompt ausgeben, damit man am anderen Ende der Strippe weiß, daß
das Kommando jetzt erledigt ist.
Aber fange eben NICHT mit dem sturen Zusammenbauen eines Strings an -
schon garnicht mit Ausrufezeichen und so. Prinzip: vom Einfachen zum
Komplexen gehen und nicht mittendrin mit irgendwas anfangen.
Nochwas: das Zeilenende per LF zu finden, ist Unix-Denke. Es trifft
häufig genug bei Terminalprogrammen nicht zu, weswegen es im Prinzip
besser ist, auf CR oder LF zu orientieren. Wenn eines von beiden kommt,
ist die Zeile fertig. Man muß dabei allerdings ein direkt auf CR
folgendes LF wegschmeißen, weil der zugrunde liegende
Schreibmaschinen-Standard eigentlich CR und folgendes LF ist und die
meisten Terminalprogramme genau diese Kombination senden.
W.S.