Forum: Mikrocontroller und Digitale Elektronik Probleme mit RS232-Kommunikation (ATMega128)


von Luca B. (lucabert)


Lesenswert?

Hallo, Leute!

Ich habe ein komisches Problem und zwar: ich schreibe ein komplexes 
Programm für den ATMega128 um meinen Roboter zu steuern.
Das Programm muß Befehle vom RS232 bekommen und, über den gleichen Weg, 
Antworten schicken.
Die Kommunikation µC => PC funktioniert problemlos, aber in der andere 
Richtung (PC => µC) habe ich oft Probleme.

Also, ich habe ein Datenpaket so gemacht:

struct dataFromRS232
{
  uint8_t version;
  uint8_t deviceFrom;
  uint8_t deviceTo;
  uint8_t packetSize;
  char data[100];
  uint8_t parity;
};

Das Protokoll funktioniert wie folgendes:

die erste 4 Bytes werden geschickt, dann werden X Bytes (packetSize) 
empfangen (in data) und zum Schluß wird ein Byte für die Parität 
geschickt.
Der Empfänger prüft, ob alles stimmt (gleiche Version des Protokolls, 
Parität, usw.) und schickt ein Byte als ACK/NACK.

Das ganze wird auf dem µC mit dem UART-Library vom Fleury implementiert.

Hier ist der Abschnitt des Codes, der mir Probleme verursacht:

...
unsigned int c;
uint8_t l;
...

  c = u_getc();
  if(c & UART_NO_DATA)
    return;
  else
  {
    if(isWaitingACK())
    {
 ... Verwaltung der ACK/NACK. Das löscht das Packet vom Queue. 
Funktioniert einwandfrei!
    }
    else
    {
      receivedData.version = (uint8_t) (c & 0xFF);
      do { c = u_getc(); } while(c & UART_NO_DATA);
      receivedData.deviceFrom = (uint8_t) (c & 0xFF);
      do { c = u_getc(); } while(c & UART_NO_DATA);
      receivedData.deviceTo = (uint8_t) (c & 0xFF);
      do { c = u_getc(); } while(c & UART_NO_DATA);
      receivedData.packetSize = (uint8_t) (c & 0xFF);

      for(l = 0; l < receivedData.packetSize; l++)
      {
        do { c = u_getc(); } while(c & UART_NO_DATA);
        receivedData.data[l] = (char) (c & 0xFF);
      }
      receivedData.data[l] = 0;
      do { c = u_getc(); } while(c & UART_NO_DATA);
      receivedData.parity = (uint8_t) (c & 0xFF);
      if(receivedData.version != MYPROTOCOLVERSION)
        sendNACK();
      else if(calculateParity(receivedData) != receivedData.parity)
        sendPRMismatch();
      else
        sendACK();
      hasDataToProcess = TRUE;
    }
  }

Das Problem ist, das ab und zu, leider total zufällig!, die Routine 
hängt.
Mit einem Debugger sehe ich, daß es genau an der Schleife mit u_getc 
(Alias für uart_getc, bzw. uart1_getc je nach Konfiguration) hängt.
Warum denn? Die Daten werden vom PC geschickt, dann sollten auch vom µC 
empfangen werden, oder?

Hat jemand eine Ahnung, warum die Routine nicht geht? Oder, besser 
gesagt, warum ab und zu hängt?
Ich werde langsam verrückt...

Danke euch!
Luca Bertoncello

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Der Codefetzen ist IMHO nicht ausreichend, um den sporatischen Fehler zu 
finden.

Der Empfängercode hat auch einige Probleme. So wird der Eingangspuffer 
zuerst mit Daten überschrieben, dann wird auf Fehler geprüft und u.U. 
trotz entdeckter Fehler im Programmablauf weitergemacht 
(hasDataToProcess = TRUE;). Das Füllen von receivedData.data hat bei 
fehlerhaft empfangenem receivedData.packetSize das Potenzial zum 
Pufferüberlauf.

von Luca B. (lucabert)


Angehängte Dateien:

