USB: Fragen und Antworten

Fragen und Antworten bei der Programmierung von Mikrocontrollern und der Geräte-Entwicklung. Weniger für den Anwender, denn bei diesem soll ja alles reibungslos per Plug&Play funktionieren.

Hier kann man sich (englisch) einlesen:

Generell

Welche Mikrocontroller sind empfehlenswert?
Faustregeln:
Welche USB-Analysatoren (Windows) sind empfehlenswert?
Früher (32 Bit) war der freie Simple USB Logger eine gute Wahl. Heutzutage kann ich keinen Tipp abgeben, da alle Geld kosten und nur wenige funktionieren. Gelegentlich nutze ich Eltima USB Analyzer.
Welche USB-Analysatoren (Hardware) sind empfehlenswert?
Keine! Viel zu teuer! Genau auf die Firmware gucken um Fehler zu vermeiden. Hardwarefehler findet man mit dem Oszilloskop.
Eine gute Wahl ist die Verwendung von PulseView (a.k.a. sigrok) und Zadig in Verbindung mit einem preiswerten CY7C68013A-Entwicklungsboard! Eingebaute Protokollfilter erlauben das Beobachten von Low-Speed und (knapp!) Full-Speed.
Was ist der Unterschied von Resume und Remote Wakeup?
Resume bedeutet Aufwecken eines schlafenden USB-Gerätes durch den Host. Dies passiert, wenn der Host aufgeweckt worden ist, typischerweise mit dem Soft-Netztaster. Dabei ist es egal, ob „Suspend to RAM“ oder „Suspend to disk“ genutzt wird. Beim Hochfahren des Betriebssystems bekommen die schlafenden USB-Geräte hingegen einen USB-Reset.
Remote Wakeup bedeutet Aufwecken eines schlafenden Hosts durch ein USB-Gerät. Typisch für Tastaturen, manchmal für Mäuse, vielleicht für USB-Netzwerkadapter, im Hobbybereich eher selten verwendet, am ehesten für Infrarotempfänger. Das Feature muss sowohl im Device-Deskriptor angemeldet sein als auch per SET_FEATURE aktiviert worden sein, nur dann darf ein USB-Gerät den Bus mit K (entgegengesetzte Polarität) für 1..15 ms belegen, wenn es vorher mindestens 5 ms Idle gegeben hat. Ein busversorgtes Gerät darf für diese Zeit mehr als 500 µA Strom ziehen. Der Host übernimmt das K-Signal innerhalb 1 ms für das Gerät, weckt sich und alle anderen Geräte. Das Ende von Wakeup wird durch 1,3 µs SE0 (Low-Speed-EOP) vom Host gemeldet, dies löst im Mikrocontroller einen Interrupt aus. Danach ist das Bussignal J, der Ruhezustand. Ein Remote-Wakeup-fähiges und -freigegebenes Gerät darf während Idle 2,5 mA ziehen, wenn es im Configuration-Deskriptor mehr als 100 mA nominelle Stromaufnahme angemeldet hat.
Ich habe festgestellt, dass meine USB-Anschlüsse am PC während Tiefschlaf keine Spannung UBUS führen. Wie soll da Remote Wakeup funktionieren?
Richtig: An spannungsfreien USB-Buchsen funktioniert das nicht. Womöglich muss dazu diese Möglichkeit im BIOS-Setup aktiviert werden. Wahrscheinlicher ist, dass es im PC auf dem Motherboard dafür Jumper gibt, in der Nähe der USB-Anschlüsse. Diese wählen zwischen geschalteten 5 V (UCC) und dauerhaften 5 V (USTBY) vom Netzteil aus. Häufig ist die Dauerstromversorgung nur an den Buchsen in der Nähe der PS/2-Tastatur- und -Mausanschlüsse verfügbar.
Muss ich USB tiefgründig verstehen, um ein Gerät zu bauen?
Im Fall CDC (Serielle Schnittstelle): Nein. Hier genügt der Einsatz entsprechend vorkonfigurierter Software. Klassisches Beispiel ist der Arduino, der auf LUFA aufsetzt. Nachteil: Es wird kein echtes Plug&Play draus; der Anwendersoftware muss die COM-Portnummer mitgeteilt werden, und die zu übertragenden Daten sind niemandem außer dem Programmierer bekannt. Auch muss (unter Windows) ein Treiber geladen werden, was beim Umherziehen nervt.
Für einen USB-Seriell-Umsetzer im Mikrocontroller und einstellbarer Baudrate muss man sich trotzdem tiefgründig mit USB beschäftigen. Fertige Chips nehmen diese Arbeit (gegen Geld) ab.
In allen anderen Fällen: Ja! Sie haben vor, ein echtes Plug&Play-Gerät zu bauen, das von der Anwendungssoftware direkt erkannt und gefunden werden kann.
Kann ich über USB-CDC serielles Plug&Play realisieren?
Ja. Ist jedoch deutlich aufwändiger und fehleranfälliger als beispielsweise ein HID-Gerät zu bauen.
Serielles Plug&Play bedeutet, dass vom PC eine serielle Maus automatisch erkannt wird. Dieses Feature ist kaum bekannt. Es erfordert das Abhören des DTR-Zustandes und der Baudrate von 1200 Baud und 7 Datenbits. Daher ist so etwas recht komplex und niemals in Frameworks eingebaut. Wie man das Ganze dann am PC nutzbringend findet ist mir noch unbekannt.
Können auf USB gleichzeitig Daten in beide Richtungen fließen?
Scheinbar ja, tatsächlich nicht. USB ist keine Telefonleitung, bei der per Richtkoppler an den beiden Enden der jeweilige Datenstrom eingespeist bzw. abgenommen wird. Das wäre viel zu aufwändig.
Ein USB-Oszilloskop ist demnach nichts weiter als ein Mikrocontroller mit A/D-Wandler und Hi-Speed-USB?
Wenn's so einfach wäre, wär's schon längst erfunden! Hin wie her, ein Digitaloszi („headless“) besteht auch weiterhin aus den folgenden Komponenten: Ob es während des Auslesens zu Totzeiten kommt (also kein Sampling und keine Triggersuche) und ob man bei ausreichendem USB-Datendurchsatz auch alles durchgehend mitschneiden kann unterscheidet das Oszilloskop vom Transientenrecorder. Für bildschirmgerechte 1000..2000 Samples reicht sogar USB Full-Speed.

Hingegen funktioniert das einfache Konzept bei Ultraschall-Recordern. Deswegen wurde das auch mal aufgebaut.

Stimmt es, dass Hi-Speed-Geräte an SuperSpeed-Hostcontrollern schneller sind?
Ja. Der USB-3.0-Hostcontroller kann mehr Bulk-Transfers in einen USB-Frame stecken als ein EHCI-Hostcontroller, er nutzt die verfügbare Bandbreite von Hi-Speed besser aus. Daher lohnt sich der SuperSpeed-Host auch dann, wenn man gar kein SuperSpeed-Gerät hat. Das betrifft vor allem USB-2.0-Speichersticks.

Endpoints

