Forum: PC-Programmierung c# SerialPort verliert Daten


von Martin S. (maklin)


Lesenswert?

Hallo!

Ich habe mir eine Schaltung gebaut, welche einen FT232RL verwendet, um 
Daten vom Mikrocontroller an den PC zu senden. Dabei wird eine 
Geschwindigkeit von 1.000.000Bit/s (etwas überdimensioniert, aber für 
spätere Erweiterungen ggf. nötig) verwendet und c.a. 15.000 Byte pro 
Sekunden versendet.
Auf der PC-Seite soll nun ein Programm diese Daten eine Sekunde lang 
aufzeichnen und speichern. Dafür habe ich mir folgende (einfache) 
Programmierung überlegt, um die Daten zu speichern:
1
StringBuilder Empfangsdaten = new StringBuilder();
2
3
        private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
4
        {
5
            if (timer2.Enabled)
6
            {
7
                Empfangsdaten.Append(serialPort1.ReadExisting());
8
            }
9
        }
10
11
        private void timer2_Tick(object sender, EventArgs e)
12
        {
13
            timer2.Stop();
14
            timer2.Enabled = false;
15
            //...Werte die Empfangsdaten ab dieser Stelle aus, um die Übersichtlichkeit zu bewahren habe ich mir das mal geschenkt...
16
        }

Der Timer ist so eingestellt, dass er eine Sekunde abzählt. Die 
Einstellungen des SerialPorts sind natürlich passend zum FT232RL. Als 
Encoding verwende ich:
1
serialPort1.Encoding = Encoding.GetEncoding(28591);
um 8 bit Encoding zu bekommen.

Obwohl die serialPort1_DataReceived-Funktion recht einfach gehalten ist, 
fehlen Daten in dem Stringbuilder. Dabei ist der Punkt, wann die Daten 
fehlen, von der Größe des Eingangspuffers abhängig. Ich habe den 
Eindruck, dass die Funktion aufgerufen wird, während sie selbst noch in 
der Bearbeitung ist (wie bei Interrupts auf uCs). Dabei würde ich von 
meinem Rechner (mit einer vielfach größeren Taktrate als ein uC) 
erwarten, dass er die Funktion rechtzeitig abarbeitet.

Hat vielleicht einer von euch eine Idee, warum es nicht funktioniert und 
wie das Problem gelöst werden kann? Vielen Dank!

von Arc N. (arc)


Lesenswert?

Was ich hier bei einem Projekt gesehen hatte (Controller C8051F585 + 
FT232R, Baudrate 1.5 M) war, dass einige Parameter zusätzlich gesetzt 
werden mussten damit es stabil läuft (allerdings über die FTD2XX.dll):
1
// read timeout 1000 ms, write timeout 500 ms
2
FT_SetTimeouts(handle, 1000, 500);
3
// flush receive buffer timeout 2 ms
4
FT_SetLatencyTimer(handle, 2);
5
// transfer size for in/out transfers 256 bytes
6
FT_SetUSBParameters(handle, 256, 256);
Was man u.U. noch testen kann ist: Statt wie üblich ein Stopbit, zwei 
Stopbits nehmen...

von Peter II (Gast)


Lesenswert?

if (timer2.Enabled)
            {
                Empfangsdaten.Append(serialPort1.ReadExisting());
            }

das darfst du nicht machen. du musst immer die Daten abrufen. Wenn du 
sie nicht brauchst musst du sie nach den abrufen wegschmeissen.



> Als Encoding verwende ich:
> serialPort1.Encoding = Encoding.GetEncoding(28591);
> um 8 bit Encoding zu bekommen.
Geht es überhaupt um Strings oder geht es um Daten? Wenn es daten sind, 
dann verwende auch bitte keine String funktionen. (Read oder ReadByte).

von Martin S. (maklin)


Lesenswert?

@Arc Net: Die dll wäre durchaus auch eine interessante Lösung. Ich würde 
allerdings erstmal versuchen, ohne dll auszukommen und über bekannte 
Funktionen zu arbeiten. Aber falls ich doch die dll verwenden werde, so 
sind deine Hinweise schonmal sehr sinnvoll!

@Peter II: Ich möchte alle Daten verwenden. Ich war davon ausgegangen, 
dass sie durch das Abrufen auch automatisch gelöscht werden. Ist dies 
nicht so?

Es geht tatsächlich um Daten. Ich hatte mir von ReadExisiting erhofft, 
dass dadurch tatsächlich alles gelesen wird. Bei ReadByte hatte ich die 
Befürchtung, dass es zu komplex (Rechenzeit) wird.

von Peter II (Gast)


Lesenswert?

Martin S. schrieb:
> @Peter II: Ich möchte alle Daten verwenden. Ich war davon ausgegangen,
> dass sie durch das Abrufen auch automatisch gelöscht werden. Ist dies
> nicht so?
erst durch serialPort1.ReadExisting(); wird gelöscht. das rufst du aber 
nicht immer auf.