Lesenswert?

Stefan B. schrieb:

> Der Codefetzen ist IMHO nicht ausreichend, um den sporatischen Fehler zu
> finden.

Wenn du mir noch sagst, was fehlt, kann ich gern schicken...

> Der Empfängercode hat auch einige Probleme. So wird der Eingangspuffer
> zuerst mit Daten überschrieben, dann wird auf Fehler geprüft und u.U.
> trotz entdeckter Fehler im Programmablauf weitergemacht

Weil ich beim kopieren ein paar Klammer vergessen habe...
Eigentlich hasDataToProcess = TRUE wird nur nach sendACK() aufgerufen...

> (hasDataToProcess = TRUE;). Das Füllen von receivedData.data hat bei
> fehlerhaft empfangenem receivedData.packetSize das Potenzial zum
> Pufferüberlauf.

Sicherheitsprobleme habe ich im den Fall nicht... receivedData.data ist 
100 char groß und der größte Befehl, den ich schicke ich vielleicht 15 
Bytes...
Jedenfalls, werde ich später noch eine Prüfung schreiben, daß ein NACK 
schickt, wenn packetSize > 100 ist. Und selbstverständlich werden 
maximal 100 Zeichen gelesen.

Aber eine Sache ist jetzt sehr komisch...

Heute früh habe ich ein kleines Programm geschrieben, das ich als ZIP 
mitschicke.
Es liest die Daten vom RS232 mit dem obengenannten Protokoll, und 
schickt sie zurück.

An der anderen Seite (Laptop) habe ich das Kommunikationsprogramm, das 
genau das gleiche Protokoll implementiert.
Ich schicke alle Sekunden ein Paket seit kurz nach 9 Uhr, und jetzt 
läuft immer noch stabil.

Die einzige Änderung ist, anstatt:
1
  if(c & UART_NO_DATA)
2
    return;
3
  else
4
  {
5
....
6
  }

habe ich geschrieben:
1
  if((c & 0xFF00) == 0)
2
  {
3
...
4
  }

Kann es sein, daß das das Problem ist?

Danke!
Luca Bertoncello

von Karl H. (kbuchegg)


Lesenswert?

Luca Bertoncello schrieb:

> Die einzige Änderung ist, anstatt:
>
>
1
>   if(c & UART_NO_DATA)
2
>     return;
3
>   else
4
>   {
5
> ....
6
>   }
7
>
>
> habe ich geschrieben:
>
>
1
>   if((c & 0xFF00) == 0)
2
>   {
3
> ...
4
>   }
5
>
>
> Kann es sein, daß das das Problem ist?

Möglich.
Beide Codefetzen machen verschiedene Dinge.

Der erste returned nur im Fall, dass die Fleury Lib nichts vorliegen 
hat. Der andere wertet nur dann aus, wenn die Fleury Lib keinen Fehler 
gemeldet hat.
Im ersten Fall wiederrum wertest du auch dann aus, wenn die Fleury Lib 
einen Fehler mitteilen will.

Es gibt in der Fleury Lib auch noch andere Return-Codes im Highbyte als 
nur NO_DATA

von Luca B. (lucabert)


Lesenswert?

Karl heinz Buchegger schrieb:

>> Kann es sein, daß das das Problem ist?
>
> Möglich.
> Beide Codefetzen machen verschiedene Dinge.
>
> Der erste returned nur im Fall, dass die Fleury Lib nichts vorliegen
> hat. Der andere wertet nur dann aus, wenn die Fleury Lib keinen Fehler
> gemeldet hat.
> Im ersten Fall wiederrum wertest du auch dann aus, wenn die Fleury Lib
> einen Fehler mitteilen will.
>
> Es gibt in der Fleury Lib auch noch andere Return-Codes im Highbyte als
> nur NO_DATA

Ich habe heute nur zufällig diese andere Return-Codes gesehen...
Vorher habe ich immer der erste Code benutzt, weil es mir so gegeben 
worden ist (Asche auf meinem Kopf!).

