本帖最后由 lszisgood 于 2020-10-31 10:56 编辑
Kinetis/LPC MCU实现WinUSB免驱设备
在Windows操作系统下,除了USB HID设备,其他类型的USB设备需要安装内核驱动程序。但USB HID设备Interrupt Type中断传输端点速度慢。如果使用Bulk Type批量传输端点时需要自己开发USB内核驱动,并申请Windows 驱动程序签名,开发难度非常大。 为了简化USB设备开发,微软开发了WinUSB,可以将Winusb.sys作为设备功能驱动程序安装,并提供WinUSB API供应用程序访问设备。支持WinUSB的设备,在Win8之前不用编写内核驱动程序,只需要编写inf文件,匹配设备。在Win8之后,如果USB设备支持WCID((Windows Compatible ID),则不需要inf文件,实现完全免驱动。 下面将介绍如何在MCUXpresso SDK USB Stack基础上实现WinUSB功能,让Kinetis/LPC/I.MX RT MCU在Windows上实现免驱即插即用。 要符合WinUSB的要求, 需要修改MCUXpressoSDK USB Stack,满足以下 3 项:
1. 响应字符串描述符请求,返回索引 0xEE的 OS 字符串描述符 #define bMS_VendorCode ( 0xA0 ) //"MSFT100" : index : 0xEE : langId : 0x0000 uint8_tOS_StringDescritpor[ ] = {0x12, 0x03, 'M', 0, 'S', 0, 'F', 0, 'T', 0, '1', 0, '0', 0, '0', 0, bMS_VendorCode, 0 };
处理 USB 字符串描述符请求时, 当字符串索引wIndex==0xEE时, 返回OS_StringDescritpor响应字符串描述符。返回这个描述符后,USB设备被识别为WCID设备。 请注意,Windows只会在USB设备第一次连接后发送获取OS字符串描述符的请求,索引为0xEE 。OS描述符存储在注册表中HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\UsbFlags\VVVVPPPPRRRR(VVVV- VID; PPPP - PID; RRRR - 版本号)。在开发过程中如果无法正确识别为WCID设备,请删除USB设备对应的注册表项,然后在设备管理器中卸载USB设备,以便始终获得新的设备插入行为。
2. 响应VendorRequest厂商自带定义请求,bRequest请求号为OS字符描述符定义的bMS_VendorCode值(本例程为0xA0)。如果bRequest== 0xA0 && wIndex == 0x04,返回WCID扩展兼容特征描述符:“WINUSB”。 //"WINUSB\0\0" : wIndex : 0x0004 uint8_tWINUSB_ExtendedCompatId_Descritpor[ ] = { 0x28,0x00, 0x00, 0x00, // dwLength 0x00,0x01, // bcdVersion 0x04,0x00, // wIndex 0x01, // bCount 0x00,0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Reserved[7] 0x00, //bFirstInterfaceNumber 0x01, // RESERVED ( 0x01 ) 'W','I', 'N', 'U', 'S', 'B', 0x00, 0x00, //compactiableID[8] 0x00,0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // subCompactiableID[8] 0x00,0x00, 0x00, 0x00, 0x00, 0x00 // Reserved[6] }; 3. 响应VendorRequest厂商自带定义请求,bRequest请求号为OS字符描述符定义的bMS_VendorCode值(本例程为0xA0)。如果bRequest== 0xA0 && wIndex == 0x05,返回设备接口 GUID描述符:WINUSB_ExtendedProperty_InterfaceGUID_Descritpor。该描述符用于区分不同的WinUSB设备。 //L"DeviceInterfaceGUID" : wIndex = 0x0005 // L"{1D4B2365-4749-48EA-B38A-7C6FDDDD7E26}" // uint8_tWINUSB_ExtendedProperty_InterfaceGUID_Descritpor[ ] = { /////////////////////////////////////// /// WCID property descriptor /////////////////////////////////////// 0x8e, 0x00, 0x00, 0x00, /* dwLength */ 0x00, 0x01, /* bcdVersion */ 0x05, 0x00, /* wIndex */ 0x01, 0x00, /* wCount */ /////////////////////////////////////// /// registry propter descriptor /////////////////////////////////////// 0x84, 0x00, 0x00, 0x00, /* dwSize */ 0x01, 0x00, 0x00, 0x00, /*dwPropertyDataType */ 0x28, 0x00, /* wPropertyNameLength */ /* DeviceInterfaceGUID */ 'D', 0x00, 'e', 0x00, 'v', 0x00, 'i',0x00, /* wcName_20 */ 'c', 0x00, 'e', 0x00, 'I', 0x00, 'n',0x00, /* wcName_20 */ 't', 0x00, 'e', 0x00, 'r', 0x00, 'f',0x00, /* wcName_20 */ 'a', 0x00, 'c', 0x00, 'e', 0x00, 'G',0x00, /* wcName_20 */ 'U', 0x00, 'I', 0x00, 'D', 0x00, 0x00,0x00, /* wcName_20 */ 0x4e, 0x00,0x00, 0x00, /* dwPropertyDataLength */ /* {7D9ADCFC-E570-4B38-BF4E-8F81F68964E0}*/ '{', 0x00, '7', 0x00, 'D', 0x00,'9', 0x00, /* wcData_39 */ 'A', 0x00, 'D', 0x00, 'C', 0x00,'F', 0x00, /* wcData_39 */ 'C', 0x00, '-',0x00, 'E', 0x00, '5', 0x00, /*wcData_39 */ '7', 0x00, '0', 0x00, '-', 0x00, '4',0x00, /* wcData_39 */ 'B', 0x00, '3', 0x00, '8', 0x00, '-',0x00, /* wcData_39 */ 'B', 0x00, 'F', 0x00, '4', 0x00, 'E', 0x00, /* wcData_39 */ '-', 0x00, '8', 0x00, 'F', 0x00, '8',0x00, /* wcData_39 */ '1', 0x00, 'F', 0x00, '6', 0x00, '8',0x00, /* wcData_39 */ '9', 0x00, '6', 0x00, '4', 0x00, 'E',0x00, /* wcData_39 */ '0', 0x00, '}', 0x00, 0x00, 0x00, /* wcData_39 */ } 4. 修改MCUXpressoSDK USB例程,支持WinUSB。 NXP的MCUXpresso SDK USB例程 dev_printer_virtual_plain_text_bm原本是一个打印机例程,包含二个Bulk批量传输端点。可以在这个例程基础之上快速修改支持WinUSB功能。
修改下面宏定义,将打印机设备宏定义修改用户自定义协议WinUSB设备。 #defineUSB_DEVICE_DEMO_BCD_VERSION (0x0200U)
#defineUSB_DEVICE_STRING_COUNT (4U)
#defineUSB_PRINTER_CLASS (0xFFU) #defineUSB_PRINTER_SUBCLASS (0xFFU) #defineUSB_PRINTER_PROTOCOL (0x00U)
#defineUSB_DEVICE_CONFIG_PRINTER_CLASS_CODE (0xFFU) 在USB协议栈描述请求中增加VendorRequest厂商自带定义请求处理代码: /* Getdevice string descriptor request */ usb_status_tUSB_DeviceGetStringDescriptor(usb_device_handle handle, usb_device_get_string_descriptor_struct_t*stringDescriptor) { uint8_t languageIndex = 0U; uint8_t stringIndex = USB_DEVICE_STRING_COUNT;
if (stringDescriptor->stringIndex == 0U) { stringDescriptor->buffer = (uint8_t*)g_UsbDeviceLanguageList.languageString; stringDescriptor->length =g_UsbDeviceLanguageList.stringLength; }else if(stringDescriptor->stringIndex == 0xEE) // OSString { stringDescriptor->buffer = (uint8_t*)OS_StringDescritpor; stringDescriptor->length =OS_StringDescritpor[0]; }else { ………………………………………… ………………………………………… } return kStatus_USB_Success; }
staticusb_status_t USB_DeviceCallback(usb_device_handle handle, uint32_t event, void*param) { ………………………………………… switch (event) { ………………………………………… ………………………………………… case kUSB_DeviceEventVendorRequest: if (param) { status =USB_HandleVendorRequest(handle, event, param); } break; default: break; } return status; }
usb_status_tUSB_HandleVendorRequest(usb_device_handle handle, uint32_t event, void *param) { usb_status_t error = kStatus_USB_Success; uint8_t* pFlash;
/* Handle the vendor specific request. */ usb_device_control_request_struct_t*controlRequest = (usb_device_control_request_struct_t *)param;
if (controlRequest->setup->bRequest== WCID_VENDOR_CODE) { switch(controlRequest->setup->wIndex) { case 4: controlRequest->buffer =(uint8_t*)WINUSB_ExtendedCompatId_Descritpor; controlRequest->length =USB_LEN_OS_FEATURE_DESC; break; case 5: controlRequest->buffer =(uint8_t*)WINUSB_ExtendedProperty_InterfaceGUID_Descritpor; controlRequest->length =USB_LEN_OS_PROPERTY_DESC; break; default: error = kStatus_USB_InvalidRequest; break; } } return error; }
正确响应上述三个描述符请求后,就可以成功枚举为WinUSB设备。在Windows设备管理器中可以看到WinUSB设备工作正常: 5. WinUSB配套测试上位机软件。 微软官方提供了PC测操作WinUSB设备的示例代码。与之前通过VID/PID打开USB设备句柄不一样,上位机程序通过设备GUID参数获取WinUSB设备句柄。需要与嵌入式设备的描述符中的GUID保持一致。 调用 SetupDiGetClassDevs 获取设备信息集的句柄;调用SetupDiEnumDeviceInterfaces 枚举设备信息并获取有关设备接口的信息;调用WinUsb_Initialize创建的文件句柄。WinUsb_ReadPipe 从设备的批量输入端点读取数据。调用 WinUsb_WritePipe 通过批量输出端点将数据写入设备。最后WinUsb_Free释放由 WinUsb_Initialize 获得的WinUSB 接口句柄。 KEIL uVision5 安装目录自带了WinUSB上位机测试工具,支持中断传输,批量传输和控制传输。提供了Visual Studio VC++源代码工程,用户可以在此基础上实现自己的上位机软件。可参考KEIL网页( https://www.keil.com/pack/doc/mw/USB/html/dev_cc_tutorial.html)了解更多细节。The example can be tested on a Windows PC using theWinUSB_Test.exe utility provided with MDK Middleware. The program runsstand-alone without installation. Simply run"install_dir\ARM\PACK\Keil\MDK-Middleware\x.y.z\Utilities\WinUSB_Test\Release\WinUSB_Test.exe"application (where x >= 7, y >= 5, z >= 0):
|