Nuoeriris · 2020年06月22日

MM32 USB功能学习笔记 —— WinUSB设备

在上一节我们介绍了 MM32 MCU 的 HID 功能,对于HID 来说,虽然免驱,但是速度相对慢,有没有更好的选择呢?当然有,那就是WinUSB ,速度更快,对于现在的 PC 来说,基本上都是  Win10 ,WinUSB 在 Win10 免驱,此节我们就介绍如何使用 MM32 MCU的 WinUSB 功能。

WinUSB 设备是一种通用串行总线 (USB) 设备,其固件定义了某些 Microsoft 操作系统 (OS) 特征描述符,这些描述符将兼容 ID 报告为 "WINUSB"。

WinUSB 设备的用途是让 Windows 将 Winusb.sys 作为设备的功能驱动程序载入,而无需自定义 INF 文件。对于 WinUSB 设备,你无须为设备分发 INF 文件,对最终用户而言,这大大简化了驱动程序安装过程。相反,如果你需要提供自定义 INF,则不应将设备定义为 WinUSB 设备和在 INF 中指定设备的硬件 ID。

通常USB自定义设备都需要用户自己开发驱动,为了免去USB驱动开发,减少用户开发时间,微软花了不少心思,在Win8 或更高版本的Windows 系统中,集成了WinUSB 的WCID设备,从而WinUSB 作为微软提供的一个USB设备的通用驱动程序提供给用户,通过使用这个驱动,用户不再需要编写内核层的驱动程序就能够访问到USB设备。WCID是USB驱动一种新的匹配机制,在2012年左右引入的,通常USB设备都是通过VID和PID来进行匹配的,然而使用了WCID之后,USB设备不再通过VID和PID来匹配驱动,而是通过一个叫做兼容ID(Windows Compatible ID)来匹配,这样用户就不用为每一个VID和PID不同的设备编写INF文件了。当然我们要注意到,这里所说的免驱动包含两层含义:一层是用户不需要编写驱动程序,系统自带了驱动程序,只需写一个INF文件,比如USB串口;另一层则是不需要编写INF文件,系统会根据设备类型来安装驱动,这需要操作系统的支持。对于WinUSB设备来说,在Win8之前不用编写驱动程序,但是需要编写INF文件,匹配设备。在Win8之后,如果设备支持WCID,连INF也不用编写。
1.jpg
图1  WinUSB配置

下面将介绍如何在设备中增加对WCID的支持,让在能在Win8之后的系统上实在真正免驱即插即用。

首先我们得要有一个能够使用起来的自定义设备,在这个设备的设备描述符中,USB版本号设置为2.00,在这个设备的基础之上进行如下的修改:

**修改一:**响应ID为0xEE的字符描述符请求,字符描述的内容为:

{
0x12,                                       /* bLength */
USB_STRING_DESCRIPTOR_TYPE,          /* bDescriptorType */
'M', 0x00,                                    /* wcChar0 */
'S', 0x00,                                    /* wcChar1 */
'F', 0x00,                                    /* wcChar2 */
'T', 0x00,                                    /* wcChar3 */
'1', 0x00,                                    /* wcChar4 */
'0', 0x00,                                    /* wcChar5 */
'0', 0x00,                                    /* wcChar6 */
0x17,                                       /* bVendorCode */
0x00,                                       /* bReserved */
}

修改一是为了让我们的自定设备被识别为WCID设备。

**修改二:**响应请求号为0x17并且index为4的厂商自定义请求,返回内容为:

{
0x28, 0x00, 0x00, 0x00,                       /* dwLength */
0x00, 0x01,                                  /* bcdVersion */
0x04, 0x00,                                  /* wIndex */
0x01,                                        /* bCount */
0,0,0,0,0,0,0,                                 /* Reserved */
/* WCID Function  */
0x00,                                        /* bFirstInterfaceNumber */
0x01,                                        /* bReserved */
/* CID */
'W', 'I', 'N', 'U', 'S', 'B', 0x00, 0x00, 
/* sub CID */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0,0,0,0,0,0,                                  /* Reserved */
}

修改二是为了向Windows系统上报我们USB设备的WCID值,因为我们需要的是WinUSB的驱动程序,所以我们上报的内容信息就是WinUSB驱动的WCID:“WINUSB”。

在修改上述内容完成后,将MM32 MCU的USB插入电脑,会发现设备能够自动安装上WinUSB驱动程序,然后显示如下所示:
2.png
图2  WinUSB驱动程序

本次我们采用MM32L373 miniboard作为测试开发板。为了方便大家使用MM32 MCU的WinUSB功能,我们已经封装好全部代码,用户不需要自己配置以上的那些描述符等参数,只需要了解MM32 MCU WinUSB的VID和PID以及如何处理WinUSB的数据接收和发送即可。

对于MM32 MCU的WinUSB功能来说,在使用WinUSB功能之前先调用USB初始化函数来初始化USB协议栈。

int main(void)
{
// USB Device Initialization and connect
usbd_init();
usbd_connect(__TRUE);
while (!usbd_configured())   // Wait for USB Device to configure
{
}
while (1)
{
}
}

然后就是WinUSB数据收发处理函数,USB数据处理函数如下:

static U8 *ptrDataIn;
static U16 DataInReceLen;
static Bulk_queue Bulk_Cmd_queue;
 
