Share via


Senden einer USB-Interruptübertragungsanforderung (UWP-App)

Interruptübertragungen treten auf, wenn der Host das Gerät abruft. Dieser Artikel demonstriert Folgendes:

Wichtige APIs

Ein USB-Gerät kann Unterbrechungsendpunkte unterstützen, sodass es in regelmäßigen Abständen Daten senden oder empfangen kann. Um dies zu erreichen, ruft der Host das Gerät in regelmäßigen Abständen ab, und jedes Mal, wenn der Host das Gerät abruft, werden Daten übertragen. Interruptübertragungen werden hauptsächlich zum Abrufen von Interruptdaten vom Gerät verwendet. In diesem Thema wird beschrieben, wie eine UWP-App fortlaufende Unterbrechungsdaten vom Gerät abrufen kann.

Informationen zum Unterbrechen des Endpunkts

Für Interruptendpunkte macht der Deskriptor diese Eigenschaften verfügbar. Diese Werte dienen nur informationen und sollten sich nicht darauf auswirken, wie Sie den Pufferübertragungspuffer verwalten.

  • Wie oft können Daten übertragen werden?

    Rufen Sie diese Informationen ab, indem Sie den Interval-Wert des Endpunktdeskriptors abrufen (siehe UsbInterruptOutEndpointDescriptor.Interval oder UsbInterruptInEndpointDescriptor.Interval). Dieser Wert gibt an, wie oft Daten in jedem Frame auf dem Bus an das Gerät gesendet oder von diesem empfangen werden.

    Die Interval-Eigenschaft ist nicht der bInterval-Wert (definiert in der USB-Spezifikation).

    Dieser Wert gibt an, wie oft Daten an oder vom Gerät übertragen werden. Wenn das Intervall beispielsweise 125 Mikrosekunden beträgt, werden bei einem Hochgeschwindigkeitsgerät alle 125 Mikrosekunden Daten übertragen. Wenn das Intervall 1.000 Mikrosekunden beträgt, werden daten jede Millisekunde übertragen.

  • Wie viele Daten können in jedem Dienstintervall übertragen werden?

    Rufen Sie die Anzahl der Bytes ab, die übertragen werden können, indem Sie die maximale Paketgröße abrufen, die vom Endpunktdeskriptor unterstützt wird (siehe UsbInterruptOutEndpointDescriptor.MaxPacketSize oder UsbInterruptInEndpointDescriptor.MaxPacketSize). Die maximale Paketgröße, die auf die Geschwindigkeit des Geräts beschränkt ist. Für Geräte mit niedriger Geschwindigkeit bis zu 8 Bytes. Für Geräte mit voller Geschwindigkeit bis zu 64 Bytes. Bei Geräten mit hoher Geschwindigkeit und hoher Bandbreite kann die App mehr als die maximale Paketgröße von bis zu 3072 Bytes pro Microframe senden oder empfangen.

    Interruptendpunkte auf SuperSpeed-Geräten können noch mehr Bytes übertragen. Dieser Wert wird durch den wBytesPerInterval des USB_SUPERSPEED_ENDPOINT_COMPANION_DESCRIPTOR angegeben. Um den Deskriptor abzurufen, rufen Sie den Deskriptorpuffer mithilfe der UsbEndpointDescriptor.AsByte-Eigenschaft ab, und analysieren Sie diesen Puffer dann mithilfe von DataReader-Methoden .

Unterbrechen von OUT-Übertragungen

Ein USB-Gerät kann Unterbrechungs-OUT-Endpunkte unterstützen, die in regelmäßigen Abständen Daten vom Host empfangen. Jedes Mal, wenn der Host das Gerät abruft, sendet der Host Daten. Eine UWP-App kann eine Out-Übertragungsanforderung initiieren, die die zu sendenden Daten angibt. Diese Anforderung wird abgeschlossen, wenn das Gerät die Daten vom Host bestätigt. Eine UWP-App kann Daten in die UsbInterruptOutPipe schreiben.

Unterbrechen von IN-Übertragungen

