UART Übertragungsfehler bei bestimmten Zahlenwerten

OP #7341977

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!

Angehängte Dateien:

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 ?

Gast

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.

Gast

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.

OP

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.

OP

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.

Gast

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?

OP

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);
Angehängte Dateien:
Moderator (Firma: Titel) Persönliche Seite

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:

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

Moderator (Firma: Titel) Persönliche Seite

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

Moderator Persönliche Seite

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.

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.

OP

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.

Angehängte Dateien:
Gast

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?

OP

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!

Gast

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.

Angehängte Dateien:
Gast

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.

Beitrag #7343573 wurde von einem Moderator gelöscht.

Antwort schreiben

Eine eigene Frage? Dieser Thread ist über 3 Jahre alt. Stell sie in einem eigenen Thread – dort lesen mehr Leute mit, und sie wird öfter und schneller beantwortet.

Neuen Thread starten

Eine Lösung, Ergänzung oder Rückmeldung zu diesem Thread? Dann antworte hier.

Formatierung
  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] – Link zu einem Artikel
  • Verweis auf einen anderen Beitrag: Rechtsklick auf dessen Beitrags-ID (#8078628), „Adresse kopieren“, in den Text einfügen

Mehr zur Formatierung

Längeren Sourcecode als Anhang, nicht in den Text. JPG nur für Fotos und Scans; Zeichnungen und Screenshots als PNG oder GIF. Siehe Bildformate.

Schon registriert? Anmelden

Wird nicht angezeigt. Wir schicken dir einen Link; sobald du damit bestätigst, erscheint dein Beitrag.

Mit dem Abschicken legst du einen kostenlosen Account an und erkennst die Nutzungsbedingungen und die Datenschutzerklärung an.