Hallo, ich komme da nicht weiter. Ich hab ein PiPico-Projekt, das über Stdtio und USB mit dem Windows-PC reden soll. Das Picoprogramm mach ich mit Vscode und einem 2.Pico zum debuggen. Das PC-Programm mit QT. Statt mit QT-Progr. teste ich auch mit einem PC-Terminal.
Im Terminal-Mode bekomme ich nach Start eine Verbindung mit Pico, alles läuft rund. Das Qt-Progr. meldet jedoch immer "Can't open serial". "serialPortInfos" zeigt aber immer alle verfügbaren Ports an.
Jetzt wird es lustig: Wenn ich gleichzeitig das Terminal öffne, ist der Port schon belegt. Also beende ich Terminal und QT. Bei nächsten öffnen des Terminals kommen dann sofort die Antworten vom Pico, die er vorher noch von QT empfangen hatte.
Wenn ich nun Terminal beende und QT starte, dann funktioniert QT wie soll.
Wie kann ich das Problem beseitigen?
Ich habe deinen Post drei mal gelesen und versuche immer noch herauszufinden, was genau du meinst.
In deinem Kopf erscheint das Sinn zu ergeben, wenn man nicht weiß, worum es genau geht, sieht das leider etwas schlechter aus.
Erwartet der Pico vielleicht einen Handshake? Ein "Clear to send" über (in deinem Fall virtuelle) RTS/CTS-Leitungen?
Das Terminal setzt dann vielleicht den Status dieser Leitungen, dein Qt aber nicht.
Erwartet der Pico vielleicht einen Handshake
Ich hab geschrieben: "über Stdtio und USB"
Da gibt es das nicht.
Ja, die Umstände sind verwirrend.
Kurz und knapp: Warum kann QT das nicht öffnen?
1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
COM8 gibt es natürlich. Und mit Terminal geht es ja auch. Nochmal dieser Ablauf:
1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
WAs ist zu tun, dass das in Zeile 3 funktioniert?
serial.setPortName("COM8");
Müssen die bei Windows nicht "COM8:" heißen? (Mit Doppelpunkt)
Warum setPortName und nicht setPort wie empfohlen? https://doc.qt.io/qt-6/qserialport.html#setPort
und über availablePorts() abfragen https://doc.qt.io/qt-6/qserialportinfo.html
Und wie im Beispiel https://doc.qt.io/qt-6/qtserialport-terminal-example.html den error string ausgeben.
Welche Einstellungen wurden im Terminal getätigt? Das eigene Programm scheint keine vorzunehmen.
Nun habe ich festgestellt:
Wenn ich den Pico einschalte dann zeigt mir Win11\cmd bei >mode das an:
1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
Nach starten von Terminal Putty und dem wiederbeenden ist der Status von DTR auf ON gestellt. Danach funktioniert auch die Verbindung zum QT-Programm.
DTR sollte keinen Einfluss darauf haben, ob ein Programm den COM Port öffnen kann.
Ist das USB Kabel dick genug und sitzen die Stecker fest, falls du das Board via USB mit Strom versorgst?
Schnittstellenparameter stellt man unter Windows weder über die Systemsteuerung noch mit dem mode-Kommando ein, sondern das macht man in dem Programm, das die Schnittstelle anfasst. Alles andere ist Irrsinn.
Hier also muss Deine Qt-Anwendung die Schnittstellenparameter einstellen.
Wenn ich den Pico einschalte dann zeigt mir Win11\cmd bei >mode das an: ...
Und wie ist die Schnittstelle bzgl. Handshake auf der anderen Seite eingestellt?
Ist das USB Kabel dick genug
Wenn das Kabel für Putty reicht, dann wohl auch für anderes.
Schnittstellenparameter stellt man unter Windows weder über die Systemsteuerung noch
Das weiß ich auch. Aber man kann es abfragen...
Und wie ist die Schnittstelle bzgl. Handshake auf der anderen Seite eingestellt?
Das ist die Frage, auf die ich eine Lösung suche, da brauche ich keine Frage die nach der Frage fragt. Irgentwie kommt hier nix rein was, was weiter bringt.
Hier also muss Deine Qt-Anwendung die Schnittstellenparameter einstellen.
Dachte ich schon, aber wie? Wenn du mir das mitteilen könntest.
Harald K. schrieb:
Hier also muss Deine Qt-Anwendung die Schnittstellenparameter einstellen.
Dachte ich schon, aber wie? Wenn du mir das mitteilen könntest.
In der oben verlinkten Dokumentation stehen alle Parameter die einstellbar sind. Auch wie man DTR toggelt, falls das nötig sein sollte für flow control. Ob das nötig ist sollte in der PiPico Dokumentation stehen.
Auch wie man DTR toggelt,
Hab ich gesucht und nicht gefunden, oder nicht verstanden.
Aber man kann es abfragen
Nein, was auch immer das da anzeigt, hat nichts mit dem zu tun, was die die Schnittstelle nutzende Anwendung macht.
Auch wie man DTR toggelt,
Hab ich gesucht und nicht gefunden, oder nicht verstanden.
setDataTerminalReady(true)
nachdem der port geöffnet wurde. Und ich würde immer noch den error code abfragen, um rauszukriegen warum das nicht geht.
was die die Schnittstelle nutzende Anwendung macht.
Das ist richtig. Aber in diesem Fall hat nach Beendigung von Putty, Putty etwas verändert. DTR war vorher Off, nachher ON.
setDataTerminalReady(true)
Ja, sieht gut aus damit. Ich teste demnächst noch genauer. Danke.
Rainer W. schrieb:
Und wie ist die Schnittstelle bzgl. Handshake auf der anderen Seite eingestellt?
Das ist die Frage, auf die ich eine Lösung suche, da brauche ich keine Frage die nach der Frage fragt. Irgentwie kommt hier nix rein was, was weiter bringt.
DU schreibst doch die Programme mit VSCode bzw. in Qt. Dann solltest DU doch auch wissen, was du da tust.
Das Qt-Progr. meldet jedoch immer "Can't open serial".
Geht das auch etwas genauer? "Geht nicht" ist keine Diagnose.
Das ist die Frage, auf die ich eine Lösung suche, da brauche ich keine Frage die nach der Frage fragt. Irgentwie kommt hier nix rein was, was weiter bringt.
Achso? Ich dachte, Handshake gibt es über Stdio (ohne t) und USB gar nicht?
Ich hab geschrieben: "über Stdtio und USB" Da gibt es das nicht.
Das Qt-Progr. meldet jedoch immer "Can't open serial".
setDataTerminalReady(true)
Ja, sieht gut aus damit.
Wie passt das zusammen?
Nach meinem Kenntnisstand öffnet man zuerst den Port, und kann erst danach die Handshake Signale ändern.
Zeige mal den relevanten Quelltext.
Ich dachte, Handshake gibt es über Stdio (ohne t) und USB gar nicht?
Natürlich gibt es das, wenn es die Hardware unterstützt. USB-Seriell-Bridges können sehr wohl Hardwarehandshake.
Aber es ist halt entscheidend, daß man in seinem eigenen Programm die Schnittstelle richtig konfiguriert, statt irgendwelche Weihrauchbecher und getrocknete Hasenpfoten zu schwenken, indem man vorher irgendwelche anderen Programme aufruft und hofft, daß die irgendwas irgendwie verändern.
Dokumentation lesen. Die verwendete Qt-Klasse für serielle Schnittstellen hat ganz sicher 'ne Dokumentation.
Natürlich gibt es das, wenn es die Hardware unterstützt. USB-Seriell-Bridges können sehr wohl Hardwarehandshake.
Allerdings ist auf dem Pi-Pico überhaupt keine USB-Serial-Bridge. Der Mikrocontroller RP2040 hat eine native USB-Schnittstelle, und die eigene Firmware gaukelt dem PC eine serielle Schnittstelle vor. d.h. die Flusskontroll-Leitungen DTR & Co existieren überhaupt nicht.
Wahrscheinlich implementiert die Firmware das USB-CDC-ACM -Protokoll, welches der Host dann als 56k-Analog-Modem erkennen soll für die Einwahl in das analoge Telefonnetz. Unter den üblichen Betriebssystemen ist es aber so umgesetzt, dass stattdessen ein serieller Port simuliert wird, über den man dann ein Modem ansteuern kann, aber nicht muss. Den Port kann man dann auch per PuTTY oder eigener Software zweckentfremden.
USB-CDC-ACM hat ein Datenpaket zum Setzen der Steuerleitungen. Viele Firmwares ignorieren das einfach komplett. Vermutlich merkt sich deine Implementation den vom PC gewünschten Zustand der "DTR"-Leitung, ohne aber eine physische DTR-Leitung zu setzen, weil eine solche gar nicht existiert. Dann wird deine Implementation offenbar das Senden von Daten verweigern wenn dieser gemerkte Zustand nicht korrekt ist.
Die Lösung ist also ganz einfach, in deiner Firmware diese Abfrage zu entfernen, d.h. die entsprechenden Pakete auf dem USB-Endpoint einfach immer zu senden völlig unabhängig davon, ob der PC zuvor die DTR-Leitung setzen wollte oder nicht. Der Nachteil ist: Wenn das Gerät etwas sendet ohne dass eine PC-Anwendung den Port geöffnet hätte, gehen die Daten ins Nirvana.
Bzw noch verwirrender: Das erste Datenpaket, welches du an den PC senden wolltest während keine PC-Anwendung den Port geöffnet hatte, bleibt im USB-Puffer stecken und kommt dann irgendwann später an, wenn du den Port wieder öffnest. Alle in der Zwischenzeit anfallenden Pakete gehen verloren, weil das eine im USB-Puffer befindliche Paket diesen blockiert. Dadurch sieht eine PC-Anwendung beim Öffnen des Ports ein einzelnes, uraltes Paket, dann eine große Lücke, und dann erst aktuelle Pakete. Kann aber im Detail anders sein je nachdem was deine Firmware genau mit dem USB-Puffer macht.
Die Lösung ist also ganz einfach, in deiner Firmware diese Abfrage zu entfernen
Noch einfacher ist es jedoch, die serielle Schnittstelle auf dem PC korrekt anzusteuern.
D.h. beim Initialisieren das Hardwarehandshake abzuschalten und gegebenenfalls die virtuellen Handshakeleitungen auf sinnvolle Zustände zu setzen.
Die Abfrage in der Firmware des Pico ist sicherlich nicht sinnlos; hinter der virtualisierten seriellen Schnittstelle des Pico steckt ja noch irgendwas anderes, was diese Schnittstelle bedient. Das wird irgendeine weitere Komponente in der Firmware sein, die man sich selbst geschrieben hat o.ä. Und wenn diese Komponente gerade nicht in der Lage ist, ihr gesendete Kommandos auszuwerten, dann kann diese die virtuelle Handshakeleitung bedienen, um dem Gegenstück auf dem PC zu signalisieren, daß es jetzt einfach mal die Klappe halten soll.
Und genau das gleiche ist auch in der Gegenrichtung möglich; die Applikation auf dem PC kann dem µC mitteilen, daß er jetzt mal die Füße stillhalten soll.
Ob das nötig ist, sei dahingestellt, aber die naheliegende Lösung ist, die Handshakeleitungen einfach korrekt zu setzen.
Man kann sein System natürlich auch so gestalten, daß empfangene Daten weggeworfen werden, wenn sie gerade nicht verarbeitet werden können, aber dann können Firmware und korrespondierende Anwendung auf dem PC durchaus durcheinanderkommen, weil sie nicht mehr synchron laufen.
Das ist dann halt primitiv und fehleranfällig.
Allerdings ist auf dem Pi-Pico überhaupt keine USB-Serial-Bridge.
Das hast du sehr gut beschrieben. Genau so lief das hier ab. Da genügt es nicht, nur die API zu lesen, man braucht auch noch genügend Hintergrundwissen um sie anzuwenden.
Aber so wie es mit der Dokumentation üblich ist, werden meist nur die Parameter einer Funktion beschrieben, aber nicht der genaue Ablauf und Wirkungsweise. Nur, das was sie im Prinzip halt macht. Die Details muss man sich mühsam selbst zusammenkratzen.
aber nicht der genaue Ablauf und Wirkungsweise.
Das sind halt die Grundlagen serieller Schnittstellen, die es schon seit Jahrzehnten gibt. Erwartest Du tatsächlich, daß die Qt-Dokumentation für die von Dir verwendete Klasse zur Ansteuerung der seriellen Schnittstelle das alles wiederkäut?
Noch einfacher ist es jedoch, die serielle Schnittstelle auf dem PC korrekt anzusteuern. D.h. beim Initialisieren das Hardwarehandshake abzuschalten und gegebenenfalls die virtuellen Handshakeleitungen auf sinnvolle Zustände zu setzen.
Naja, eine Ansteuerung für ein nicht vorhandenes Signal zu basteln um eine komische Firmwarekonstruktion auszugleichen finde ich nicht unbedingt sinnvoll. Ich hab solche simulierten Flusskontroll-Leitungen nie angesteuert und diese Probleme anders gelöst.
Und wenn diese Komponente gerade nicht in der Lage ist, ihr gesendete Kommandos auszuwerten, dann kann diese die virtuelle Handshakeleitung bedienen, um dem Gegenstück auf dem PC zu signalisieren, daß es jetzt einfach mal die Klappe halten soll.
Nö, das ist bei CDC absolut sinnlos. Vom Gerät signalisierte Handshake-Zustände (über den separaten Interrupt-Endpoint) werden vom PC typischerweise geflissentlich ignoriert. Praktischerweise hat USB seine eigene Flusskontrolle hardwareseitig integriert. Solange das Gerät die vom PC gesendeten Daten nicht aus dem USB-Puffer abholt, kann der PC keine neuen Daten senden, und die entsprechende write() -Funktion des jeweiligen APIs blockiert. Somit kann die PC-Anwendung das Gerät überhaupt nicht überfahren.
Und genau das gleiche ist auch in der Gegenrichtung möglich; die Applikation auf dem PC kann dem µC mitteilen, daß er jetzt mal die Füße stillhalten soll.
Unnötig, das tut das Gerät automatisch. USB-Geräte können überhaupt nur dann Daten senden, wenn der PC dies explizit anfordert. Die USB-CDC-Treiber der üblichen PC-Betriebssysteme tun das nur, wenn eine Anwendung explizit versucht, vom Serial-Port zu lesen. Solange keine Anwendung was liest, bleiben die Daten im USB-Puffer des Geräts stecken.
Man kann sein System natürlich auch so gestalten, daß empfangene Daten weggeworfen werden, wenn sie gerade nicht verarbeitet werden können
Dazu müsste man schon die CDC-Treiber des Betriebssystems (Richtung Gerät->PC) und die Firmware (Richtung PC->Gerät) entsprechend umbauen, damit hier explizit was verworfen wird.
Das ist dann halt primitiv und fehleranfällig.
Das Schöne an USB ist ja, dass für all das schon gesorgt ist und überhaupt nichts verloren gehen kann, das einzige Problem ist das mit dem alten festhängenden Datenblock, welches man mit dem DTR-Signal versucht zu "lösen".
Das von mir bevorzugte Vorgehen ist es, dass das Gerät von sich aus erstmal gar nichts sendet, und die PC-Anwendung Befehle für Start/Stop an das Gerät schickt; alternativ sendet die PC-Anwendung immer einzelne Anfragen und das Gerät darauf eine einzelne Antwort und dann nichts mehr. Wenn die Anwendung beendet wird, sollte das Gerät nichts mehr senden, und somit kann auch kein altes Datenpaket im USB-Puffer festhängen. Beim nächsten Start der Anwendung sendet diese den Start-Befehl und das Gerät antwortet mit einem frischen Paket. Nur wenn die Anwendung abstürzt und den Stop-Befehl nicht sendet, kann das Gerät versuchen etwas zu senden, was dann im USB-Puffer stecken bleibt. Das lässt sich lösen indem die Anwendung beim Start erstmal alles liest was da ist und verwirft, bis der Puffer leer ist, und dann erst den Start-Befehl sendet. Im schlimmsten Fall kann man einfach das Gerät abstecken/resetten. Dadurch spart man sich die Flusskontroll-Leitungen komplett und es funktioniert alle sauber auch dann wenn dann doch mal ein echter USB-Serial-Adapter genutzt wird und nur die RX+TX Leitungen verdrahtet sind, und man braucht nicht mit den schrägen APIs für die Flusskontrolle herumhantieren.
Die beste Lösung ist es natürlich ein eigenes natives USB-Protokoll zu implementieren, statt einen Serial-Port zu simulieren wo keiner ist.
Die Details muss man sich mühsam selbst zusammenkratzen.
Die Qt-Dokumentation weiß natürlich nichts darüber was deine Firmware macht und warum die das DTR-Signal haben will.
Naja, eine Ansteuerung für ein nicht vorhandenes Signal zu basteln
Es ist natürlich irrwitzig aufwendig, dem Schnittstellentreiber mitzuteilen, wie er das Signal zu setzen hat, da kann ich mitfühlen. Daß das ganze auch zur Folge hätte, daß es auch mit einer echten USB-Seriell-Bridge (gar einer, die kein CDC implementiert, wie die üblichen von FTDI) oder gar einer "echten" seriellen Schnittstelle funktionieren könnte ...
Das hier https://doc.qt.io/qt-6/qserialport.html
ist die Dokumentation der Qt-Klasse QSerialPort. Die stellt das Property dataTerminalReady zur Verfügung. Es ist also ein Einzeiler, das Ding zu setzen.
Oh, welch unerträgliches Leid.
Daß das ganze auch zur Folge hätte, daß es auch mit einer echten USB-Seriell-Bridge (gar einer, die kein CDC implementiert, wie die üblichen von FTDI) oder gar einer "echten" seriellen Schnittstelle funktionieren könnte ...
Wie viele Geräte werten das DTR-Signal vom FTDI wirklich aus?