如何傳送 USB 中斷傳輸要求 (UWP 應用程式)
當主機輪詢裝置時,就會發生中斷傳輸。 本文會示範如何:
- 實作 UsbInterruptInPipe.DataReceived 的事件處理程式
- 註冊和取消註冊事件處理程式
重要 API
USB 裝置可以支援中斷端點,以便定期傳送或接收數據。 若要達成此目的,主機會定期輪詢裝置,而且每次主機輪詢裝置時都會傳輸數據。 中斷傳輸主要用於從裝置取得中斷數據。 本主題描述 UWP 應用程式如何從裝置取得連續中斷數據。
中斷端點資訊
對於中斷端點,描述項會公開這些屬性。 這些值僅供資訊使用,且不應影響您管理緩衝區傳輸緩衝區的方式。
數據傳輸的頻率如何?
取得端點描述符中的 Interval 值以獲取該資訊(請參見 UsbInterruptOutEndpointDescriptor.Interval 或 UsbInterruptInEndpointDescriptor.Interval)。 該值表示數據在總線上每一幀中與裝置之間傳送或接收的頻率。
Interval 屬性不是 bInterval 值(定義在 USB 規格中)。
該值表示數據在裝置之間傳輸的頻率。 例如,針對高速裝置,如果 Interval 為 125 微秒,則會每隔 125 毫秒傳輸一次數據。 如果 Interval 為 1000 毫秒,則會每毫秒傳輸數據一次。
每個服務間隔可以傳輸多少數據?
取得端點描述元支援的封包大小上限所傳輸的位元組數目(請參閱 UsbInterruptOutEndpointDescriptor.MaxPacketSize 或 UsbInterruptInEndpointDescriptor.MaxPacketSize)。 封包大小的上限受限於裝置的速度。 針對最多8個位元組的低速設備。 針對全速裝置,最多64個字節。 針對高速、高頻寬的裝置,應用程式可以傳送或接收超過每個微框架 3072 個字節的封包大小上限。
SuperSpeed 裝置上的中斷端點能夠傳輸更多的位元組。 USB_SUPERSPEED_ENDPOINT_COMPANION_DESCRIPTOR 的 wBytesPerInterval 表示該值。 若要擷取描述元,請使用 UsbEndpointDescriptor.AsByte 屬性取得描述元緩衝區,然後使用 DataReader 方法剖析該緩衝區。
中斷 OUT 傳輸
USB 裝置可以支援中斷輸出端點,以定期接收來自主機的數據。 每次主機輪詢裝置時,主機都會傳送數據。 UWP 應用程式可以起始中斷 OUT 傳輸要求,以指定要傳送的數據。 當裝置認可來自主機的數據時,即會完成該要求。 UWP 應用程式可以將資料寫入至 UsbInterruptOutPipe。
中斷 IN 傳輸
相反地,USB 裝置可以支援中斷 IN 端點,以通知主機裝置所產生的硬體中斷。 通常 USB 人機介面設備(HID),例如鍵盤和指向裝置,支援中斷輸出連接點。 當中斷發生時,端點會儲存中斷數據,但該數據不會立即到達主機。 端點必須等候主機控制器輪詢裝置。 因為產生數據並到達主機的時間之間必須有最少的延遲,所以它會定期輪詢裝置。 UWP 應用程式可以取得在 UsbInterruptInPipe中接收的數據。 當主機接收到來自裝置的數據時完成的請求。
開始之前
- 您必須開啟裝置,並取得 UsbDevice 物件。 閱讀 如何連接到 USB 裝置(UWP 應用程式)。
- 您可以在 CustomUsbDeviceAccess 範例Scenario3_InterruptPipes檔案中看到本主題中顯示的完整程序代碼。
寫入中斷 OUT 端點
應用程式傳送中斷 OUT 傳輸要求的方式與大量 OUT 傳輸相同,但目標為中斷 OUT 管道,由 UsbInterruptOutPipe表示。 如需詳細資訊,請參閱 如何傳送 USB 大量傳輸要求 (UWP app)。
步驟 1:實現中斷事件處理程式(Interrupt 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 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);
}));
}
步驟 2:取得中斷管道物件(中斷 IN)
若要註冊 DataReceived 事件的事件處理常式,請使用以下任一屬性來取得 UsbInterruptInPipe 的參考:
- UsbDevice.DefaultInterface.InterruptInPipes[n] 如果您的插斷端點出現在第一個 USB 介面中。
- UsbDevice.Configuration.UsbInterfaces[m],InterruptInPipes[n],以列舉裝置支援的每個介面的所有中斷 IN 管道。
- UsbInterface.InterfaceSettings[m].InterruptInEndpoints[n].Pipe,用於列舉介面設置中定義的中斷 IN 管道。
- UsbEndpointDescriptor.AsInterruptInEndpointDescriptor.Pipe,用於從中斷 IN 端點的描述元取得管道物件。
注意 列舉目前未選取之介面設定的中斷端點,以避免取得管道物件。 若要傳輸數據,管道必須與使用中設定中的端點相關聯。
步驟 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;
}
}
取消註冊事件處理程式之後,應用程式會停止從中斷管道接收數據,因為不會在中斷事件上叫用事件處理程式。 這並不表示中斷管道會停止取得數據。