Forum: PC-Programmierung Probleme mit WinUSB


von OS (Gast)


Lesenswert?

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.

: Verschoben durch User
von Clemens L. (c_l)


Lesenswert?

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?

von OS (Gast)


Lesenswert?

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.

von OS (Gast)


Lesenswert?

der 2. Rechner wo es nicht funktioniert ist Windows 10 Version 10240, 
also ganz alt. Aber eben auch USB 3 Anschlüssen und xhci Host Controller

von Jim M. (turboj)


Lesenswert?

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.

von OS (Gast)


Lesenswert?

Ich glaube im Code gibt es nicht viel spannendes zu sehen:

Initialisierung
1
m_oUSBDevice = USBDevice.GetSingleDevice(GUID); 
2
m_oUSBDevice.Interfaces[0].InPipe.Policy.PipeTransferTimeout = 200; // problem tritt genauso mit 5000 oder ganz ohne Timeout auf

Daten abholen
1
            byte[] data = new byte[25500];
2
            int len2 = 0;
3
            while (len2 == 0)
4
            {
5
                try
6
                {
7
                    len2 = m_oUSBDevice.Interfaces[0].InPipe.Read(data);
8
                }
9
                catch (Exception e)
10
                {
11
                }
12
13
                Console.WriteLine("Received " + len2.ToString() + " Bytes");
14
            }

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.

von Clemens L. (c_l)


Lesenswert?

1
                catch (Exception e)
2
                {
3
                }
Autsch. Warum?

von OS (Gast)


Lesenswert?

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.

von Jim M. (turboj)


Lesenswert?

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.

von OS (Gast)


Lesenswert?

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

von Christian R. (supachris)


Lesenswert?

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.

von OS (Gast)


Lesenswert?

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.

von Christian R. (supachris)


Lesenswert?

Stelle doch mal testweise Pakete mit exakt der Paketlänge bereit die im 
zugehörigen Endpoint Descriptor angegeben ist. Klappt das?

von OS (Gast)


Lesenswert?

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.

von Christian R. (supachris)


Lesenswert?

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.

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.