UART Übertragungsfehler bei bestimmten Zahlenwerten

OP #7341977
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!

Angehängte Dateien:
Gast #7341992
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.

OP #7341998
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.

Gast #7342015
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?

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

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

Moderator Persönliche Seite #7342060
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.

OP #7342081
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.

Angehängte Dateien:
Gast #7342088
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?

OP #7342092
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!

Gast #7342130
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.

Beitrag #7343573 wurde von einem Moderator gelöscht.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren