Senden einer USB-Interrupt-Übertragungsanfrage (UWP-App)
Interrupt-Übertragungen finden statt, wenn der Host das Gerät abfragt. Dieser Artikel demonstriert Folgendes:
- Implementieren des Event-Handlers für UsbInterruptInPipe.DataReceived
- Registrieren Sie den Event-Handler, und heben Sie die Registrierung auf.
Wichtige APIs
Ein USB-Gerät kann Endpunkte für Interrupts unterstützen, sodass es in regelmäßigen Abständen Daten senden oder empfangen kann. Zu diesem Zweck fragt der Host das Gerät in regelmäßigen Abständen ab, und jedes Mal, wenn der Host das Gerät abfragt, werden Daten übertragen. Interrupt-Übertragungen werden meist dazu verwendet, Interrupt-Daten vom Gerät zu erhalten. Dieses Thema beschreibt, wie eine UWP-App kontinuierlich Interrupt-Daten vom Gerät abrufen kann.
Informationen zum Interrupt-Endpunkt
Für Interrupt-Endpunkte gibt der Deskriptor diese Eigenschaften zurück. Diese Werte dienen nur zur Information und sollten keinen Einfluss darauf haben, wie Sie den Pufferübertragungspuffer verwalten.
Wie oft können Daten übertragen werden?
Holen Sie sich diese Informationen, indem Sie den Wert Intervall des Endpunkt-Deskriptors abrufen (siehe UsbInterruptOutEndpointDescriptor.Interval oder UsbInterruptInEndpointDescriptor.Interval). Dieser Wert gibt an, wie oft in jedem Frame auf dem Bus Daten an das Gerät gesendet oder von ihm empfangen werden.
Die Eigenschaft Intervall ist nicht der Wert bIntervall (definiert in der USB-Spezifikation).
Dieser Wert gibt an, wie oft Daten zu oder von dem Gerät übertragen werden. Wenn Intervall zum Beispiel 125 Mikrosekunden beträgt, werden die Daten bei einem Hochgeschwindigkeitsgerät alle 125 Mikrosekunden übertragen. Wenn Intervall 1000 Mikrosekunden beträgt, werden die Daten jede Millisekunde übertragen.
Wie viele Daten können in jedem Serviceintervall übertragen werden?
Ermitteln Sie die Anzahl der Bytes, die übertragen werden können, indem Sie die vom Endpunkt-Deskriptor unterstützte maximale Paketgröße ermitteln (siehe UsbInterruptOutEndpointDescriptor.MaxPacketSize oder UsbInterruptInEndpointDescriptor.MaxPacketSize). Die maximale Größe der Pakete hängt von der Geschwindigkeit des Geräts ab. Für langsame Geräte bis zu 8 Bytes. Für Geräte mit voller Geschwindigkeit bis zu 64 Bytes. Bei Hochgeschwindigkeitsgeräten mit hoher Bandbreite kann die App mehr als die maximale Paketgröße bis zu 3072 Bytes pro Microframe senden oder empfangen.
Interrupt-Endpunkte auf SuperSpeed-Geräten können sogar noch mehr Bytes übertragen. Dieser Wert wird durch den wBytesPerInterval von USB_SUPERSPEED_ENDPOINT_COMPANION_DESCRIPTOR angegeben. Um den Deskriptor abzurufen, holen Sie sich den Deskriptorpuffer mit Hilfe der Eigenschaft UsbEndpointDescriptor.AsByte und analysieren Sie diesen Puffer dann mit den Methoden DataReader.
Unterbrechung von OUT-Übertragungen
Ein USB-Gerät kann Interrupt-OUT-Endpunkte unterstützen, die in regelmäßigen Abständen Daten vom Host empfangen. Jedes Mal, wenn der Host das Gerät abfragt, sendet der Host Daten. Eine UWP-App kann eine Interrupt-OUT-Übertragungsanfrage initiieren, in der die zu sendenden Daten angegeben werden. Diese Anfrage ist abgeschlossen, wenn das Gerät die Daten vom Host bestätigt. Eine UWP-App kann Daten in UsbInterruptOutPipe schreiben.
IN-Übertragungen unterbrechen
Umgekehrt kann ein USB-Gerät Interrupt-IN-Endpunkte unterstützen, um den Host über vom Gerät generierte Hardware-Interrupts zu informieren. Normalerweise unterstützen USB Human Interface Devices (HID) wie Tastaturen und Zeigegeräte Interrupt-OUT-Endpunkte. Wenn ein Interrupt auftritt, speichert der Endpunkt die Interrupt-Daten, aber diese Daten erreichen den Host nicht sofort. Der Endpunkt muss warten, bis der Host-Controller das Gerät abfragt. Da zwischen dem Zeitpunkt, an dem die Daten generiert werden und den Host erreichen, nur eine minimale Verzögerung auftreten darf, wird das Gerät in regelmäßigen Abständen abgefragt. Eine UWP-App kann Daten abrufen, die in UsbInterruptInPipe empfangen werden. Die Anfrage, die abgeschlossen wird, wenn Daten vom Gerät vom Host empfangen werden.
Vor der Installation
- Sie müssen das Gerät geöffnet und das UsbDevice-Objekt abgerufen haben. Lesen Sie Wie man eine Verbindung zu einem USB-Gerät herstellt (UWP-App).
- Den kompletten Code, der in diesem Thema gezeigt wird, finden Sie im Beispiel CustomUsbDeviceAccess, Scenario3_InterruptPipes Dateien.
Schreiben an den Interrupt-OUT-Endpunkt
Die Art und Weise, wie die App eine Interrupt OUT-Übertragungsanfrage sendet, ist identisch mit Bulk-OUT-Übertragungen, außer dass das Ziel eine Interrupt-OUT-Pipe ist, die durch UsbInterruptOutPipe dargestellt wird. Weitere Informationen finden Sie unter Senden einer USB-Bulk-Transfer-Anfrage (UWP-App).
Schritt 1: Implementierung des Handlers für Interrupt-Ereignisse (Interrupt IN)
Wenn Daten vom Gerät in der Interrupt-Pipe empfangen werden, löst dies das Ereignis DataReceived aus. Um die Interrupt-Daten zu erhalten, muss die App einen Event-Handler implementieren. Der Parameter eventArgs des Handlers zeigt auf den Datenpuffer.
Dieser Beispiel-Code zeigt eine einfache Implementierung des Event-Handlers. Der Handler verwaltet die Anzahl der empfangenen Interrupts. Jedes Mal, wenn der Handler aufgerufen wird, erhöht er den Zähler. Der Handler holt sich den Datenpuffer aus dem Parameter eventArgs und zeigt die Anzahl der Interrupts und die Länge der empfangenen Bytes an.
private async void OnInterruptDataReceivedEvent(UsbInterruptInPipe sender, UsbInterruptInEventArgs eventArgs)
{
numInterruptsReceived++;
// The data from the interrupt
IBuffer buffer = eventArgs.InterruptData;
// Create a DispatchedHandler for the because we are interacting with the UI directly and the
// thread that this function is running on may not be the UI thread; if a non-UI thread modifies
// the UI, an exception is thrown
await Dispatcher.RunAsync(
CoreDispatcherPriority.Normal,
new DispatchedHandler(() =>
{
ShowData(
"Number of interrupt events received: " + numInterruptsReceived.ToString()
+ "\nReceived " + buffer.Length.ToString() + " bytes");
}));
}
void OnInterruptDataReceivedEvent(UsbInterruptInPipe^ /* sender */, UsbInterruptInEventArgs^ eventArgs )
{
numInterruptsReceived++;
// The data from the interrupt
IBuffer^ buffer = eventArgs->InterruptData;
// Create a DispatchedHandler for the because we are interracting with the UI directly and the
// thread that this function is running on may not be the UI thread; if a non-UI thread modifies
// the UI, an exception is thrown
MainPage::Current->Dispatcher->RunAsync(
CoreDispatcherPriority::Normal,
ref new DispatchedHandler([this, buffer]()
{
ShowData(
"Number of interrupt events received: " + numInterruptsReceived.ToString()
+ "\nReceived " + buffer->Length.ToString() + " bytes",
NotifyType::StatusMessage);
}));
}
Schritt 2: Abrufen des Interrupt-Pipe-Objekts (Interrupt IN)
Um den Event-Handler für das DataReceived-Ereignis zu registrieren, rufen Sie eine Referenz auf die UsbInterruptInPipe ab, indem Sie eine dieser Eigenschaften verwenden:
- UsbDevice.DefaultInterface.InterruptInPipes[n], wenn Ihr Interrupt-Endpunkt in der ersten USB-Schnittstelle vorhanden ist.
- UsbDevice.Configuration.UsbInterfaces[m].InterruptInPipes[n] zum Auflisten aller Interrupt-IN-Pipes pro Schnittstelle, die vom Gerät unterstützt werden.
- UsbInterface.InterfaceSettings[m].InterruptInEndpoints [n].Pipe zum Auflisten der Interrupt-IN-Pipes, die durch die Einstellungen einer Schnittstelle festgelegt wurden.
- UsbEndpointDescriptor.AsInterruptInEndpointDescriptor.Pipe zum Abrufen des Pipe-Objekts aus dem Endpunktdeskriptor für den Interrupt-IN-Endpunkt.
Hinweis Vermeiden Sie das Abrufen des Pipe-Objekts durch Auflisten von Interrupt-Endpunkten einer Schnittstelleneinstellung, die derzeit nicht ausgewählt ist. Um Daten zu übertragen, müssen Pipes mit Endpunkten in der aktiven Einstellung verbunden sein.
Schritt 3: Registrieren des Event-Handlers, um den Datenempfang zu starten (Interrupt IN)
Als Nächstes müssen Sie den Event-Handler für das UsbInterruptInPipe-Objekt registrieren, der das DataReceived-Ereignis auslöst.
Dieser Beispiel-Code zeigt, wie Sie den Event-Handler registrieren. In diesem Beispiel merkt sich die Klasse den Event-Handler, die Pipe, für die der Event-Handler registriert ist, und ob die Pipe gerade Daten empfängt. All diese Informationen werden für die Aufhebung der Registrierung des Event-Handlers verwendet, die im nächsten Schritt gezeigt wird.
private void RegisterForInterruptEvent(TypedEventHandler<UsbInterruptInPipe, UsbInterruptInEventArgs> eventHandler)
{
// Search for the correct pipe that has the specified endpoint number
interruptPipe = usbDevice.DefaultInterface.InterruptInPipes[0];
// Save the interrupt handler so we can use it to unregister
interruptEventHandler = eventHandler;
interruptPipe.DataReceived += interruptEventHandler;
registeredInterruptHandler = true;
}
void RegisterForInterruptEvent(TypedEventHandler<UsbInterruptInPipe, UsbInterruptInEventArgs> eventHandler)
// Search for the correct pipe that has the specified endpoint number
interruptInPipe = usbDevice.DefaultInterface.InterruptInPipes.GetAt(pipeIndex);
// Save the token so we can unregister from the event later
interruptEventHandler = interruptInPipe.DataReceived += eventHandler;
registeredInterrupt = true;
}
Nachdem der Event-Handler registriert ist, wird er jedes Mal aufgerufen, wenn Daten in der zugehörigen Interrupt-Pipe empfangen werden.
Schritt 4: Deregistrieren des Event-Handlers, um den Datenempfang zu beenden (Interrupt IN)
Nachdem Sie mit dem Empfang von Daten fertig sind, heben Sie die Registrierung des Event-Handlers auf.
Dieser Code zeigt Ihnen, wie Sie die Registrierung des Event-Handlers aufheben können. Wenn die App über einen zuvor registrierten Event-Handler verfügt, ruft die Methode in diesem Beispiel den verfolgten Event-Handler ab und hebt dessen Registrierung in der Interrupt-Pipe auf.
private void UnregisterInterruptEventHandler()
{
if (registeredInterruptHandler)
{
interruptPipe.DataReceived -= interruptEventHandler;
registeredInterruptHandler = false;
}
}
void UnregisterFromInterruptEvent(void)
{
if (registeredInterrupt)
{
interruptInPipe.DataReceived -= eventHandler;
registeredInterrupt = false;
}
}
Nachdem der Event-Handler deregistriert wurde, erhält die App keine Daten mehr von der Interrupt-Pipe, da der Event-Handler bei Interrupt-Ereignissen nicht mehr aufgerufen wird. Das bedeutet nicht, dass die Interrupt-Pipe keine Daten mehr erhält.