Was ist ein Endpoint?
Eine logische Pipe. Vergleichbar mit der Portnummer bei TCP. Man stelle sich dazu ein USB-Kabel als bis zu 16 voneinander unabhängige bidirektionale serielle Schnittstellen vor.
Was ist ein Dual-Port-RAM als USB-FIFO? (PIC-Mikrocontroller)
Sowohl die Begriffe Dual-Port-RAM als auch USB-FIFO sind irreführend und (auch meiner Sicht) falsch! FIFO bedeutet First-In-First-Out und impliziert, dass Daten nur seriell gelesen und geschrieben werden können. Bei den meisten Controllern (MSP430, PIC) ist der RAM wahlfrei les- und schreibbar.
Dual-Port-RAM würde bedeuten, dass dieser gleichzeitig vom Controller und der SIE gelesen und beschrieben werden könnte. Dies ist so nicht der Fall! Der RAM-Bereich kann nur zwischen CPU und SIE umgeschaltet werden. Da beide mit unterschiedlichen Taktdomänen arbeiten können, erleichtert sich so der jeweilige Zugriff erheblich. Ein RAM im Besitz der SIE ist für die CPU des Controllers tabu. Was bei einem Zugriff passiert ist undefiniert.
Undefiniert heißt, es kann alles zwischen erwartungsgemäßer Funktion und Feuer auf dem Chip passieren. Dazwischen liegen:
  • Es wird 0x00 oder 0xFF gelesen
  • Es wird irgendetwas geschrieben (auch beim Lesen!)
  • Es wird eine Backdoor geöffnet
  • Der Controller frisst eine ungewöhnliche Stromspitze
  • Die CPU oder die Peripherie (bspw. der Taktgenerator!) friert ein (unmerklich kurzzeitig oder bis zum Reset)
  • Die CPU springt zu einer wilden Adresse
  • Flash-Speicher wird gelöscht
  • Undokumentierte Fuses werden gesetzt, die zum Chipausfall führen
Kann man den Endpoint-Puffer lesen, während die OUT-Daten vom USB noch eintrudeln?
Nein. Erstens sind Mikrocontroller zu langsam, um Full-Speed-Daten „live“ zu lesen, und zweitens muss ein Paket mit ACK bestätigt werden, wenn die CRC16 stimmt. (Außer bei Iso-Pipes.)
Möglicherweise lassen sich Daten im Dual-Port-RAM „live“ mitlesen aber das ist schlicht undokumentiert und kaum zu etwas nütze.
Wenn Latenz eine Rolle spielt, müssen kleine Iso-Pakete gewählt werden, aber unter 1 ms geht (bei Full-Speed) nichts.
Was passiert, wenn der Host ein OUT-Paket mit falscher CRC16 sendet?
Die Daten werden in die USB-FIFO eingelesen (sofern im Besitz der SIE), aber es gibt keine Antwort, der Controller stellt sich für dieses Paket tot. Auch gibt es keinen Interrupt. Außer bei einem ISO-Paket.
Was passiert, wenn der Host ein IN-Paket von einem nicht eingerichteten Endpoint holen möchte?
Die USB-Hardware des Mikrocontrollers sollte keine Antwort schicken, sich für dieses Paket tot stellen.
Was passiert, wenn der Host ein IN-Paket von einem Endpoint mit FIFO im „Besitz des Controllers“ (nicht SIE) holen möchte?
Die vom Controller generierte Antwort ist NAK. Einige Mikrocontroller können für diesen Fall einen NAK-Interrupt generieren.
Was passiert, wenn der Host ein OUT-Paket zu einem Endpoint mit FIFO im „Besitz des Controllers“ (nicht SIE) senden möchte?
Die ankommenden Daten werden ignoriert, und die vom Controller generierte Antwort ist NAK.
Das gilt prinzipiell auch für das Setup-Paket! Allerdings niemals mit NAK, schlimmstenfalls mit Schweigen (keine Antwort). Der Host probiert es dann noch zweimal und reagiert mit Fehler, wenn's dann immer noch nicht klappt, also ACK geantwortet wird. Es ist deshalb sehr wichtig, dass an Endpoint 0 immer eine OUT-FIFO im SIE-Besitz „hängt“.
Bei allen anderen Endpoints darf der Zustand beliebig lange anhalten, hier hängt das Timeout-Verhalten vom Klassentreiber ab. Einige Mikrocontroller können für diesen Fall einen NAK-Interrupt generieren.
Kann man beim Interrupt für Endpoint-IN sofort Daten senden?
Kein Mikrocontroller ist schnell genug, um beim Anfordern die Daten (innerhalb von 2 USB-Takten!) zu senden. Der Controller sendet die Daten der Endpoint-FIFO oder NAK. Der Endpoint-IN-Interrupt zeigt also „FIFO leer“ an, bereit zum Füllen mit den nächsten Daten.
Kann man Bulk-Daten stückeln oder zusammensetzen? Kann man erwarten, dass Bulk-Daten in bestimmter Stückelung eintreffen?
Das hängt vom Klassentreiber sowie von der FIFO-Größe ab. Im allgemeinen werden Datenpakete genau so durchgereicht, wie sie abgesendet werden. Beispiel: Sendet ein Windows-Programm erst 4 Bytes, dann 2 Bytes zur seriellen Schnittstelle, macht usbcdc.sys zwei Bulk-Pakete daraus, eins mit 4 Bytes, dann eins mit 2 Bytes. Genauso auch umgekehrt: Wartet ein Windows-Programm in einer Schleife mit ReadFile() und einem Puffer von 4 KBytes auf ankommende serielle Daten, und der Controller sendet erst 4 und dann 2 Bytes, kehrt ReadFile() erst mit dwBytesRead = 4 und in der nächsten Runde mit 2 Bytes zurück. Wäre der Puffer für ReadFile() nur 2 Bytes groß, würde das erste Paket von usbcdc.sys in 2 Teile zerstückelt werden. Unter Linux ist das Verhalten genauso. Bei HID-Geräten ist die Stückelung exakt so wie im Report-Deskriptor angegeben, nur zu große Pakete werden auf mehrere Setup-Daten- oder Interrupt-Transfers aufgeteilt.
Wozu dienen Null-Byte-Pakete? Wie generiert man solche?
Null-Byte-Pakete am Endpoint 0 sind Quittungen (EP0-ACK).
Null-Byte-Pakete dienen dazu, (meist Bulk-)Transfers, die gerade an der FIFO-Grenze enden, zu terminieren. Das bedeutet, dass die Gegenseite das Zusammensetzen von Paketen entweder an sog. Short Packets (kürzer als FIFO-Größe) oder an Null-Byte-Paketen (was ebenfalls ein Short Packet ist) beendet und ein solches Paket der nächsthöheren Datenverarbeitungsschicht „zum Fraß vorwirft“. (Man denke an das OSI-Referenzmodell. Es geht von Schicht 5 zu Schicht 6.)
Null-Byte-Pakete können dazu dienen, den wartenden Thread auf der Gegenseite zu erlösen. Wenn ReadFile() ansonsten endlos auf Daten wartet und den Thread blockiert, führt ein Null-Byte-Paket zur „Erlösung“, und dwBytesReturned ist Null. Der bessere Weg für ReadFile() mit Timeout ist ein asynchroner Aufruf mit einem OVERLAPPED-Parameter. Zum Glück sind alle USB-Treiberaufrufe im Hostcontroller-Treiber cancelbar, man muss sich bei der eigenen Treiberprogrammierung nicht selbst darum kümmern.
Der Mikrocontroller generiert Null-Byte-IN-Pakete durch Setzen der Endpoint-Pufferlänge auf Null und Übergabe des Pakets an die SIE. Der PC generiert Null-Byte-OUT-Pakete durch WriteFile(h,NULL,0,NULL,&o), also durch Senden von Null Daten an das Gerätehandle. Das kann zwar der Treiber wegfiltern, üblicherweise tut dieser das nicht.
Was kann man mit den NAK-Interrupts anfangen? Und mit den Interrupts, die sich mit Übertragungsfehlern beschäftigen?
Diese kann man getrost ignorieren. Die hinter USB steckende Idee ist ja, Devices so einfach und billig wie möglich zu machen.

