Hallo Community
Ich arbeite zur Zeit an einer Routine für die Kommunikation zwischen
meinen µc.
Leider muss ich selber feststellen, dass es absoluter Käse ist.
Das hier soll das folgende Programm aufrufen und die Daten über den
USART senden.
Fakt ist es funktioniert nicht und gefällt mir vom aufbau her auch gar
nicht.
Zuerst einmal temp1 enthält werde von 0-48.
kenn enthält Werte von 0-255
Das Protokoll soll folgendermaßen aussehen.
S(Start der Nachricht)
z(was wird gesendet)
Datenstelle im EEprom( 2Bytes)
2Bytes frei
3Bytes Daten
/n(Nachricht beendet)
USART_Send_2("S","z",itoa((uint16_t)temp1,&buffer,10),"00",itoa((uint16_
t)kenn[temp1],&buffer2,10));
void USART_Send_2(char *type, char *source, char *slot ,char *slot2
,char *data)
{
unsigned int i = 6;
unsigned char muffer;
unsigned int nextchar= 0;
TxBuffer[0]= *type;
TxBuffer[1]= *source;
TxBuffer[2]= slot[0];
TxBuffer[3]= slot[1];
TxBuffer[4]= slot2[0];
TxBuffer[5]= slot2[1];
TxBuffer[9]= '\n';
muffer=*TxBuffer;
while (i<=ProtokollSize - 1)
{
if(data[i-5] && nextchar != 1) {
TxBuffer[i]=data[i-5];
} else {
nextchar = 1;
TxBuffer[i] = 'x';
}
i++;
}
USART_ITConfig(USARTy, USART_IT_TXE, ENABLE);
}
Wenn nicht alle Bytes belegt sind soll der Rest durch x ersetzt werden.
Wie kann man es etwas smarter machen ?
mfg Marc
> S(Start der Nachricht)> z(was wird gesendet)> Datenstelle im EEprom( 2Bytes)> 2Bytes frei> 3Bytes Daten> /n(Nachricht beendet)>>USART_Send_2("S",
"z",
itoa((uint16_t)temp1,&buffer,10),
"00",
itoa((uint16_t)kenn[temp1],&buffer2,10));
Das sind aber nicht "2 bytes frei". Das sind: Nach der ersten Zahl
kommen 2 0-en und dann die nächste Zahl.
D.h. der Empfänger kriegt
Sz40028\n
und das ist Käse, weil der Empfänger nicht erkennen kann, wo die eine
Zahl aufhört und die andere Zahl anfängt.
Nach jeder Zahl benötigst du ein Trennzeichen, das eindeutig sagt: Hier
ist die Zahl zu Ende. Für die 2.te Zahl hast du so ein Trennzeichen. Das
\n fungiert als solche. Aber nach der ersten Zahl hast du keines.
Aber das lässt sich ja ändern:
S(Start der Nachricht)
z(was wird gesendet)
Datenstelle im EEprom( 2Bytes)
',' als Trenner
Daten
/n(Nachricht beendet)
Jetzt kriegt der Empfänger
Sz4,28\n
und kann das eindeutig dekodieren.
>USART_Send_2("S",
"z",
itoa((uint16_t)temp1,&buffer,10),
"00",
itoa((uint16_t)kenn[temp1],&buffer2,10));
Die itoa Aufrufe wird man sinnvollerweise in die UART_SEnd_2 Funktion
verlagern.
Der Aufruf sieht dann so aus
UART_SEnd_2( 'S', 'z', temp1, temp2 );
wenn das 'S' und das'z' sowieso immer gleich ist, dann legt man auch das
in die Funktion hinein.
Sieh dir doch einfach an, was der Aufrufer immer wieder alles angeben
muss und was davon im Prinzip eh immer gleich ist. Das kannst du
genausogut auch in die Funktion heinein verlagern. Oder eine
Zwischenfunktion machen, die fehlende Argumente für eine allgmeine
Send-Funktion ergänzt:
void UpdateValue( uint8_t Address, uint8_t Value )
{
UART_Send_2( 'S', 'z', Address, Value );
}
> USART_Send_2("S","z",itoa((uint16_t)temp1,&buffer,10),"00",itoa((uint16_
t)kenn[temp1],&buffer2,10));
void USART_Send_2(char *type, char *source, char *slot ,char *slot2
Wozu Strings, wenn du dann sowieso nur 1 Buchstaben davon auswertest?
Sowas blödes wieso bin ich denn nicht auf die Idee mit dem Komma
gekommen?
Auch die Auslagerung des itoa in die Senderoutine macht Sinn
Ich denke ich war einfach zu versessen darauf, in beide Richtungen das
Protokoll einzuhalten mit dem ich mir selbst vorgeschrieben hatte, dass
jeweils 10 bytes einen Datensatz ergeben.
Danke für die schnelle Hilfe
mfg Marc
Marc schrieb:> Sowas blödes wieso bin ich denn nicht auf die Idee mit dem Komma> gekommen?> Auch die Auslagerung des itoa in die Senderoutine macht Sinn>> Ich denke ich war einfach zu versessen darauf, in beide Richtungen das> Protokoll einzuhalten mit dem ich mir selbst vorgeschrieben hatte, dass> jeweils 10 bytes einen Datensatz ergeben.
Solche fixen Bytelängen machen nur bei binärer Übertragung Sinn.
Schickt man Texte hin und her, dann ist die Sichtweise "Ich habe da eine
Command-line" (so wie man eben vor der Klickibunti-Maus-Ära einem
Computer seine Wünsche in Form von textuellen Kommandos vorgetragen hat)
sehr viel ergiebiger.
Der Rest ist dann Handwerk. Bzw. eigentlich Faulheit des Programmierers.
Ich will nicht bei jedem Aufruf den itoa machen müssen. Ich will auch
nicht bei jedem AUfruf das 'S' angeben müssen, wenn sowieso jede Message
immer mit einem 'S' anfängt. Das ist mir zuviel Arbeit, das kann die
aufgerufene Funktion auch alles selber machen. :-)
Wenn ich Funktionsschnittstellen designe, dann liegt bei mir ein großes
Augenmerk immer darauf, dass die Funktion einfach benutzt werden kann.
Denn in einem Programm gibt es viele Aufrufe der Funktion. Die sollen
einfach und leicht lesbar sein. Da trage ich den Aufwand lieber in die
Funktion hinein, habe ihn an einer Stelle zentral, als dass ich den
Aufwand über das halbe Programm immer auf die gleiche Art und Weise
verstreut habe.
Da bin ich schon wieder
void USART_Send(char *type, char *source, uint16_t *data)
{
uint8_t i = 2,j=0;
char buffer[20];
itoa((int)*data,&buffer);
TxBuffer[0]= *type;
TxBuffer[1]= *source;
while(buffer[j] != '\000') {
TxBuffer[i++]=buffer[j++];
}
TxBuffer[i]='\n';
USART_ITConfig(USARTy, USART_IT_TXE, ENABLE);
}
USART_Send("S","Z",&zzp);
Hier meine verbesserte Routine du hattest recht es macht die Sache viel
einfacher.
Was haltet ihr davon ?
mfg Jan
Marc schrieb:> Nicht jede Message fängt bei mir mit S an> Die mit E wie Error ist in letzter Zeit häufiger aufgetreten ;)
Dann mach ich mir eben 2 Helper-Funktionen :-)
1
voidNotify(chartype,uint16_tdata)
2
{
3
USART_Send('S',type,data);
4
}
5
6
voidError(chartype,uint16_tdata)
7
{
8
USART_Send('E',type,data);
9
}
:-) Sei nicht so faul! Funktionen sind dein Werkzeug um Übersicht in ein
Design zu bringen. Source Code soll sich im Idealfall fast wie
Fliesstext mit einer etwas schrägen Grammatik lesen.
1
if(value<0)
2
Error(ErrorUnderflow,value);
3
else
4
Notify(ValueChanged,value);
tut das ungleich besser als
1
if(value<0)
2
USART_Send('E','u',value);
3
else
4
USART_Send('S','z',value);
Die erste Version kannst du lesen und wenn du dir noch ein paar
Füllwörter dazudenkst, dann steht da mehr oder weniger im Klartext was
passiert, ohne dass man groß überlegen muss. Im zweiten Fall muss man
dauern im Hinterkopf halten: Wie war das? Was war noch mal 'S' und was
war 'z' und warum steht da jetzt 'u'?
void USART_Send(char type, char source, uint16_t data)
{
uint8_t i=2,j=0;
char buffer[20];
itoa((int)data,&buffer[20]);
TxBuffer[0]= type;
TxBuffer[1]= source;
while(buffer[j] != '\000') {
TxBuffer[i++]=buffer[j++];
}
TxBuffer[i]='n';
USART_ITConfig(USARTy, USART_IT_TXE, ENABLE);
}
Gefällt mir eigentlich sehr gut diese Routine.
Aber wenn ich debugge ist in Char buffer an Stelle [0] ein S das
eigentlich in type steht und an Stelle [4] ein Z was eigentlich in
source steht. Wie zur Hölle passiert denn sowas.
mfg Jan
Erstens ist Dein Wert unsigned, also nimm auch utoa und nicht itoa. Und
in einen Speicherbereich zu schreiben, wo Du absolut nichts zu suchen
hast ist gar nicht gut. Und dann fehlt auch noch der dritte Parameter.
Keine Ahnung wie Du das überhaupt durch den Compiler gebracht hast.
Richtig wär:
1
utoa(data,buffer,10);
PS: Und bitte gewöhn Dir an hier im Forum die Code-Tags zu benutzen.
Marc schrieb:> Da muss Buffer doch als Speicheradresse übergeben werden.
eben
Und die Startadresse des buffers kriegst du mittels, tata, buffer.
Ohne irgendwelche Zusätze.
Der Name eines Arrays fungiert an dieser Stelle als Pointer auf den
Anfang des Arrays.
Du hast übergeben (&buffer[20]): Die Adresse des ersten Bytes hinter
dem Array. Also einen Pointer auf Speicher der nicht mehr zum Array
gehört.
&buffer[0] wäre noch gegangen. Aber das ist gleichwertig mit plain and
simple buffer. Gewöhn dich daran.
Für welchen µC ist das eigentlich? Und welche Toolchain? itoa ist doch
meist in den zugehörigen Bibliotheken mit drin. Aber von mir aus hier
ein passendes utoa:
1
voidutoa(unsignedz,char*Buffer)
2
{
3
inti=0;
4
intj;
5
chartmp;
6
7
// die einzelnen Stellen der Zahl berechnen
8
do{
9
Buffer[i++]='0'+z%10;
10
z/=10;
11
}while(z>0);
12
13
// den String in sich spiegeln
14
for(j=0;j<i/2;++j){
15
tmp=Buffer[j];
16
Buffer[j]=Buffer[i-j-1];
17
Buffer[i-j-1]=tmp;
18
}
19
Buffer[i]='\0';
20
}
Der Aufruf wäre dann:
1
utoa(data,buffer);
Und "buffer" wird in diesem Fall als Pointer auf das erste Element des
Arrays interpretiert. Wenn Du es koplizierter schreiben willst, dann
halt:
Marc schrieb:> Nicht jede Message fängt bei mir mit S an> Die mit E wie Error ist in letzter Zeit häufiger aufgetreten ;)
Dann hast du den Protokollrahmen (Start-/Endzeichen) nicht sauber vom
Dateninhalt getrennt. Den Rahmen geht es nix an, was da für ein Inhalt
transportiert wird.