USB 인터럽트 전송 요청을 보내는 방법(UWP 앱)
인터럽트 전송은 호스트가 디바이스를 폴링할 때 발생합니다. 이 문서에서는 다음을 수행하는 방법을 보여줍니다.
- UsbInterruptInPipe.DataReceived에 대한 이벤트 처리기 구현
- 이벤트 처리기 등록 및 등록 취소
중요 API
USB 디바이스는 정기적으로 데이터를 보내거나 받을 수 있도록 인터럽트 엔드포인트를 지원할 수 있습니다. 이를 위해 호스트는 정기적으로 디바이스를 폴링하고 호스트가 디바이스를 폴링할 때마다 데이터가 전송됩니다. 인터럽트 전송은 주로 디바이스에서 인터럽트 데이터를 가져오는 데 사용됩니다. 이 항목에서는 UWP 앱이 디바이스에서 연속 인터럽트 데이터를 가져오는 방법을 설명합니다.
인터럽트 엔드포인트 정보
인터럽트 엔드포인트의 경우 설명자는 이러한 속성을 노출합니다. 이러한 값은 정보 전용이며 버퍼 전송 버퍼를 관리하는 방법에 영향을 미치지 않아야 합니다.
데이터를 얼마나 자주 전송할 수 있나요?
엔드포인트 설명자의 간격 값을 가져와서 해당 정보를 가져옵니다(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 엔드포인트를 지원할 수 있습니다. 일반적으로 키보드 및 포인팅 디바이스와 같은 HID(USB 휴먼 인터페이스 디바이스)는 인터럽트 OUT 엔드포인트를 지원합니다. 인터럽트 발생 시 엔드포인트는 인터럽트 데이터를 저장하지만 해당 데이터는 호스트에 즉시 도달하지 않습니다. 엔드포인트는 호스트 컨트롤러가 디바이스를 폴링할 때까지 기다려야 합니다. 데이터가 생성되고 호스트에 도달하는 시간 사이에 최소 지연이 있어야 하므로 정기적으로 디바이스를 폴링합니다. UWP 앱은 UsbInterruptInPipe에서 받은 데이터를 가져올 수 있습니다. 호스트가 디바이스의 데이터를 수신할 때 완료되는 요청입니다.
시작하기 전에
- 디바이스를 열고 UsbDevice 개체를 가져와야 합니다. USB 디바이스(UWP 앱)에 연결하는 방법을 읽습니다.
- 이 항목의 전체 코드는 CustomUsbDeviceAccess 샘플 Scenario3_InterruptPipes 파일에서 확인할 수 있습니다.
인터럽트 OUT 엔드포인트에 쓰기
앱이 인터럽트 OUT 전송 요청을 보내는 방식은 대상이 UsbInterruptOutPipe로 표시되는 인터럽트 OUT 파이프라는 점을 제외하고 대량 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 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]. 인터럽트InPipes[n] - 디바이스에서 지원하는 인터페이스당 모든 인터럽트 IN 파이프를 열거합니다.
- UsbInterface.Interface설정[m]. InterruptInEndpoints [n]. 인터페이스의 설정에 의해 정의된 인터럽트 IN 파이프를 열거하기 위한 파이프입니다.
- 인터럽트 IN 엔드포인트에 대한 엔드포인트 설명자에서 파이프 개체를 가져오기 위한 UsbEndpointDescriptor.AsInterruptInEndpointDescriptor.Pipe 입니다.
참고 현재 선택되지 않은 인터페이스 설정의 인터럽트 엔드포인트를 열거하여 파이프 개체를 가져오지 않습니다. 데이터를 전송하려면 파이프를 활성 설정의 엔드포인트와 연결해야 합니다.
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;
}
}
이벤트 처리기가 등록 취소된 후에는 인터럽트 이벤트에서 이벤트 처리기가 호출되지 않으므로 앱이 인터럽트 파이프에서 데이터 수신을 중지합니다. 그렇다고 인터럽트 파이프가 데이터 가져오기를 중지한다는 의미는 아닙니다.