如何傳送 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) 例如鍵盤和指向裝置支援中斷 OUT 連接點。 當中斷發生時,端點會儲存中斷數據,但該數據不會立即到達主機。 端點必須等候主機控制器輪詢裝置。 因為產生數據並到達主機的時間之間必須有最少的延遲,所以它會定期輪詢裝置。 UWP app 可以取得在 UsbInterruptInPipe 中接收的數據。 主機收到來自裝置的數據時完成的要求。
在您開始使用 Intune 之前
- 您必須開啟裝置並取得 UsbDevice 物件。 請參閱 如何連線到 USB 裝置 (UWP app) 。
- 您可以在 CustomUsbDeviceAccess 範例Scenario3_InterruptPipes檔案中看到本主題中顯示的完整程序代碼。
寫入中斷 OUT 端點
應用程式傳送中斷 OUT 傳輸要求的方式與大量 OUT 傳輸相同,但目標為 UsbInterruptOutPipe 所代表的中斷 OUT 管道。 如需詳細資訊,請參閱如何傳送USB大量傳輸要求(UWP app)。
步驟 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 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 的參考:
- 如果您的插斷端點出現在第一個 USB 介面中,則 UsbDevice.DefaultInterface.InterruptInPipes[n] 。
- UsbDevice.Configuration.UsbInterfaces[m]。InterruptInPipes[n] 用於列舉裝置所支援的每個介面的所有插斷 IN 管道。
- UsbInterface.Interface 設定[m]。InterruptInEndpoints [n]。列舉介面設定所定義的插斷 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;
}
}
取消註冊事件處理程式之後,應用程式會停止從中斷管道接收數據,因為不會在中斷事件上叫用事件處理程式。 這並不表示中斷管道會停止取得數據。