Einen Sonderfall nimmt NAK-IN ein: Mit NAK-IN (also Not-Acknowledge-Interrupt auf einer IN-Pipe) bekommt man mit, wenn der PC beginnt auf ein Gerät zu horchen. Das passiert bei Windows-USB-CDC dann, wenn die serielle Schnittstelle geöffnet wird, kann jedoch bei anderen Klassentreibern und Betriebssystemen anders gelöst sein. Bei USB-HID ist es hingegen egal ob ein Programm das Gerätetreiber-Handle hat oder nicht: Die Interrupt-Pipes werden immer abgefragt.

Wie konfiguriere ich einen Endpoint, wenn der Mikrocontroller (ATmega32U4) nur Zweierpotenzen zulässt, ich aber genau 9 Bytes brauche? (Typisch für HID.) Sollte ich aufrunden?
Der Endpoint-Deskriptor sollte genau 9 Bytes als Endpoint-FIFO-Größe festlegen. Aufrunden verschwendet USB-Bandbreite. Die Reservierung von Speicher muss auf die nächste Zweierpotenz, also 16, aufgerundet werden.
Welche Probleme können sich dabei auftun, abgesehen von der RAM-Verschwendung?
OUT-Pipe: Man kann ein überlanges OUT-Paket empfangen, das mit ACK bestätigt wird. Der Hostcontroller sollte so etwas tunlichst unterlassen, er kann ja nicht wissen, dass die FIFO größer als 9 Bytes ist. Im Normalfall handelt es sich um BABBLE (Überlauf), den die SIE ohne Quittung verwirft.
IN-Pipe: Der Mikrocontroller könnte ein längeres Paket als gemeldet schicken. Was der Host daraus macht ist undefiniert; wahrscheinlich wird er per DMA nicht zugeordneten RAM überschreiben und daraufhin mit Bluescreen abstürzen. Sofort oder später. Man kann sicherlich so auch Fernseher und Set-Top-Boxen in die ewigen Jagdgründe schicken. Oder der Host entdeckt BABBLE byte-genau, und es passiert nichts.
Was ist Bandbreite bei USB? Megahertz?
Bandbreite ist hier gleichbedeutend mit Zeit. Denn USB arbeitet im Zeitschlitzverfahren (Zeitmultiplex), nicht im Frequenzmultiplex (wie Rundfunk). In Prozent angegeben: Zeitverbrauch (eines Transfers) innerhalb eines Frames.
Wie groß ist Endpoint 0 festzulegen?
Der Standard lässt hier keine krummen Werte zu. Man gibt so viel wie nach Aufteilung der anderen Endpoints noch übrig bleibt. Bei PIC16F145x sind 8 Bytes beliebt damit auch alle anderen Endpoints in der Speicherbank 0 liegen. Das erleichtert den Zugriff insbesondere in Assemblerprogrammen. 64 Bytes kann den Vorteil haben, dass alle Deskriptoren da hineinpassen. Oder zumindest fast alle bis auf den recht langen Device-Deskriptor.
Was passiert, wenn ein Urlader den Flash beschreibt und nichts passiert?
Die CPU wird für 5..10 ms angehalten, die Peripherie läuft weiter. Das bedeutet, dass entsprechend Puffer gelesen/gefüllt werden oder mit NAK beantwortet werden. Also alles in bester Ordnung. Im Sonderfall V-USB werden alle Transfers für diese Zeit überhört. Das kann zum Abbruch der USB-Verbindung führen. Das PC-Programm sollte den USB-Hostcontroller für diese Zeit möglichst nicht abfragen.
Wieviel Zeit habe ich, um Endpoint-OUT-Daten auszulesen? Werden diese nicht vom nächsten Endpoint-OUT irgendwann überschrieben?
Beliebig viel Zeit. Je nach Endpoint-Typ und Klassentreiber. Mindestens 2 ms. Der RAM wird niemals vom USB überschrieben, wenn er nicht wieder im Besitz der SIE ist. Den man erst übergeben darf, wenn man mit dem Auslesen fertig ist. Folgende Endpoint-OUT-Daten werden mit NAK quittiert. Der Host wird es daraufhin bei nächster Gelegenheit nochmal probieren. Erfolgt die Zeitschlitzplanung mit SOF, kann die Datenrate auf 1 Paket pro Millisekunde sinken. (Maximal möglich sind immerhin 13 Bulk-Pakete à 64 Byte pro Millisekunde, bei Full-Speed.) Möchte man das vermeiden, ist Doppelpufferung zu verwenden.
Wie funktioniert Doppelpufferung?
Grundsätzlich muss zwischen Controllern unterschieden werden, die die Doppelpufferung anhand der Daten-PID (DATA0 und DATA1) vornehmen (PIC, ATmega, vmtl. MSP430) oder selbständig organisieren (CY7C68013A). Letztere erkennt man daran, dass sie auch Dreifachpufferung unterstützen. USB diktiert dass Daten stets mit wechselnder Daten-PID übertragen werden. Die Ausnahmen sind SETUP (immer DATA0), Set Interface und Reset Stall (danach DATA0). Sowie Full-Speed-Isochronous-Transfer (immer DATA0). Im einfachen Fall gelangt DATA0 in den einen und DATA1 in den anderen Puffer. Nichts weiter. Während die CPU des Controllers den einen Speicherbereich ausliest bzw. füllt, kann die SIE den anderen Speicherbereich zugreifen. Einige Mikrocontroller können diese Speicherbereiche übereinander legen, andere nicht (PIC). Daher muss im letzteren Fall mit einem Zeiger gearbeitet werden, während im Fall „übereinander“ Datenstrukturen mit festen Adressen angesprochen werden können. Bei Implementierungen mit „1-Byte-Datenloch“ (ATmega mit UEDATX) entfällt das Problem mangels Datenzeiger.
Lohnt sich Doppelpufferung?
Ja solange noch genügend Pufferspeicher da ist und sich der Zugriff nicht unnötig erschwert (wegen der dann erforderlichen indirekten Adressierung bei verschiedenen RAM-Adressen). Alles was massiv Daten schaufelt (Massenspeicher, Audio u.ä.) profitiert davon. Bei HID ist der Vorteil eher gering.
Nein wenn die Programmgröße knapp ist, etwa bei Urladern. Auch bei reinen HID-Geräten kann man gut und gerne darauf verzichten.
Beim CY7C68013A gibt es noch ein GPIF. Wer besitzt die FIFOs dort?
Kompliziert! Da weiterlesen!
Wieviel Zeit habe ich, um Endpoint-OUT-Daten bei Doppelpufferung auszulesen?
Bei Full-Speed nur reichlich 50 µs, um die maximale Datenrate zu halten. Wenn länger sinkt diese — je nach Hostcontroller — auf bis zu 2 Pakete pro Millisekunde herab. Dabei hilft es nicht, wenn man bspw. 100 µs schafft, um 10 Pakete pro Millisekunde zu bekommen. Dann hat man 500 µs Zeit für die 2 Pakete pro Millisekunde. Bei modernen Host-Controllern, die in der Lage sind, während der Rest-Zeit eines 1-ms-Frames zu planen, werden OUT-Pakete öfter wiederholt.
Was bedeutet „planen“ bei USB?
Das betrifft nur Hosts. Hosts dürfen einen 1-ms-Frame nur so voll planen, dass der nächste SOF pünktlich ausgesendet werden kann. Vor dem SOF wird daher immer etwas zeitliche Reserve gelassen. Andererseits sollte der Frame so voll wie möglich sein. Bei Hi-Speed und SuperSpeed erfolgt die Planung analog in Mikroframes (125 µs).
Was passiert auf dem USB, solange ReadFile() auf USB-Bulk-IN-Daten wartet?
Ja, ReadFile() blockiert den Thread, und nichts passiert. Scheinbar! Der Host-Controller sendet in jeder Millisekunde mindestens eine Paketanforderung, die mit NAK beantwortet wird. Das geschieht in Hardware, die Host-CPU wird nicht belastet. Allerdings geht (bei älteren Hostcontrollern) USB-Bandbreite nutzlos verloren, weil die Zeit für den vollen Transfer für jeden USB-Frame am Anfang reserviert wird.
Was passiert mit Interrupt-Pipes (bspw. HID oder CDC)?
Der Treiber stellt unabhängig von seinem Aufrufzustand einen Datentransferkanal zur Verfügung, mit der er das Gerät in regelmäßigen Abständen (wie im Endpoint-Deskriptor angegeben oder kürzer, häufig 8 ms) Daten abfragt (IN) oder liefert (OUT). Im Fall HID liefert ReadFile() den zuletzt gelesenen Input-Report und blockiert IMHO nicht. Dasselbe bei WriteFile(), das den nächsten Output-Report setzt.
Wie zum Geier hole ich einen bestimmten Input-Report per Report-ID?
Völlig unterdokumentiert: In diesem Fall ist der Puffer von ReadFile() vom Typ INOUT! Das erste Byte wird vor dem Aufruf mit der gewünschten Report-ID belegt.
PIC: Kann man mehrere Pipes mit den gleichen oder überlappenden FIFO-Puffern bedienen?
Laut Datenblatt hat niemand etwas dagegen. Beim Endpoint 0 wird das so sogar vorgeführt. Man muss sich nur über die Konsequenzen im Klaren sein. Solange nur einer der zugehörigen BDT-Einträge den Puffer zur SIE zuordnet, kann gar nichts ungewöhnliches passieren. Selbst wenn's mehrere sind: USB ist seriell und auch die Transfers kommen im Gänsemarsch.
Kann man FIFOs im SIE-Besitz wieder „entreißen“ und der CPU zuordnen?
Im Fall USB-Reset und Reset Stall ist das ein normaler Vorgang. Im Normalbetrieb ist das jedoch keine gute Idee, weil nicht klar ist, ob das gerade entrissene Paket abgeholt bzw. gefüllt wurde (mit ACK) oder nicht. Es tritt eine sogenannte Wettlaufsituation (race condition) ein.

