Forum: PC-Programmierung Visual C# Express und SerialPort


von Andreas R. (blackpuma)


Lesenswert?

Schönen Abend!

Nachdem ich jetzt mal mein Dev Board fertig habe und ein bisschen 
herumspiele möchte ich Daten von meinem uC empfangen und Ausgeben. z.B. 
Temperatur in Feld 1 und Luftfeuchte in Feld 2. Dazu nutze ich 
SerialPort.ReadLine() von Visual C# (Ich kenne mich nicht so aus in C#). 
Kann mir das nur jemand erklären wie das genau abläuft? Die Daten werden 
ja im Puffer abgelegt und von dort werden dann die Daten gelesen oder? 
Nur wieviel Daten liest der Befehl ein? Wie kann ich diese Auswerten? 
Wie macht man das normalerweise?

Vielleicht kann mir das jemand in einfachen Worten erklären.

BG Andreas

von Sam .. (sam1994)


Lesenswert?

ReadLine() liest so viel ein bis eine neue Zeile beginnt. Ich glaub für 
den c# Serialport ist ein Return CR-LF also Dezimal 13 - 10. Das musst 
du dann senden.

Ich würde allerdings eher das Ereignis DataReceived zum empfangen 
nehmen.
In meinem Terminalprog hab ich das so gemacht:
1
static void sp_DataReceived(object sender, SerialDataReceivedEventArgs e)
2
{
3
            if (!pause)
4
            {
5
                Console.ForegroundColor = ConsoleColor.Green;
6
                if (show == false)
7
                    Console.Write(sp.ReadExisting());
8
                else
9
                {
10
                    string temp = sp.ReadExisting();
11
                    foreach (char c in temp)
12
                        Console.Write(c.ToString() +"[" +((int)c)+"]");
13
                }
14
                Console.ResetColor();
15
            }
16
}

Mit ReadExisting kannst du dann auslesen, was empfangen wurde, also im 
Puffer liegt.

von Thomas E. (thomase)


Lesenswert?

Andreas Riegebauer schrieb:
> Vielleicht kann mir das jemand in einfachen Worten erklären.

Nein!

Weil das ist nicht einfach.
Ich nehme mal an, du willst die Werte in einer WindowsForms-Anwendung in 
einer Textbox ausgeben.
Und jetzt beginnt dein Problem:

