Forum: PC-Programmierung C# Serialport.write - Abfragen / Warten bis Senden erfolgt


von Alex B. (Firma: Ucore Fotografie www.ucore.de) (alex22) Benutzerseite


Lesenswert?

Hallo zusammen.

Ich erstelle eine SW für einen Raspberry Pi in C# (bzw. GTK# mit Mono). 
In der SW verwende ich die Serialport-Klasse. Das funktioniert auch 
soweit.

Meine Frage ist: Gibt es eine Möglichkeit festzustellen, wann 
Serialport.Write (oder .Writeline) "fertig" ist (d.h. "alle Bytes wurden 
aus dem Ausgangspuffer "herausgetaktet"")?

Der Grund der Frage: Ich verwende einen RS485-Transceiver im 
Halbduplex-Modus, d.h. nach dem Senden muss ich den Driver 
deaktivieren und den Receiver aktivieren. Dazu muss ich jedoch wissen, 
wann tatsächlich die Daten aus dem Port "herausgetaktet" wurden.

Zwei mögliche "Workarounds" sind mir schon eingefallen:
1) Eine "Wartezeit" nach .Write() einfügen (z.B. 500 ms), wobei das 
natürlich nicht "schön" ist (da nicht deterministisch).
2) Den Receiver dauerhaft eingeschaltet lassen und die Sende-Botschaft 
auf der Sender-Seite mitlesen und nach vollständiger Botschaft den 
Driver abschalten.

Was denkt ihr? Habt ihr das schon mal gemacht?
Eure Anregungen sind willkommen!
Viele Grüße,
Alex

von Peter II (Gast)


Lesenswert?

Alex B. schrieb:
> Habt ihr das schon mal gemacht?

ja, aber ohne Raspi. Ich habe einen USB-RS485 Konverter, der schaltet 
selbstständig ab, wenn er nichts mehr zu senden hat. Dabei musste ich 
nichts in C# beachten.

Ich bin mir gar nicht sicher ob das Betriebssystem überhaupt mitbekommt, 
wenn das letzte bit übertragen wurde (also wirklich die Hardware 
verlassen hat)

von Horst S. (Gast)


Lesenswert?

Was sagt denn die BytesToWrite-Eigenschaft? Springt die nicht auf 0, 
wenn der Puffer leer ist?

Wenn die nix sagt, kannst Du die Wartezeit vielleicht anhand der 
Baudrate etwas tunen, indem Du

Wartezeit = (Anzahl Bits/Byte * Anzahl zu sendender Bytes)/ Baudrate + 
Sicherheitsaufschlag

rechnest? (Hmm, ggf. müsste man wohl wissen, wie, wann und wie schnell 
beim Raspi die Daten vom Buffer des SerPort in den FIFO übertragen 
werden)

von bluppdidupp (Gast)


Lesenswert?

Kontrolle über die Hardware-Puffer/Treiber-Puffer hat man über die 
normale SerialPort-Klasse nicht, lediglich über die Windows-eigenen 
Puffer.

Im .NET Micro Framework, das eher für kleinere Geräte gedacht ist (aber 
vermutlich in Mono nicht verfügbar ist), hat die SerialPort-Klasse eine 
Flush()-Methode, die hier vermutlich brauchbar gewesen wäre ;D

...aber macht die "Wandlung" nach Halbduplex nicht normalerweise der 
Treiber (oder gar erst die Hardware) und in der Software hat man damit 
gar nichts zu tun?

von Arc N. (arc)


Lesenswert?

Alex B. schrieb:
> Hallo zusammen.
>
> Ich erstelle eine SW für einen Raspberry Pi in C# (bzw. GTK# mit Mono).
> In der SW verwende ich die Serialport-Klasse. Das funktioniert auch
> soweit.
>
> Meine Frage ist: Gibt es eine Möglichkeit festzustellen, wann
> Serialport.Write (oder .Writeline) "fertig" ist (d.h. "alle Bytes wurden
> aus dem Ausgangspuffer "herausgetaktet"")?
>
> Der Grund der Frage: Ich verwende einen RS485-Transceiver im
> Halbduplex-Modus, d.h. nach dem Senden muss ich den /Driver/
> deaktivieren und den Receiver aktivieren. Dazu muss ich jedoch wissen,
> wann tatsächlich die Daten aus dem Port "herausgetaktet" wurden.
>
> Zwei mögliche "Workarounds" sind mir schon eingefallen:
> 1) Eine "Wartezeit" nach .Write() einfügen (z.B. 500 ms), wobei das
> natürlich nicht "schön" ist (da nicht deterministisch).
> 2) Den Receiver dauerhaft eingeschaltet lassen und die Sende-Botschaft
> auf der Sender-Seite mitlesen und nach vollständiger Botschaft den
> Driver abschalten.
>
> Was denkt ihr? Habt ihr das schon mal gemacht?
> Eure Anregungen sind willkommen!
> Viele Grüße,
> Alex

Keine Ahnung wie das Mono implementiert. Im .NET-Framework hat der 
BaseStream die Methode Flush 1) die intern die Win32-Funktion 
FlushFileBuffers 2) aufruft. Zu FlushFileBuffers ist z.B. unter 
PurgeComm 3) zu finden: "Note, however, that FlushFileBuffers is subject 
to flow control but not to write time-outs, and it will not return until 
all pending write operations have been transmitted."

Kurzfassung: aSerialPort.BaseStream.Flush() sollte das Problem lösen.

