Forum: Mikrocontroller und Digitale Elektronik USART Routine für selbst entwickeltes Protokoll


von Marc (Gast)


Lesenswert?

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

von Karl H. (kbuchegg)


Lesenswert?

> 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.

von Karl H. (kbuchegg)


Lesenswert?

>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 );
}

von Karl H. (kbuchegg)


Lesenswert?

> 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?

von Marc (Gast)


Lesenswert?

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

von Karl H. (kbuchegg)


Lesenswert?

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.

von Marc (Gast)


Lesenswert?

Nicht jede Message fängt bei mir mit S an
Die mit E wie Error ist in letzter Zeit häufiger aufgetreten ;)

von Marc (Gast)


Lesenswert?

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

von ... (Gast)


Lesenswert?

Warum sinnlos mit Pointern rumhantieren. Ich würd es eher so machen:
1
void USART_Send(char type, char source, uint16_t data)
2
{
3
  uint8_t i = 2,j=0;
4
  char buffer[10];
5
  utoa(data, buffer, 10);
6
  TxBuffer[0] = type;
7
  TxBuffer[1] = source;
8
  while(buffer[j]) {
9
    TxBuffer[i++] = buffer[j++];
10
  }
11
  TxBuffer[i] = '\n';
12
  USART_ITConfig(USARTy, USART_IT_TXE, ENABLE);
13
}
14
15
USART_Send('S','Z',zzp);
Oder auch so:
1
void USART_Send(char type, char source, uint16_t data)
2
{
3
  TxBuffer[0]= type;
4
  TxBuffer[1]= source;
5
  utoa(data,&TxBuffer[2],10);
6
  strcat(TxBuffer, "\n");
7
  USART_ITConfig(USARTy, USART_IT_TXE, ENABLE);
8
}
Falls man nicht mit Flash und jedem Cycle sparen muß eventuell auch so:
1
void USART_Send(char type, char source, uint16_t data)
2
{
3
  sprintf(TxBuffer, "%c%c%u\n", type, source, data);
4
  USART_ITConfig(USARTy, USART_IT_TXE, ENABLE);
5
}

von Karl H. (kbuchegg)


Lesenswert?

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
void Notify( char type, uint16_t data )
2
{
3
  USART_Send( 'S', type, data );
4
}
5
6
void Error( char type, uint16_t data )
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'?

von Marc (Gast)


Lesenswert?

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

von Marc (Gast)


Lesenswert?

Achja das steht da bevor itoa ausgeführt wurde und beim HTerm kommt nur 
Käse an...

von ... (Gast)


Lesenswert?

Die Zeile ist völliger Mist:
1
  itoa((int)data,&buffer[20]);
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.

von ... (Gast)


Lesenswert?

Achja, "buffer" 20 Bytes groß zu machen ist auch etwas übertrieben, Du 
brauchst maximal 6 (7 mit itoa statt utoa).

von Marc (Gast)


Lesenswert?

1
void itoa( int z, char* Buffer )
2
{
3
  int i = 0;
4
  int j;
5
  char tmp;
6
  unsigned u;    // In u bearbeiten wir den Absolutbetrag von z.
7
8
    // ist die Zahl negativ?
9
    // gleich mal ein - hinterlassen und die Zahl positiv machen
10
    if( z < 0 ) {
11
      Buffer[0] = '-';
12
      Buffer++;
13
      // -INT_MIN ist idR. größer als INT_MAX und nicht mehr
14
      // als int darstellbar! Man muss daher bei der Bildung
15
      // des Absolutbetrages aufpassen.
16
      u = ( (unsigned)-(z+1) ) + 1;
17
    }
18
    else {
19
      u = (unsigned)z;
20
    }
21
    // die einzelnen Stellen der Zahl berechnen
22
    do {
23
      Buffer[i++] = '0' + u % 10;
24
      u /= 10;
25
    } while( u > 0 );
26
27
    // den String in sich spiegeln
28
    for( j = 0; j < i / 2; ++j ) {
29
      tmp = Buffer[j];
30
      Buffer[j] = Buffer[i-j-1];
31
      Buffer[i-j-1] = tmp;
32
    }
33
    Buffer[i] = '\0';
34
}

Ich benutze diese Variante von itoa aus
http://www.mikrocontroller.net/articles/FAQ#itoa.28.29_.28utoa.28.29.2C_ltoa.28.29.2C_ultoa.28.29.2C_ftoa.28.29_.29

Da muss Buffer doch als Speicheradresse übergeben werden.

mfg Marc

von Karl H. (kbuchegg)


Lesenswert?

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.

von ... (Gast)


Lesenswert?

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
void utoa( unsigned z, char* Buffer )
2
{
3
  int i = 0;
4
  int j;
5
  char tmp;
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:
1
utoa(data, &buffer[0]);
Ist aber im Endeffekt das Selbe.

von Michael (Gast)


Lesenswert?

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.

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.