static volatile uint8_t  USB_ResponseIdle;
 
void usbd_bulk_init(void)
{
ptrDataIn = USBD_Bulk_BulkOutBuf;
DataInReceLen = 0;
Bulk_queue_init(&Bulk_Cmd_queue);
USB_ResponseIdle = 1;
}
 
/*
 *  USB Device Bulk In Endpoint Event Callback
 *    Parameters:      event: not used (just for compatibility)
 *    Return Value:    None
 */
void USBD_BULK_EP_BULKIN_Event(U32 event)
{
uint8_t * sbuf = 0;
int slen;
if(Bulk_queue_get_send_buf(&Bulk_Cmd_queue, &sbuf, &slen)){
USBD_WriteEP(usbd_bulk_ep_bulkin | 0x80, sbuf, slen);
} else {
USB_ResponseIdle = 1;
}
}
 
/*
 *  USB Device Bulk Out Endpoint Event Callback
 *    Parameters:      event: not used (just for compatibility)
 *    Return Value:    None
 */
void USBD_BULK_EP_BULKOUT_Event(U32 event)
{
U16 bytes_rece;
uint8_t * rbuf;
 
bytes_rec = USBD_ReadEP(usbd_bulk_ep_bulkout, ptrDataIn, USBD_Bulk_BulkBufSize - DataInReceLen);
ptrDataIn += bytes_rece;
DataInReceLen  += bytes_rece;
if ((DataInReceLen >= USBD_Bulk_BulkBufSize) ||
(bytes_rece <  usbd_bulk_maxpacketsize[USBD_HighSpeed])) {
if(Bulk_queue_execute_buf(&Bulk_Cmd_queue,USBD_Bulk_BulkOutBuf, DataInReceLen, &rbuf)) {
//Trigger the BULKIn for the reply
if (USB_ResponseIdle) {
USBD_BULK_EP_BULKIN_Event(0);
USB_ResponseIdle = 0;
}
}
//revert the input pointers
DataInReceLen = 0;
ptrDataIn = USBD_Bulk_BulkOutBuf;
}
}
 
/*
 *  USB Device Bulk In/Out Endpoint Event Callback
 *    Parameters:      event: USB Device Event
 *                       USBD_EVT_OUT: Output Event
 *                       USBD_EVT_IN:  Input Event
 *    Return Value:    None
 */
void USBD_BULK_EP_BULK_Event(U32 event)
{
if (event & USBD_EVT_OUT) {
USBD_BULK_EP_BULKOUT_Event(0);
}
 
if (event & USBD_EVT_IN) {
USBD_BULK_EP_BULKIN_Event(0);
}
}
 
void Bulk_queue_init(Bulk_queue *queue)
{
queue->recv_idx = 0;
queue->send_idx = 0;
queue->free_count = FREE_COUNT_INIT;
queue->send_count = SEND_COUNT_INIT;
}
BOOL Bulk_queue_get_send_buf(Bulk_queue *queue, uint8_t **buf, int *len)
{
if (queue->send_count)
{
queue->send_count--;
*buf = queue->USB_Request[queue->send_idx];
*len = queue->resp_size[queue->send_idx];
queue->send_idx = (queue->send_idx + 1) % Bulk_PACKET_COUNT;
queue->free_count++;
return (__TRUE);
}
return (__FALSE);
}
 
BOOL Bulk_queue_execute_buf(Bulk_queue *queue, const uint8_t *reqbuf, int len, uint8_t **retbuf)
{
uint32_t rsize;
if (queue->free_count > 0)
{
if (len > Bulk_PACKET_SIZE)
{
len = Bulk_PACKET_SIZE;
}
queue->free_count--;
memcpy(queue->USB_Request[queue->recv_idx], reqbuf, len);
rsize = Bulk_ExecuteCommand(reqbuf, queue->USB_Request[queue->recv_idx]);
queue->resp_size[queue->recv_idx] = rsize & 0xFFFF; //get the response size
*retbuf = queue->USB_Request[queue->recv_idx];
queue->recv_idx = (queue->recv_idx + 1) % Bulk_PACKET_COUNT;
queue->send_count++;
return (__TRUE);
}
return (__FALSE);
}

如上,我们只需要实现修改如下Bulk\_ExecuteCommand即可处理我们收到的收据,并且填入我们发送数据出去队列即可发送出去。

为了验证MM32 MCU WinUSB功能,使用如下测试工具测试通信情况。
3.jpg
图3  USB调试工具

在VID栏填入我们MM32 MCU WinUSB的VID号0x2F81,点击Refresh刷新即可识别,点击OK就可以打开WinUSB。在图上可以看到有输入输出端点,我们MM32 MCU WinUSB采用的的端点EP3来收发数据,填入数据收发如下:4.png
图4  USB数据通信

发送端填入01 02 03 04 05 06 07 08 09,点击Send发送,接收端收到01 ff数据,测试结果说明我们的MM32 MCU的WinUSB数据通信正常。

以上就是MM32 MCU USB的WinUSB功能,下一节我们继续介绍MM32 MCU USB的虚拟串口CDC功能。

推荐阅读
关注数
6143
内容数
276
灵动MM32 MCU相关技术知识,欢迎关注~
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息