Hallo.
Ich möchte ein Messgerät mit dem PC per USB verbinden. Mikrocontroller:
STM32F767, USB Core von ST, WinUSB Umsetzung angelehnt an
http://searchingforbit.blogspot.de/2014/05/winusb-communication-with-stm32-round-2.html
PC seitig verwende ich WinUSB.Net.
https://github.com/madwizard-thomas/winusbnet
Auf meinem Rechner hat die Kommunikation super funktioniert
(Announcements per Control Transfer, Datentransfer über Bulk In/Out
Pipe).
Auf einem anderen Rechner auch, beide Windows 10 und USB 2.0 Anschlüsse.
(Gerät ist USB 2.0 High Speed)
Auf neueren Rechnern mit USB3.0 Anschlüssen funktioniert es nicht. (Auch
mit USB 2.0 Hub nicht)
Es lässt sich runterbrechen, dass Readpipe nach einer handvoll Aufrufen
keine Daten liefert sondern hängen bleibt bzw. in Timeout geht wenn
eingestellt. Wenn ich in einer while-Schleife direkt danach wieder
abfrage, geht es immer wieder schief. Wenn ich kurz warte und per Hand
nochmal abrufe, klappt es.
Ich habe es auch schon im RAW-Modus probiert, keine Veränderung.
Hat jemand eine Idee was da schief geht? Ich weiß ja noch nichtmal so
genau ob das Problem im Mikrocontrollercode liegt oder PC seitig, wobei
ich eher die PC-Seite in Verdacht habe, weil mikrocontrollerseitig alles
soweit in Ordnung aussieht dass die Daten im Sende-FIFO bereit liegen
aber einfach nicht abgeholt werden in dem Fall wo es schief geht.
Exakt die gleiche Windows-Version?
OS schrieb:> Wenn ich kurz warte und per Hand nochmal abrufe, klappt es.
Was heißt "per Hand"? Hast du das Gerät zwischendurch neu geöffnet?
Zugegeben, die Rechner wo es funktioniert sind Windows 10 1511 und 1607,
einer wo es nicht geht ist 1703 und der andere da komme ich grad nicht
ran.
per Hand heißt einfach nur über einen Button ReadPipe nochmal gestartet.
Es scheint so dass der USB Stack im Windows durcheinander kommt und nach
dem Timeout einen Moment braucht sich zu erholen. Wenn ich zu schnell
Readpipe starte klappt es wieder nicht.
OS schrieb:> Es lässt sich runterbrechen, dass Readpipe nach einer handvoll Aufrufen> keine Daten liefert sondern hängen bleibt bzw. in Timeout geht wenn> eingestellt. Wenn ich in einer while-Schleife direkt danach wieder> abfrage, geht es immer wieder schief. Wenn ich kurz warte und per Hand> nochmal abrufe, klappt es.
Häng doch mal Deinen Source Code an. Eventuell sind Timeouts o.ä. zu
kurz geraten.
Wie gesagt steht dann immer "Received 0 Bytes" da. Wenn ich am Ende der
while Schleife 100ms warte, werden die Daten dann im 2. Durchlauf
korrekt abgeholt. Das ist aber keine Lösung weil ich so nicht die
benötigte Datenrate schaffe wenn ich andauernd 100ms warten muss
Auf den älteren Rechnern kommt InPipe.Read halt nicht in den Timeout.
Ich hatte dort schon mehr drin stehen. Es kommt dort im Fehlerfall immer
dieselbe Exception - Semaphore timeout.
Siehe auch (return values..):
https://msdn.microsoft.com/en-us/library/windows/hardware/ff540297(v=vs.85).aspx
Das ist also ein ganz normaler Fehler weil das Timeout kommt (welches
ich selber gesetzt habe). Die Exception wird von WinUSB.Net selbst
geworfen auf niedrigerer Ebene, wo der API32 Call return value
ausgewertet wird. Ich habe mir auch eine Kopie dieser Funktion gemacht
die diese Exception gar nicht erst wirft.
Ich sehe das Verhalten auch wenn ich mit einem (SW-) USB Sniffer
mitschneide, sobald das Timeout greift bekomme ich 0x0 Bytes
zurückgeliefert.
OS schrieb:> Es kommt dort im Fehlerfall immer> dieselbe Exception - Semaphore timeout.> Siehe auch (return values..):> https://msdn.microsoft.com/en-us/library/windows/hardware/ff540297(v=vs.85).aspx> Das ist also ein ganz normaler Fehler weil das Timeout kommt (welches> ich selber gesetzt habe)
Das ist kein wirklicher Fehler, sondern normales Verhalten wenn "nix"
gesendet wurde.
Außerdem würde ich mal nachschauen ob da nicht zufällig ein Vielfaches
von MaxPacketSize (im EP Deskriptor) gesendet wurde: Dann gibt der USB
Host Treiber nämlich nix aus, siehe USB 2.0 Spec Kapitel 5.8.3 "Bulk
Transfer Packet Size Constraints".
Entsprechendes Handling (z.B. zusätzliches Zero Packet) musste ich
bisher immer selber einbauen.
Das ist schon richtig, dass das Timeout normal wäre, wenn ich nichts
senden würde. Aber im Mikrocontroller liegen 200 Byte zum abholen. Der
nächste InPipe.Read Befehl liefert dann auch die Daten, wenn ich
mindestens 100ms nach Timeout warte.
MaxPacketSize habe ich auf 0xFF, das sollte mit 200 Bytes Nutzdaten also
kein Problem sein. Ich sehe im Mikrocontroller im SendeFIFO ja auch,
dass der PC (bzw. der Hostcontroller) die Daten überhaupt nicht abholt
im Fehlerfall.
Mikrocontrollerseitig weiß ich auch nicht wo ich noch schauen könnte,
ich habe die Daten ja quasi schon an den Hardwareteil übergeben.
Könnte es vielleicht was damit zu tun haben dass ich im DeviceDescriptor
bcdUSB auf 2.00 gesetzt habe? Der STM USB Stack unterstützt theoretisch
auch LPM und damit USB 2.01 wenn das entsprechende define gesetzt ist.
Das habe ich aber bisher noch nicht probiert weil ich dann eher noch
mehr Probleme erwarte
Setzt du denn auf der uC Seite auch das PacketEnd wenn du nur 200Byte zu
liefern hast? Wenn du weniger als die Endpoint Größe hast musst du das
setzen. Wir setzen WinUSB erfolgreich seit Jahren ein, mit Cypress
Controllern.
Ich nutze auf Mikrocontrollerseite die Funktion USBD_LL_Transmit des ST
USB Stacks. Da gibt es wenig Doku darüber.
Wenn ich in den Code reinschaue, wird wiederum HAL_PCD_EP_Transmit
aufgerufen. Darin werden Datenpointer und Länge im Endpoint vermerkt,
dann USB_EPStartXfer aufgerufen.
Darin:
- setzen der Transfergröße und Paketanzahl im DIEPTSIZ Register
- TX FIFO Empty Interrupt aktivieren im DIEPEMPMSK Register
- Clear NAK und setze EndointEnable im DIEPCTL Register
Im Tx FIFO Empty Interrupt werden dann die Daten entsprechend in den Tx
FIFO kopiert. Um den Rest kümmert sich dann der (HW-)Core.
Wenn der PC die Daten korrekt abgeholt hat, erkenne ich das darin, dass
der Endpoint nicht mehr enabled ist und der FIFO wieder leer ist
Ich weiß nicht ob ich noch irgendwo PacketEnd setzen kann/muss. Ich
finde dazu nichts im Reference Manual zum STM32F7, nichts im STM32Cube
USB Device Library Manual (dort werden eh nur die typischen sachen MSD,
CDC, HID etc. erklärt), nichts im Code. Ich denke das macht der Core
automatisch. Auch wenn ich mir die CDC-Klasse von ST angucke, da wird
auch einfach nur USBD_LL_Transmit genutzt.
Der Fehler tritt genauso auf.
Ich habe aber gerade mal noch etwas versucht. Weiter oben kam ja der
Hinweis mit den USB Specs. Ich habe nochmal genau nachgelesen:
"All Host Controllers are required to have support for 8-, 16-, 32-, and
64-byte maximum packet sizes for
full-speed bulk endpoints and 512 bytes for high-speed bulk endpoints.
No Host Controller is required to
support larger or smaller maximum packet sizes"
Man kann das also so deuten, dass für USB HS nur 512 Bytes zugelassen
sind als maximum packet size. An anderen Stellen (USB in a Nutshell)
steht nur was von maximal 512 (nicht GENAU 512).
Ich habe mal umgebaut auf 512 und jetzt scheint es an einem Rechner zu
gehen, an anderen Rechnern muss ich erstmal testen.
Ja die maximale Packet Size sollte man tunlichst auf 512 Byte bei
HighSpeed und 64 Byte bei FullSpeed setzen. Beim Cypress kann man auch
1024 einstellen, das bringt etwas mehr Speed aber läuft nicht überall.
Wie viel man dann pro Transfer wirklich überträgt ist ja davon
unabhängig. Denk aber dran dass pro abgeschlossenem Paket ein gesamter
Microframe weg ist, wenn du viel Geschwindigkeit schaffen willst, musst
du immer gleich große Brocken anfordern.