Forum: PC Hard- und Software [vb.net] SerialPort Baudrate automatisch ändern


von schichtarbeiter (Gast)


Lesenswert?

Hallo, habe folgendes Problem:
Ein Gerät wird per RS232 an den PC angeschlossen.

Will die Software sich mit dem Gerät verbinden, erwartet dieses einige 
Zeilen, die mit Baudrate 56000 gesendet werden müssen.

Dann wird der Port geschlossen, und mit Baudrate 9600 final geöffnet. 
Dann folgt eine weitere Zeile und die Verbindung steht endlich.

Nun hätte ich gerne einen einzigen Button ("Verbinden") der das 
erledigt.

Aktuell hänge ich an der Stelle mit der Baudrate Änderung
1
Port.Write ("Zeilen die zum verbinden mit B56000 gesendet werden müssen
2
Port.close
3
---ab hier funktioniert es nicht mehr, der Port ist ja nun geschlossen---
4
Port.open
5
Port.baurdate = 9600
6
Port.Write ("Zeile die zum verbinden final mit B9600 gesendet werden muss")

Bis zum
1
Port.close
 funktioniert es, aber dann will er den Port nicht mit der neuen 
Baudrate erneut öffnen sondern hört einfach auf.

Testweise hab ich stattdessen folgendes versucht:
1
Port.Write ("Zeilen die zum verbinden mit B56000 gesendet werden müssen
2
Port.close
3
---ab hier funktioniert es nicht mehr, der Port ist ja nun geschlossen---
4
Timer2.start

und Code vom Timer
1
Port.open 
2
Port.write ("Zeile die zum verbinden final mit B9600 gesendet werden muss")

So, damit funktioniert es tatsächlich, man sieht, wenn man den Port 
überwacht dass er erst alles mit Baudrate 56000 sendet, den Port 
schließt, mit Baudrate 9600 wieder öffnet und dann den Rest sendet, 
alles wie es soll.

Im gleichen Moment erscheint dann allerdings eine unhandeled expection 
"Der Anschluss ist geschlossen" - wahrscheinlich geht irgendwas zu 
schnell. Der Timer steht aktuell auf 5000ms, wenn ich diesen weiter 
erhöhe wird dem Gerät der Abstand zwischen der ersten Sequenz mit 
Baudarte 56000 und der zweiten mit Baudrate 9600 zu hoch, und es 
streikt.

Wie könnte man das lösen?
Danke

von Helmut -. (dc3yc)


Lesenswert?

schichtarbeiter schrieb:
> Port.baurdate = 9600

Indem man baudrate richtig schreibt? Ist ja kein Date eines Bauern wie 
bei "Bauer sucht Frau".

von Thomas W. (schichtarbeiter)


Lesenswert?

Helmut -. schrieb:
> Indem man baudrate richtig schreibt?

Gute Idee, das ist aber nur beim tippen des Beitrags passiert, im Code 
ist es richtig.

Ab
1
Port.close

passiert nichts mehr, das nachstehende
1
Port.open
 und alles weitere wird nicht ausgeführt.

von Sascha W. (sascha-w)


Lesenswert?

Vorm erneuten öffnen sollte evl. der Sendepuffer leer sein. I.d.R. gibts 
dafür sowas wie port.flush was bis zum vollständigen versenden wartet.
Und sollte man nicht erst die Baudrate setzen und dann den Port öffnen?!

Sascha

: Bearbeitet durch User
von c-hater (Gast)


Lesenswert?

schichtarbeiter schrieb:

> Port.close
> ---ab hier funktioniert es nicht mehr, der Port ist ja nun
> geschlossen---
> Port.open
> Port.baurdate = 9600
> Port.Write ("Zeile die zum verbinden final mit B9600 gesendet werden
> muss")

Und das machst du vermutlich in einem synchronisierten Ereignishandler 
von Port.Received oder unter Benutzung der synchronen Methoden des 
Serialport?

Dann kann das nicht sauber funktionieren. Das hinterläßt mit hoher 
Wahrscheinlichkeit (mindestens) eine Thread-Leiche und macht die 
Port-Instanz in der Folge komplett unbenutzbar.

Nunja, SerialPort wollte es allen "Nutzergruppen" einfach machen... Das 
Ergebnis ist allerdings eine Komponente mit sehr unsauberem 
Multithreading.

Man muss wissen, wie sie intern funktioniert, um diese Probleme richtig 
umschiffen zu können. Kein Problem: Der Quelltext ist öffentlich.

von Schlaumaier (Gast)


Lesenswert?

Ich habs noch nie mit der COM-Schnittstelle versucht.

Aber eine gute Methode um Res. wieder frei zu bekommen ist die DISPOSE 
-Methode.

https://docs.microsoft.com/de-de/dotnet/standard/garbage-collection/implementing-dispose

von Schlaumaier (Gast)


Lesenswert?

Kleiner Tipp am Rand.

Manchmal hilft es wenn man die Anweisungen in Subs verlegt.

Also in dein Beispiel

sub button_click
Call sub Sende_56000
Call sub Sende_9600
end sub

sub Sende_56000
 Port.Write ("Zeilen die zum verbinden mit B56000 gesendet werden müssen

 Port.close

 port.dispose '(falls er den Befehl nimmt)
end  sub


sub Sende_56000

 Port.Write ("Zeilen die zum verbinden mit B56000 gesendet werden müssen

 Port.close

 port.dispose '(falls er den Befehl nimmt)
end  sub


sub Sende_9600
 Port.Write ("Zeilen die zum verbinden mit B56000 gesendet werden müssen

 Port.close

 port.dispose '(falls er den Befehl nimmt)
end  sub


Davon abgesehen würde ich mir das da mal durchlesen.
https://docs.microsoft.com/de-de/dotnet/api/microsoft.visualbasic.devices.ports.openserialport?view=netframework-4.8#microsoft-visualbasic-devices-ports-openserialport(system-string-system-int32-system-io-ports-parity-system-int32-system-io-ports-stopbits)

von c-hater (Gast)


Lesenswert?

Schlaumaier schrieb:
> Ich habs noch nie mit der COM-Schnittstelle versucht.
>
> Aber eine gute Methode um Res. wieder frei zu bekommen ist die DISPOSE
> -Methode.
>
> 
https://docs.microsoft.com/de-de/dotnet/standard/garbage-collection/implementing-dispose

Das ist völlig daneben. Er will (und kann vermutlich) ja keine bessere 
SerialPort-Komponente schreiben, sondern nur die verfügbare auch für 
seinen speziellen Anwendungsfall zum Laufen bringen.

Und da hilft halt nur, die Schwächen der Vorhandenen zu erkennen, zu 
verstehen und zu umschiffen. Die Verwendung eines Timers ist dazu u.U. 
schon OK. Man muss es nur richtig machen. Das Problem ist: wie genau es 
richtig ist, hängt vom gesamten konkreten Code ab, der den SerialPort 
und seine Methoden benutzt und den hat der TO nicht geposted.

Also muss er sich's selber machen...

von Schlaumaier (Gast)


Lesenswert?

c-hater schrieb:
> Das ist völlig daneben. Er will (und kann vermutlich) ja keine bessere
> SerialPort-Komponente schreiben, sondern nur die verfügbare auch für
> seinen speziellen Anwendungsfall zum Laufen bringen.

Dispose ist ein normaler Befehl für ein Objekt.

Wenn ich was ich dauernd mache z.b. ein Bild lade, kann ich das nicht 
mehr löschen. Die einzige Methode die ich kenne ist, das ich das Bild in 
ein Temp-Bild im Speicher kopiere, dieses in das Picture-Objekt lade, 
und dann Original-Bild via DISPOSE aus den Speicher schmeiße.

Dispose ist nix andere wie eine Objekt-bezogene "Garbage Collection ". 
Einfach gesagt, mit den Befehl wird der Speicher (rund um das Objekt) 
und sämtliche Res. die das Objekt betreffen gelöscht.

In seinen Fall bedeutet das. Sollten noch Anweisungen "irgendwelche 
blockierten Speicherreste" da sein, so entfernt der Befehl sie mit 
PORT.DISPOSE . Also eine Art "objekt-bezogenes Reset".

Ich wende den Befehl sehr häufig an, um Reste zu entfernen. Das hält den 
Speicher sauber, und das Prg. schnell.

Die Automatik von MS ist bei allen MS-Sprachen dafür, nennen wir es mal 
freundlich : sehr schwach und bescheiden.

Und er muss garnix schreiben an Komponente.

Es reicht aus wenn er nach den Job (in sein Fall "machen was mit 54000 
Baud) den Port schließt und möglicherweise ein Dispose hinterher 
schickt.
Das macht er in einer SUB

Dann wechselt er in eine andere Sub, macht den Port wieder auf, und 
weiter gehts.

Ich habe unter VB auf diese Weise mal Testweise mit einen Arduino herum 
gespielt. Hat hervorragend funktioniert.

In mein 2. Beitrag steht übrigens wie man unter C !!!! den Port RICHTIG 
öffnet.  DA liegt nämlich das Hässchen im Pfeffer beim TO.

von c-hater (Gast)


Lesenswert?

Schlaumaier schrieb:

> Dispose ist ein normaler Befehl für ein Objekt.

"Befehl"? Methode! Klar. Nur hilft das im konkreten Fall eben nicht. Die 
Thread-Referenz wird dadurch zwar freigeben, aber der Thread selber 
bleibt weiter existent (natürlich inaktiv). Diese Instanz ist verbrannt.

Man könnte nun eine neue (für denselben logischen COM-Port!) aufmachen 
und diese dann tatsächlich auch benutzen. In diesem Fall hakelt es dann 
erst beim Beenden der Anwendung. Die läßt sich nicht beenden, weil das 
der verwaiste tote Thread der alten Instanz verhindert. Der macht zwar 
absolut nix mehr, außer zu existieren. Aber das reicht.

Das willst du? Ernsthaft?

von Schlaumaier (Gast)


Lesenswert?

c-hater schrieb:
> Man könnte nun eine neue (für denselben logischen COM-Port!) aufmachen
> und diese dann tatsächlich auch benutzen. In diesem Fall hakelt es dann
> erst beim Beenden der Anwendung. Die läßt sich nicht beenden, weil das
> der verwaiste tote Thread der alten Instanz verhindert. Der macht zwar
> absolut nix mehr, außer zu existieren. Aber das reicht.

NAja.

Es ist in VB/VC durchaus üblich, alle paar Sekunden neue Instanzen auf 
zumachen.

Dim S_Port As New IO.Ports.SerialPort

sub senden
If S_Port.IsOpen Then
      S_Port.Close()
    End If
    S_Port.PortName = "COM10"
    S_Port.BaudRate = 9600
    S_Port.Open()
    tx$ = hp_e_text_senden.Text + Chr(13)
    S_Port.Write(tx$)
    S_Port.Close()
end sub

das ist zwar VB , ist aber 1:1 auf VC anzuwenden (nur andere Syntax, zu 
viel Klammern und ; )

Ist aus mein Test-Prg. "ich sende mit meinen Nano Daten an einen anderen 
Nano"  PC -> Nano 1 -> via 433 mhz an Nano 2 -> Usb an PC 2.

Hat cool geklappt. ;)
Hab gerade geschaut. S_Port.Dispose  funktioniert.

Kleiner Hinweis an den TO.

*ERSTE DIE PARAMETER SETZEN, DANN DEN COM-PORT öffnen.  NICHT ANDERS 
HERUM *
Man kann die Parameter eines aktiven Port nicht ändern. !!!!

von Schlaumaier (Gast)


Lesenswert?

@to

Kleiner Nachtrag:

Verstell mal deine Debug-Optionen.

Dann weißt du oft schon warum es nicht funktioniert.

Und zwar die "AUSNAHMEN". Stell sie so ein, das er KEINE Ausnahmen 
zulässt.

Ist beim Entwickeln viel besser als die Standart-Vorgaben.

von Thomas W. (schichtarbeiter)


Lesenswert?

Danke erstmal an alle für die Antworten.

Erstmal: Der eine Fehler lag wo anders...Die Anwendung hat einen 
weiteren Timer, der beim öffnen der Form gestartet wird und fürs 
einlesen zuständig ist, also was vom Gerät zurück kommt. Dieser Vorgang 
begann schon beim öffnen der Form, dadurch, dass dann beim ändern der 
Baudrate der Port aber nochmal geschlossen wird, gibts dann durch diesen 
Timer natürlich einen Fehler...das hab ich jetzt geändert.


Das eigentliche Problem hab ich mangels vernünftiger VB Kenntnisse auf 
die Idioten-Art gelöst...nicht besonders logisch, aber für meine Zwecke 
ausreichend:

Die erste Sequenz mit Baudrate 56000 wird durch SerialPort1 
erledigt...dann wieder dieser ja geschlossen. Aber statt selbigen dann 
mit Baudarate 9600 wieder zu öffnen, übernimmt ab hier Serialport!2!, 
welcher genau wie Port1 den physischen COM Port COM1 des PC's benutzt. 
Das scheint soweit erstmal zu funktionieren. Irgendwo her bekomme ich 
jetzt noch die Meldung "der Anschluss ist bereits geöffnet" - ich muss 
das ganze nochmal durchgehen, aber ansich scheint es so zu 
funktionieren.

Danke!

von Schlaumaier (Gast)


Lesenswert?

Thomas W. schrieb:
> der Anschluss ist bereits geöffnet"

Das ist ein reiner Code-Ablauf-Fehler von DEINEN Programm.

Das MS-Zeug meldet "fehler beim Zugriff auf den Port/die Datei" wenn 
er/sie durch ein anderes Prg. blockiert ist.

Egro blockierst du dich selbst.

Irgendwo verlässt du ne Sub OHNE den Port zu schließen.

Mach mal mit f5 ein Schritt für Schritt Ausführung. Dabei merke die den 
Zustand des Ports. Als ob auf oder zu.

Passiert mir oft wenn ich eine Abbruchbedingung programmiere, und nur 
unten in der Sub den Port schließe. Bei mir ist es aber meist sql.close 
was ich vergesse. ;). Ist aber technisch die selbe Fehlermeldung in 
anderer Form.

Hinweis. Offene Ports/Dateien sind FORM-Übergreifend. Deren Zustand gilt 
für das ganze Prg.

von Thomas W. (schichtarbeiter)


Lesenswert?

Thomas W. schrieb:
> Irgendwo her bekomme ich
> jetzt noch die Meldung "der Anschluss ist bereits geöffnet"

Auch gelöst. Wenn ich den Port2 über einen Timer öffne...muss ich den 
Timer nach dem öffnen natürlich wieder stoppen, sonst versucht er alle 
xy Millisekunden den Port wieder zu öffnen.

von Schlaumaier (Gast)


Lesenswert?

Thomas W. schrieb:
> Auch gelöst.

Fein. Hab doch gesagt, Ablauf-Fehler ;)

von Pandur S. (jetztnicht)


Lesenswert?

Da die Baudrate bei offenem Port gesetzt wird, denke ich, das Schliessen 
und wieder oeffnen bringt nichts. Einfach offen lassen und die baud rate 
aendern. Allerdings sollte man sich vergewaertigen, dass das setzen 
einer neuen Baudrate nicht einfach das Baudrate register im Port neu 
beschreibt, obwohl das technisch genuegen wuerde. Der Prozess kann sich 
hinziehen, speziell wenn das Device seine eigene Vorstellung davon hat.
Also erst mal mit dem Oszilloskop testen.
 Setbaud(56k);
 Write(123)
 Setbaud(9600);
 Write(123);

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.