Beispiel: Kommen Daten byteweise tröpfelnd von einer seriellen Schnittstelle und man will sie in möglichst große USB-Happen packen, müsste man diese in die USB-FIFO stecken und (um Latenzzeiten zu vermeiden) sofort die FIFO der SIE übergeben. Kommt das nächte Byte, müsste man das nächste Byte anhängen und die Länge erhöhen. Das ist so nicht erlaubt! Obwohl es meistens funktioniert. Ganz böser Fehler! Lösungen:

Was bedeutet Stall?
Strömungsabriss. Wie beim Flugzeug: Es beginnt zu trudeln und abzustürzen.
Wann darf der Mikrocontroller Stall setzen?
Beim Endpoint 0: Bei fehlerhaften Requests muss Stall gesetzt werden. Wird beim nächsten SETUP-Paket automatisch (von der SIE-Hardware) zurückgenommen.
Bei jedem anderen Endpoint: Wenn der Mikrocontroller bei der Datenauswertung auf Unstimmigkeiten trifft und der Klassentreiber das Zurücknehmen von Stall unterstützt. hid.sys und usbcdc.sys (serielle Schnittstelle) tut das nicht! Hingegen usbmsc (Massenspeicher) schon. Beim selbstgeschriebenen Treiber muss man's entsprechend einbauen.
Wann darf der Mikrocontroller Stall wegnehmen?
Nie. Außer beim entsprechenden Clear Feature. Es ist unüblich, zusätzlich zum Clear Feature einen Vendor-Request einzusetzen, der das gleiche tut.
Wie kann ich dafür sorgen, dass mehrere USB-Geräte gleichzeitig Daten abtasten bzw. ausgeben?
Abtasten: Beim Start-Of-Frame (SOF) abtasten und die Werte mitsamt Frame-Nummer in den Endpoint-In-Puffer ablegen. (Mit Low-Speed funktioniert das nicht so einfach: Da muss man die gemeinsame Frame-Nummer möglichst gleichzeitig an alle Geräte übermitteln.) Sind Abtastraten höher als 1 kSa/s erforderlich, ist entsprechend per SOF eine Interruptquelle zu synchronisieren, die die Abtastung einleitet. Auch eine USB-asynchrone Abtastung ist machbar, wenn die Abtastzeitspanne nach dem SOF gemessen und übermittelt wird. MSP430 bietet genau für diesen Zweck einen Sub-SOF-Zähler an. Dann hat die Auswertesoftware eine Chance, die Werte exakt zu synchronisieren. Das alles funktioniert sogar mit USB-CDC (Emulation serielle Schnittstelle, also bspw. Arduino Leonardo), aber nicht mit vorgesetztem USB-Seriell-Wandler (Arduino Uno).
Ausgeben: Der PC sollte die Daten mitsamt Frame-Nummer dem Endpoint-Out-Puffer übergeben, und das Mikrocontroller-Programm mit dem Ausgeben solange warten, bis die SOF-Nummer stimmt. In Verbindung mit synchroner Abtastung ergeben sich feste Totzeiten etwa für ein Regelglied. Es gelten analoge Vorgehensweisen wie bei der Abtastung. Will man nur ausgeben, hilft Sync Frame, die aktuelle Frame-Nummer in Erfahrung zu bringen, und addiert eine kleine Konstante, um mit einem Zeitversatz mit der Ausgabe auf alle Geräte zu beginnen.

Wichtig: Die USB-Geräte müssen am gleichen Host-Controller hängen. Um das sicher zu stellen, benutzt man einen USB-Hub. Bei verschiedenen Host-Controllern kann nicht sicher gesagt werden, dass die SOFs gleichzeitig kommen oder die gleiche Nummer haben. Was zu untersuchen wäre.

