Guten Morgen Leute! Ich habe ein Problem. Und zwar sende ich Daten von einem Sensor auf einem Breakoutboard, der mit einem NUCLEOF411RE MC verbunden ist, per UART an meinen PC. Funktioniert auch alles richtig gut. Mit Putty kann ich mir die Daten anschauen die ankommen( siehe Bild ). Jetzt habe ich noch eine Anwendung in C# programmiert die mir den String aufteilt und in Textboxen anzeigt ( siehe Bild ). Das Funktioniert auch. Nur ist die Aktualisierung dieser werte ziemlich schlecht. Die neuen Werte sollten eigentlich die alten ziemlich schnell überschreiben so das es wie eine Echtzeitübertragung wirkt. Manchmal werden die Werte überhaupt nicht erneuert und manchmal mehrere hintereinander. Mein UART auf dem MC sowie mein UART in meiner C# anwendung ist auf eine BaudRate von 115200 eingestellt. Hat jemand vielleicht einen Tipp?
:
Verschoben durch User
Moin, gabs da nicht nen Bug in ner Version des SerialPort - DataReceiveEvent (falls du das nutzt) ? Grüße
H.Joachim S. schrieb: > Echte UART auf dem PC oder ein USB-Wandler? USB Wandler also einen Virtual COM Port
kabelklaus schrieb: > SerialPort - DataReceiveEvent > (falls du das nutzt) ? Ob es da einen Bug gibt weiß ich nicht. Bin neu in der C# und Applikation Programmierung. habe in der Form1.cs halt einen serialPort hinzugefügt und auf Buttonklick dann das hier. private void button1_Click(object sender, EventArgs e) { serial.Open(); try { while (true) { string A = serial.ReadLine(); char[] Leerzeile = new char[] { ' ', '\t' }; String[] Stringsizes = A.Split(Leerzeile); textBox1.Text = Stringsizes[0]; textBox2.Text = Stringsizes[1]; textBox3.Text = Stringsizes[2]; textBox4.Text = Stringsizes[3]; textBox5.Text = A; Task.Delay(500); } } catch (System.IO.IOException) { } }
Wie liest du die serielle Schnittstelle aus? Kannst du deinen Code zeigen?
Daniel -. schrieb: > Wie liest du die serielle Schnittstelle aus? > Kannst du deinen Code zeigen? string A = serial.ReadLine(); genau so eigentlich
Walt N. schrieb: > Daniel -. schrieb: >> Wie liest du die serielle Schnittstelle aus? >> Kannst du deinen Code zeigen? > > string A = serial.ReadLine(); > > genau so eigentlich Ja super. Und wann immer? Nutze ein Event. Das Problem liegt sicher nicht am ComPort oder an C# sondern an deinem Programm.
Readline habe ich nie benutzt. Ich habe read benutzt und die Daten in Buffer gesammelt. Anderes mal habe ich readexisting benutzt. Hast du versucht timeoutread kleiner zu setzen?
Ein While und darin ein Task.Delay sind natürlich sehr, sehr übel und gehören so nicht in eine C# Applikation. Du musst Dir unbedingt angewöhnen, mit Events zu arbeiten. Für Deinen konkreten Fall machst Du einen Event-Handler und meldest ihn beim SerialPort an (dieser Code wird automatisch erzeugt, wenn Du Dir das im Designer zusammenklickst):
1 | serialPort = new System.IO.Ports.SerialPort(); |
2 | serialPort.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler); |
Der Handler zum empfangen der Daten könnte etwa so aussehen:
1 | private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e) |
2 | { |
3 | int bytecount = ((SerialPort)sender).BytesToRead; |
4 | byte[] rxbuffer = new byte[bytecount]; |
5 | ((SerialPort)sender).Read(rxbuffer, 0, bytecount); |
6 | |
7 | // Im Array "rxbuffer" stehen nun die empfangenen Daten drin |
8 | } |
Erstmal würde ich sagen falsches Forum, zweitens: Nutz doch deinen alten Thread weiter? Ansonsten wurde alles gesagt, nutze Events, du kannst auch thresholds setzen und ganze Blöcke auslesen. Somit wird das ganze recht performant.
so ähnlich musst Du das machen
1 | SerialPort ComPort = new SerialPort(); |
2 | internal delegate void SerialDataReceivedEventHandlerDelegate( |
3 | object sender, SerialDataReceivedEventArgs e); |
4 | delegate void SetTextCallback(string text); |
5 | |
6 | string InputData = String.Empty; |
7 | string[] daten = new string[200000]; |
8 | int pointer = 0; |
9 | Dictionary<double,double> Datenreihe; |
10 | Series kurve; |
11 | Series kurve_soll; |
12 | |
13 | private void port_DataReceived_1(object sender, SerialDataReceivedEventArgs e) |
14 | {
|
15 | |
16 | InputData = ComPort.ReadExisting(); |
17 | if (InputData != String.Empty) |
18 | {
|
19 | this.BeginInvoke(new SetTextCallback(SetText), new object[] { InputData }); |
20 | }
|
21 | |
22 | |
23 | }
|
24 | private void SetText(string text) |
25 | {
|
26 | |
27 | if (text.StartsWith("+M")) |
28 | {
|
29 | string[] roh_daten = text.Split('\r'); |
30 | |
31 | int i = 0; |
32 | var bilgi = new Dictionary<double, double>(); |
33 | while(i<roh_daten.Length && roh_daten[i].StartsWith("+M")) |
34 | {
|
35 | /*
|
36 | string MS = roh_daten[i].Substring(3);
|
37 | string MM = roh_daten[i+1].Substring(3);
|
38 | string MT = roh_daten[i+2].Substring(3);
|
39 | */
|
40 | |
41 | double Zeitstempel = (Convert.ToDouble(roh_daten[i].Substring(3)) + (Convert.ToDouble(roh_daten[i + 1].Substring(3)) / 1000)); |
42 | double Temperatur = Convert.ToDouble(roh_daten[i + 2].Substring(3)); |
43 | |
44 | bilgi.Add(Zeitstempel, Temperatur); |
45 | |
46 | |
47 | i += 3; |
48 | }
|
49 | |
50 | Datenreihe = bilgi; |
51 | UpdateGraph(); |
52 | |
53 | }
|
54 | |
55 | if (text.StartsWith("+P")) |
56 | {
|
57 | |
58 | string[] roh_daten = text.Split('\r'); |
59 | textBox1.Text = roh_daten[0].Substring(3); |
60 | textBox2.Text = roh_daten[1].Substring(3); |
61 | textBox3.Text = roh_daten[2].Substring(3); |
62 | textBox4.Text = roh_daten[3].Substring(3); |
63 | |
64 | |
65 | }
|
66 | |
67 | if (text.Equals("_A")) |
68 | lbl_auto.Visible = true; |
69 | if (text.Equals("_M")) |
70 | lbl_auto.Visible = false; |
71 | |
72 | if (text.StartsWith(":I")) |
73 | lbl_Phase.Text = text.Substring(2); |
74 | |
75 | |
76 | |
77 | |
78 | |
79 | }
|
:
Bearbeitet durch User
Mit ReadLine() und ReadExisting() wäre ich sehr vorsichtig, denn das Problem damit ist, dass diese noch mit dem auf dem PC eingestellten Textencoding zusammenhängen (Codepage, UTF, etc.). Es kann dann passieren, dass Dein programmierter Textparser z.B. auf einem Deutschen Windows geht und auf einem Englischen nicht oder umgekehrt. Daher würde ich solche Daten immer unverfälscht mit Read() einlesen und danach erst das Bytearray mit dem passenden Encoding in einen String umwandeln. Für ASCII, wie es von einem Mikrocontroller kommt, so: System.Text.Encoding.ASCII.GetString(rxbuffer);
Die API kann ich Dir empfehlen. Kannst Du einfach per Nuget Package zu deinem Projekt adden. https://github.com/jcurl/serialportstream Beispiele: https://github.com/jcurl/SerialPortStream/wiki
Ich habe jetzt einen Event erstellt. Nur bin ich mir noch nicht so sicher wie das ganze funktioniert mit den Events. In diesem Eventhandler von dir lese ich quasi jedesmal ein und mehr nicht. Die Umwandlung in den String und die ausgabe an die Textboxen mache ich wohl eher nicht auch noch in dem Eventhandler? Wie komme ich jetzt außerhalb dieses eventhandlers an diesen rxbuffer um damit die umwandlungen zu machen und kontinuierlich die neuen werte an die textboxen weitergebe
Walt N. schrieb: > Ich habe jetzt einen Event erstellt. Nur bin ich mir noch nicht so > sicher wie das ganze funktioniert mit den Events. In diesem Eventhandler > von dir lese ich quasi jedesmal ein und mehr nicht. Die Umwandlung in > den String und die ausgabe an die Textboxen mache ich wohl eher nicht > auch noch in dem Eventhandler? Wie komme ich jetzt außerhalb dieses > eventhandlers an diesen rxbuffer um damit die umwandlungen zu machen und > kontinuierlich die neuen werte an die textboxen weitergebe Lern erst mal mal ein paar Grundlagen. Wie wär das?
Walt N. schrieb: > Ich habe jetzt einen Event erstellt. Nur bin ich mir noch nicht so > sicher wie das ganze funktioniert mit den Events. In diesem Eventhandler > von dir lese ich quasi jedesmal ein und mehr nicht. Die Umwandlung in > den String und die ausgabe an die Textboxen mache ich wohl eher nicht > auch noch in dem Eventhandler? Wie komme ich jetzt außerhalb dieses > eventhandlers an diesen rxbuffer um damit die umwandlungen zu machen und > kontinuierlich die neuen werte an die textboxen weitergebe Du hast es mundgerecht serviert bekommen, und kannst damit immer noch nichts anfangen? Wenn man einige Techniken nicht kennt, sollte man aber zumindestens Code lesen können, wenn man an sowas rangeht.... Geh Tee kochen
:
Bearbeitet durch User
> Die Umwandlung in > den String und die ausgabe an die Textboxen mache ich wohl eher nicht > auch noch in dem Eventhandler? auf jeden Fall ist der Event-Handler die Quelle deiner Daten und dann rufst du dort eben die Funktion welche die Daten weiter verarbeiten soll auf - ob du dann irgendwann darunter eine Textbox fuellst oder sonstiges machst ist völlig egal ganz wichtig: serielle Kommunikation ist kein Protokoll mit festen Datengrößen oder irgendwelchen Garantien sondern ein Stream wie TCP/IP wenn der Sender "hallo" schickt kann es sein das dein Event-Handler mehrfach aufgerufen wird 1. Event "ha" 2. Event "ll" 3. Event "o" und alle anderen Varianten - das MUSS deine weiter Verarbeitung können (denk nicht nur weil das jetzt so aussieht als wenn alles als Block kommt das es auch so bleibt - GRÖSSTER Fehler in der RS232 programmierung) und über RS232 gibt es keinen Protokollstandard - d.h. es ist total abhängig vom Sender wie die Verarbeitung aussehen muss - hast du eine Protokollbeschreibung? folge Varianten könnte es geben du schickst per RS232 ein Befehl und das Gerät antwortet (oder sendet z.B. auch ständig Daten): dabei kann es sein: -du kennst die notwendige Datenmenge aufgrund des abgesetzen Befehls (und wartes auf die Menge) -die ist irgendwo am Anfang der Antwort mit eingebettet (du merkst dir die Anzahl und wartest so lange) -es gibt eine Endekennung (Zeichenfolge), du wartest auf die Endekennung damit kannst du die Daten die per seriell kommen wieder in logische Pakete zerlegen Wenn du uns die Beschreibung zu dem RS32 Protokoll zeigst wäres es einfacher - oder einen Gerätenamen usw.
Walt N. schrieb: > Jetzt habe ich > noch eine Anwendung in C# programmiert die mir den String aufteilt und > in Textboxen anzeigt und
1 | private void button1_Click(object sender, EventArgs e) |
2 | { serial.Open(); |
3 | ...
|
4 | while (true) |
5 | { string A = serial.ReadLine(); |
6 | ...
|
7 | }
|
8 | ...
|
Dein Programm ist es, was den Mist baut. Nun hab ich mich bislang NICHT mit Cdoppelvollpfosten befaßt, aber so ganz allgemein macht man das etwa so: 1. man kreiert einen zweiten Thread, der rein prozedural läuft (also NICHT ereignisgesteuert) und der in sinnvollen Zeitabschnitten (20..50 ms) den seriellen Kanal (serial.xxxx...) pollt und bei Vorliegen von Empfangszeichen diese zu einer Art Kommandozeile, also einem String zusammenfaßt. Wenn diese Zeile voll ist (entweder ein Zeilenende erkannt oder eine Maximalanzahl von Zeichen drin), dann übergibt dieser Thread dem Hauptthread die Zeile. Entweder per Event oder wenn's geht, eben direkt, z.B. Refresh oder per Invalidate eines der Anzeigen oder so ähnlich. W.S.
>1. man kreiert einen zweiten Thread, der rein prozedural läuft (also >NICHT ereignisgesteuert) und der in sinnvollen Zeitabschnitten (20..50 >ms) den seriellen Kanal (serial.xxxx...) pollt und bei Vorliegen von >Empfangszeichen diese zu einer Art Kommandozeile, also einem String >zusammenfaßt. Äußerst unnötig verkompiliziert, warum sollte er dafür einen Thread brauchen - die Unabhängigkeit von seiner Konsole/UI ist doch schon durch den Event gegeben, du erweiterst das Push-Prinzip des Events noch mit einem pollenden Pull-Prinzip und machst das ganze dann auch noch gleich Data-Race-Condition freundlich Er ist doch schon genug am rudern da sind solche Tips nicht wirklich hilfreich
W.S. schrieb: > 1. man kreiert einen zweiten Thread, der rein prozedural läuft (also > NICHT ereignisgesteuert) und der in sinnvollen Zeitabschnitten (20..50 > ms) den seriellen Kanal (serial.xxxx...) pollt zu kompliziert
1 | private void port_DataReceived_1(object sender, SerialDataReceivedEventArgs e) |
2 | {
|
3 | |
4 | InputData = ComPort.ReadExisting(); |
5 | if (InputData != String.Empty) |
6 | {
|
7 | this.BeginInvoke(new SetTextCallback(SetText), new object[] { InputData }); |
8 | }
|
:
Bearbeitet durch User
Walt N. schrieb: > Ich habe jetzt einen Event erstellt. Nur bin ich mir noch nicht so > sicher wie das ganze funktioniert mit den Events. In diesem Eventhandler > von dir lese ich quasi jedesmal ein und mehr nicht. Die Umwandlung in > den String und die ausgabe an die Textboxen mache ich wohl eher nicht > auch noch in dem Eventhandler? Wie komme ich jetzt außerhalb dieses > eventhandlers an diesen rxbuffer um damit die umwandlungen zu machen und > kontinuierlich die neuen werte an die textboxen weitergebe Für einfache Dinge kannst die Verarbeitung und Ausgabe des Strings schon im Eventhandler erledigen. Der Computer erledigt das ja normalerweise im (geschätzt) Millisekundenbereich. Wie bereits erwähnt wurde, weisst Du nicht, wievielmal der Eventhandler aufgerufen wird, bis Deine Daten vollständig empfangen wurden; daher wirst Du noch einen Buffer brauchen, welcher alles empfangene sammelt. Das geht gut mit z.B. einer Liste:
1 | List<byte> empfangsbuffer = new List<byte>(); |
Im Eventhandler kannst Du dann einfach die empfangenen Daten zufügen:
1 | empfangsbuffer.AddRange(rxbuffer); |
2 | |
3 | // Nun auswerten ob Du ein komplettes Datenpacket hast und wenn ja, dieses verarbeiten und aus "empfangsbuffer" löschen. |
Da der Eventhandler asynchron von einem anderen Thread aufgerufen wird, kannst Du darin nicht einfach GUI-Elemente ansprechen, sonst stürtzt das Programm ab. In einem Beispiel oben findest Du die Lösung dafür mit "BeginInvoke"...
Ich würde lieber diese Eigenschaft nutzen: https://msdn.microsoft.com/de-de/library/system.io.ports.serialport.receivedbytesthreshold(v=vs.110).aspx Threshold so setzen, das Minimum ein Paket da ist, auslesen, fertig.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.