1) 
http://referencesource.microsoft.com/#System/sys/system/io/ports/SerialStream.cs,825
2) 
https://msdn.microsoft.com/en-us/library/windows/desktop/aa364439%28v=vs.85%29.aspx
3) 
https://msdn.microsoft.com/de-de/library/windows/desktop/aa363428%28v=vs.85%29.aspx


Edith sagt: Mono macht da genau gar nichts, versucht noch nicht mal was 
zu tun
https://github.com/mono/mono/blob/88d2b9da2a87b4e5c82abaea4e5110188d49601d/mcs/class/System/System.IO.Ports/WinSerialStream.cs
bzw. unter Unices 
https://github.com/mono/mono/blob/master/mcs/class/System/System.IO.Ports/SerialPortStream.cs

: Bearbeitet durch User
von Läubi .. (laeubi) Benutzerseite


Lesenswert?

Alex B. schrieb:
> da nicht deterministisch
Seit wann ist ein fixes Delay "nicht deterministisch"?

: Bearbeitet durch User
von Alex B. (Firma: Ucore Fotografie www.ucore.de) (alex22) Benutzerseite


Lesenswert?

Läubi .. schrieb:
> Alex B. schrieb:
>> da nicht deterministisch
> Seit wann ist ein fixes Delay "nicht deterministisch"?

Vielleicht kenne ich mich mit Betriebssystemen nicht gut genug aus oder 
habe es einfach ungeschickt formuliert.

Meine Annahme ist es, dass die Zeit zwischen dem Aufruf der Methode 
SerialPort.Write() und dem tatsächlichen physikalischen heraustakten der 
Bits eine unbestimmte Zeit vergeht, deren Länge von verschiedenen, nicht 
zu beeinflussenden Umständen (z.B. CPU-Auslastung) abhängt. Wählt man 
eine beliebige Verzögerung ("Delay"), dann passt dies zwar vermutlich 
für "die meißten" Fälle, es ist aber nicht 100%-ig sicher, dass es 
immer klappt.

von Klaus (Gast)


Lesenswert?

Alex B. schrieb:
> Meine Frage ist: Gibt es eine Möglichkeit festzustellen, wann
> Serialport.Write (oder .Writeline) "fertig" ist (d.h. "alle Bytes wurden
> aus dem Ausgangspuffer "herausgetaktet"")?

Ganz einfach: Häng einen IO-Pin an die Leitung und zähl die 
Pegelwechsel, oder ...

Alex B. schrieb:
> Der Grund der Frage: Ich verwende einen RS485-Transceiver im
> Halbduplex-Modus

gib noch 20 Cent mehr aus und mach das Ganze Full-Duplex.

Eine dritte Möglichkeit wäre, einen IO-Pin als Enable-Pin zu verwenden: 
Die Gegenstelle setzt dann den Pin auf high, wenn alle angefragten Daten 
angekommen sind.

Gruß Klaus

von Alex B. (Firma: Ucore Fotografie www.ucore.de) (alex22) Benutzerseite


Lesenswert?

Danke für die Anregungen, Klaus!

Klaus schrieb:
> gib noch 20 Cent mehr aus und mach das Ganze Full-Duplex.

Ich denke das würde mehr werden, die ganze Verkabelung müsste dann zwei 
Leitungen mehr bekommen.

Klaus schrieb:
> Eine dritte Möglichkeit wäre, einen IO-Pin als Enable-Pin zu verwenden:
> Die Gegenstelle setzt dann den Pin auf high, wenn alle angefragten Daten
> angekommen sind.

Also quasi "flow control".

Ich werde es zunächst mit einem großzügigen Delay versuchen, da es in 
diesem Fall nicht auf eine besonders gute Latenz ankommt. Sollte ich 
feststellen, dass Datenverlust auftritt, dann werde ich es so versuchen, 
wie oben beschrieben: Auf Sender-Seite die eigenen, gesendeten Daten 
selbst mitlesen und erst nach erfolgreichem Senden den Treiber 
ausschalten.

Danke euch allen und viele Grüße,
Alex

von Georg (Gast)


Lesenswert?

Alex B. schrieb:
> es ist aber nicht 100%-ig sicher, dass es
> immer klappt.

Eine BS- und überhaupt softwareunabhängige Methode: einen retriggerbaren 
Monoflop an den Ausgang schalten, der ca. eine ganze Zeichen-Zeit nach 
der letzten Flanke den Treiber abschaltet. Das funktioniert zuverlässig 
bei bekannter Baudrate bzw. muss an diese angepasst werden.

Per Software liesse sich das zuverlässig nur im Low-Level-Treiber 
realisieren und man bräuchte einen zusätzlichen I/O dazu.

Georg

von Klaus (Gast)


Lesenswert?

Alex B. schrieb:
> Ich denke das würde mehr werden, die ganze Verkabelung müsste dann zwei
> Leitungen mehr bekommen.

Na wenn das so ist, dann würde ich natürlich auch eher auf die 
Bastellösung setzen...

von Georg (Gast)


Lesenswert?

Klaus schrieb:
> Die Gegenstelle setzt dann den Pin auf high, wenn alle angefragten Daten
> angekommen sind.

Das geht sinnvollerweise nur bei Punkt-zu-Punkt, nicht an einem Bus. In 
dem Fall kann man aber gleich Sende- und Empfangsleitungen getrennt 
führen und hat das ganze Problem nicht.

Jetzt kommt natürlich gleich das Argument, dass man dann 2 Drähte mehr 
braucht und nicht nur einen: solche Kabel bekommt man aber i.d.R. 
sowieso nur mit Leiterpaaren.

Georg

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.