> Es geht tatsächlich um Daten. Ich hatte mir von ReadExisiting erhofft,
> dass dadurch tatsächlich alles gelesen wird. Bei ReadByte hatte ich die
> Befürchtung, dass es zu komplex (Rechenzeit) wird.
andersrum. Die Stringfunktionen machen eine Zeichsatzkonvertierung und 
sind damit langsamer.

von Martin S. (maklin)


Lesenswert?

Das SerialPortExisting wird im Grunde genommen während 1s regelmäßig 
aufgerufen, was danach am Port passiert ist mir erstmal egal. Ich habe 
nun einen Test gemacht mit ReadByte, und ich habe den Eindruck, noch 
weniger Daten zu bekommen. Auf jeden Fall findet der 1. Datenverlusst an 
der Stelle statt, wo er auch bei dem String stattfindet. Es kommen 
danach nochmal die gleiche Menge Daten wie vorher, und das war es dann.

von Peter II (Gast)


Lesenswert?

Martin S. schrieb:
> Ich habe nun einen Test gemacht mit ReadByte, und ich habe den Eindruck,
> noch weniger Daten zu bekommen.
zeigt doch mal bitte den code.

von Martin S. (maklin)


Lesenswert?

Es sieht jetzt so aus:
1
        List<int> eingang = new List<int>();
2
        private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
3
        {
4
            if (timer2.Enabled)
5
            {
6
                while(serialPort1.BytesToRead>0)
7
                {
8
                    eingang.Add(serialPort1.ReadByte());
9
                }
10
            }
11
        }

von Peter II (Gast)


Lesenswert?

Martin S. schrieb:
> Es sieht jetzt so aus:
müsste eigentlich passen, viele vergessen alle Daten abzurufen.

Kommen denn die Daten in einem anderen Programm vollständig an? Nicht 
das das Problem woanders liegt. Letzen war hier auch jemand der einen 
Defekten USB/Com adapter hatte wo Zeichen weggekommen sind.

von Martin S. (maklin)


Lesenswert?

Ich habe einen Test mit dem Terminal-Programm "HTerm" gemacht. Meines 
Erachtens fehlern hier keine Daten. Wie oben gesagt, ist der Punkt, wann 
die Daten in meinem eigenen Programm fehlen, von der Größe des Puffers 
abhängig, d.h. der Schnitt kann somit ja auch nicht durch die Elektronik 
verursacht sein (weil er dann ja Pufferunabhängig wäre).

von Peter II (Gast)


Lesenswert?

versuche mal nicht jedes Byte einzeln abzurufen. man kann auch alle 
bytes gleichzeitig lesen.
http://msdn.microsoft.com/de-de/library/ms143549(v=VS.80).aspx

von Arc N. (arc)


Lesenswert?

Martin S. schrieb:
> Ich habe einen Test mit dem Terminal-Programm "HTerm" gemacht. Meines
> Erachtens fehlern hier keine Daten. Wie oben gesagt, ist der Punkt, wann
> die Daten in meinem eigenen Programm fehlen, von der Größe des Puffers
> abhängig, d.h. der Schnitt kann somit ja auch nicht durch die Elektronik
> verursacht sein (weil er dann ja Pufferunabhängig wäre).

Schon ausprobiert was passiert, wenn die o.g. Einstellungen über den 
Gerätemanager gemacht werden (Anschlusseinstellungen -> Erweitert)?

von Martin S. (maklin)


Lesenswert?

Also eine Änderung der sogenannten "BM-Einstellung" (was ist das??) 
verschiebt die Fehlstelle etwas, aber löst das Problem nicht. Auch die 
Verwendung von Read anstelle von ReadByte bringt keinen Vorteil.
1
        private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
2
        {
3
            if (timer2.Enabled)
4
            {
5
                int bytes_lesen = serialPort1.BytesToRead;
6
                byte[] lesebuffer = new byte[bytes_lesen];
7
                serialPort1.Read(lesebuffer, 0, bytes_lesen);
8
                for (int i = 0; i < bytes_lesen; i++)
9
                {
10
                    eingang.Add(lesebuffer[i]);
11
                }
12
            }
13
        }

Ich glaub ich geh erstmal ins Bett, vielleicht läuft es ja morgen 
besser...^^

von Reinhard Kern (Gast)


Lesenswert?

Hallo,

strings sollte man grundsätzlich vermeiden, wenn man nicht ganz genau 
weiss, was der Compiler draus macht. Basic z.B. arbeitet so, dass beim 
Anhängen eines Zeichens an einen vorhandenen String für einen neuen 
String Speicher reserviert wird und der bisherige String plus das 
zusätzliche Zeichen dorthin kopiert wird (das ist glaube ich sogar eine 
der wenigen Sachen, die Bill Gates miterfunden hat). Klar dass das nicht 
so schnell geht.

Ich misstraue daher grundsätzlich Funktion wie append bei strings und 
schreibe lieber selber eine Bufferverwaltung mit einem Pointer, die ist 
bei jeder Grösse und Füllgrad gleich schnell.