Wie kann ich dafür sorgen, dass verschiedene Geräte (USB und Nicht-USB oder solche ohne Synchronisation per Frame-Nummer) gleichzeitig abtasten?
Gar nicht! Das Kind ist in den Brunnen gefallen. Sorry. Es hilft nur die berühmt-berüchtigte Synchronisationsleitung oder ein unäres Ereignis, das sich auf allen Sensoren widerspiegelt.
Achtung: Das ist aktueller Industriestandard! „Weil's koa USB hamm.“ Beim Beobachten von Crash-Tests mit mehreren Kameras und Sensoren sorgt ein Lichtblitz mit gemeinsamer Spannungsspitze auf den Dehnmessstreifen-A/D-Wandlern für die nachträgliche Synchronisierbarkeit aller High-Speed-Kameraaufnahmen mit den Messwerten. Willkommen im letzten Jahrtausend. Wenn die Pistole den Marathonlauf startet.

Endpoint-Typen

Wozu mehrere Endpoints? Reicht es nicht, alles über Endpoint 0 abzuwickeln?
Für Geschwindigkeit. Der CONTROL-Endpoint 0 ist in seiner Konstruktion langsam und mit hoher Übertragungssicherheit. Vor allem dient dieser der PnP-Fähigkeit von USB. Alles was irgendwie schnell gehen soll wird über gesonderte Endpoints abgewickelt. So verlangen auch die USB-Klassen die Verwendung bestimmter zusätzlicher Endpoints. Einzig zum Herumbasteln (eigener oder generischer Treiber) sowie für die USB-Klasse HID kann man ohne zusätzliche Endpoints auskommen.
Wieviel Endpoints gibt es?
16 laut USB-Standard. Macht 31 Pipes. Viele Mikrocontroller bieten weniger. Die Einschränkung verringert die Implementierungsmöglichkeit von Multifunktionsgeräten. Mindestens wird Endpoint 0 unterstützt. Mit nur diesem Endpoint kann nur die HID-Klasse oder Eigenbau unterstützt werden.
Kann Endpoint 1 Out und Endpoint 1 In gleichzeitig aktiviert und verwendet werden?
Laut USB-Standard ja. Mikrocontroller können dies einschränken und für jede Richtung eine andere Nummer verlangen. Beispielsweise alle AVRs (Arduino Leonardo) und CY7C68013A. In einigen Beispielen sieht man das so für Controller, die gleiche Nummern können. Daher ist es nicht erforderlich aber üblich, dass logisch zusammengehörige Pipes (etwa Data-In und Data-Out) auf der gleichen Endpoint-Nummer arbeiten. Auch kann es sein, dass bei Doppelpufferung einige Endpoint-Nummern tabu werden. Muss man im Datenblatt genau nachlesen, was dann noch übrig bleibt.
Welcher Vergleich zu Netzwerken drängt sich bei den verschiedenen Endpoint-Typen auf?
Isochronous entspricht weitestgehend UDP. Alles andere entspricht TCP, allerdings nur in eine Richtung, wenn für Bulk oder (viel häufiger) Interrupt nur eine Pipe exisitiert. Bulk und Interrupt unterscheiden sich aus Gerätesicht sowie in höheren Treiberschichten des Hosts so gut wie gar nicht voneinander.
Kann man zwei korrespondierende Bulk-Endpoints (einmal OUT, einmal IN) dazu verwenden, um sich ein völlig eigenes Datenübertragungsprotokoll auszudenken?
Das ist der Normalfall! Also ähnlich wie bei der althergebrachten seriellen Schnittstelle. Fast alle USB-Geräteklassen machen das genauso, wobei dann die übertragenen Daten ebenfalls noch definiert sind. Etwa USB-MSC (USB-Massenspeicher): Hier sind die übertragenen Daten eingepackt in Rahmen aus SCSI-Steuerkommandos. Auf dem Endpoint 0 passiert nach dem Anstecken nichts mehr.
Kann man Bulk-Pipes auf Low-Speed-Geräten anmelden?
Nein, besser nicht. Das ist nicht standardkonform. Bis zu Windows XP funktioniert das, dann nur noch mit Hilfstreibern. Inzwischen sind genügend Mikrocontroller mit eingebautem Full-Speed-USB-Controller verfügbar.
Kann man statt USB-klassenkonformen Bulk-Pipes auch Interrupt-Pipes anmelden?
Gute Idee, weil diese beiden Typen vom Host beinahe gleich behandelt werden. Nein, besser nicht: Die Klassentreiber prüfen das ab und verweigern die Kommunikation mit einem solchen Gerät.
Kann man statt USB-klassenkonformen Iso-Pipes auch Bulk- oder Interrupt-Pipes anmelden?
Nein, das geht nicht. Die Klassentreiber verweigern die Kommunikation mit einem solchen Gerät. Mit der USB-Schnittstelle der MSP430F5xxx kann man keine Audiogeräte bauen.
Kann man bei Full-Speed und USB 2.0 auch größere Bulk-Längen als 64 Byte angeben?
Das wird vom Host-Controller oder vom Host-Controller-Treiber abgelehnt. Aus gutem Grund, weil das Bandbreite verschwendet, die anderen Geräten am gleichen Host fehlen kann.

Datenverarbeitung

Braucht man für USB Interrupts? Anscheinend jedes Framework nutzt diese.
Nein! Nein! Nein! USB funktioniert sehr gut ohne Interrupts. Halten Sie die Interrupts für wirklich zeitkritische Routinen frei, wie Software-PWM oder Überstromüberwachung am Analogvergleicher. Klar, ohne Interrupts muss die Hauptschleife des Mikrocontroller-Programms (Firmware) zyklisch usbPoll() o.ä. aufrufen, um eingehende Daten, insbesondere Setup-Transfers zu bedienen. Im speziellen Fall eiliger Iso- und Bulk-OUT-Behandlung (immer mit Doppelpufferung, sonst sinnlos) sind Interrupts für diese Pipes dennoch sinnvoll, aber für Setup-Transfers sind Interrupts schlichtweg Overkill.
Ist es eine gute Idee, bei USB-Reset den Mikrocontroller zurückzusetzen?
Das kommt auf die Aufgabe des Controllers an. Ein Reset kann vorbeikommen, wenn ein anderes Gerät am Bus angesteckt wird. Ein Reset kommt (IMHO) stets beim Aufwecken des Computers vorbei. Bei HID-Geräten wie Joyticks oder LED-Anzeigen ist das sinnvoll, bei komplexeren Geräten eher nicht. Grundsätzlich sollte USB-Reset nicht zum Mikrocontroller-Reset benutzt werden, wenn der Controller eine gesonderte Speisung hat, nicht vom USB gespeist wird.
Wie erreiche ich den USB-Ruhezustand, ohne den Computer herunterzufahren?
Im Geräte-Manager „Gerät deaktivieren“. Das genügt. Das Aufwecken erfolgt mit „Gerät aktivieren“.
Was ist beim USB-Ruhezustand zu tun?
Busgespeiste Geräte: Es ist alles zu tun, um die Stromaufnahme auf unter 500 µA zu drücken. Da der Pullup bereits 200 µA verbrät, bleiben für den Controller und dessen angeschlossene Hardware 300 µA übrig. In der Regel ist und man sollte die Stromaufnahme kontrollieren. Oftmals fressen nicht ganz auf 0 V bzw. 5 V liegende Eingänge zu viel Ruhestrom. Daher ist ein gutes Konzept der Spannungspegel Voraussetzung für einen einwandfreien USB-Schlafmodus.
Selbstgespeiste Geräte: Alle USB-Aktivitäten sind einzustellen, alles andere kann weiterlaufen. Beim Aufwecken kommt ein USB-Wakeup vorbei, dann geht es weiter.
Bei der Kernmodus-Treiberprogrammierung taucht als Bulk-Endpoint-Größe 4 KByte auf, nicht die 64 Byte der Mikrocontroller-FIFO. Erklärung?
Der USB-Hostcontroller kann automatisch Pakete zusammensetzen bzw. zerteilen. Diese Angabe ist schlicht die maximale Blockgröße, die der Gerätetreiber der nächsttieferen Schicht durchreichen darf bzw. erwarten kann. Es ist genau eine Speicherseite vom „nonpaged pool“.

