Forum: PC-Programmierung C# Serial Port Write unzuverlässig


von Kevin H. (ke_he)


Lesenswert?

Hallo zusammen,

ich habe folgendes Problem bei meiner C# Anwendung. (Ich bin blutiger 
Anfänger in der C# Programmierung)
Anwendung: Ich möchte einen STM32 Controller über UART Befehle senden 
und die Antwort mit einem Windows Programm auslesen.
Baudrate:115200
Zum testen habe ich mir ein Formular gebaut mit dem ich Befehle senden 
und die Antwort lesen kann.
Sende ich einen Befehl der nur ein Zeichen lang ist (z.b. "?\r") 
funktioniert alles wunderbar. Werden die Befehle länger verschluckt die 
Serielle Schnittstelle meistens den 4. Buchstaben.
Beispiel: Ich sende "Data p p\r" dann kommt am Controller nur "Dat p p" 
an. Bei erneutem Senden werden dann aber auch andere Zeichen 
verschluckt.
Verwende ich ein Terminal Programm wie TerraTerm funktioniert die 
Kommunikation tadellos.
StopBits, Databits und Baudrate sind richtig eingestellt. Das Lesen der 
Daten funktioniert auch soweit.
Ich habe auch mit einem Port Splitter und Port Monitor die Daten 
angefangen und da sehe ich ebenfalls das meistens das 4. Zeichen fehlt.
1
C# Code von deer Aktion beim Senden: 
2
private void Send_Click(object sender, EventArgs e)
3
        {
4
            int Laenge = Sendtext.Text.Length+1;// Sendetext.Text ist ein Textbox in meinem Formular
5
            string a  = Sendtext.Text + "\r";
6
            byte[] Command= new byte[Laenge];
7
            Command= Encoding.ASCII.GetBytes(a);
8
            if (serialPort2.IsOpen)
9
            {                
10
               serialPort2.Write(Command, 0, Command.Length);
11
                Console.WriteLine("Befehl gesendet");
12
            }
13
            serialPort2.BaseStream.Flush();
14
        }

Woran kann das liegen? Habt ihr einen Tipp?
Das Empfangen ist in einem gesonderten Thread ausgelagert.
Danke schon mal im Vorraus.

Gesamter C# Code:
1
using System;
2
using System.Collections.Generic;
3
using System.ComponentModel;
4
using System.Configuration;
5
using System.Data;
6
using System.Drawing;
7
using System.IO.Ports;
8
using System.Linq;
9
using System.Text;
10
using System.Threading;
11
using System.Threading.Tasks;
12
using System.Windows.Forms;
13
14
namespace PortChecker3
15
{
16
    public partial class Form_1 : Form
17
    {
18
        public Thread ReadSerialDataThread;
19
        public Thread ReadSerialDataThread2;
20
        public string readserialvalue;
21
        public string readserialvalu2;
22
        public Form_1()
23
        {
24
            InitializeComponent();
25
        }
26
        private void Form1_Load(object sender, EventArgs e)
27
        {
28
            serialPort1.Open();
29
            serialPort2.Open();
30
            ReadSerialData();
31
            ReadSerialData2();
32
            serialPort2.NewLine = "\r";
33
        }
34
        private void ReadSerialData()
35
        {
36
            try
37
            {
38
                ReadSerialDataThread = new Thread(ReadSerial);
39
                ReadSerialDataThread.Start();
40
            }
41
            catch (Exception e)
42
            {
43
                MessageBox.Show(@"Read Serial thread. " + e.Message);
44
            }
45
        }
46
        private void ReadSerial()
47
        {
48
            while (serialPort1.IsOpen)
49
            {
50
                if (serialPort1.BytesToRead > 3)
51
                {
52
                    readserialvalue = serialPort1.ReadExisting();
53
                    ShowSerialData(readserialvalue);
54
                }
55
                Thread.Sleep(100);
56
            }
57
        }
58
        private void ReadSerialData2()
59
        {
60
            try
61
            {
62
                ReadSerialDataThread2 = new Thread(ReadSerial2);
63
                ReadSerialDataThread2.Start();
64
            }
65
            catch (Exception e)
66
            {
67
                MessageBox.Show(@"Read Serial thread. " + e.Message);
68
            }
69
        }
70
        private void ReadSerial2()
71
        {
72
            while (serialPort2.IsOpen)
73
            {
74
                if (serialPort2.BytesToRead > 3)
75
                {
76
                    readserialvalu2 = serialPort2.ReadExisting();
77
                    ShowSerialData2(readserialvalu2);
78
                }
79
                Thread.Sleep(100);
80
            }
81
        }
82
        public delegate void ShowSerialDatadelegate(string r);
83
        public delegate void ShowSerialDatadelegate2(string r);
84
        private void ShowSerialData(string s)
85
        {
86
            //Console.WriteLine("Port1 empfangen:" + s);
87
            if (IncomeData_1.InvokeRequired)
88
            {
89
                ShowSerialDatadelegate SSDD = ShowSerialData;
90
                Invoke(SSDD, s);
91
            }
92
            else
93
            {
94
                IncomeData_1.AppendText(Environment.NewLine + s);
95
                IncomeData_1.ScrollToCaret();
96
            }
97
        }
98
        private void ShowSerialData2(string s)
99
        {
100
            Console.WriteLine("Port2 empfangen:" + s);
101
            if (IncomeData_2.InvokeRequired)
102
            {
103
                ShowSerialDatadelegate2 SSDD = ShowSerialData2;
104
                Invoke(SSDD, s);
105
            }
106
            else
107
            {
108
                IncomeData_2.AppendText(Environment.NewLine + s);
109
                IncomeData_2.ScrollToCaret();
110
            }
111
        }
112
        private void Send_Click(object sender, EventArgs e)
113
        {
114
            int Laenge = Sendtext.Text.Length+1;
115
            string a  = Sendtext.Text + "\r";
116
            byte[] Command= new byte[Laenge];
117
            Command= Encoding.ASCII.GetBytes(a);
118
            if (serialPort2.IsOpen)
119
            {                
120
               serialPort2.Write(Command, 0, Command.Length);
121
                Console.WriteLine("Befehl gesendet");
122
            }
123
            serialPort2.BaseStream.Flush();
124
        }
125
    }
126
}

