Forum: Mikrocontroller und Digitale Elektronik UART Übertragungsfehler bei bestimmten Zahlenwerten


von Thomas F. (thomas_f923)


Angehängte Dateien:

Lesenswert?

Hallo!

Ich benötige eine UART Datenübertragung zwischen uC (STM32G031K6T6) und 
meinem Windows Rechner. Dazu habe ich ein cpp Programm geschrieben 
welches einen 16 Bit Wert aufteilt in ein höherwertiges und ein 
niederwertiges Byte. Diese werden beide über UART an den PC gesendet. Um 
am PC eine Struktur zu erkennen, werden zwischendurch bestimmte Zeichen 
übertragen.
Nur leider werden manche Zahlen nicht korrekt übermittelt, sondern an 
stelle dieser einfach der Wert 63 (sechs aufeinanderfolgende 1-Bits).

STM32 MDK Arm Code:
char input,output;
bool test = 0;
bool change = 0;
uint32_t run = 0;
uint16_t tx_value = 12340;
1
void Task1(uint32_t T_ms){
2
  
3
  output = NULL;
4
  if (run==1) tx_value=0b0000010100000101;
5
  //else if(run==2)tx_value='a';
6
  /*else if(run<=26)
7
  {
8
    if(change==0){
9
    tx_value = ((run << 8) | (run+1));
10
    }
11
    else tx_value = 'n';
12
    run++;
13
  } */
14
  else if(run==2) {
15
    tx_value = 'a';
16
  }
17
  else if(run==3) {
18
    tx_value = (1872);
19
    }
20
  else if(run==4) {
21
    tx_value = 'n';
22
    }
23
  else if(run==5) {
24
    tx_value = (1234);
25
    }
26
  else if(run==6) {
27
    tx_value = 'n';
28
    }
29
  else if(run==7) {
30
    tx_value = (50);
31
  }  
32
  else if(run==8) {
33
    tx_value = 'n';
34
    }
35
  else if(run==9) {
36
    tx_value = (255);
37
  }  
38
  else {
39
    tx_value = 'z';
40
    run=0;
41
  }
42
  
43
  send16Bit(tx_value);
44
  run++;
45
  
46
}
47
uint8_t lower_byte;
48
uint8_t upper_byte;
49
50
  void send16Bit(uint16_t data) {
51
  lower_byte = data & 0xFF;
52
  upper_byte =  data >> 8;//(data >> 8) & 0xFF;
53
  sendByte(lower_byte);
54
  sendByte(upper_byte);
55
}
56
  
57
void sendByte(uint8_t data) {
58
  while (!(USART1->ISR & (1<<7)));  // warten, bis Übertragungsbuffers bereit ist
59
  USART1->TDR = data;  // Daten in Übertragungsbuffer schreiben
60
}
C# Consolen Anwendung zum Auswerten der Daten:
1
 SerialPort port = new SerialPort("COM8", 115200, Parity.Odd, 8, StopBits.One);
2
            port.Open();
3
            UInt16 value = '0';
4
            string output = "", output_csv="";
5
            UInt16 lowByte = 0;
6
            UInt16 highByte = 0;
7
            UInt16 rx_value = 0;
8
            string state = "waiting";
9
            Int32 run_csv = 0;
10
11
            while (run_csv < 1)
12
            {
13
14
                rx_value = Convert.ToByte(port.ReadChar());
15
                if (rx_value == 0b101)
16
                {
17
                    Console.WriteLine("Init Byte received");
18
                    output = "";
19
                    state = "lowByte";
20
                }
21
                else if (state == "lowByte")
22
                {
23
                    lowByte = rx_value;
24
                    state = "highByte";
25
                }
26
                else if (state == "highByte")
27
                {
28
                    highByte = rx_value;
29
                    value = (UInt16)((highByte << 8) | lowByte); //zusammensetzen von low und highByte
30
31
                    Console.WriteLine("highByte: {0} \tlowByte: {1}\tvalue: {2}",highByte,lowByte,value);
32
33
                    if (value == 'a')
34
                    {
35
                        output += "New: ";
36
                        Console.WriteLine("a erkannt");
37
                    }
38
                    else if (value != 'z')
39
                    {
40
                        Console.WriteLine("kein z erkannt");
41
                        if (value == 'n')
42
                        {
43
                            Console.WriteLine("n erkannt");
44
                            output += " Next:";
45
                            output_csv += ";";
46
                        }
47
48
                        else
49
                        {
50
                            Console.WriteLine("neue Zahl erkannt erkannt");
51
                            output += value + ";";
52
                            output_csv += value;
53
                        }
54
                    }
55
                    else if (value == 'z')
56
                    {
57
                        Console.WriteLine("\n z erkannt");
58
                        output += " Done!";
59
                        Console.WriteLine(output);
60
                        output_csv += "\n";
61
                        run_csv++;
62
                    }
63
                    state = "lowByte";
64
                    lowByte = 0;
65
                    highByte = 0;
66
                }                
67
            }
