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
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
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.
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...
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.
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?
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. !!!!
@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.
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!
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.
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.
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);