von Armin K. (-donald-) Benutzerseite


Lesenswert?

Ich habe die Erfahrung gemacht, dass es besser ist nicht mit
1
serialPort2.ReadExisting()
 zu lesen, sondern mit
1
serialPort2.ReadLine()

Und davor natürlich
1
serialPort2.NewLine = "\r"
 oder was auch immer.

von N. M. (mani)


Lesenswert?

Zeig mal den STM Code.
Vielleicht ist auch da das Problem.

von Kevin H. (ke_he)


Lesenswert?

Hallo
Danke für die schnellen Antworten.
Ich habe es lösen können in dem ich jedes Zeichen einzeln sende:
1
 private void Send_Click(object sender, EventArgs e)
2
        {
3
            int Laenge = Sendtext.Text.Length;
4
            string[] Befehl = new string[Laenge + 1];
5
            for (int i=0; i< Laenge; i++)
6
            {
7
                Befehl[i] =Sendtext.Text.Substring(i,1);
8
            }
9
            Befehl[Laenge] = "\r";
10
            int counter4 = 0;
11
            if (serialPort2.IsOpen)
12
            {
13
                while (counter4<Befehl.Length)
14
                {
15
                    serialPort2.Write(Befehl[counter4]);
16
                    counter4++;
17
                }
18
                Console.WriteLine("Befehl gesendet");
19
            }
20
            serialPort2.BaseStream.Flush();
21
        }
Ich weiß nicht warum es jetzt klappt aber Hauptsache es funktioniert.
Den Code vom STM kann ich ja eigentlich ausschließen weil ich mit einem 
normalen Terminal Programm wie TerraTerm alle Funktionen einwandfrei 
funktionieren.
Trotzdem danke fürs Helfen.

Gruß

von N. M. (mani)


Lesenswert?

Kevin H. schrieb:
> Ich weiß nicht warum es jetzt klappt aber Hauptsache es funktioniert.

Dein PC Programm ist dadurch evtl. langsamer wodurch dein uC scheinbar 
besser mit den Daten zurechtkommt.

Kevin H. schrieb:
> Den Code vom STM kann ich ja eigentlich ausschließen weil

Kannst du eigentlich nicht. Vielleicht sendet TerraTerm die Zeichen auch 
Einzel. Oder ist aus einem anderen Grund langsamer.

Du schreibst leider nicht was für einen genauen Typ du verwendest. 
Vielleicht hat der STM einen Hardware FIFO o.ä. der nach 3-4 Zeichen 
voll ist.
Oder du wartest wo wo du nicht warten solltest. Das erklärt dann wieso 
du Zeichen verlierst.

von Rudis Bua (Gast)


Lesenswert?