Endpoint 0

Genügt es, bRequest auszuwerten und auf bmRequestType zu verzichten?
Für einfache Geräte: Ja. Muss man Set Feature, Clear Feature oder Get Feature mit Endpoint-Stall bearbeiten, muss man bmRequestType zumindest dafür auswerten. Die meisten klassenspezifischen bRequest überlappen sich nicht mit den Standard-Werten, sodass man auch dafür auf bmRequestType verzichten kann.
Kann man bmRequestType und bRequest nicht gleich als einen 16-Bit-Wert in die switch/case-Anweisung stecken?
Für 16- und 32-bit-Controller ist das sogar am besten! Der USB-Standard definiert genau, welche Werte für bmRequestType und bRequest zusammengehören. Bei 8-bit-Controllern kann das zu umständlichem Kode führen. Ist genügend Kodespeicher vorhanden, sollte bmRequestType in einem äußeren switch/case und die Auswertung von bRequest in einigen inneren switch/case eingebaut werden.
Kann man auf Set Feature, Clear Feature oder Get Feature mit Endpoint-Stall verzichten?
Wenn man keine weiteren Endpoints hat, ja, braucht man nicht. Wenn die Daten der Endpoints weder vom Controller noch vom Host „ausgewertet“ werden, ja, werden nie aufgerufen. Beispielsweise bei den Klassen HID (Human Interface Device) und CDC (Serielle Schnittstelle).
Kann man alle Setup-Pakete mit ACK bestätigen? Oder muss man auch mal Stall für Endpoint 0 melden?
Um USB-standardkonform zu sein, muss ein Paket mit einem ungültigen bRequest mit Stall beantwortet werden. Kompatibilitätstest-Software prüft dies ab, und künftige Betriebssysteme könnten dies auch testen und „blöde“ Geräte ablehnen.
Kann man auf die Auswertung von wLengthH (High-Byte von wLength) verzichten?
Nein! Windows Vista und neuer holen sich den Device Descriptor mit einer Puffergröße von 0x109 Bytes ab. Geräte, die wLengthH ignorieren, senden „folgerichtig“ nur 9 Bytes und verhalten sich daraufhin fehlerhaft.
Kann man auf die Limitierung der IN-Daten auf wLength verzichten?
Grundsätzlich nein. Der allererste SETUP-Transfer ist ein Get Descriptor (Device) mit 8 Byte Länge. Dadurch erfährt der Host die Größe der EP0-FIFO, die nach Standard minimal 8 Byte und maximal 64 Byte (bei Full-Speed) groß sein kann. Man kann aber auch den Datenausstoß mit dem nächsten SETUP-Transfer einstellen.
Kann der Host das Übertragen von IN-Daten terminieren, indem er vorzeitig ein Null-Byte-OUT-Paket oder das nächste SETUP-Paket schickt? Kann das Device das Übertragen von OUT-Daten stoppen, indem es ein Null-Byte-IN-Paket schickt?
Der Standard sieht das nicht vor, aber es ist besser, man ist davor gewappnet. Denn sonst kann sich der Mikrocontroller beim Senden langer Deskriptordaten verklemmen. (Im Fall einer modalen Schleife, weil der Host keine IN-Daten mehr abholt.)
Der letztere Fall sollte nicht programmiert werden.
Muss ich abprüfen, ob wLength stimmt? Etwa ob bei Get Feature wLength==2 ist?
Nein. Kein einziges USB-Beispiel macht dies vor. Man darf sogar 2 Bytes absenden, obwohl wLength==1 ist. Auf der sicheren Seite ist man jedoch, wenn man auch hier wLength abprüft und die Antwortlänge entsprechend begrenzt. Der Transfer sollte bei Unstimmigkeit nicht mit Stall abgebrochen werden.
Wie legt man am besten Deskriptoren im Speicher ab? Variable Structs machen sich in C/C++ nicht allzu vorteilhaft …
Als Byte-Array hat sich am besten bewährt. Um das Abzählen von Bytes kommt man sowieso nicht herum. Für 16-Bit-Werte benutzt man am besten ein Makro:

#define W(x) (BYTE)(x),(BYTE)((x)>>8)

