USB 割り込み転送要求の送信方法 (UWP アプリ)

割り込み転送は、ホストがデバイスをポーリングするときに発生します。 この記事では、次の方法を示します。

重要な API

USB デバイスは割り込みエンドポイントをサポートできるため、一定の間隔でデータを送受信できます。 これを実現するために、ホストは一定の間隔でデバイスをポーリングし、ホストがデバイスをポーリングするたびにデータが送信されます。 割り込み転送は、主にデバイスから割り込みデータを取得するために使用されます。 このトピックでは、UWP アプリがデバイスから連続割り込みデータを取得する方法について説明します。

割り込みエンドポイント情報

割り込みエンドポイントの場合、記述子はこれらのプロパティを公開します。 これらの値は情報提供のみを目的としており、バッファー転送バッファーの管理方法には影響しません。

  • どのくらいの頻度でデータを送信できますか?

    エンドポイント記述子の Interval 値を取得して、その情報を取得します (UsbInterruptOutEndpointDescriptor.Interval または UsbInterruptInEndpointDescriptor.Interval を参照)。 この値は、バス上の各フレームでデバイスとの間でデータが送受信される頻度を示します。

    Interval プロパティは bInterval 値ではありません (USB 仕様で定義されています)。

    この値は、デバイスとの間でデータが送信される頻度を示します。 たとえば、高速デバイスの場合、間隔が 125 マイクロ秒の場合、データは 125 マイクロ秒ごとに送信されます。 間隔が 1000 マイクロ秒の場合、データはミリ秒ごとに送信されます。

  • 各サービス間隔で送信できるデータの量はどのくらいですか?

    エンドポイント記述子でサポートされている最大パケット サイズを取得することで送信できるバイト数を取得します (UsbInterruptOutEndpointDescriptor.MaxPacketSize または UsbInterruptInEndpointDescriptor.MaxPacketSize を参照)。 デバイスの速度に制約される最大パケット サイズ。 低速デバイスの場合、最大 8 バイト。 フルスピードのデバイスの場合、最大 64 バイト。 高速で高帯域幅のデバイスの場合、アプリはマイクロフレームあたり最大パケット サイズ 3072 バイトを超える送受信を行うことができます。

    SuperSpeed デバイスの割り込みエンドポイントは、さらに多くのバイト数を送信できます。 この値は、USB_SUPERSPEED_ENDPOINT_COMPANION_DESCRIPTOR の wBytesPerInterval によって示されます。 記述子を取得するには、UsbEndpointDescriptor.AsByte プロパティを使用して記述子バッファーを取得し、DataReader メソッドを使用してそのバッファーを解析します。

OUT 転送の割り込み

USB デバイスは、ホストから一定の間隔でデータを受信する割り込み OUT エンドポイントをサポートできます。 ホストがデバイスをポーリングするたびに、ホストはデータを送信します。 UWP アプリは、送信するデータを指定する割り込み OUT 転送要求を開始できます。 この要求は、デバイスがホストからのデータを確認したときに完了します。 UWP アプリは、UsbInterruptOutPipe にデータを書き込むことができます。

IN 転送の割り込み

逆に、USB デバイスは、デバイスによって生成されたハードウェア割り込みについてホストに通知する方法として、割り込み IN エンドポイントをサポートできます。 通常、キーボードやポインティング デバイスなどの USB ヒューマン インターフェイス デバイス (HID) では、割り込み OUT エンドポイントがサポートされます。 割り込みが発生すると、エンドポイントは割り込みデータを格納しますが、そのデータはすぐにホストに到達しません。 エンドポイントは、ホスト コントローラーがデバイスをポーリングするまで待機する必要があります。 データが生成されてからホストに到達するまでの遅延は最小限にする必要があるため、定期的にデバイスをポーリングします。 UWP アプリは、UsbInterruptInPipe で受信したデータを取得できます。 デバイスからのデータがホストによって受信されると完了する要求。

開始する前に

  • デバイスを開き、UsbDevice オブジェクトを取得している必要があります。 「USB デバイスへの接続方法 (UWP アプリ)」を参照してください。
  • このトピックに示す完全なコードは、CustomUsbDeviceAccess サンプルの Scenario3_InterruptPipes ファイルで確認できます。

割り込み OUT エンドポイントへの書き込み

アプリが割り込み OUT 転送要求を送信する方法は、一括 OUT 転送と同じですが、ターゲットが UsbInterruptOutPipe で表される割り込み OUT パイプである点が異なります。 詳細については、「USB一括転送要求を送信する方法 (UWPアプリ)」を参照してください。

手順 1: 割り込みイベント ハンドラーを実装する (割り込み IN)

デバイスから割り込みパイプにデータを受信すると、DataReceived イベントが発生します。 割り込みデータを取得するには、アプリでイベント ハンドラーを実装する必要があります。 ハンドラーの eventArgs パラメーターは、データ バッファーを指します。

このコード例は、イベント ハンドラーの簡単な実装を示しています。 ハンドラーメイン受信した割り込みの数が含まれます。 ハンドラーが呼び出されるたびに、カウントがインクリメントされます。 ハンドラーは eventArgs パラメーターからデータ バッファーを取得し、割り込みの数と受信したバイトの長さを表示します。

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

手順 2: 割り込みパイプ オブジェクトを取得する (割り込み IN)

DataReceived イベントのイベント ハンドラーを登録するには、次のプロパティを使用して UsbInterruptInPipe への参照を取得します。

注: 現在選択されていないインターフェイス設定の割り込みエンドポイントを列挙して、パイプ オブジェクトを取得しないようにします。 データを転送するには、アクティブな設定のエンドポイントにパイプを関連付ける必要があります。

手順 3: イベント ハンドラーを登録してデータの受信を開始する (割り込み IN)

次に、DataReceived イベントを発生させる UsbInterruptInPipe オブジェクトにイベント ハンドラーを登録する必要があります。

このコード例は、イベント ハンドラーを登録する方法を示しています。 この例では、クラスはイベント ハンドラー、イベント ハンドラーが登録されているパイプ、およびパイプが現在データを受信しているかどうかを追跡します。 すべての情報は、次の手順で示すように、イベント ハンドラーの登録解除に使用されます。

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;    

}

イベント ハンドラーが登録されると、関連付けられている割り込みパイプでデータが受信されるたびに呼び出されます。

手順 4: イベントハンドラの登録を解除してデータ受信を停止する(割り込みIN)

データの受信が完了したら、イベント ハンドラーの登録を解除します。

このコード例は、イベント ハンドラーの登録を解除する方法を示しています。 この例では、アプリに以前に登録されたイベント ハンドラーがある場合、メソッドは追跡されたイベント ハンドラーを取得し、それを割り込みパイプから登録解除します。

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

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

        registeredInterrupt = false;
    }
}

イベント ハンドラーが登録解除されると、イベント ハンドラーは割り込みイベントで呼び出されないため、アプリは割り込みパイプからのデータの受信を停止します。 これは、割り込みパイプがデータの取得を停止することを意味するものではありません。