Kevin H. schrieb:
> (Ich bin blutiger
> Anfänger in der C# Programmierung)

Ich diesem Fall ein ernstgemeinter Rat: Vergiss C# ganz schnell wieder 
und lern eine vernünftige Programmiersprache.

von Programmierer (Gast)


Lesenswert?

Rudis Bua schrieb:
> Kevin H. schrieb:
>
>> (Ich bin blutiger
>> Anfänger in der C# Programmierung)
>
> Ich diesem Fall ein ernstgemeinter Rat: Vergiss C# ganz schnell wieder
> und lern eine vernünftige Programmiersprache.

Ernst gemeinter Rat: Verzapf nicht so einen Blödsinn und lass den TO in 
Ruhe.

von W.S. (Gast)


Lesenswert?

Programmierer schrieb:
>> Ich diesem Fall ein ernstgemeinter Rat: Vergiss C# ganz schnell wieder
>> und lern eine vernünftige Programmiersprache.
>
> Ernst gemeinter Rat: Verzapf nicht so einen Blödsinn und lass den TO in
> Ruhe.

Vergiß du mal lieber deinen Einspruch. Der Punkt, auf den unsereiner 
hinaus will, ist, daß viele Leute sich irgendwelche "einfach" 
aussehenden Programmiersprachen aussuchen und dann ihre Probleme haben 
diese tatsächlich zu verstehen bzw. deren komplexe Bibliotheksfunktionen 
richtig anzuwenden.

Dann treten die eigentlichen Probleme in den Hintergrund und die Leute 
kämpfen erstmal nur gegen die Eigenheiten der Programmiersprache, 
anstatt sich mit den Dingen zu befassen, die sie egentlich tun wollen.

Gerade bei seriellen Kanälen machen die meisten Anfänger den Fehler, daß 
sie einen Ablauf wie beim Rumpelstilzchen annehmen: Heute back ich, 
morgen brau ich, übermorgen... na und so weiter. Sie bedenken nicht, daß 
sie eine Kommunikation zuwege bringen müssen zwischen zwei voneinander 
völlig unabhängigen Geräten. Da wird gedacht: "Wenn ich jetzt 'ottokar' 
sende, dann empfängt die andere Seite auch 'ottokar' und dann gibt sie 
mir auch die korrekte Antwort 'karlheinz'. Immer dieses Wenn-Dann wie 
beim Rumpelstilzchen. Und wenn dann sowas wie "Data p p\r" fest in die 
Quelldatei eines Programmes geschrieben wird, und man erwartet (bei 
110.5kBaud), daß sowas genau so ankommt, obwohl man sich vermutlich an 
keiner Seite um irgendwelche Synchronisation gekümmert hat, dann wird 
hier geklagt.

W.S.

von Programmierer (Gast)


Lesenswert?

W.S. schrieb:
> daß viele Leute sich irgendwelche "einfach"
> aussehenden Programmiersprachen aussuchen

Toll dass du so genau weißt was andere machen bzw. sich denken. C# ist 
eine mächtige moderne Sprache, welche in manchen Bereichen absolut das 
Mittel der Wahl ist. "Einfach" ist C# gewiss nicht, aber deshalb nicht 
als erste Programmiersprache ungeeignet. Der TO wird seine Gründe haben, 
die zu wählen.

W.S. schrieb:
> Dann treten die eigentlichen Probleme in den Hintergrund und die Leute
> kämpfen erstmal nur gegen die Eigenheiten der Programmiersprache,
> anstatt sich mit den Dingen zu befassen, die sie egentlich tun wollen.

Daher ist es sinnvoll, eine Sprache zu wählen, die nicht so viele 
hakelige Eigenheiten hat wie z.B. C. C# ist daher schon eine ziemlich 
gute Wahl. Andere Möglichkeiten wären Python oder Java.

W.S. schrieb:
> Gerade bei seriellen Kanälen machen die meisten Anfänger den Fehler, daß
> sie einen Ablauf wie beim Rumpelstilzchen annehmen

Das hat aber nichts mit der Programmiersprache zu tun. Wenn man sich 
jetzt zusätzlich noch mit der komplizierten Speicherverwaltung von C 
plagen müsste...

von W.S. (Gast)


Lesenswert?

Programmierer schrieb:
> Das hat aber nichts mit der Programmiersprache zu tun. Wenn man sich
> jetzt zusätzlich noch mit der komplizierten Speicherverwaltung von C
> plagen müsste...

Um einen seriellen Kanal des OS zu öffnen und dort Zeichen zu senden 
bzw. zu empfangen, braucht man keine "komplizierte Speicherverwaltung". 
Das was man tatsächlich braucht, ist etwas Nachdenken über das, was man 
zu tun beabsichtigt.

Und da kommt die verwendete Programmiersprache ins Spiel.

Ich sag's nochmal: Wenn die Aufmerksamkeit von jemandem vom eigentlichen 
Problem abgelenkt wird, weil er mit der verwendeten Programmiersprache 
kämpft oder wenn er irgendwelche komplexen Bibliotheksfunktionen 
benutzen muß, dann kommt am Ende sowas heraus, wie dieser Thread.

Das ist so und es hat keinen Zweck, wenn du hier C# über den grünen Klee 
lobst. Deine Einlassung geht nämlich zu 100% am eigentlichen Problem 
vorbei.

W.S.

von Programmierer (Gast)


Lesenswert?

W.S. schrieb:
> Um einen seriellen Kanal des OS zu öffnen und dort Zeichen zu senden
> bzw. zu empfangen, braucht man keine "komplizierte Speicherverwaltung"

Das ist sicherlich nicht das einzige was das Programm macht.

W.S. schrieb:
> Ich sag's nochmal: Wenn die Aufmerksamkeit von jemandem vom eigentlichen
> Problem abgelenkt wird,

Und wieso meinst du dass das bei C# mehr der Fall ist als bei anderen 
Sprachen? Und wie kommst du darauf dass der TO Probleme mit C# hat?

W.S. schrieb:
> Deine Einlassung geht nämlich zu 100% am eigentlichen Problem vorbei.

Deine auch. Meine Einlassung richtet sich nur gegen den anderen Unsinn.

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.