Open Seriellport Problem; Pi Pico & Win11 & QT & Usb

OP #8107071
Lesenswert?
• ▲
▼

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?

#8107082
Lesenswert?
• ▲
▼

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.

OP #8107141
Lesenswert?
• ▲
▼

Sebastian R. schrieb:

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
    serial.setPortName("COM8");
2

3
    if (!serial.open(QIODevice::ReadWrite)){
4
        ui->m_statusLabel->setText(tr("Can't open Serial Port"));
5
        return;
6
    }
7
    serial.write(.....)

COM8 gibt es natürlich. Und mit Terminal geht es ja auch. Nochmal dieser Ablauf:

1
Pico on
2
QT-Prog an, serial.open öffnet
3
serial.write   kommt beim Pico NICHT an, auch wiederholt nicht. Kein Response.
4
erneutes open() nach close() hilft nicht,
5
QT-Prog beenden
6
Termialprog. starten: sofort kommt die vermisste Response am Terminal an.
7
Terminalprog. beenden
8
QT-Prog an, serial.open öffnet
9
serial.write   Pico empfängt und sendet Response. Auch wiederholt.

WAs ist zu tun, dass das in Zeile 3 funktioniert?

#8107152
Lesenswert?
• ▲
▼

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.

OP #8107165
Lesenswert?
• ▲
▼

Nun habe ich festgestellt:
Wenn ich den Pico einschalte dann zeigt mir Win11\cmd bei >mode das an:

1
 Status von Gerät COM8:
2
----------------------
3
    Baudrate:        115200
4
    Parität:         None
5
    Datenbits:       8
6
    Stoppbits:       1
7
    Timeout:       OFF
8
    XON/XOFF:        OFF
9
    CTS-Handshaking: OFF
10
    DSR-Handshaking: OFF
11
    DSR-Prüfung:     OFF
12
    DTR-Signal:      OFF
13
    RTS-Signal:      ON

Nach starten von Terminal Putty und dem wiederbeenden ist der Status von DTR auf ON gestellt. Danach funktioniert auch die Verbindung zum QT-Programm.

OP #8107191
Lesenswert?
• ▲
▼

Moritz L. schrieb:

Ist das USB Kabel dick genug

Wenn das Kabel für Putty reicht, dann wohl auch für anderes.

Harald K. schrieb:

Schnittstellenparameter stellt man unter Windows weder über die Systemsteuerung noch

Das weiß ich auch. Aber man kann es abfragen...

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.

Harald K. schrieb:

Hier also muss Deine Qt-Anwendung die Schnittstellenparameter einstellen.

Dachte ich schon, aber wie? Wenn du mir das mitteilen könntest.

#8107195
Lesenswert?
• ▲
▼

Rudi schrieb:

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.

#8107233
Lesenswert?
• ▲
▼

Rudi schrieb:

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.

Rudi schrieb:

Das Qt-Progr. meldet jedoch immer "Can't open serial".

Geht das auch etwas genauer? "Geht nicht" ist keine Diagnose.

: Bearbeitet durch User
#8107297
Lesenswert?
• ▲
▼

Sebastian R. schrieb:

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.

Persönliche Seite #8107306
Lesenswert?
• ▲
▼

Harald K. schrieb:

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.

#8107338
Lesenswert?
• ▲
▼

Niklas G. schrieb:

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.

OP #8107348
Lesenswert?
• ▲
▼

Niklas G. schrieb:

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.

Persönliche Seite #8107440
Lesenswert?
• ▲
▼

Harald K. schrieb:

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.

Harald K. schrieb:

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.

Harald K. schrieb:

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.

Harald K. schrieb:

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.

Harald K. schrieb:

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.

Rudi schrieb:

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.

#8107483
Lesenswert?
• ▲
▼

Niklas G. schrieb:

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.

Antwort schreiben

Formatierung
  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] – Link zu einem Artikel
  • Verweis auf einen anderen Beitrag: Rechtsklick auf dessen Beitrags-ID (#8078628), „Adresse kopieren“, in den Text einfügen

Mehr zur Formatierung

Längeren Sourcecode als Anhang, nicht in den Text. JPG nur für Fotos und Scans; Zeichnungen und Screenshots als PNG oder GIF. Siehe Bildformate.

Schon registriert? Anmelden

Wird nicht angezeigt. Wir schicken dir einen Link; sobald du damit bestätigst, erscheint dein Beitrag.

Mit dem Abschicken legst du einen kostenlosen Account an und erkennst die Nutzungsbedingungen und die Datenschutzerklärung an.