Forum: PC-Programmierung C# Probleme mit dem Einlesen seriellen Ports


von Steffen t. (steka)


Lesenswert?

Hallo,

ich arbeite seit einiger Zeit mit C# an einer Aufgabe zur Datenerfassung 
mehrerer serieller Schnittstellen. Das Einlesen einer einzigen seriellen 
Schnittstelle funktioniert sehr gut.

Hierzu nutze ich den DataReceveived-Handler und es werden nacheinander 
14 Bytes eingelesen, gespeichert, verarbeitet und auf einem Label 
angezeigt.

Nun zu meinem Problem:

Leider muss ich nicht nur einen seriellen Port auslesen, sondern fünf. 
Das Programm funktioniert weiterhin, allerdings brauchst das Programm 
bis zu 4 Sekunden, um das Label eines seriellen Ports zu aktualisieren. 
Das ist für meine Anwendung viel zu langsam.

Hier beispielhaft der Code für die Datenerfassung von nur zwei Ports:

Das Programm schaltet hierbei den seriellen Port 1 an und die 
Verarbeitung wird in den Unterprogrammen durchlaufen. Ist das geschehen, 
wird Port 1 geschlossen und Port 2 geöffnet. Ist die Verarbeitung dort 
abgeschlossen, wird Port 2 geschlossen und Port 1 wieder geöffnet.


// DATA RECEIVED1 
------------------------------------------------------------------------ 
---

        private void serialPort1_DataReceived(object sender, 
System.IO.Ports.SerialDataReceivedEventArgs e)
        {
                try
                {
                    if (erstesByte == 1)
                    {
                        do
                        {
                            ByteBack = serialPort1.ReadByte();
                        }
                        while (ByteBack > 32);

                        this.Invoke(new EventHandler(Fkt_CreateArray));

                        i++;

                        for (i = 1; i < 14; i++)
                        {
                            ByteBack = serialPort1.ReadByte();
                            this.Invoke(new 
EventHandler(Fkt_CreateArray));
                        }

                        this.Invoke(new EventHandler(Fkt_auswertung));

                        erstesByte = 0;

                    }

                    i = 0;

                    serialPort1.Close();
                    serialPort2.Open();

                }
                catch (System.TimeoutException)
                {
                    MessageBox.Show("Hallo?");
                }

            }

// DATA RECEIVED 2 
------------------------------------------------------------------------ 
---

        private void serialPort2_DataReceived(object sender, 
System.IO.Ports.SerialDataReceivedEventArgs e)
        {
                try
                {

                    if (erstesByte == 1)
                    {
                        do
                        {
                            ByteBack = serialPort2.ReadByte();
                        }
                        while (ByteBack > 32);

                        this.Invoke(new EventHandler(Fkt_CreateArray));

                        i++;

                        for (i = 1; i < 14; i++)
                        {
                            ByteBack = serialPort2.ReadByte();
                            this.Invoke(new 
EventHandler(Fkt_CreateArray));
                        }

                        this.Invoke(new EventHandler(Fkt_auswertung));

                        erstesByte = 0;

                    }

                    i = 0;

                    serialPort2.Close();
                    serialPort1.Open();

                }
                catch (System.TimeoutException)
                {
                    MessageBox.Show("Hallo?");
                }

        }

------------------------------------------------------------------------


Kann sich jemand vorstellen, warum das Programm so langsam ist? Ich 
komme absolut nicht weiter. Liegt es vielleicht an dem 
DataReceived-Handler? Ich wäre sehr dankbar, wenn mir jemand unter die 
Arme greifen könnte!

Gruß

steka

von Peter II (Gast)


Lesenswert?

Steffen Kauth schrieb:
> Leider muss ich nicht nur einen seriellen Port auslesen, sondern fünf.
> Das Programm funktioniert weiterhin, allerdings brauchst das Programm
> bis zu 4 Sekunden, um das Label eines seriellen Ports zu aktualisieren.
> Das ist für meine Anwendung viel zu langsam.

ist ja klar, dein Programmstuktur ist nicht brauchbar.

In einem DataReceivedEventArgs sollte man genau so viel Byte einlesen 
wie gerade im Buffer sind, nicht mehr und nicht weniger.