68
            port.Close();
69
            Console.WriteLine("output_csv:");
70
            Console.WriteLine(output_csv);
71
72
            
73
            Console.ReadKey();
74
75
        }

Die Ausgabe auf der Konsole sieht wie folgt aus (Bild im Anhang):
Der zweite Zahlenwert sollte wie aus dem cpp Programm zu entnehmen 1234 
(highByte 4, lowByte 210) sein, jedoch ist dieser 1087 ((highByte 4, 
lowByte 63).
Ähnliches beim letzten Wert 255.

Bitte um Hilfe und herzlichen Dank im Voraus!

von Purzel H. (hacky)


Lesenswert?

Etwas wie ein Protokoll duerft's schon sein. Auf einem Tisch, dh 
zwischen Board und PC sollte man ja schon keine Uebertragungsfehler 
auflesen. Kann aber dank EMV trotzdem geschehen. Und wie unterscheidest 
du dann low byte und high byte ?

von mitlesa (Gast)


Lesenswert?

Thomas F. schrieb:
> Nur leider werden manche Zahlen nicht korrekt übermittelt

Da frage ich mich sofort ob denn die Baudrate ausreichend genau
stimmt. Wenn man im Controller einen internen Oszillator als
Zeitbasis verwendet kann das schon mal schiefgehen.

von PittyJ (Gast)


Lesenswert?

Ich schliesse dann immer ein Scope an, und schaue, was wirklich auf der 
Leitung versendet wird.
Dann braucht man nur im Sender oder nur im Empfänger schauen.

Odd parity soll so sein? Ich lass die immer weg.

von mitlesa (Gast)


Angehängte Dateien:

Lesenswert?

Thomas F. schrieb:
> C# Consolen Anwendung zum Auswerten der Daten:

Bitte Anhang lesen, verstehen, verinnerlichen und danach handeln.
"Längerer Sourcecode" ist es wenn man dauernd scrollen muss um
alles zu sehen.

von Thomas F. (thomas_f923)


Lesenswert?

Purzel H. schrieb:
> Etwas wie ein Protokoll duerft's schon sein. Auf einem Tisch, dh
> zwischen Board und PC sollte man ja schon keine Uebertragungsfehler
> auflesen. Kann aber dank EMV trotzdem geschehen. Und wie unterscheidest
> du dann low byte und high byte ?

Übertragungsfehler habe ich bereits ausgeschlossen, der Fehler passiert 
nur bei manchen bestimmten Zahlenwerten. Ich habe schon >1000 
Übertragungen ohne Fehler hintereinander, wenn ich eben nicht die paar 
besonderen Werte übertrage.

von mitlesa (Gast)


Lesenswert?

Thomas F. schrieb:
> Übertragungsfehler habe ich bereits ausgeschlossen

Na und? Sicherlich sind bei grösseren Baudraten-Abweichungen nicht
alle Bytes gleichmässig von Übertragungsfehlern betroffen.

von Thomas F. (thomas_f923)


Lesenswert?

PittyJ schrieb:
> Ich schliesse dann immer ein Scope an, und schaue, was wirklich auf der
> Leitung versendet wird.
> Dann braucht man nur im Sender oder nur im Empfänger schauen.
>
> Odd parity soll so sein? Ich lass die immer weg.

Odd parity hab ich dann aus Verzweiflung probiert, normal mach ichs auch 
ohne.
Zugang zu einem Osci habe ich aktuell leider nicht.

von mitlesa (Gast)


Lesenswert?

Thomas F. schrieb:
> tx_value = 'z';

Was erwartest du denn beim Empfänger wenn du einen Character
über eine Funktion sendest die 16 Bit überträgt?

Thomas F. schrieb:
> void send16Bit(uint16_t data) {

von S. Landolt (Gast)


Lesenswert?

Wäre nett bzw. zuvorkommend gewesen, Soll- und Istwerte direkt 
gegenüberzustellen, damit die Angesprochenen sich das nicht erst 
zusammensuchen müssen.

Kann es sein, dass Werte größer 127 schiefgehen - irgendwas mit 
7-bit-Übertragung?

von Thomas F. (thomas_f923)


Angehängte Dateien:

Lesenswert?

S. Landolt schrieb:
> Kann es sein, dass Werte größer 127 schiefgehen - irgendwas mit
> 7-bit-Übertragung?

Hab es grad getestet und scheint so korrekt zu sein, die 8 Bit Werte 
125, 126 und 127 werden korrekt übertragen. Ab 128 ist es lediglich 63.
Weiß jemand woran das liegen könnte?
Im Bild im Anhang ist meine uC UART Konfiguration zu sehen.
So sieht die Empfängerseite Port Config aus:
1
SerialPort port = new SerialPort("COM8", 115200, Parity.Odd, 8, StopBits.One);

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Thomas F. schrieb:
> Zugang zu einem Osci habe ich aktuell leider nicht.
Irgendwo hatte ich schon wiederholt mal was zur Inbetriebnahme von 
seriellen Schnittstellen geschrieben.

Habs wieder gefunden, dort war es:
* Beitrag "Re: Atmega Uart - Falsche Byte Übertragung"
* Beitrag "Re: WS2812b flackern / 3600 LED Matrix"

Fazit: kauf dir wenigstens einen 10€ Logic-Analyzer, dann bist du 
wenigstens nicht mehr völlig blind.

Thomas F. schrieb:
> Nur leider werden manche Zahlen nicht korrekt übermittelt, sondern an
> stelle dieser einfach der Wert 63 (sechs aufeinanderfolgende 1-Bits).
Da wird wohl fälschlicherweise ein Startbit erkannt.

mitlesa schrieb:
> Was erwartest du denn beim Empfänger wenn du einen Character
> über eine Funktion sendest die 16 Bit überträgt?
Hast du den Code dieser Funktion mit ihren beiden *sendByte()* auch 
schon angeschaut?

Thomas F. schrieb:
> Bild_2023-02-08_125144747.png
Text bitte **nicht** als Bild posten.

Wenn die Formatierung wichtig ist, dann gibt es (wie in den Zeilen über 
jeder Textbox hier beschrieben) die [pre] Tags:
https://www.mikrocontroller.net/articles/Formatierung_im_Forum

von S. Landolt (Gast)


Lesenswert?

Da das 8. Bit als letztes übertragen wird: vielleicht doch abweichende 
Geschwindigkeiten, wie von mitlesa in seiner ersten Antwort vermutet.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Thomas F. schrieb:
> Ab 128 ist es lediglich 63.
> Weiß jemand woran das liegen könnte?
Da gibt es mehrere Ansätze: du kannst mangels eines Oszis/LA nur den 
einen probieren und kontrollieren, was der Compiler da tatsächlich ins 
TX-Register schreibt. Nicht, dass das ein trivialer 
singed/unsigned-Effekt ist.

Nimm mal für den Test einfache Bitmuster wie 0x55aa und 0xaa55 und 
0xcc33 oder so...

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Thomas F. schrieb:
> [Low Byte & High-Byte]...
> Diese werden beide über UART an den PC gesendet.

Sobald ein kleiner Übertragungsfehler passiert, wird der PC das 
nachfolgende High-Byte als Low-Byte (und umgekehrt) interpretieren. Du 
hast keine Chance, dass die beiden überhaupt noch einmal zueinander 
finden - außer durch einen weiteren Übertragungsfehler.

Von daher ist Dein Konzept unbrauchbar. Ich empfehle, die beiden zu 
übertragenen Bytes in einen gesicherten, eindeutigen Frame zu betten, 
z.B. durch ETX, STX und Checksum, wobei ETX und STX selbst innerhalb des 
Frames "escaped" werden.

Andere (einfachere) Möglichkeit: Du verteilst die 16 Bit auf 3 oder 4 
Bytes (24 bit oder 32 bit), wobei Du die höherwertigen Bits als Index 
benutzt z.B. so:
1
01XXXXXX
2
100XXXXX
3
110XXXXX
Durch Plausibilitätsprüfungen lassen sich Übertragungsfehler zumindest 
erkennen, aber nicht ausmerzen.

von Peter D. (peda)


Lesenswert?

Du brauchst ein richtiges Protokoll und nicht was selber ausgedachtes 
und ungeprüftes.
Daten müssen sich eindeutig von Steuercodes unterscheiden lassen, auch 
bei Synchronisationsverlust. Eine CRC zur Fehlererkennung ist heutzutage 
auch Standard.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Lothar M. schrieb:
> Nimm mal für den Test einfache Bitmuster wie 0x55aa und 0xaa55 und
> 0xcc33 oder so...

Eventuell ist es auch sinnvoll, auf der Senderseite die Anzahl der 
Stoppbits auf 1.5 oder 2 zu erhöhen. Dann werden bei andauernder 
Befeuerung ohne irgendeine Pause die einzelnen Bytes auf Empfängerseite 
eher als solche erkannt.

von Thomas F. (thomas_f923)


Angehängte Dateien:

Lesenswert?

Peter D. schrieb:
> Du brauchst ein richtiges Protokoll und nicht was selber ausgedachtes
> und ungeprüftes.
> Daten müssen sich eindeutig von Steuercodes unterscheiden lassen, auch
> bei Synchronisationsverlust. Eine CRC zur Fehlererkennung ist heutzutage
> auch Standard.

Bevor ich ein vernünftiges Protokoll implementiere, benötige ich erstmal 
eine funktionierende Schnittstelle.

Lothar M. schrieb:
> Nicht, dass das ein trivialer
> singed/unsigned-Effekt ist.

Scheint tatsächlich so zu sein.
Da ich kein Osci zur verfügung habe, habe ich einen Arduino Nano 
parallel an de Schnittstelle gehängt und mitprotokolliert.
Hier sind die Daten korrekt (siehe Bild im Anhang).
Hier hatte ich anfangs auch eigenartige Werte, teils mit minus. Durch 
Verwendung von unsigned jedoch nicht mehr.

Arduino Code:
1
#include <SoftwareSerial.h>
2
3
SoftwareSerial mySerial(2, 3); // RX, TX
4
5
void setup() {
6
  Serial.begin(115200);
7
  mySerial.begin(115200);
8
}
9
10
void loop() {
11
  if (mySerial.available()) {
12
    unsigned int i = mySerial.read();
13
    if (i == 'a' || i == 'n' || i == 'z') {
14
      Serial.println((char)i);
15
    } else {
16
      Serial.println(i);
17
    }
18
  }
19
}

Also liegt das Problem an der Empfängerseite, jedoch finde ich nicht an 
welcher Stelle sich der Fehler verbergen könnte.

: Bearbeitet durch User
von Max M. (Gast)


Lesenswert?

Thomas F. schrieb:
> Bevor ich ein vernünftiges Protokoll implementiere, benötige ich erstmal
> eine funktionierende Schnittstelle.
Mit der Schnittstelle ist alles in Ordnung.

Der Takt + max Fehler bei 115Kbps beim STM32 ist unklar.
Da kein Protokoll vorhanden ist sondern nur ein undurchdachter Wust der 
fullspeed Daten raushaut, fliegt es Dir eben auseinander.

Funktioniert es bei 9600bps?

von Thomas F. (thomas_f923)


Lesenswert?

So danke für die Hilfe an alle.
Es lag nicht an falschen werten die übermittelt wurden, der einzige 
Fehler war die Verwendung der Funktion
1
rx_value = Convert.ToByte(port.ReadChar())
in meinem C# Programm.
Wenn ich
1
Convert.ToByte(ReadChar())
 durch
1
Convert.ToByte(ReadByte())
 ersetze funktionert es einwandfrei.

Anscheinend macht die Funktion Convert.ToByte etwas seltsames mit allen 
Char werten über 127.
Convert.ToByte ist trotz der verwendung von ReadByte noch notwendig, 
aber dann funktionierts.

Danke für eure mithilfe!

von S. Landolt (Gast)


Lesenswert?

> Problem an der Empfängerseite

Yô - was für ein Programm liefert denn die eingangs gezeigte Ausgabe? 
Oder habe ich etwas überlesen?

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Thomas F. schrieb:
> etwas seltsames mit allen Char werten über 127.
Es kommt drauf an, wie char definiert ist. Es ist nicht völlig abwegig, 
dass das ein signed 8-Bit-Wert ist:

https://www.google.com/search?q=char+signed+unsigned

BTW: Mein Tipp mit dem 10€ Logicanalyzer gilt weiterhin  ;-)

: Bearbeitet durch Moderator
von S. Landolt (Gast)


Angehängte Dateien:

Lesenswert?

Wäre in diesem Fall also ein simples Terminalprogramm ein geeigneter 
Logik-Analysator gewesen.

Übrigens - fand ich lustig (s. 3. Bedeutung):

> Nicht, dass das ein trivialer singed/unsigned-Effekt ist.

von Steve van de Grens (roehrmond)


Lesenswert?

Convert.ToByte(char)

Unterstützt Unicode Zeichen als Eingabe, kann aber nur 8 Bit breite 
Zeichen konvertieren. Siehe 
https://learn.microsoft.com/de-de/dotnet/api/system.convert.tobyte?view=net-7.0#system-convert-tobyte(system-char)

Außerdem überträgst du 16 Bit Zahlen die sicher nicht alle einem 16 Bit 
Unicode Zeichen entsprechen. Siehe
https://learn.microsoft.com/de-de/dotnet/api/system.io.binaryreader.readchar?view=net-7.0
https://learn.microsoft.com/de-de/dotnet/api/system.char?view=net-7.0

Deswegen bringt ReadByte() anstelle von ReadChar() die Lösung.

: Bearbeitet durch User
Beitrag #7343573 wurde von einem Moderator gelöscht.
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.