Andreas Riegebauer schrieb:
> (Ich kenne mich nicht so aus in C#).

Deine Applikation, das WindowsForms-Fenster, ist ein Thread. Und mit der 
Instanziierung der Serialport-Klasse wird ein weiterer Thread angelegt. 
Nun ist aber .Net threadsicher. D.h. man kann keine Daten direkt von 
einem in einen anderen Thread schreiben. Das lässt der Compiler zwar 
durchgehen, das Programm stürzt aber ab.

Das heisst natürlich nicht, daß man keine Daten, die mit serialport 
empfangen wurden, in eine Textbox schreiben kann.

Auch auf das Risiko hin, daß mir wieder einer vorwirft, ich mache hier 
auf dicke Hose, solltes du folgende Fragen mit ja beantworten können:

Ich weiss...
  ...was ein delegate ist
  ...wie man einen event anlegt und abfeuert
  ...was invokerequired bedeutet
  ...wie man ein callback anlegt und abfeuert

Dann können wir hier gerne weitermachen. Ansonsten wird das nichts.

mfg.

von Andreas R. (blackpuma)


Angehängte Dateien:

Lesenswert?

Hallo!

Hmhm... Ich weiß nicht ob ich die Sachen Verstehe aber ich habe bereits 
ein Programm das mir Daten von der Seriellen einließt und in eine 
TextBox ausgibt. Ich habe nur das Problem das mir anscheinend Daten 
verschluckt werden. Also er macht 3 mal ein Update von Feld 1, 1 mal von 
Feld 2 und 5 mal von Feld 3. Mein Programmcode kommt gleich und ein Bild 
von der Oberfläche ist auch dabei.

Vorher nur kurz zur Funktion des Puffers. Funktioniert das so wie im 
angehängten PDF?

Hier jetzt der Programcode den ich mir zusammengebastelt habe:
1
using System.Collections.Generic;
2
using System.ComponentModel;
3
using System.Data;
4
using System.Drawing;
5
using System.Linq;
6
using System.Text;
7
using System.Windows.Forms;
8
using System.IO.Ports;
9
10
namespace SimpleSerial_mit_EventHandler
11
{
12
    public partial class Form1 : Form
13
    {
14
        SerialPort sp = new SerialPort("com4", 9600, Parity.None, 8, StopBits.One);
15
        string test2;
16
        
17
        public Form1()
18
        {
19
            InitializeComponent();
20
        }
21
22
        /********************************************
23
         * 
24
         * Start des Empfangs
25
         * 
26
         *******************************************/
27
        private void button1_Click(object sender, EventArgs e)
28
        {
29
            try
30
            {
31
                sp.Open();
32
                sp.DataReceived += new SerialDataReceivedEventHandler(sp_datareceived);
33
                button1.Enabled = false;
34
                button2.Enabled = true;
35
            }
36
            catch (Exception ex)
37
            { MessageBox.Show(ex.ToString()); }
38
39
        }
40
41
        /********************************************
42
         * 
43
         * Auslesen des Puffers
44
         * 
45
         *******************************************/
46
        private void sp_datareceived(object sender, SerialDataReceivedEventArgs args)
47
        {
48
            test2 = sp.ReadLine();
49
            sp.DiscardInBuffer();
50
            this.Invoke(new EventHandler(DisplayText));
51
        }
52
53
54
        /********************************************
55
         * 
56
         * Auswertung und Ausgabe der Messwerte
57
         * 
58
         *******************************************/
59
        private void DisplayText(object sender, EventArgs e)
60
        {
61
            if (test2.IndexOf("CH5") != -1)
62
            {
63
                int c = 0;
64
                test2 = test2.Remove(0, 3);
65
                try
66
                {
67
                    c = Convert.ToInt32(test2.Trim());
68
                    textBoxCH1.Text = String.Format("{0,7:0.00}", c / 1000.0);                    
69
                }
70
                catch (OverflowException)
71
                {
72
                    MessageBox.Show("{0} is outside the range of the Int32 type." + test2);
73
                }
74
                catch (FormatException)
75
                {
76
                    MessageBox.Show("The {0} value '{1}' is not in a recognizable format." +
77
                                      test2.GetType().Name + test2);
78
                } 
79
            }
80
81
            if (test2.IndexOf("CH6") != -1)
82
            {
83
                int c = 0;
84
                test2 = test2.Remove(0, 3);
85
                try
86
                {
87
                    c = Convert.ToInt32(test2.Trim());
88
                    textBoxCH2.Text = String.Format("{0,7:0.00}", c / 1000.0);
89
                }
90
                catch (OverflowException)
91
                {
92
                    MessageBox.Show("{0} is outside the range of the Int32 type." + test2);
93
                }
94
                catch (FormatException)
95
                {
96
                    MessageBox.Show("The {0} value '{1}' is not in a recognizable format." +
97
                                      test2.GetType().Name + test2);
98
                }
99
            }
100
            if (test2.IndexOf("CH7") != -1)
101
            {
102
                int c = 0;
103
                test2 = test2.Remove(0, 3);
104
                try
105
                {
106
                    c = Convert.ToInt32(test2.Trim());
107
                    textBoxCH3.Text = String.Format("{0,7:0.00}", c / 1000.0);
108
                }
109
                catch (OverflowException)
110
                {
111
                    MessageBox.Show("{0} is outside the range of the Int32 type." + test2);
112
                }
113
                catch (FormatException)
114
                {
115
                    MessageBox.Show("The {0} value '{1}' is not in a recognizable format." +
116
                                      test2.GetType().Name + test2);
117
                }
118
            }
119
        }
120
121
        /********************************************
122
         * 
123
         * Stop des Empfangs
124
         * 
125
         *******************************************/
126
        private void button2_Click(object sender, EventArgs e)
127
        {
128
            sp.Close();
129
            button1.Enabled = true;
130
            button2.Enabled = false;
131
        }
132
    }
133
}

BG
Andreas

von Thomas E. (thomase)


Lesenswert?

Andreas Riegebauer schrieb:
> private void sp_datareceived(object sender, SerialDataReceivedEventArgs args)
>
>         {
>
>             test2 = sp.ReadLine();
>
>             sp.DiscardInBuffer();
>
>             this.Invoke(new EventHandler(DisplayText));
>
>         }

Das Problem scheint mir folgendes zu sein:
Wenn Daten vorhanden sind, sagen wir mal 2 Byte, feuert der sp den 
Event.
Die Daten werden ausgelesen und angezeigt. Bevor du diese Daten 
überhaupt nur siehst, kommt aber schon der nächste Event, womit 
letztlich diese Daten überschrieben werden.

Ändere mal folgende Zeile:
textBoxCH1.Text = String.Format("{0,7:0.00}", c / 1000.0);  in
textBoxCH1.Text += String.Format("{0,7:0.00}", c / 1000.0);

zieh' die Textbox etwas länger, damit alles reinpasst.
Dann siehst ob deine Daten irgendwo sonsthin gehen oder ob ich mit 
meiner Vermutung richtig liege.

mfg.

von Andreas R. (blackpuma)


Lesenswert?

Hallo!

Ich werde es zu hause versuchen nachdem ich meinen uC nicht bei der Hand 
habe. Funktioniert das mit Readline aber wie in meinem PDF skizziert?

Wie wird das normalerweise gelöst? Das was ich habe wird ja nichts 
exotisches sein. Das wird es doch öfter geben das ein Datenstrom auf der 
Seriellen daher kommt der ausgewertet werden möchte.

BG
Andreas

von Thomas E. (thomase)


Lesenswert?

Noch was zu dem Buffer:



Warum löscht du den Buffer?
Den würde ich gar nicht anrühren. Denn wenn du die Daten liest und dann 
den Puffer löscht, weisst du nicht, ob in der Zwischenzeit schon neue 
Daten angekommen sind.

im Eventhandler:
statt:      test2 = sp.ReadLine();
            sp.DiscardInBuffer();  unnötig. Würde ich höchstens nach dem
            Öffnen machen, um noch evtl vorhandenen Schrott rauszu- 
schmeissen.
so:
            test2 = "";
            byte[] nRB = new byte[sp.BytesToRead];
            sp.Read(nRB, 0, sp.BytesToRead);
            for (int nInd = 0; nInd < nRB.Length; nInd++)
            {
               char chData = (char)nRB[nInd];
               test2 += chData.ToString();
            }

Das ist dann völlig Datentransparent und erwartet auf der 
Schnittstelleseite keine Endebedingung.

Die Daten solltest du dann auswerten, bevor du sie anzeigst. Und solange 
zwischenspeichern, bis alles da ist.

mfg.

von K.S. (Gast)


Lesenswert?

Wieso nimmst Du nicht die Komponente SerialPort aus der Toolbox ?
Da musst Du Dich um keine Threads kümmern, das macht die Komponente 
selbst.

Viele Grüße,
Klaus

von Andreas R. (blackpuma)


Lesenswert?

@K.S.

Das mit den Threads ist ja nicht das Problem. Das funktioniert ja 
soweit. Nur das er mir hin und wieder was verschluckt.

Ich würde halt gerne einen ganzen Datensatz lesen also bis zum NewLine 
um das auf einmal zu verarbeiten. Ich weiß aber eben nicht ob das so 
gelesen wird wie ich es skizziert habe. wenn es so ist dann sollte es 
doch funktionieren.

Das mit dem Löschen des Buffers weiß ich auch nicht wieso ich das 
drinnen habe. Muss ein Überbleibsel sein aus dem Code von dem ich das 
entnommen habe.

Wie kann ich das Abfeuern des DataReceived steuern? Also das er z.B. nur 
anspringt wenn NewLine gekommen ist?

BG
Andreas

von Arc N. (arc)


Lesenswert?

K.S. schrieb:
> Wieso nimmst Du nicht die Komponente SerialPort aus der Toolbox ?
> Da musst Du Dich um keine Threads kümmern, das macht die Komponente
> selbst.

Das ist die gleiche Komponente bei der die gleiche Vorgehensweise beim 
Updaten von UI-Elementen nötig ist.

> Viele Grüße,
> Klaus

von Sam .. (sam1994)


Lesenswert?

Thomas Eckmann schrieb:
> Deine Applikation, das WindowsForms-Fenster, ist ein Thread. Und mit der
> Instanziierung der Serialport-Klasse wird ein weiterer Thread angelegt.
> Nun ist aber .Net threadsicher. D.h. man kann keine Daten direkt von
> einem in einen anderen Thread schreiben. Das lässt der Compiler zwar
> durchgehen, das Programm stürzt aber ab.

Man kann die Threadüberorüfung abschalten und dann wedern keine 
Ausnahmen mehr ausgelöst. Ich habe fast noch nie so korrekt wie du es 
beschreibst programmiert, und trotzdem hatte ich nie Probleme. Und Daten 
von einen Thread in einen anderen gehen eben doch! Man setzt 
CheckForIllegalCrossThreadCalls auf false.

Thomas Eckmann schrieb:
> Ich weiss...
>   ...was ein delegate ist
>   ...wie man einen event anlegt und abfeuert
>   ...was invokerequired bedeutet
>   ...wie man ein callback anlegt und abfeuert

Ja delegate musst ich schon benutzen
Event auch
Invoke habe ich glaube ich nur einmal gebraucht.
Und Callback hab ich noch nie benutzt

Und hab Treadübergreifende Sachen alle ohne Events gemacht. Das 
funktioniert wuderbar.

von Thomas E. (thomase)


Lesenswert?

Andreas Riegebauer schrieb:
> Wie kann ich das Abfeuern des DataReceived steuern? Also das er z.B. nur
>
> anspringt wenn NewLine gekommen ist?

gar nicht. Der liest ja nicht mit, sondern schiebt nur durch. Die 
Auswertung muß nachher erfolgen.

mfg.

von Thomas E. (thomase)


Lesenswert?

Samuel K. schrieb:
> Man kann die Threadüberorüfung abschalten und dann wedern keine
>
> Ausnahmen mehr ausgelöst. Ich habe fast noch nie so korrekt wie du es
>
> beschreibst programmiert, und trotzdem hatte ich nie Probleme. Und Daten
>
> von einen Thread in einen anderen gehen eben doch! Man setzt
>
> CheckForIllegalCrossThreadCalls auf false.

Das kann man natürlich machen. Aber funktioniert das morgen auch noch?
Also ich meine jetzt nicht Samstag, sondern z.B. mit Visual Studio 2012 
mit Service Pack 2 von Windows 8.
Dann konvertiert man das und guckt nur noch blöd.
Damals mit Visual Studio 2003 und Framework 1 war das alles überhaupt 
kein Problem. Dann kam VS 2005 und Framework 2 und Schluss mit lustig.

mfg.

von Thomas E. (thomase)


Lesenswert?

Samuel K. schrieb:
> Ja delegate musst ich schon benutzen
>
> Event auch
>
> Invoke habe ich glaube ich nur einmal gebraucht.
>
> Und Callback hab ich noch nie benutzt

Bei der richtigen Implementierung sind wir noch gar nicht. Bislang ist 
das noch etwas halbgar.

mfg.

von Sam .. (sam1994)


Lesenswert?

Thomas Eckmann schrieb:
> Damals mit Visual Studio 2003 und Framework 1 war das alles überhaupt
> kein Problem. Dann kam VS 2005 und Framework 2 und Schluss mit lustig.
>
> mfg.

ICh hab das immer mit NET2.0 gemacht. Aber heißt dass man braucht wenn 
man es richtig macht immer ein event?

von Thomas E. (thomase)


Lesenswert?

Samuel K. schrieb:
> ICh hab das immer mit NET2.0 gemacht. Aber heißt dass man braucht wenn
>
> man es richtig macht immer ein event?

Nein braucht man nicht.
Ich pack' das Ganze nur in ein Usercontrol und zieh das dann einfach in 
die Anwendung. Dann ist das mit drei Klicks fertig. Und dafür brauch ich 
den Event.
In dem Usercontrol kann ich dann auf bestimmte Zeichen triggern und die 
Daten, erst wenn sie komplett da sind, weiterreichen. So wie Andreas das 
ja auch gerne haben möchte.

mfg.

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.