Die Kommunikation zwischen Mikrocontroller und PC ist erfolgreich hergestellt worden, was uns in die Lage versetzt Daten über USB zwischen zwei Geräten hin- und herzusenden. Um eine universelle Lösung zu erhalten, ist es aber notwendig, dass man auf bereits etablierte Protokolle und Treiber zurückgreift, sodass ein einzelner Treiber Geräte von unterschiedlichen Herstellern bedienen kann. Ich möchte, beginnend mit diesem Teil, zeigen, wie eine USB-Maus über den Mikrocontroller umgesetzt werden kann.
Eingabegeräte, wie z. B. Mäuse, Tastaturen und Joysticks, werden beim USB-Protokoll als HID (Human Interface Device) behandelt und entsprechend klassifiziert. Viele Betriebssysteme besitzen gängige Treiber um mit verschiedenen Eingabegeräten, wie z. B. Mäuse, umgehen zu können. Die Spezifikationen für die HID-Klasse des USB-Protokolls hat das USB-IF in dem Dokument Device Class Definition for Human Interface Devices (HID) zusammengetragen.
Klassenspezifische Gerätebeschreibungen:
Um ein Gerät der HID-Klasse zuzuordnen wird das Feld bInterfaceClass
des Interface-Deskriptors auf den Wert 0x03 (USB_CLASS_HID
) gesetzt.
Hinweis:
Die vergebenen Klassencodes und der entsprechende Deskriptor für die Konfiguration können beim USB-IF eingesehen werden.
Für jedes HID-konforme Interface muss dann noch ein entsprechender HID-Deskriptor angelegt werden.
Zeige den HID-DeskriptorDieser HID-Deskriptor wird dann um beliebig viele Deskriptoren (mindestens aber einen Report-Deskriptor) erweitert.
Beide Deskriptorarten werden genutzt um das Eingabegerät und die Datenstruktur des Gerätes möglichst genau zu beschreiben. Der Host wählt dann, basierend auf diesen Informationen, einen geeigneten Treiber aus und lädt diesen.
Über das Feld bInterfaceSubClass
kann zudem definiert werden ob das Gerät bereits während des Bootvorgangs genutzt werden kann.
Info:
Dieser Mechanismus wurde eingeführt, weil das Parsen des kompletten Report-Deskriptors immens viel Code benötigt und bei alten Systemen nicht komplett in den BIOS-Code eines Computers implementiert werden konnte. Daher können Eingabegeräte ein alternatives Boot-Interface aktivieren, um ein definiertes Protokoll zu verwenden. Dadurch sparte man sich einen generischen Parser und konnte stattdessen einen schlanken Parser für das eigene Protokoll implementieren.
Dies führe zu dem Umstand, dass man bei älteren Computern oftmals das Problem hatte, dass USB-Eingabegeräte während des Bootens nicht funktioniert haben. Bei modernen Computern ist das zum Glück kein Problem mehr.
Das Feld bInterfaceProtocol
wird in Kombination mit dem bInterfaceSubClass
-Feld genutzt um dem System während des Bootens mitzuteilen um welche Art von Eingabegerät es sich bei dem verwendeten Boot-Interface handelt.
Aufbau der Kommunikation:
Die Klasse der HID-Geräte unterstützt sowohl Low-Speed, als auch High-Speed Geräte. Für die Kommunikation zwischen Treiber und Gerät werden folgende Pipe- (bzw. Endpunkt-) Konfigurationen genutzt:
Jedes Gerät aus der HID-Klasse benötigt zwei feste und einen optionalen Endpunkt, die für die Kommunikation mit dem Host genutzt werden:
Der Host hat, neben den Standard-Requests, Zugriff auf klassenspezifische HID-Requests, mit denen er das Gerät steuern kann. Für die klassenspezifischen Requests werden die Bitfelder Type
und Recipient
im Feld bmRequestType
auf 0x01 gesetzt.
Der GET_PROTOCOL– und der SET_PROTOCOL-Request können nur zusammen mit der Boot-Interface Unterklasse genutzt werden.
Über den GET_IDLE– und SET_IDLE-Request wird die Pollrate für das Gerät eingestellt, was dazu führt, dass das Gerät jede frühere Anfrage mit einem NAK beantwortet. Für die Pollrate können Werte von 1 – 255 übertragen werden. Die Auflösung beträgt 4 ms, woraus ein Intervall von 4 bis 1020 ms resultiert. Wird ein Wert von 0 übertragen, so wartet der Endpunkt mit der Datenübertragung bis sich die Daten geändert haben. Die Genauigkeit der Zeit beträgt ±(10% + 2 ms).
Für eine erfolgreiche Kommunikation mit einem Gerät aus der HID-Klasse muss der Host das Format der empfangenen oder zu sendenen Daten kennen. Dieses Datenformat unterscheidet sich je nach Art des Eingabegerätes. So werden bei einer Maus deutlich weniger Daten benötigt als bei einer Tastatur. Damit die HID-Klasse möglichst alle Eingabegeräte abdecken kann, wurde der Report-Deskriptor eingeführt. Dieser Deskriptor beinhaltet genaue Angaben über das Ein- und Ausgabeformat eines Eingabegerätes. Sobald der Host den Report-Deskriptor ausgelesen hat, kann er über den GET_REPORT-Request einen Report von dem Gerät abfragen oder mittels eines SET_REPORT-Requests Daten zum Gerät senden.
Achtung:
Der GET_REPORT-Request verwendet den Kontrollendpunkt anstatt des entsprechenden Interruptendpunktes zur Kommunikation und soll nur verwendet werden, wenn Daten unabhängig vom Pollingintervall beim Gerät angefragt werden müssen.
Ein Report besteht aus einem Datenfeld mit einer Länge n, sowie einer optionalen Report ID.
Das Datenfeld selber besteht aus mehreren Elementen mit unterschiedlicher Länge. Der Aufbau wird im Report-Deskriptor festgelegt, wobei die einzelnen Elemente in verschiedene Gruppen unterteilt werden:
Aus der Reihenfolge ergibt sich auch gleichzeitig die Hierarchie der Gruppen. Ein Element aus der Gruppe Global verändert bis zum nächsten Global-Element alle Elemente aus der Gruppe Main, wohingegen ein Element aus der Gruppe Local nur für das nächste Main-Element gilt.
So ergibt sich z. B. aus dieser beispielhaften Beschreibung der folgende Report:
Report Size(3) - Global Report Count(2) - Global Input - Main Report Size(8) - Global Input - Main Output - Main
Der Report-Deskriptor beschreibt das Datenformat eines jeden Ein- und Ausgabeelementes des Gerätes, wobei ein Main-Item die Länge, den Bezug der Daten (absolut oder relativ) oder andere relevante Informationen definiert. Local– oder Global-Items werden hingegen genutzt um den Wertebereich der Elemente zu definieren.
Ein Report-Deskriptor muss mindestens die folgenden Elemente beinhalten:
- Input, Output oder Feature
- Usage
- Usage Page
- Logical Minimum
- Logical Maximum
- Report Size
- Report Count
Für die Beschreibung der einzelnen Gruppenelemente werden zwei feste Datenformate genutzt:
Mit diesen Definitionen wird nun ein Report-Deskriptor zusammengebaut, der das Ein- und Ausgabeformat des Eingabegerätes beschreibt. Für eine USB-Maus sieht der Deskriptor z. B. folgendermaßen aus:
Usage Page(GenericDesktop), Usage Page Usage(Mouse), Collection(Application), Usage(Pointer), Collection(Physical), Usage Page(Buttons) Usage Minimum(1), Usage Maximum(3), Logical Minimum(0), LogicalMaximum(1), Report Count(3), ReportSize(1), Input(Data, Variable, Absolute), Report Count(1), ReportSize(5), Input(Constant), Usage Page(GenericDesktop), Usage(X), Usage(Y), Logical Minimum(-127), LogicalMaximum(127), Report Size(8), ReportCount(2), Input(Data, Variable, Relative), EndCollection, EndCollection
Damit wären die Grundzüge des HID-Protokolls abgeschlossen. Auf einen Physical-Deskriptor möchte ich aktuell verzichten, da dieser Deskriptor nur optional benötigt wird.
Im nächsten Teil des Tutorials wird der Mikrocontrollercode so angepasst, dass sich der Mikrocontroller als USB-Maus beim Host anmeldet.
→ Zurück
Schreibe einen Kommentar