CH32-USB模拟HID设备分析
USB HID 设备是符合 USB HID 类规范的设备,它允许设备与计算机进行通信,实现人机交互功能,带有USB功能的CH32的EVT库里有模拟HID的dmeo,此例程与串口2交互实现USB与串口的透传功能。
鉴于很多USB初学者,此博客旨在通过详细的步骤,让客户快速上手此例程。

以V307的USBFS接口为例,USBD的与USBFS也是同理,USBHS的端点最大包长为512,而不同于USBD与USBFS的64字节。
一,重要外设参数初始化
/* Variables init */
Var_Init();//USB与串口2交互缓冲区参数初始化
/* UART2 init */
UART2_Init();//串口2初始化
UART2_DMA_Init();//串口2DMA初始化
/* Usb Init */
USBFS_RCC_Init();
USBFS_Device_Init(ENABLE);
NVIC_EnableIRQ(USBFS_IRQn);
/* Timer init */
TIM2_Init();//用作超时定时器
配置描述符里说明端点描述符与USB端点初始化一一对应,1为下传端点,2为上传端点。


二,数据交互。
if(USBFS_DevEnumStatus)//USBFS_DevEnumStatus USB枚举成功标志,不判断此位进行上传USB数据,可能会导致丢包或者异常。
{
UART2_Rx_Service();//串口接收的数据,通过USB端点2上传至USB主机。
UART2_Tx_Service();//USB主机下发的HID数据通过串口2发送出来。
HID_Set_Report_Deal();//此位HID类命令,通过端点0下发。
}
1.先看ART2_Rx_Service();函数
这里只贴出USB上传部分
if (pkg_len)//如果串口2的DMA缓冲区有数据
{
USBFSD->UEP2_DMA = (uint16_t)(uint32_t)USBFS_EP2_Buf;//USB端点(上传)的DMA指向USBFS_EP2_Buf缓冲区
USBFS_EP2_Buf[0] = pkg_len;//此次传输的串口接收的一包数据的总长度赋值给USB缓冲区的第一个元素。
memcpy(USBFS_EP2_Buf + 1,&UART2_RxBuffer[UART2_Rx_Deal_Ptr],pkg_len);将剩下的数据赋值给剩下的buf。
USBFSD->UEP2_TX_LEN = pkg_len + 1;
USBFS_Endp_Busy[DEF_UEP2] = 1;//上传忙标志位,在USB上传完成中断中清零,需要上传的时候可以判断此标志为0再进行上传操作。
USBFSD->UEP2_TX_CTRL = (USBFSD->UEP2_TX_CTRL & ~USBFS_UEP_T_RES_MASK) | USBFS_UEP_T_RES_ACK; // Start Upload
UART2_Rx_RemainLen -= pkg_len;//剩余数据
UART2_Rx_Deal_Ptr += pkg_len;//处理数据
if (UART2_Rx_Deal_Ptr >= DEF_UART2_BUF_SIZE)
{
UART2_Rx_Deal_Ptr = 0x00;//处理数据清零结束
}
}
此处理获取串口一包接收的数据,然后将这些数据拆成若干个慢64字节的数据包发出去(最后一包满64或不满64字节),此处理将第一个数据改成此包的数据长度,所有pkg_len 最大为63,占一个字节,若是数据满63字节,加上pkg_len 的本身的一个字节,则构成满包64字节。此点是透传的重要之点。处理的时候加上了本包的数据长度。
2.
这里必须要贴出端点1的接收中断,串口2发送的数据来自于此。
case USBFS_UIS_TOKEN_OUT | DEF_UEP1:
if ( intst & USBFS_UIS_TOG_OK )
{
/* Write In Buffer */
USBFSD->UEP1_RX_CTRL ^= USBFS_UEP_R_TOG;
RingBuffer_Comm.PackLen[RingBuffer_Comm.LoadPtr] = USBFSD->RX_LEN;//此次接收的长度
RingBuffer_Comm.LoadPtr ++;//环形缓冲区加载包数
if(RingBuffer_Comm.LoadPtr == DEF_Ring_Buffer_Max_Blks)//满了归零
{
RingBuffer_Comm.LoadPtr = 0;
}
USBFSD->UEP1_DMA = (uint32_t)(&Data_Buffer[(RingBuffer_Comm.LoadPtr) * DEF_USBD_FS_PACK_SIZE]);//USB端点接收DMA根据接收包数在环形缓冲区里偏移。
RingBuffer_Comm.RemainPack ++;//环形缓冲区未处理包数自增
if(RingBuffer_Comm.RemainPack >= DEF_Ring_Buffer_Max_Blks-DEF_RING_BUFFER_REMINE)//当未处理包距离缓冲区最大包还有4包时,停止USB停止接收
{
USBFSD->UEP1_RX_CTRL = (USBFSD->UEP1_RX_CTRL & ~USBFS_UEP_R_RES_MASK) | USBFS_UEP_R_RES_NAK;
RingBuffer_Comm.StopFlag = 1;//置停止接收标志位。
}
}
UART2_Tx_Service();
/* Pass-through USB-HID data to uart2 */
if(RingBuffer_Comm.RemainPack)
{
pkg_len = Data_Buffer[(RingBuffer_Comm.DealPtr) * DEF_USBD_FS_PACK_SIZE]; // Get the valid data length
if (pkg_len)
{
if (pkg_len > ( DEF_USBD_FS_PACK_SIZE - 1 ) )
{
pkg_len = DEF_USBD_FS_PACK_SIZE - 1; // Limit the length of this transmission
}
pbuf = &Data_Buffer[(RingBuffer_Comm.DealPtr) * DEF_USBD_FS_PACK_SIZE] + 1;//同理此处buf加一,将HID的调试助手发送的第一个数据不通过串口发送,第一个数据只代表帧头或者同步标志等等,用于程序判断,此处应注意。
UART2_DMA_Tx( pbuf, pkg_len );
UART2_Tx_Flag = 1;
}
三,至此,透传数据流程大致走完。现在看具体实操。
1.打开bushound选择HID设备
2.串口2接PA2接RX,PA3接TX。打开HIDTool,选择HID设备1A86的VID的,连接。工具下载地址
https://www.wch.cn/downloads/HIDAssist_ZIP.htm
串口发送63个字节,程序处理后将第一个数据改成0x3f等与63,后面的是数据。串口发送,HID工具与bushound显示如下
注:HID调试助手,需要一次接受满64字节才会显示,多了的也不显示,但是此时你可以用bushound观察。



通过HIDtool工具下发数据如下
当HIDtool发送不满64字节时,工具会自动补全。由此看到发送前面3个U,到串口这边只显示两个U,第一个U没有显示,由此第一个字节用户使用可以当作标志或同步位。



浙公网安备 33010602011771号