Condividi tramite


Come inviare una richiesta di trasferimento di interrupt USB (app UWP)

I trasferimenti di interrupt si verificano quando l'host esegue il polling del dispositivo. Questo articolo illustra come:

API importanti

Un dispositivo USB può supportare gli endpoint di interrupt in modo che possa inviare o ricevere dati a intervalli regolari. A tale scopo, l'host esegue il polling del dispositivo a intervalli regolari e i dati vengono trasmessi ogni volta che l'host esegue il polling del dispositivo. I trasferimenti di interrupt vengono usati principalmente per ottenere dati di interrupt dal dispositivo. Questo argomento descrive come un'app UWP può ottenere dati di interruzione continui dal dispositivo.

Informazioni sull'endpoint di interrupt

Per gli endpoint di interrupt, il descrittore espone queste proprietà. Questi valori sono solo per informazioni e non devono influire sulla modalità di gestione del buffer di trasferimento del buffer.

  • Con quale frequenza è possibile trasmettere i dati?

    Ottenere queste informazioni ottenendo il valore Interval del descrittore dell'endpoint (vedere UsbInterruptOutEndpointDescriptor.Interval o UsbInterruptInEndpointDescriptor.Interval). Questo valore indica la frequenza con cui i dati vengono inviati o ricevuti dal dispositivo in ogni frame del bus.

    La proprietà Interval non è il valore bInterval (definito nella specifica USB).

    Questo valore indica la frequenza con cui i dati vengono trasmessi da o verso il dispositivo. Ad esempio, per un dispositivo ad alta velocità, se Interval è di 125 microsecondi, i dati vengono trasmessi ogni 125 microsecondi. Se Interval è di 1000 microsecondi, i dati vengono trasmessi ogni millisecondo.

  • Quanti dati possono essere trasmessi in ogni intervallo di servizio?

    Ottenere il numero di byte che possono essere trasmessi ottenendo le dimensioni massime del pacchetto supportate dal descrittore dell'endpoint (vedere UsbInterruptOutEndpointDescriptor.MaxPacketSize o UsbInterruptInEndpointDescriptor.MaxPacketSize). Dimensione massima del pacchetto vincolata alla velocità del dispositivo. Per i dispositivi a bassa velocità fino a 8 byte. Per i dispositivi a velocità completa, fino a 64 byte. Per i dispositivi a larghezza di banda elevata e ad alta velocità, l'app può inviare o ricevere dimensioni massime di pacchetti fino a 3072 byte per microframe.

    Gli endpoint di interrupt nei dispositivi SuperSpeed sono in grado di trasmettere un numero ancora maggiore di byte. Tale valore è indicato dal wBytesPerInterval del USB_SUPERSP edizione Enterprise D_ENDPOINT_COMPANION_DESCRIPTOR. Per recuperare il descrittore, ottenere il buffer del descrittore usando la proprietà UsbEndpointDescriptor.AsByte e quindi analizzare il buffer usando i metodi DataReader.

Interrompere i trasferimenti OUT

Un dispositivo USB può supportare gli endpoint OUT di interrupt che ricevono dati dall'host a intervalli regolari. Ogni volta che l'host esegue il polling del dispositivo, l'host invia i dati. Un'app UWP può avviare una richiesta di trasferimento OUT di interrupt che specifica i dati da inviare. La richiesta viene completata quando il dispositivo riconosce i dati dell'host. Un'app UWP può scrivere dati in UsbInterruptOutPipe.

Interrompi trasferimenti IN

Al contrario, un dispositivo USB può supportare gli endpoint IN di interrupt come modo per informare l'host sugli interrupt hardware generati dal dispositivo. In genere, i dispositivi HID (Human Interface Devices) USB, ad esempio le tastiere e i dispositivi di puntamento, supportano gli endpoint OUT di interrupt. Quando si verifica un interrupt, l'endpoint archivia i dati di interrupt, ma i dati non raggiungono immediatamente l'host. L'endpoint deve attendere che il controller host esegua il polling del dispositivo. Poiché deve esserci un ritardo minimo tra i dati temporali generati e raggiunge l'host, esegue il polling del dispositivo a intervalli regolari. Un'app UWP può ottenere i dati ricevuti in UsbInterruptInPipe. Richiesta che viene completata quando i dati del dispositivo sono ricevuti dall'host.

Prima di iniziare

Scrittura nell'endpoint di interrupt OUT

Il modo in cui l'app invia una richiesta di trasferimento interrupt OUT è identica ai trasferimenti OUT in blocco, tranne che la destinazione è una pipe OUT di interrupt, rappresentata da UsbInterruptOutPipe. Per altre informazioni, vedi Come inviare una richiesta di trasferimento bulk USB (app UWP).

Passaggio 1: Implementare il gestore eventi interrupt (Interrupt IN)

Quando i dati sono ricevuti dal dispositivo nella pipe di interrupt, genera l'evento DataReceived . Per ottenere i dati di interrupt, l'app deve implementare un gestore eventi. Il parametro eventArgs del gestore punta al buffer dei dati.

Questo codice di esempio mostra una semplice implementazione del gestore eventi. Il gestore gestisce il numero di interrupt ricevuti. Ogni volta che viene richiamato il gestore, incrementa il conteggio. Il gestore ottiene il buffer di dati dal parametro eventArgs e visualizza il numero di interrupt e la lunghezza dei byte ricevuti.

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);
        }));
}

Passaggio 2: Ottenere l'oggetto interrupt pipe (Interrupt IN)

Per registrare il gestore eventi per l'evento DataReceived, ottenere un riferimento a UsbInterruptInPipe usando le proprietà seguenti:

Nota Evitare di ottenere l'oggetto pipe enumerando gli endpoint interrupt di un'impostazione di interfaccia non attualmente selezionata. Per trasferire i dati, le pipe devono essere associate agli endpoint nell'impostazione attiva.

Passaggio 3: Registrare il gestore eventi per avviare la ricezione dei dati (Interrupt IN)

Successivamente, devi registrare il gestore eventi nell'oggetto UsbInterruptInPipe che genera l'evento DataReceived.

Questo codice di esempio illustra come registrare il gestore eventi. In questo esempio la classe tiene traccia del gestore eventi, della pipe per cui è registrato il gestore eventi e se la pipe sta ricevendo dati. Tutte le informazioni vengono usate per annullare la registrazione del gestore eventi, come illustrato nel passaggio successivo.

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;    

}

Dopo la registrazione del gestore eventi, viene richiamato ogni volta che i dati sono ricevuti nella pipe di interrupt associata.

Passaggio 4: Annullare la registrazione del gestore eventi per interrompere la ricezione dei dati (Interrupt IN)

Al termine della ricezione dei dati, annullare la registrazione del gestore eventi.

Questo codice di esempio illustra come annullare la registrazione del gestore eventi. In questo esempio, se l'app ha un gestore eventi registrato in precedenza, il metodo ottiene il gestore eventi rilevato e lo annulla sulla pipe di interrupt.

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

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

        registeredInterrupt = false;
    }
}

Dopo l'annullamento della registrazione del gestore eventi, l'app smette di ricevere dati dalla pipe di interrupt perché il gestore eventi non viene richiamato sugli eventi di interrupt. Ciò non significa che la pipe di interrupt interrompe il recupero dei dati.