Also, ich probiere heute Abend den Code so zu ändern, wie bei diesem 
Testprogramm, dann mal sehen.

Ich danke euch!
Luca Bertoncello

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Luca Bertoncello schrieb:

> Wenn du mir noch sagst, was fehlt, kann ich gern schicken...

Das ZIP ist ausreichend.

>> (hasDataToProcess = TRUE;). Das Füllen von receivedData.data hat bei
>> fehlerhaft empfangenem receivedData.packetSize das Potenzial zum
>> Pufferüberlauf.
>
> Sicherheitsprobleme habe ich im den Fall nicht... receivedData.data ist
> 100 char groß und der größte Befehl, den ich schicke ich vielleicht 15
> Bytes...

Das meine ich nicht. Probleme treten auf, wenn das Telegramm unsynchron 
wird und irgendein Byte als receivedData.packetSize interpretiert wird. 
Wenn es >99 ist, schreibst du in der for-Schleife hinter das 
receivedData.data Feld...

> Jedenfalls, werde ich später noch eine Prüfung schreiben, daß ein NACK
> schickt, wenn packetSize > 100 ist. Und selbstverständlich werden
> maximal 100 Zeichen gelesen.

Das wird dieses Problem anpacken und ist notwendig. Noch besser wäre es, 
den Start eines Paketes sauber zu erkennen.

> Aber eine Sache ist jetzt sehr komisch...

Dein neuer Code verwirft alle Zeichen, bei denen ein UART-Fehler (FE1, 
DOR1, UART_BUFFER_OVERFLOW, UART_NO_DATA) aufgetreten ist. Der alte Code 
verwirft explizit nur die Zeichen, bei denen das Bit UART_NO_DATA 
gesetzt ist.

1. Finde den genauen Fehlercode raus
2. Setze mal #define UART_RX_BUFFER_SIZE 32 vom Default weg nach oben!

von Luca B. (lucabert)


Lesenswert?

Stefan B. schrieb:

>> Sicherheitsprobleme habe ich im den Fall nicht... receivedData.data ist
>> 100 char groß und der größte Befehl, den ich schicke ich vielleicht 15
>> Bytes...
>
> Das meine ich nicht. Probleme treten auf, wenn das Telegramm unsynchron
> wird und irgendein Byte als receivedData.packetSize interpretiert wird.
> Wenn es >99 ist, schreibst du in der for-Schleife hinter das
> receivedData.data Feld...

Ich verstehe nicht was diese "Wenn es >99 ist" bedeutet...
Ein Byte ist nicht 8bit (0-255)?
Jedenfalls, wie gesagt, es wird die Kontrolle hinzugefügt, obwohl es 
sicher nicht passieren kann, daß ein Paket länger als 15-20 Bytes ist...

> Das wird dieses Problem anpacken und ist notwendig. Noch besser wäre es,
> den Start eines Paketes sauber zu erkennen.

Vorschläge? Als Definition, der erste Byte ist die Versionsnummer, also 
immer ein "2".
Aber wie gesagt, die Probleme kamen dann später, bei auslesen der 
receivedData.data...

>> Aber eine Sache ist jetzt sehr komisch...
>
> Dein neuer Code verwirft alle Zeichen, bei denen ein UART-Fehler (FE1,
> DOR1, UART_BUFFER_OVERFLOW, UART_NO_DATA) aufgetreten ist. Der alte Code
> verwirft explizit nur die Zeichen, bei denen das Bit UART_NO_DATA
> gesetzt ist.
>
> 1. Finde den genauen Fehlercode raus

Das werde ich machen

> 2. Setze mal #define UART_RX_BUFFER_SIZE 32 vom Default weg nach oben!

Was meinst du?
Soll ich die Buffergröße vergrößern? Was schlägst du vor? 100 Byte? 
Oder, besser gesagt, 105 (maximale Größe eines Pakets)?