Dieser code ist also dort fehl am Platz:

 for (i = 1; i < 14; i++)
                        {
                            ByteBack = serialPort2.ReadByte();



Man könnte auch für jeden Port einen Thread Starten und einfach mit Read 
einlesen.

von Steffen t. (steka)


Lesenswert?

Danke für deine schnelle Antwort!

Von meiner Sendeeinheit werden kontinuierlich Daten gesendet. Muss ich 
also den Zählerstand der Buffer einlesen und ab 14 Bytes meine 
Unterprogramme aufrufen? Wie kann ich in diesem Fall auf die einzelnen 
Bytes zugreifen? Wenn ich die Bytes einlese, wird der Buffer dann wieder 
freigegeben?

Threading ist ein ganz neues Thema für mich. Was kann ich damit 
erreichen?

von Peter II (Gast)


Lesenswert?

Steffen Kauth schrieb:
> Von meiner Sendeeinheit werden kontinuierlich Daten gesendet. Muss ich
> also den Zählerstand der Buffer einlesen und ab 14 Bytes meine
> Unterprogramme aufrufen? Wie kann ich in diesem Fall auf die einzelnen
> Bytes zugreifen? Wenn ich die Bytes einlese, wird der Buffer dann wieder
> freigegeben?

ja, genauso.

dafür kann man einen liste nehmen:

list<Byte> buffer = new list<Byte>();

im Event machst du dann

int anzahl = Port.BytesToRead();
while ( anzahl ) {
   buffer.Add( Port.ReadByte() );
   anzahl--;
}

if (buffer.length >= 14) {
   //jetzt kannst du auf die Byte mit buffer[0..13] zugreifen

   //jetzt den buffer wieder leer machen
   buffer.Clear();
}


(code hat bestimmt viele Fehler, ist nur schnell getippt)

Wenn man es mit Thread macht, braucht man diese Events nicht. Das ist 
wie als ob dein Programm dann viele dinge gleichzeitig mache kann. Aber 
dabei muss man einiges beachten.

von Steffen t. (steka)


Lesenswert?

Ich werde es ausprobieren und berichten.

Meinst du, dass das Programm dadurch schneller wird oder liegt das 
Problem vielleicht an anderer Stelle?

von Peter II (Gast)


Lesenswert?

Steffen trinkt schrieb:
> Meinst du, dass das Programm dadurch schneller wird oder liegt das
> Problem vielleicht an anderer Stelle?

das Programm ist nicht langsam, es blockiert nur. Das liegt daran das du 
erst alle Daten einsammelst und in dieser zeit kann nichts anders 
Passieren. Ja ich denke das es nach dem umbau flüssiger läuft

von Steffen t. (steka)


Lesenswert?

Es funktioniert jetzt auch mit deiner Vorgehensweise, allerdings hat 
sich das Programmablauf nicht "beschleunigt". Trotzdem danke!

Bei Google habe ich etwas gefunden, was sich "DoEvents()" nennt. Könnte 
man dadurch eine Verbesserung erreichen?

von Peter II (Gast)


Lesenswert?

Steffen trinkt schrieb:
> Bei Google habe ich etwas gefunden, was sich "DoEvents()" nennt. Könnte
> man dadurch eine Verbesserung erreichen?

nein. Die Frage ist was langsam ist. Also wo hängt denn das Programm 
fest? Zeige noch mal den akuellen quellcode.

von jmp (Gast)


Lesenswert?

Hallo Steffen,

Warum machst Du die Ports jeweils wieder zu?
Wenn ein Port zu ist, dann empfängst du auf dem auch nichts.

--jmp

von Steffen t. (steka)


Lesenswert?

Ich schließe die Ports, da sie alle auf die gleiche 
Verarbeitungsfunktion zugreifen (Fkt_auswertung). Damit die Funktion 
nicht gleichzeitig durchlaufen wird und es zu Fehlern kommt, möchte ich 
die Ports nacheinander "durchschalten".

Vielleicht sollte ich einfach eine seperate Verarbeitungsfunktion für 
jeden einzelnen Port anlegen. Nicht sehr elegant, aber vielleicht 
funktioniert es.

von Peter II (Gast)


Lesenswert?

Steffen trinkt schrieb:
> Vielleicht sollte ich einfach eine seperate Verarbeitungsfunktion für
> jeden einzelnen Port anlegen. Nicht sehr elegant, aber vielleicht
> funktioniert es.

warum sollte eine Funktion nicht für viele Ports funktionieren?

Zeig uns doch mal den Quellcode, nur so können wie eventuell helfen. 
Keine kann wissen was du alles geändert hast.

von Steffen t. (steka)


Lesenswert?

Hier noch einmal der Ablauf für 2 serielle Ports mit Auswertung der 
Zahlenwerte, welche von Sendeeinheit kommen:


// DATA RECEIVED1
------------------------------------------------------------------------
---

        private void serialPort1_DataReceived(object sender,
System.IO.Ports.SerialDataReceivedEventArgs e)
        {
                int anzahl = serialPort1.BytesToRead;

            while (anzahl != 0)
            {
                buffer.Add(serialPort1.ReadByte());
                anzahl--;
            }

            if (buffer.Count >= 28)
            {
                // Suche erstes Byte in Buffer --> Wert muss kleiner 32 
sein!
                do
                {
                    if (buffer[lauf] <= 32)
                    {
                        erstesByteGefunden = true;
                    }
                    else
                    {
                        lauf++;
                    }
                } while (erstesByteGefunden == false);

                for (i = 0; i < 14; i++)
                {
                    ByteBack = buffer[lauf];
                    this.Invoke(new EventHandler(Fkt_CreateArray));

                    lauf++;
                }

                this.Invoke(new EventHandler(Fkt_auswertung));

                lauf = 0;
                i = 0;
                erstesByteGefunden = false;

                buffer.Clear();

                    serialPort1.Close();
                    serialPort2.Open();

                }
            }

// DATA RECEIVED 2
------------------------------------------------------------------------
---

        private void serialPort2_DataReceived(object sender,
System.IO.Ports.SerialDataReceivedEventArgs e)
        {
                {
                int anzahl = serialPort2.BytesToRead;

            while (anzahl != 0)
            {
                buffer.Add(serialPort2.ReadByte());
                anzahl--;
            }

            if (buffer.Count >= 28)
            {
                do
                {
                    if (buffer[lauf] <= 32)
                    {
                        erstesByteGefunden = true;
                    }
                    else
                    {
                        lauf++;
                    }
                } while (erstesByteGefunden == false);

                for (i = 0; i < 14; i++)
                {
                    ByteBack = buffer[lauf];
                    this.Invoke(new EventHandler(Fkt_CreateArray));

                    lauf++;
                }

                this.Invoke(new EventHandler(Fkt_auswertung));

                lauf = 0;
                i = 0;
                erstesByteGefunden = false;

                buffer.Clear();

                    serialPort2.Close();
                    serialPort1.Open();

                }
        }

// ERZEUGUNG ARRAY - SCHREIBE BYTES IN ARRAY 
------------------------------------------------------------------------ 
---------------

        private void Fkt_CreateArray(object serialPort1_DataReceived, 
EventArgs e)
        {
            array1[i] = ByteBack;
        }

// AUSWERTUNG DER EINZELNEN ARRAYWERTE 
------------------------------------------------------------------------ 
--------------------

        private void Fkt_auswertung(object s, EventArgs e)
        {
            // AUSWERTUNG ZAHL A

            switch (((array1[1] & 0x07) * 16) + (array1[2] & 0x0f))
            {
                case 125:
                    ZahlA = 0;
                    break;
                case 5:
                    ZahlA = 1;
                    break;
                case 91:
                    ZahlA = 2;
                    break;
                case 31:
                    ZahlA = 3;
                    break;
                case 39:
                    ZahlA = 4;
                    break;
                case 62:
                    ZahlA = 5;
                    break;
                case 126:
                    ZahlA = 6;
                    break;
                case 21:
                    ZahlA = 7;
                    break;
                case 127:
                    ZahlA = 8;
                    break;
                case 63:
                    ZahlA = 9;
                    break;
                default:
                    label_Messwert_1.Text = "WAIT";
                    break;
            }

            // AUSWERTUNG ZAHL B

            switch (((array1[3] & 0x07)) * 16 + (array1[4] & 0x0F))
            {
                case 125:
                    ZahlB = 0;
                    break;
                case 5:
                    ZahlB = 1;
                    break;
                case 91:
                    ZahlB = 2;
                    break;
                case 31:
                    ZahlB = 3;
                    break;
                case 39:
                    ZahlB = 4;
                    break;
                case 62:
                    ZahlB = 5;
                    break;
                case 126:
                    ZahlB = 6;
                    break;
                case 21:
                    ZahlB = 7;
                    break;
                case 127:
                    ZahlB = 8;
                    break;
                case 63:
                    ZahlB = 9;
                    break;
                default:
                    label_Messwert_1.Text = "WAIT";
                    break;
            }


            // AUSWERTUNG ZAHL C

            switch (((array1[5] & 0x07) * 16) + (array1[6] & 0x0F))
            {
                case 125:
                    ZahlC = 0;
                    break;
                case 5:
                    ZahlC = 1;
                    break;
                case 91:
                    ZahlC = 2;
                    break;
                case 31:
                    ZahlC = 3;
                    break;
                case 39:
                    ZahlC = 4;
                    break;
                case 62:
                    ZahlC = 5;
                    break;
                case 126:
                    ZahlC = 6;
                    break;
                case 21:
                    ZahlC = 7;
                    break;
                case 127:
                    ZahlC = 8;
                    break;
                case 63:
                    ZahlC = 9;
                    break;
                default:
                    label_Messwert_1.Text = "WAIT";
                    break;
            }


            // AUSWERTUNG ZAHL D

            switch (((array1[7] & 0x07) * 16) + (array1[8] & 0x0F))
            {
                case 125:
                    ZahlD = 0;
                    break;
                case 5:
                    ZahlD = 1;
                    break;
                case 91:
                    ZahlD = 2;
                    break;
                case 31:
                    ZahlD = 3;
                    break;
                case 39:
                    ZahlD = 4;
                    break;
                case 62:
                    ZahlD = 5;
                    break;
                case 126:
                    ZahlD = 6;
                    break;
                case 21:
                    ZahlD = 7;
                    break;
                case 127:
                    ZahlD = 8;
                    break;
                case 63:
                    ZahlD = 9;
                    break;
                default:
                    label_Messwert_1.Text = "WAIT";
                    break;
            }

            Fkt_createAnzeige();
        }


*****************************************************************

Ungefährer Ablauf ist also:

1. Öffne Port1 --> Schreibe Werte in Array1
2. Auswertung Array1 --> Fkt_createAnzeige gibt nur noch Werte auf 
jeweiliges Label aus
3. Schließe Port 1 --> Öffne Port 2
4. das gleiche nochmal mit Port2

Das große Problem der Vergangenheit: Wenn Port1 und Port2 aktiv sind 
wird Array1 teilweise mit Werten von POrt1 beschrieben und teilweise mit 
Werten von Port2!

Was haltet ihr von dem Ansatz alle Ports zu öffnen, dafür allerdings 
mehrere Verarbeitungsfunktionen mit unterschiedlichen Variablen zu 
schreiben?

Z.B.: Fkt_createArray_Port1 und Fkt_auswertung_Port1, 
Fkt_createArray_Port2 und Fkt_auswertung_Port2

von Peter II (Gast)


Lesenswert?

naja, der code ist noch recht umständlich.

1
for (i = 0; i < 14; i++)
2
                {
3
                    ByteBack = buffer[lauf];
4
                    this.Invoke(new EventHandler(Fkt_CreateArray));
5
6
                    lauf++;
7
                }

warum übergibt so jedes Byte einzeln über eine globale Variabel über 
eine Invoke Funktion?

fülle doch gleich das Array selber ab.

lauf wird auch erstmals verwendet ohne das man sieht woher es kommt. 
Muss lauf denn global sein?

der Funktion Fkt_auswertung kannst du doch einfach das Array mit den 
Daten übergeben.

Fkt_auswertung( array1 );
oder halt
Fkt_auswertung( array2 );

du hast mir zu viele Globale Variablen ( ZahlA, ZahlB ... ) du sollte 
die Variablen in einer Stukt oder Classen zusammenfassen. Dann kann man 
das Programm auf beliebig viele Ports gleichzeitig laufen lassen, ohne 
das man den code x mal schreiben muss.


DataReceived kann man auch für viele Ports gleichzeitig nutzen, dafür 
muss man dann den Sender Parameter auswerten.

Also mache eine neue Klasse wo folgenden drin ist

ComPort
BufferArray der Rowdaten
DatenArray mit den nutzdaten
ZahlA
Zahlb
...


damit kann man dann schon mal besser arbeiten und hat auch noch einen 
überblick.

von bluppdidupp (Gast)


Lesenswert?

Steffen trinkt schrieb:
> Bei Google habe ich etwas gefunden, was sich "DoEvents()" nennt. Könnte
> man dadurch eine Verbesserung erreichen?

"Keeping your UI Responsive and the Dangers of Application.DoEvents":
http://blogs.msdn.com/b/jfoscoding/archive/2005/08/06/448560.aspx

von Steffen t. (steka)


Lesenswert?

Vielen Dank super Tipps! Ich werde es heute umsetzen und erneut testen, 
ob es läuft!

von Steffen t. (steka)


Lesenswert?

Ich glaube, ich habe jetzt alles unter Kontrolle. Vielen Dank für eure 
Hilfe!!

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.