Gruss Reinhard

von Reinhard Kern (Gast)


Lesenswert?

Hallo,

strings sollte man grundsätzlich vermeiden, wenn man nicht ganz genau 
weiss, was der Compiler draus macht. Basic z.B. arbeitet so, dass beim 
Anhängen eines Zeichens an einen vorhandenen String für einen neuen 
String Speicher reserviert wird und der bisherige String plus das 
zusätzliche Zeichen dorthin kopiert wird (das ist glaube ich sogar eine 
der wenigen Sachen, die Bill Gates miterfunden hat). Klar dass das nicht 
so schnell geht.

Ich misstraue daher grundsätzlich Funktionen wie append bei strings und 
schreibe lieber selber eine Bufferverwaltung mit einem Pointer, die ist 
bei jeder Grösse und Füllgrad gleich schnell.

Gruss Reinhard

von bluppdidupp (Gast)


Lesenswert?


von A.G. (Gast)


Lesenswert?

@ Reinhard Kern:
öhm Stringveraltung unter basic mag ja selber geschrieben gut sein ....
nunja c# kennt nicht mal mehr pointer
aber du hast recht, dass eine liste, in welcher die bytes erstmal 
abgespeichert werden, sinnvoller ist
dann könnte man per debugger auch nachsehen, ob dort alles angekommen 
ist

von Reinhard Kern (Gast)


Lesenswert?

A.G. schrieb:
> öhm Stringveraltung unter basic mag ja selber geschrieben gut sein ....
> nunja c# kennt nicht mal mehr pointer

Auf die Gefahr hin, dass du mich für einen ausgestorbenen Dinosaurier 
hältst, solche Bufferverwaltungen habe ich meistens in Assembler 
geschrieben, als Bestandteil des ohnehin nötigen Gerätetreibers, 
allerdings waren das meistens Mikroprozessoren und Embedded Systeme. Ich 
habe das zunächst auch ganz allgemein gemeint. Für einen PC geht das 
nicht so ohne weiteres, aber es gibt ja Arrays usw. je nach Sprache, 
d.h. einen Zugriff auf Buffer[i] gibt es auch ohne Pointertyp.

Allerdings richtet ja auch der Windowstreiber einen Buffer wählbarer 
Länge ein, warum nicht den benutzen? Dann kann eigentlich nichts 
verlorengehen.

Gruss Reinhard

von Martin S. (maklin)


Lesenswert?

Aus irgendeinem Grunde geht es jetzt recht gut. Ich hatte gestern noch 
Versuche mit der FTDI-DLL vom Hersteller gemacht (und es ging recht gut 
damit), allerdings geht es komischerweise jetzt auch in der 
Ursprungsversion. Warum auch immer, vielleicht brauchte ja die serielle 
Schnittstelle nach den ganzen Parameteränderungen etwas Ruhe über 
Nacht^^.
Wie auch immer, es klappt nun auch sehr schön über die Eigenschaftsseite 
von Arc Net sehr kleine Datenmengen zu bekommen. Leider kann ich in c# 
keine passende Einstellmöglichkeit finden, die das Einstellen im 
Eigenschaftsfenster der Seriellen Schnitstelle ersetzt. Es ist 
interessanterweise nicht ReadBufferSize, oder die Wahl in der 
Eigenschaftsseite hat höhere Priorität.
Unterschiede der aktuellen Variante zu den oberen sind auf jeden Fall, 
dass ich den Port erst ganz kurzfristig vor dem Benutzen öffne, und dass 
ich unmittelbar davor nochmal die Eigenschaftswerte setze (die 
eigentlich schon vorher für die serielle Schnittstelle im 
Eigenschaftsfenster gesetzt wurden). Ebenfalls wird der Port so schnell 
wie möglich wieder geschlossen.
Vielen Dank nochmal für die vielen Hinweise!

von Martin S. (maklin)


Lesenswert?

Nochmal ein kleiner Nachtrag: Es kamen doch nochmal dann und wann 
Fehler. Diese konnte ich aber loswerden, indem ich zu Beginn der Messung 
"ReadExisting" ausgeführte habe (ohne die Daten weiter zu benutzen). 
Dies ist sowieso sinnvoll, um sicherzustellen, dass tatsächlich keine 
Daten empfangen werden, die vor dem Startzeitpunkt gesendet wurden und 
bereits im Buffer sein könnten.

von Reinhard Kern (Gast)


Lesenswert?

Hallo,

dafür sollte es auch einen Befehl "flush" geben.

Gruss Reinhard

von Niffko _. (niffko)


Lesenswert?

Die SerialPort-Klasse beinhaltet für diesen Zweck die Methode 
DiscardInBuffer().


//Niffko

von R.Z. (Gast)


Lesenswert?

Pointer gibt es in C# sehr wohl. In der Bildverarbeitung wird sehr gerne 
darauf zurückgegriffen. Siehe auch das Schlüsselwort unsafe.

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.