Danke
Luca Bertoncello

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Luca Bertoncello schrieb:
> Stefan B. schrieb:
>
>>> Sicherheitsprobleme habe ich im den Fall nicht... receivedData.data ist
>>> 100 char groß und der größte Befehl, den ich schicke ich vielleicht 15
>>> Bytes...
>>
>> Das meine ich nicht. Probleme treten auf, wenn das Telegramm unsynchron
>> wird und irgendein Byte als receivedData.packetSize interpretiert wird.
>> Wenn es >99 ist, schreibst du in der for-Schleife hinter das
>> receivedData.data Feld...
>
> Ich verstehe nicht was diese "Wenn es >99 ist" bedeutet...

Sorry 99 ist der max. Index. Das erste kritische Zeichen bzgl. der 
folgenden for-Schleife ist der Wert 101.

Angenommen hier
1
     do { c = u_getc(); } while(c & UART_NO_DATA);
2
      receivedData.packetSize = (uint8_t) (c & 0xFF);
3
4
      for(l = 0; l < receivedData.packetSize; l++)
5
      {
6
        do { c = u_getc(); } while(c & UART_NO_DATA);
7
        receivedData.data[l] = (char) (c & 0xFF);
8
      }

ist das Telegramm unsynchron oder gestört und für 
receivedData.packetSize wird z.B. c = 101 eingelesen. Dann wird in 
receivedData.data[l==100] = (char) (c & 0xFF); hinter die Feldgrenzen 
geschrieben!

      for(l = 0; l < ((receivedData.packetSize < MAXDATALENGTH) ? : 
MAXDATALENGTH); l++)

>> 2. Setze mal #define UART_RX_BUFFER_SIZE 32 vom Default weg nach oben!
>
> Was meinst du?
> Soll ich die Buffergröße vergrößern? Was schlägst du vor? 100 Byte?
> Oder, besser gesagt, 105 (maximale Größe eines Pakets)?

Laut Fleury-Library soll es eine Potenz von 2 sein. Wenn das RAM es noch 
her gibt, würde ich 128 nehmen, ansonsten 64

von Luca B. (lucabert)


Lesenswert?

Stefan B. schrieb:

> Sorry 99 ist der max. Index. Das erste kritische Zeichen bzgl. der
> folgenden for-Schleife ist der Wert 101.
>
> Angenommen hier
>
>
1
>      do { c = u_getc(); } while(c & UART_NO_DATA);
2
>       receivedData.packetSize = (uint8_t) (c & 0xFF);
3
> 
4
>       for(l = 0; l < receivedData.packetSize; l++)
5
>       {
6
>         do { c = u_getc(); } while(c & UART_NO_DATA);
7
>         receivedData.data[l] = (char) (c & 0xFF);
8
>       }
9
>
>
> ist das Telegramm unsynchron oder gestört und für
> receivedData.packetSize wird z.B. c = 101 eingelesen. Dann wird in
> receivedData.data[l==100] = (char) (c & 0xFF); hinter die Feldgrenzen
> geschrieben!

Verstanden!

>>> 2. Setze mal #define UART_RX_BUFFER_SIZE 32 vom Default weg nach oben!
>>
>> Was meinst du?
>> Soll ich die Buffergröße vergrößern? Was schlägst du vor? 100 Byte?
>> Oder, besser gesagt, 105 (maximale Größe eines Pakets)?
>
> Laut Fleury-Library soll es eine Potenz von 2 sein. Wenn das RAM es noch
> her gibt, würde ich 128 nehmen, ansonsten 64

Aha! OK, ich sollte genugend Speicher noch zur Verfügung haben, ich 
probiere also ein Wert von 128.

Danke!
Luca Bertoncello

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Stefan B. schrieb:

>       for(l = 0; l < ((receivedData.packetSize < MAXDATALENGTH) ? :
> MAXDATALENGTH); l++)

Ähm, Korrektur - du schreibst ja nach dem for... noch ein Nullbyte ins 
Feld und l ist dann ggf. schon gleich MAXDATALENGTH. Das muss auch 
noch berücksichtigt werden! Also statt MAXDATALENGTH 2x eintragen 
MAXDATALENGTH-1

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.