Freigeben über


Senden einer USB-Interrupt-Übertragungsanfrage (UWP-App)

Interrupt-Übertragungen finden statt, wenn der Host das Gerät abfragt. Dieser Artikel demonstriert Folgendes:

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

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:

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.