Forum: PC-Programmierung c#: SerialPort kommt beim Empfang durcheinander


von Gero (Gast)


Lesenswert?

Hallo,
ich möchte gerade ein kleines Programm schreiben mit welchem ich an 
meinen µC eine 5 Byte lange Anfrage senden kann, darauf hin erwarte ich 
eine 5 Byte lange Antwort.
ich verwende den DataReceived-Event und hier liegt vermutlich das 
Problem. nach dem Senden bekomme ich das gespiegelte Telegramm nochmal 
zurück (callback), anschließend sollte die Antwort vom µC kommen.
Entsprechend wird der DataReceived-Event 2x augerufen.
Lese ich hier nun die BytesToRead aus, so sollte ich beides mal 5 Bytes 
erhalten. Gelegentlich bekomme ich aber 3+7 oder 4+6.

Ich warte nun im DataReceived-Event bis beide Events getriggert wurden 
und schreibe alles in einen 10 Byte großen Empfangsbuffer, anschließend 
lösche ich die ersten 5 Bytes.

Dies geht nun schon besser, aber auch hier läuft es irgendwann mal 
schief, dann bekomme ich keine 10 Bytes mehr sondern nur 9 oder 11...


Weiß jemand warum das so ist, ich sehe keine Grund warum Windowas ausser 
tritt kommen sollte :-(

Initialisierung
1
this.serialPort = new SerialPort(this.comStructure.PortName, this.comStructure.BaudRate, this.comStructure.Parity, this.comStructure.DataBits, this.comStructure.StopBits);
2
this.serialPort.Handshake = this.comStructure.Handshake;
3
4
this.serialPort.DataReceived += this.readDataFromSerialPort;
5
this.serialPort.Open();




Event
1
private void readDataFromSerialPort(object sender, SerialDataReceivedEventArgs e)
2
{
3
if (RXmirror == false)  // ignoriere gespiegelte Daten
4
{      // warte auf Antwort vom µC
5
  RXmirror = true;
6
  return;
7
}
8
RXmirror = false;  // wenn nicht im return gelandet, dann bereits µC-Daten - setze flag zurück
9
10
11
int i = serialPort.BytesToRead;
12
WriteLogFenster(i.ToString());
13
        
14
byte[] rx = new byte[10];
15
16
serialPort.Read(rx, 0, 10);
17
string RxData = "";
18
19
for (int z = 5; z <= 9; z++)
20
{
21
  RxData += " " + rx[z];
22
}
23
serialPort.DiscardInBuffer();
24
25
26
WriteLogFenster(RxData);
27
}

von Reinhard Kern (Gast)


Lesenswert?

Gero schrieb:
> ich sehe keine Grund warum Windowas ausser
> tritt kommen sollte :-(

Umgekehrt stellt sich die Frage: woher soll Windows (oder jeder andere 
UART unter jedem anderen BS) wissen, welche 5 Bytes eine Message bilden? 
Dafür gibt es nur 2 Möglichkeiten:

A es gibt ein eindeutiges Message-End-Zeichen, z.B. CR.

B die Pause zwischen Messages ist so gross, dass man einen 
entsprechenden Timeout-Wert setzen kann.

Sonst ist alles bloss ein Zeichenmatsch. Notfalls könntest du Timeout 
nach 10 Bytes verwenden.

Gruss Reinhard

von Peter II (Gast)


Lesenswert?

int i = serialPort.BytesToRead;
WriteLogFenster(i.ToString());
byte[] rx = new byte[10];
serialPort.Read(rx, 0, 10);

wenn nur i zeichen da sind, kannst du nicht einfach 10zeichen lesen.

Im DataReceived sollte man nur genau serialPort.BytesToRead zeichen 
lesen nicht mehr und auch nicht weniger.

Ob deine nachricht damit komplett ist musst du duch eigenen code 
feststellen.

von Gero (Gast)


Lesenswert?

ja das ganze geht per timeout, ich klicke manuell auf buttons, dass sind 
dann mindestens 1 Sekunde zwischen den Nachrichten.

ich habe jetzt auch mal einen timer laufen lassen. Die ReadTimeout habe 
ich hierbei auf 200ms gesetzt und der timmer frägt alle 1000ms einen 
parameter an, selbst hier läuft das ganze nach spätestens 10 Lesezyklen 
ausser Tritt.


wenn ich jede Sekunden einen Parameter anfrage und die Übertragung bei 
9600baud nicht einmal 20ms dauert, müsste das doch funktionieren!

von Reinhard Kern (Gast)


Lesenswert?

Gero schrieb:
> ich habe jetzt auch mal einen timer laufen lassen.

Wo? Du musst den Tiemout für die serielle Schnittstelle setzen.

The SetCommTimeouts function and the COMMTIMEOUTS structure that are 
supported by the Windows Base Services in the Windows SDK.

Gruss Reinhard

von Gero (Gast)


Lesenswert?

ja, das habe ich verstanden.

Hätte ich wohl besser erklären sollen, also der Timeout steht auf 200ms.

Der timer triggert alle 1000ms eine neue Anfrage an.
so sieht es auf dem Oszi aus:


¯¯¯¯¯¯¯||-||----¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯||-||----¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯||-||---¯ 
¯¯¯

-Frei--Tx-Rx--- Frei
       |-100ms-|-------900ms------|

von Reinhard Kern (Gast)


Lesenswert?

Gero schrieb:
> Hätte ich wohl besser erklären sollen, also der Timeout steht auf 200ms.

Dann musst du nur noch dafür sorgen, dass die Timeoutzeit zu laufen 
beginnt, wenn du die Abfrage startest, dann sind 200ms aber vielleicht 
knapp, oder wenn das Senden fertig ist.

Übrigens gibt es mehrere Timeouts - gemeint ist die maximale Zeit 
zwischen 2 Bytes.

Gruss Reinhard

von Karl H. (kbuchegg)


Lesenswert?

Gero schrieb:

Und

> serialPort.DiscardInBuffer();

Discarden geht schon mal gar nicht.

von bluppdidupp (Gast)


Lesenswert?

Ich würde Protokolle die irgendwo auf Timing beruhen lieber sein lassen, 
sofern das nicht unbedingt nötig ist.
<STX><MSG-ID><PAYLOAD-LENGTH><PAYLOAD><CRC><ETX> (oder ne Teilmenge 
davon) funktioniert immer ganz gut.

von Gero (Gast)


Lesenswert?

nochmal hallo!
ein eindeutiges Endzeichen habe ich leider nicht
in den 5 Bytes ist ein 3 Byte Wert enthalten welcher von 0x00 bis 
0xffffff jeden Wert annehmen kann. somit fällt die Reservierung für ein 
Endzeichen flach :-(


Ich verstehe aber nicht wieso ich zu doof dazu bin exakt 5 Bytes 
einzulesen, ich versuche es jetzt mal ohne Event sondern einfach senden 
und dann 100ms warte bis ich den buffer wieder auslese.

Die sendPort kann ich wohl nicht so konfigurieren dass die gesendeten 
Daten nochmals im Rx-Buffer landen, oder?

von Gero (Gast)


Lesenswert?

so, nochmal ich :-)
wenn ich es wie eben beschrieben mache, funktioniert es!

Ich verzichte jetzt auf den Event und gehe stattdessen nach dem senden 
in einen Thread.Sleep(100), dannach lese ich den Buffer aus.

Schade dass das mit dem Event nicht klappt, ich habe aber gestern noch 
ewig mit den Timoutzeiten hin und her gemacht und bin zu keinem 
zufriedenstellendem Ergebniss gekommen :-(


Anm.
Ich benötige ja eigentlich gar keinen Event, ich will nur ein paar 
Parameter aus einem µC lesen und diese visualisieren...
"Einfach warten" war gestern Abend die Idee von meiner Frau, die ist 
Erzieherin :o)

von Reinhard Kern (Gast)


Lesenswert?

Gero schrieb:
> Die sendPort kann ich wohl nicht so konfigurieren dass die gesendeten
> Daten nochmals im Rx-Buffer landen, oder?

Wozu sollte das gut sein, das wäre doch keine Kontrolle. Hardwaremässig 
geht das auch nicht, die Einstellung "Echo on" bei einem Terminal ist 
immer Softwaresache und beweist daher auch nicht, dass überhaupt was 
gesendet wurde.

Gruss Reinhard

von Uhu U. (uhu)


Lesenswert?

Ich habe für ähnliche Probleme bei der Kommunikation zwischen PocketPC 
und GPS-Maus über BT eine einfache Lösung gefunden:

Wenn die Kommunikation in Stocken gerät, wird der Serial-Port 
geschlossen und und neu geöffnet. Dann läufts reibungslos weiter, bis 
der nächste Aussetzer kommt.

Hier ist das entscheidende Code-Fragment:
1
                // GPS Device
2
                string Buffer = "";
3
                while (Buffer == "") {
4
                    try {
5
                        Buffer = Gps.ReadLine();
6
                    } catch {
7
                        Buffer = "";
8
                        Gps.Close();
9
                        Gps.Open();
10
                    }
11
                }

von Bernd (Gast)


Lesenswert?

Das liest sich alles wie übelste Frickellösungen.

Solche Konstrukte fallen doch schon beim kleinsten Windstoß zusammen.

von Uhu U. (uhu)


Lesenswert?

Bernd schrieb:
> Das liest sich alles wie übelste Frickellösungen.
>
> Solche Konstrukte fallen doch schon beim kleinsten Windstoß zusammen.

Behauptest du.

Die Praxis zeigt, daß das seit über 500 Betriebsstunden bestens 
funktioniert, im Gegensatz zu allen anderen Tricksereien, eine einmal 
entgleiste COM-Verbindung zu Bluetooth wieder flott zu bekommen.

Schulwissen, keine Praxis, aber großes Maul...

von Bernd (Gast)


Lesenswert?

Was ist wenn das Sleep(100) deutlich länger braucht als erwartet, weil 
Windows mal was anderes zu tun hat?

von bluppdidupp (Gast)


Lesenswert?

naja, beim GPS-Empfänger kann man es wohl überleben, wenn mal Daten 
verloren gehen ;D
Probleme liegen teilweise ja auch einfach an:
- miesen Treibern
- bei Bluetooth an Empfangsschwierigkeiten
- Fehler im Gerät zu dem verbunden wurde.
Im Fehlerfalle den Port zu schließen und einfach neu zu verbinden ist 
jedenfalls besser als den Nutzer der Software mit Fehlermeldungen oder 
Neustart-Prozeduren auf den Keks zu gehen.

von Uhu U. (uhu)


Lesenswert?

Bernd schrieb:
> Was ist wenn das Sleep(100) deutlich länger braucht als erwartet, weil
> Windows mal was anderes zu tun hat?

Aber immerhin kann man sich darauf verlassen, daß es nicht früher als 
100 ms nach dem Aufruf zurückkehrt - für ein 
Nicht-Echtzeit-Betriebssystem reicht das aus.

von Gero (Gast)


Lesenswert?

Bernd schrieb:
> Was ist wenn das Sleep(100) deutlich länger braucht als erwartet, weil
> Windows mal was anderes zu tun hat?

Nichts!
der sleep wartet einfach bis die Antwort des µC im Empfangsbuffer ist, 
wenn das jetzt meinetwegen 300ms sind, dann dauert es eben 300ms bis ich 
die Daten von meinem µC in der TextBox auf dem Bildschirm habe.

C# auf Windows 7 kann niemals Echtzeit sein, es ist vielmehr eine 
Visualisierung mit der ich Parameter aus einem Controller auslese und 
manipuliere.

Es wäre vermutlich schlechter wenn der Sleep nur 20ms dauern würde, dann 
habe ich nämlich das Problem dass mir 2 oder 3 Byte fehlen, was dann 
wieder mein komplettes Protokoll verhagelt...


Ich halte es im übrigen wie Uhu es auch sagt: lieber ein schmuddeliges 
Workaround das im codedesign nicht so schön ist, aber dafür dann 
effektiv und zuverlässig funktioniert ohne dass ich zig Fehlermeldungen, 
MessdageBoxen oder Programmabstürze bekomme.

Der verzicht auf den Event und das verwenden des Thread.sleep war die 
richtige Lösung, mein Progrämmchen aberbeitet flüssig und zuverlässig 
ohne einen einzigen Datengau!

...Und wenn die Software funktioniert, dann schaut da sowieso niewieder 
jemand in den code hinein....

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.