Der Speicherort sollte im Flash-Speicher liegen, um nicht wertvollen RAM damit zu verschwenden. Bei AVR muss man auf die gesonderte Adressierung achten (führt zu LPM statt LD), bei PIC ist das Bit 15 der Adresse zu setzen (Flash-Speicher ist „oben“ eingeblendet). 16- und 32-Bit-Prozessoren haben keine Adressierungsbeschränkung.
Welchen Kode benötigt ein Mikrocontroller mimimal, um korrekt erkannt zu werden?
Achtung: Die Minimalfirmware hat den Nachteil, nicht mit Klassentreibern zu funktionieren. Mit dem geringsten Aufwand kann man ein HID-Gerät daraus machen. Dazu muss man sich aber mit HID-Deskriptoren auseinandersetzen und die Firmware bereits erheblich ändern.
Funktionieren mehrsprachige String-Deskriptoren?
Ja, ab Windows 8 sowie in Linux. (?) Alles andere kann nur englisch oder zeigt den einzigen Deskriptor. Mit Umlauten haben viele Programme Probleme, Windows nicht. Der String sollte nicht mit Weißraum beginnen oder enden und darf keine Steuerzeichen (etwa Zeilenvorschub) enthalten.
Wo sieht der Anwender die String-Deskriptoren?
Unter Windows wird nur der Deskriptor von iProduct bzw. von iInterface (bei Mehrfach-Geräten) angezeigt. Und zwar nur solange der passende Treiber noch nicht gefunden wurde. Also beim ersten Anstecken sowie bei der Treibersuche. Sobald eine INF-Datei passt, wird der String von dort übernommen.
Unter Linux wird mit lsusb sowohl iVendor als auch iProduct angezeigt.
Bei WebUSB-Geräten (unter Windows nur unter Google Chrome) wird aus fadenscheinigen Sicherheitsgründen gar kein String-Deskriptor angezeigt, sondern auf eine globale Datenbank aus Vendor-ID und Produkt-ID zurückgegriffen! Deshalb zeigen die üblichen Bastellösungen stets "voti" an.
Wie kann man den String-Deskriptor gut les- und editierbar im Quelltext ablegen?
In Assembler: Mit einem irpc-Makro, sofern vorhanden. (irpc bedeutet „Wiederhole mit jedem Zeichen des Strings“.)
In C oder C++: Am besten dynamisch mit einer Funktion aus einem nullterminierten UTF-8-String generieren. Das spart bei langen Strings sogar Platz. Benutzt man stattdessen L"string" im Deskriptor, muss man die Zeichen dennoch genau abzählen.
Kann man auf String-Deskriptoren verzichten, wenn der Platz knapp ist?
Ja. Alle Indizes werden auf 0 gesetzt. Klar dass der Anwender dann bei der Treibersuche keine sinnvolle Information sieht.
Ausnahme: Verlangt der Klassentreiber eine Seriennummer (usbmsc = Massenspeicher), müssen String-Deskriptoren geliefert werden.
Genügt bei der Sprachselektion die Auswertung des Low-Bytes (wIndexL)?
Ja. Kaum jemand wird etwa zwischen „Deutsch (Deutschland)“ und „Deutsch (Schweiz)“ unterscheiden. Sprach-IDs jenseits von 0xFF sind längst nicht definiert. Mehrsprachige String-Deskriptoren sind ohnehin schon Luxus.
Wer hat bloß das hässliche Format des String-Deskriptors erfunden? Ginge es nicht gleich mit UTF-8?
UTF-8 war beim Festlegen des USB-Standards entweder noch nicht erfunden oder den Entwicklern leider unbekannt. Unicode steckte noch in den Kinderschuhen, und es gab nur 16-Bit-Unicodes, später BMP genannt. Der USB-Standard pendelt zwischen BMP und UTF-16. Da echtes UTF-16 kaum von jedem Host unterstützt wird (man denke an Handys und Fernseher), sollte man bei USB-Geräten derzeit auf Surrogates und Emojis verzichten.
Im Fall eines TI-Mikrocontrollers der C2000-Serie, der aus den TMS320 erwachsen ist, ist UTF-16 sogar die beste String-Kodierung. Denn dieser kann keine Bytes adressieren, sizeof(char) == sizeof(int) == sizeof(wchar_t) == 1 !!
Wie kann ich prüfen, ob der Device Descriptor stimmt?
Ein ungültiger Device Descriptor führt zum Versagen des USB-Gerätes. Am besten genau hingucken. Leider gibt es für das Versagen sehr viele Ursachen, deshalb gewissenhaft programmieren!
Wie kann ich prüfen, ob der Configuration Descriptor stimmt?
Ein ungültiger Configuration Descriptor führt zum Problem beim Laden von Gerätetreibern. Hier hilft usbtreeview bzw. lsusb weiter, kann Fehler analysieren.
Kann bei Set Address die Adresse 0 oder größer 127 kommen?
Adresse 0: Theoretisch ja, praktisch nein. Die Adresse ist zu übernehmen, keine Sonderbehandlung.
Adresse > 127: Theoretisch ja, praktisch nein. Die unteren 7 Bit der Adresse sind zu übernehmen (MSB ignorieren), keine Sonderbehandlung. Der USB-Standard legt sogar wValue = Adresse fest, also 16 Bit. Wohl um sich eine Tür für Erweiterungen offenzuhalten.
In welcher Reihenfolge kommen Setup-Transfers?
Das legt die Spezifikation nicht genau fest, aber üblich ist: Wie man sieht, kommt standardmäßig kein Set Feature (Interface und Endpoint) vorbei. Bei High-Speed ist's komplizierter. Low-Speed und Full-Speed unterscheiden sich nicht.
Was sind Toggle-Bits?
Alle Transfers arbeiten mit wechselnden Daten-PIDs, DATA0 und DATA1. Damit kann eine Fehlübertragung detektiert und (außer bei Iso) wiederholt werden. Im Mikrocontroller wird das Bit entsprechend per Pipe reflektiert, heißt da Toggle-Bit, und muss stets korrekt gesetzt werden. Merke:
Was ist eine Pipe?
Ein Daten-Rohr. Endpoint 0 wird als eine Pipe verstanden, durch die Daten in beide Richtungen geschickt wird. Allen anderen Endpoints werden pro Richtung eine Pipe zugeordnet.
Kann SETUP zu einem anderen Endpoint als EP0 geschickt werden?
Im USB-Standard habe ich nichts gefunden, was das verbietet, aber gesehen habe ich das noch nie.
Wann sind Alternate Settings (mehr als 1) üblich oder notwendig?
Nur in Verbindung mit isochronen Endpoints, also Audio- und Video-Geräte.
Gibt es unbenutzte PIDs?
Bei Hi-Speed sind alle 16 Kombinationen aufgebraucht. Ein Host sendet jedoch niemals NAK oder NYET, ein Device niemals SETUP.
„Sieht“ ein USB-Gerät Daten, die an andere Geräte adressiert sind? (Wozu die USB-Adresse? Und wozu der Hub?)
Bei gleicher Geschwindigkeit am gleichen USB-Hub: Ja, kann sein, sonst nein. Die SIE muss falsch adressierte Pakete und Konversationen aussieben.
Stimmt es, dass Low-Speed-Geräte andere USB-Geräte ausbremsen?
Am Full-Speed-Hub: Ja. Am Hi-Speed-Hub: Nein, nicht mehr. Da Full-Speed-Hubs nahezu ausgestorben sind, braucht man keine Bange bei Low-Speed-USB-Geräten zu haben.
Was muss man wann machen? (Ausgehend von einem busversorgten Gerät mit 1 Interface und 2 einfach gepufferten Bulk-Pipes, dies betrifft die meisten USB-Geräteklassen.)
Was ist anders bei einem selbstversorgten Gerät?
Ein selbstversorgtes Gerät ist dazu da, auch ohne USB aktiv zu sein. Der Klassiker dafür ist eine Digitalkamera mit USB. Oder ein Digitaloszilloskop. Grundsätzlich ist zu entscheiden, ob mit USB das Gerät, mithin das Mikrocontrollerprogramm, in einen anderen Modus fällt (man kann nicht fotografieren) oder ob beides gleichzeitig möglich ist (das Oszilloskop funktioniert weiter).
Kann man ein USB-Gerät an einen schlafenden PC anstecken und ihn damit aufwecken?
Nein. Jedenfalls nicht mit dem K-Buszustand „Remote Wakeup“. Das ist nicht im USB-Standard vorgesehen. Aufwecken darf nur jemand, der vor dem Einschlafen angesteckt war und dem das (per Set Feature) erlaubt wurde. Ob man ein solches Gerät zwischendurch abziehen und anstecken kann ist unklar; der Host sollte eigentlich trotz Tiefschlaf das SE0 (statt J = Idle oder K = Remote Wakeup) erkennen und den USB-Anschluss für den späteren J-K-Übergang sperren. Typischerweise und dem USB-Standard entsprechend „vergisst“ das abgezogene Gerät die Erlaubnis, Remote Wakeup auszulösen.

Ein PC kann jedoch so konfiguriert sein (je nachdem was das BIOS kann), dass jegliches Anstecken eines USB-Gerätes (Übergang von SE0 nach J) zum Aufwecken führt. Da ist mir aber kein Exemplar bekannt.