Umgekehrt kann ein USB-Gerät Interrupt-IN-Endpunkte unterstützen, um den Host über vom Gerät generierte Hardwareunterbrechungen zu informieren. In der Regel unterstützen USB-Geräte (Human Interface Devices, HID) wie Tastaturen und Zeigegeräte unterbrechungsfähige OUT-Endpunkte. Wenn ein Interrupt auftritt, speichert der Endpunkt Interruptdaten, aber diese Daten erreichen den Host nicht sofort. Der Endpunkt muss warten, bis der Hostcontroller das Gerät abruft. Da zwischen der Datengenerierung und dem Erreichen des Hosts eine minimale Verzögerung auftreten muss, wird das Gerät in regelmäßigen Abständen abgerufen. Eine UWP-App kann Daten abrufen, die im UsbInterruptInPipe empfangen werden. Die Anforderung, die abgeschlossen wird, wenn Daten vom Gerät vom Host empfangen werden.

Vorbereitung

Schreiben in den Interrupt-OUT-Endpunkt

Die Art und Weise, wie die App eine Out-Übertragungsanforderung sendet, ist identisch mit Bulk-OUT-Übertragungen, mit der Ausnahme, dass es sich bei dem Ziel um eine Interrupt-OUT-Pipe handelt, die durch UsbInterruptOutPipe dargestellt wird. Weitere Informationen finden Sie unter Senden einer USB-Massenübertragungsanforderung (UWP-App).

Schritt 1: Implementieren des Interruptereignishandlers (Interrupt IN)

Wenn Daten vom Gerät in die Interruptpipe empfangen werden, löst es das DataReceived-Ereignis aus. Um die Interruptdaten abzurufen, muss die App einen Ereignishandler implementieren. Der eventArgs-Parameter des Handlers zeigt auf den Datenpuffer.

Dieser Beispielcode zeigt eine einfache Implementierung des Ereignishandlers. Der Handler verwaltet die Anzahl der empfangenen Interrupts. Jedes Mal, wenn der Handler aufgerufen wird, erhöht er die Anzahl. Der Handler ruft den Datenpuffer aus dem eventArgs-Parameter ab 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 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

    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 Interruptpipeobjekts (Interrupt IN)

Um den Ereignishandler für das DataReceived-Ereignis zu registrieren, rufen Sie mithilfe der folgenden Eigenschaften einen Verweis auf usbInterruptInPipe ab:

Hinweis Vermeiden Sie das Abrufen des Pipeobjekts, indem Sie Interruptendpunkte einer Schnittstelleneinstellung auflisten, die derzeit nicht ausgewählt ist. Zum Übertragen von Daten müssen Pipes Endpunkten in der aktiven Einstellung zugeordnet sein.

Schritt 3: Registrieren des Ereignishandlers für den Empfang von Daten (Interrupt IN)

Als Nächstes müssen Sie den Ereignishandler für das UsbInterruptInPipe-Objekt registrieren, das das DataReceived-Ereignis auslöst.

Dieser Beispielcode zeigt, wie Sie den Ereignishandler registrieren. In diesem Beispiel verfolgt die Klasse den Ereignishandler, die Pipe, für die der Ereignishandler registriert ist, und ob die Pipe derzeit Daten empfängt. Alle diese Informationen werden zum Aufheben der Registrierung des Ereignishandlers 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 Ereignishandler registriert wurde, wird er jedes Mal aufgerufen, wenn Daten in der zugeordneten Interruptpipe empfangen werden.

Schritt 4: Aufheben der Registrierung des Ereignishandlers, um den Empfang von Daten zu beenden (Interrupt IN)

Nachdem Sie den Empfang von Daten abgeschlossen haben, heben Sie die Registrierung des Ereignishandlers auf.

Dieser Beispielcode zeigt, wie Sie die Registrierung des Ereignishandlers aufheben. In diesem Beispiel ruft die -Methode den nachverfolgten Ereignishandler ab und hebt die Registrierung in der Interruptpipe auf, wenn die App über einen zuvor registrierten Ereignishandler verfügt.

private void UnregisterInterruptEventHandler()
{
    if (registeredInterruptHandler)
    {
        interruptPipe.DataReceived -= interruptEventHandler;

        registeredInterruptHandler = false;
    }
}
void UnregisterFromInterruptEvent(void)
{
    if (registeredInterrupt)
    {
        interruptInPipe.DataReceived -= eventHandler;

        registeredInterrupt = false;
    }
}

Nachdem der Ereignishandler die Registrierung aufgehoben hat, empfängt die App nicht mehr Daten von der Interruptpipe, da der Ereignishandler nicht für Interruptereignisse aufgerufen wird. Dies bedeutet nicht, dass die Interruptpipe keine Daten mehr erhält.