Hi, für meine Abschlussarbeit bin ich dabei einen STM32 Mikrocontroller per USB-Schnittstelle zu steuern. Ich habe mich mit der Generic Hid Library von Jan Axelson unter C# beschäftigt und damit eine kleine WinForm-Anwendung zur Steuerung erstellt. Die Kommunikation per USB funktioniert auch, jedoch habe ich nun ein paar Probleme bei denen ich zurzeit nicht so richtig weiter weiß und hoffe Ihr könnt mir weiterhelfen. Zur Steuerung des Geräts (STM32) werden mehrere Reports a 4 Bytes an das Gerät gesendet. Die Senderoutine hat jedoch nicht immer erfolgreich einen Report gesendet, sondern stoppte beim Versuch zu senden (Filestream.Write). Dies konnte ich durch ein flush nach dem Schreiben eines Reports und einer zusätzlichen kleinen Wartephase (thread.sleep(30)) beheben. Richtig gefallen tut mir das Sleep aber auch nicht. Ich habe das Gefühl, dass das Senden nicht (richtig) abgeschlossen wird bevor das Senden des nächsten Reports beginnt. Sollte aber eigentlich nicht passieren, da es sich um eine blockierende Funktion handelt oder liege ich falsch? Zum Empfangen von Status-Informationen vom Gerät (ebenfalls 4 Byte Reports) wollte ich einen eigenen Thread starten um die Oberfläche weiterhin bedienbar zu halten. Der Thread läuft, jedoch bleibt dieser hängen, wegen des blockierenden Aufrufs von Filestream.Read. Gibts eine elegante Möglichkeit dies zu umgehen, bzw. in bestimmten Abständen den Aufruf zu beenden? Ich möchte innerhalb des Threads abfragen ob dieser beendet werden soll, deshalb muss ich eine Variable zwischendurch abfragen. Mir ist aufgefallen, dass ich während der Thread läuft, dass das Gerät keine gesendeten Reports empfängt. Kann es sein, dass ich den geöffneten Stream entweder nur schreiben oder nur lesen kann? Das wäre ziemlich besch*, da ab und an auch mal ein Report vom PC gesendet werden muss. Wenn das so ist, wie kann ich sowohl vom Gerät lesen als auch schreiben (in kurzen Abständen)? Gibt es irgendeine andere Möglichkeit den Mikrocontroller abzufragen ohne das Senden von Reports zu verhindern.? Umstellung auf eine andere USB Api/Lib würde ich eigentlich ziemlich ungern, da die Zeit so schon sehr knapp ist...
So gerade kommt mir noch was, den zusätzlichen Protokolloverhead von USB habe ich natürlich nicht beachtet, evtl. wäre es daher geschickter ein Teil der 4 Byte Päckchen vom PC aus zusammenzufassen (Konfiguration) und in einem großen ~ 60 Byte Report zu senden? Trotzdem bäuchte ich eine Möglichkeit den STM32 auf Datenversand abzufragen als auch an diesen ein Datenpaket (dann aber nur 4 Byte) zu senden. Dies scheint aber durch das Lesen innerhalb des Threads blockiert zu sein.
So neuer Ansatz, ich verwende nun zwei HIDHandles und dazu passend zwei FileStreams, jeweils einer für das Senden und einen zum Empfangen. Desweiteren fragt der Thread mit BeginRead anstelle von Read ab. Da das Abfragen auf Datenempfang in jedem Fall blockierend arbeitet muss mein Mikrocontroller ein Report senden, damit die aufgerufene Funktion sich wieder beendet. Dann kann ich den Thread auch wieder beenden. Sind meine Ausführungen so bescheuert? Gibt es eine bessere Lösung zum Abfragen?
Hallo Dominik, wie groß sind denn Deine Sende-/Empfangspuffer in den Endpunkten. Wenn Du Reports versendest, dann kommt zu den eigentlichen Nutzdaten noch die führende Report-ID dazu. Zeig mal Deine Deskriptoren. Ich vermute, dass die Reports, die Du sendest nicht die richtige Größe haben. Beispiel: Report ID = #0x00 mit 4 Byte Nutzdaten Report ID = #0x11 mit 4 Byte Nutzdaten Du sendest nun: 0x01,0x02,0x03,0x04 für Deinen Report mit ID = 0x00 (ohne die ID) und 0x11,0x12,0x13,0x14 für Deinen Report mit ID = 0x11 (ohne die ID) Was kommt nun am Controller an? 0x01,0x02,0x03,0x04,0x11 für Deinen ersten Sendevorgang (was ja Käse ist) und für den zweiten kommt an: 0x12,0x13,0x14 (ebenfalls Käse) Das wird nämlich interpretiert mit: Report #0x01 hat die Daten 0x02,0x3,0x04 0x11 empfangen usw. Und da liegt der Hase im Pfeffer! Gruß Potter
Also die Buffer sollten schon stimmen, die 4 Byte beziehen sich auf 1 Byte Report ID und 3 Bytes Daten, da sollte eigentlich alles stimmen, da ich die gesendeten Daten auch empfange. Ich überprüfe das aber nochmal und setzte auf allen Seiten die Endpoint-Größe etwas höher (zurzeit genau 4 Byte groß). Nochmal zum besseren Verständnis, wenn ich nur ein Handle nutze und nur einen Stream kann kommt es zu Deadlocks beim Senden oder Empfangen (Read, Write). Nutze ich ein Handle Read mit einem Stream Read und das gleiche nochmal für Write, funktioniert das Senden und Empfangen von Reports. Um nun zu überpüfen ob sich etwas in meinem Mikrocontroller getan hat, sendet dieser eine Statusmeldung (Report besteht aus 1 Byte ID + 3 Bytes Daten) benötige ich eine Abfragemöglichkeit. Dafür hielt/halte ich einen eigenen Thread angebracht, dieser ruft nun filestream.BeginRead(...) auf (sofern nicht schonmal gestartet) und wartet auf Empfang. Es kann passieren, dass die Statusmeldungen häufig vorkommen, oder aber auch nur alle Stunde, daher möchte ich dem Benutzer meines Steuertools die Möglichkeit geben den Thread zu beenden. Dies schlägt leider fehl, sofern BeginRead noch auf Datenempfang wartet. Entweder ich schieße den Thread mit abort ab, was mir nicht sooo sehr gefällt, oder ich finde eine Möglichkeit BeginRead zu beenden. Den Filestream schließen und dispose aufrufen scheint auch nicht zu funktionieren (Thread nur mit abort killbar, Exceptions treten vorher beim schließen jedoch nicht auf!). Einzige andere Möglichkeit wäre ein "Dummy"-Report zu senden um auf eine "Dummy"-Antwort des µC zu warten und BeginRead nicht zu starten. Funktioniert auch, nur was mache ich wenn mein µC warum auch immer nicht antwortet. Dafür Suche ich eine Lösung die mit BeginRead gestartete Routine zu beenden Hier mal mein HID Report Descriptor: const uint8_t CustomHID_ReportDescriptor[CUSTOMHID_SIZ_REPORT_DESC] = { 0x06, 0xFF, 0x00, /* USAGE_PAGE (Vendor Page: 0xFF00) */ 0x09, 0x01, /* USAGE (Vendor Usage 01) */ 0xa1, 0x01, /* COLLECTION (Application) */ /* 7 bytes */ /* Output */ 0x85, 0x01, /* REPORT_ID (1) */ 0x09, 0x01, /* USAGE (Vendor defined) (Out data) */ 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ 0x26, 0xFF, 0x00, /* LOGICAL_MAXIMUM (255) */ 0x75, 0x08, /* REPORT_SIZE (8 bit) */ 0x95, 0x03, /* REPORT_COUNT (3) */ 0x91, 0x82, /* OUTPUT (Data, Var, Abs, Vol)*/ /* 22 bytes */ /* Input */ 0x85, 0x02, /* REPORT_ID (2) */ 0x09, 0x02, /* USAGE (Status data) */ 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ 0x26, 0xFF, 0x00, /* LOGICAL_MAXIMUM (255) */ 0x75, 0x08, /* REPORT_SIZE (8 bit) */ 0x95, 0x03, /* REPORT_COUNT (3 Items) */ 0x81, 0x82, /* INPUT (Data, Var, Abs, Vol) */ /* 37 bytes */ /* Input */ 0x85, 0x03, /* REPORT_ID (3) */ 0x09, 0x03, /* USAGE (ADC IN) */ 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ 0x26, 0xFF, 0x00, /* LOGICAL_MAXIMUM (255) */ 0x75, 0x08, /* REPORT_SIZE (8 bit) */ 0x95, 0x03, /* REPORT_COUNT (3 Items) */ 0x81, 0x82, /* INPUT (Data, Var, Abs, Vol) */ /* 52 bytes */ 0xc0 /* END_COLLECTION (Application)*/ /* 53 bytes */ }; /* CustomHID_ReportDescriptor */ Gruß und danke Dominik
Ja USB ist halt besch**. USB kann nicht gleichzeitig Senden und empfangen ! Es sind zudem zuviele Layer dazwischen und Puffer dazwischen.(FIFOs,Puffer,Kerneltreiber,MessageLoops,Windows buffer, .Net Zeug usw.) ein Senden oder empfangen dauert 1ms egal wie viele Bytes. Es muß ein flush gemacht werden bzw. es müssen dummy bytes hinten angehängt werden. USB Geräte klauen sich gegenseitig Slots (bzw. Bandbreite). Deine 4 oder 6 USB anschlüsse sind nur ein Kanal der auf eine HUB geht und teilen sich die Bandbreite und Slots in denen gesendet und empfangen werden kann. Alle hängen an den selben 2 Drähten D+ un D-, nur durch HUBs getrennt (differentielles Halfduplex Leitungspärchen) es kann immer nur ein Gerät bedient werden und zwar entweder lesen oder schreiben dann das nächste und das nächste bis alles von vorne losgeht.
http://www.tech-pro.net/intro_usb.html Traffic on the USB is regulated using time. The unit of time is the frame. The length of each frame is governed by the bus clock, which runs at a rate of 1KHz, so there are 1,000 frames per second: one per millisecond. At the start of each frame a Start Of Frame (SOF) packet is sent over the bus, allowing isochronous devices to synchronise with the bus. The concept of frames is central to how the bus shares out bus bandwidth among the various competing devices. The USB designers felt that it would not be possible to support several concurrent isochronous communication flows with fast sample rates using a system where each device must interrupt the host for each sample of data to be transferred. Consequently they designed the system so that isochronous devices are given guaranteed bandwidth by allocating them a proportion of the time in each frame. Interrupt transfers are also to an extent time critical. When a pipe is created for an interrupt endpoint, a desired bus access period of between 1 and 255ms (10 and 255ms in the case of low speed devices) is specified. The system software polls the interrupt endpoint at an interval which ensures that if an interrupt transaction is pending it is dealt with within the desired time-frame.
Mir ist schon klar, dass USB nicht exakt zum gleichen Zeitpunkt senden und empfangen kann. Auch ist mir bekannt, dass der Host den Report anfordern muss und mein Device nicht einfach einen Report raushaut. Mein Stm32 führt bestimmte Aufgaben/Funktionen aus und soll bei Abschluss den PC darüber informieren, daher die Datenabfrage im Thread. Ich dachte nur, dass ich vielleicht nur einen Filestream bräuchte und den asynchronen Aufruf von BeginRead irgendwie auch ohne Datenempfang abbrechen könnte.
>0x06, 0xFF, 0x00, /* USAGE_PAGE (Vendor Page: 0xFF00) */ >0x26, 0xFF, 0x00, /* LOGICAL_MAXIMUM (255) */ Kann das sein? Wie war das mit little und big endian? Schau mal hier: http://www.lvr.com/forum/index.php?PHPSESSID=50eedf11baaa0ef231f87a52da7c1b0f&topic=676.msg2504#msg2504 oder hier: http://stackoverflow.com/questions/8417830/is-it-possible-to-abort-cancel-a-filestream-beginread-asynchronous-read-operatio Da hat noch jemand das Problem. Ich empfehle Dir einen Original-Code für den STM32 aufzuspielen, die Routinen von J. Axelson als funktionierend anzusehen und zuerst einmal das zum Laufen zu bekommen, bevor Du selber in den Code eingreifst ohne zu wissen was Du tust.
Potter schrieb: >>0x06, 0xFF, 0x00, /* USAGE_PAGE (Vendor Page: 0xFF00) */ Ich habe meinen Descriptor mit dem kleinen Programm HID Descriptor Tool von USB.org erstellt und dann den vorhandenen Descriptor aus einem STM32 USB-Beispiel angepasst/erweitert. Wären die Werte falsch dürfte doch eigentlich gar nichts funktionieren? Da ja dann auch die HID Items falsch herum eingetragen wären... Steht so auch im Custom HID Beispiel von ST. Potter schrieb: > Ich empfehle Dir einen Original-Code für den STM32 aufzuspielen, die > Routinen von J. Axelson als funktionierend anzusehen und zuerst einmal > das zum Laufen zu bekommen, bevor Du selber in den Code eingreifst ohne > zu wissen was Du tust. Du wirst es kaum glauben aber genau so bin ich vorgegangen und übrigens die anderen beiden Threads stammen auch von mir. Ich versuche es einfach auf mehreren Wegen... Die Routinen von Axelson funktionieren auch grundsätzlich. Meinem Empfinden nach handelt es sich ja nun auch eher um ein Problem der C# .Net Funktion BeginRead, die nicht einfach abgebrochen werden kann. Evtl. auch nur im Zusammenhang mit dem geöffneten USB FileStream, da dieser wahrscheinlich kein End of Stream ausgibt und BeginRead daher nicht zurückkommt bzw. die Callback-Funktion aufruft. Wie soll das auch gehen, die USB-Kommunikation mit anderen Geräten läuft ja auch noch... Gruß Dominik
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.