Kann ein USB-Gerät mal selbstversorgt, mal busversorgt sein? Etwa wenn über USB ein Stützakku geladen wird, der irgendwann voll ist.
Ja.
Machen das Powerbanks (USB-Akkupacks) genauso?
Nein. Die sind „blöd“ und damit nicht USB-standardkonform. Das merkt man daran, dass sich beim Anstecken am PC kein Gerät anmeldet. Daher wird man auf dem Gehäuse oder der Verpackung niemals USB-Logos finden. (Das Anbringen von Logos erfordert USB-Standardkonformität.) Solche Dinger dürfen eigentlich nur an USB-Netzteilen geladen werden.

Gerätetypen

Welche Gerätetypen stehen zur Verfügung, um sich das Schreiben eines Gerätetreibers zu ersparen und das Gerät möglichst nahtlos ins System zu integrieren?
Die meisten hier aufgeführten Klassen erfordern Bulk-Pipes. Wenn keiner dieser Typen passt, kann man auch einen eigenen Typ generieren. Will man keinen Gerätetreiber schreiben, gibt es Universaltreiber, entweder vom Chiphersteller oder libusb. Hat man genug Platz für Deskriptoren, ist WinUSB/WebUSB der bessere Ausweich.
Um zwei HID-Geräte mit einem Mikrocontroller zu imlementieren, brauche ich dazu zwei (logische) USB-Interfaces mit je einem HID-Report-Deskriptor oder ein Interface mit einem HID-Report-Deskriptor mit zwei Top-Level-Collections?
Beides ist möglich! Für beste Kompatibilität zu einfachen Geräten ist erstere Variante zu bevorzugen. Ist der Host stets ein PC mit laufendem Betriebssystem (also nicht im sog. Down-Level- oder Boot-Modus), bietet sich die zweite Variante an. Man sieht die Unterschiede im Geräte-Manager am deutlichsten bei „Ansicht“ — „Geräte nach Verbindung“.
Wieviele Geräte (= USB-Interfaces) kann man in einen USB-Mikrocontroller stecken?
So viele bis keine Pipes mehr zur Verfügung stehen. Meistens ist jedoch die Rechenleistung oder der Flash-Speicher des Controllers das wirksamere Limit. Da HID-Geräte weder zusätzliche Endpoints noch allzu viel Rechenleistung brauchen, kann man davon (und nur davon) sehr, sehr viele einbauen. Vermutlich werden Hosts und Deskriptorgrößen das Ganze bei 255 beenden.
Gibt es USB-Router? Also so etwas ähnliches wie USB-Hubs, die mehrere untergeordnete Geräte zu einem Multifunktionsgerät zusammenfassen können? (Also in einem höheren OSI-Level.)
Bis jetzt hat so etwas noch niemand gemacht. USB ist dafür auch nicht vorgesehen. Abgesehen davon, die Grenze von 127 Geräten aufzubrechen und eine gesicherte Synchronisation untereinander zu erreichen, wozu sollte das gut sein? Richtig knifflig wird das Ganze, wenn herstellerspezifische Treiber auf VID&PID pochen. Denn ein Gerät kann nur eine VID&PID haben.
Muss für eine bestimmte Geräteklasse die Endpoint-Nummer stimmen? (Etwa bei CDC Daten-In = EP1IN und Daten-Out = EP1OUT)
Nein. Das legt der Konfigurations-Deskriptor fest.
Wie findet das Betriebssystem den passenden Treiber?
Das hängt von verschiedenen Konstellationen ab:
Windows: Wieso brauche ich für USB-CDC-Geräte einen Treiber, für USB-Sticks nicht? Bei Linux gibt's diesen Ärger nicht.
Gute Frage! Denn bei Windows 98 brauchte man auch für jeden USB-Stick einen Treiber. Das ist Microsoft-Politik! Windows will die serielle Schnittstelle unbeliebt machen und so verdrängen, wie sie auch schon die parallele Schnittstelle durch simple Nichtunterstützung im Betriebssystem verdrängt hat. Der Gerätehersteller ist durch Vertrag gebunden, die INF-Datei per VID+PID zu binden, und nicht per Geräteklasse. Gibt man Windows diese finale INF-Datei, findet es fortan alle USB-Seriell-Konverter. Bei Windows 98 hatte das mit dem USB-Stick auch geklappt.
Wie kann ich erzwingen, dass ein Hi-Speed-Gerät mit Full-Speed arbeitet?
Deaktivieren Sie im Windows-Gerätemanager den EHCI-Hostcontroller. Alle da angeschlossenen USB-Geräte bekommen einen USB-Reset und fallen auf Full-Speed zurück; der OHCI- oder UHCI-Hostcontroller übernimmt. Das kann man fallweise benutzen, um die Zuverlässigkeit zu erhöhen oder fehlerhafte Hi-Speed-Firmware (während der Firmware-Entwicklung) zu umgehen.
Wie kann ich erzwingen, dass ein Full-Speed-Gerät in Low-Speed arbeitet?
Gar nicht! Man kann bei manchen Controllern die Firmware auf Low-Speed ändern. Das wechselt die Datenrate der SIE und den Anschluss des Pullup-Widerstands.
Bietet Low-Speed irgendwelche Vorteile?
Ja. Heutzutage ist nur noch das Kabel entscheidend für die Entscheidung für Low-Speed. Und für Freaks die Möglichkeit das Gerät etwa in einen ATtiny10 zu pressen.
Kann man USB-Kabel verlängern?
Der Standard erlaubt das nicht: Ein Kabel mit A-Stecker und A-Buchse ist verboten. Es sei denn, da ist ein USB-Hub enthalten. Jedes Kabel ist genau so dimensioniert, dass der Querschnitt für Spannungsabfall und Signaldämpfung gerade so reicht. Ein Kostenfaktor!

Ein Kabel mit A-Stecker und A-Buchse, das zu einem USB-Gerät mitgeliefert wurde, ist nur zur Verwendung mit genau diesem Gerät bestimmt.

Möchte man das angewachsene, superkurze und dünne Kabel eines Hi-Speed-USB-Hubs verlängern, nimmt man dafür am besten geschirmtes CAT5+-Patchkabel und benutzt:

und kann damit einige 10 Meter überbrücken und trotzdem einige USB-Speichersticks daran betreiben. Ich habe das mit 15 m Kabel ausprobiert und den USB-Anschluss eines Schaltschrank-PCs in das Maschinenbedienpult (HMI) einer parallelkinematischen Fräsmaschine verlängert. Mit Verlegekabel (Nicht-Litze) bei Festinstallation des Hubs dürfte es noch besser funktionieren.

Ist das Kabel ungeschirmt, teilt man UBUS und Masse auf je 3 Adern auf. Damit ist der Spannungsabfall höher aber immer noch erträglich.

Kann man USB-Kabel potenzialtrennen?
USB ist nicht für Potenzialtrennung entwickelt. Für solche Anwendungen bevorzugt man Ethernet, Potenzialtrennung per Transformator. Ethernet ist dazu extra gleichspannungsfrei entwickelt worden. Inzwischen gibt es fertige USB-Potenzialtrennungs-ICs, IMHO allerdings nur für Full-Speed (12 MBit/s), sowie Geräte dafür. Ein Eigenbau dafür lohnt sich nicht.
Ist Potenzialtrennung am Gerät gewünscht, erfolgt das zumeist über einen USB-Seriell-Wandler im Gerät (gleich hinter der USB-B-Buchse) und die Isolierung an der seriellen Schnittstelle per Optokoppler. Das ist leicht aufzubauen und ähnelt dem Arduino Uno.