STM32 HAL库学习(六)USB识别成Custom HID通信

一、STM32F070 USB基础知识了解:

STM32F070 USB采用USB 2.0规范,时钟频率为48MHZ,有一个专用的USB数据存储区,大小为1024字节;支持16个单向传输端点、8个双向传输端点

USB外设模块构成图:

USB物理接口模块:

USB_DM、USB_DP —— 数据正、负信号

Analog Transceiver —— 模拟收发器

Embedded pull-up resister —— 可控嵌入负载电阻

Battery Charging Detection(BCD) —— 电池充电检测

USB_NOE —— 允许输出信号,可用于驱动LED或提供通信信息

Serial Interface Engine(SIE)串行接口引擎:

这个模块主要用于同步模式识别、CRC/PID的生成和检验、SOF/reset信号的生成、产生中断信号等

Timer定时器模块:

生成起始帧的锁定时钟脉冲以及检测阻塞

USB连接到到APB总线的APB接口模块构成有:Packet Memory、Arbiter(仲裁器,解决冲突访问APB的问题)、Register Mapper(寄存器映射器)、APB封装器(将USB外设映射到APB的地址空间)、中断映射

二、STM32Cube USB中间件文件了解:

在STM32CubeMX中选择USB中间件,点击生成代码就会自动加载USB的库文件,查看STM32_USB_Device_Library文件夹可见,USB库文件分为两类:一类是Class即设备类文件;一类是Core即内核文件。内核文件和设备类文件分别如下两图所示:

usbd_core.c /h —— 处理USB通信和状态机的函数

usbd_ctlreq.c/h —— 处理USB事务结果

usbd_conf_template.c/h —— 底层接口文件的模板文件,用户对其进行修改包含在应用文件中

usbd_ioreq.c/h —— 包含了USB规范列出的请求实现

三、使用STM32Cube MX配置USB

配置RCC时钟,激活HSE时钟源,作为USB时钟的输入,选择USB类为HID设备类

Configuration页面进行USB的相关设置、描述符设置,这里配置USB为用户自定义的HID设备,生成的MDK代码文件如下所示,建议先用工具生成报告描述符再根据它的长度进行配置

 

 分析描述符

首先是USB设备标准描述符,Cube MX 已经根据配置自动生成了

__ALIGN_BEGIN uint8_t USBD_FS_DeviceDesc[USB_LEN_DEV_DESC] __ALIGN_END =
{
  0x12,                       /*bLength */
  USB_DESC_TYPE_DEVICE,       /*bDescriptorType*/
  0x00,                       /*bcdUSB */
  0x02,
  0x00,                       /*bDeviceClass*/
  0x00,                       /*bDeviceSubClass*/
  0x00,                       /*bDeviceProtocol*/
  USB_MAX_EP0_SIZE,           /*bMaxPacketSize*/
  LOBYTE(USBD_VID),           /*idVendor*/
  HIBYTE(USBD_VID),           /*idVendor*/
  LOBYTE(USBD_PID_FS),        /*idProduct*/
  HIBYTE(USBD_PID_FS),        /*idProduct*/
  0x00,                       /*bcdDevice rel. 2.00*/
  0x02,
  USBD_IDX_MFC_STR,           /*Index of manufacturer  string*/
  USBD_IDX_PRODUCT_STR,       /*Index of product string*/
  USBD_IDX_SERIAL_STR,        /*Index of serial number string*/
  USBD_MAX_NUM_CONFIGURATION  /*bNumConfigurations*/
};

接下来是修改HID报告描述符,STM32HAL为用户提供了一个USB设备HID接口类的文件,即usbd_custom_hid_if.c/h,以便用户自行配置HID报告描述符,要修改的数组是CUSTOM_HID_ReportDesc_FS[USBD_CUSTOM_HID_REPORT_DESC_SIZE],建议使用 HID descriptor tool工具生成报告符。这里要注意的是报告描述符大小USBD_CUSTOM_HID_REPORT_DESC_SIZE,一定要与实际生成的报告描述符大小(使用HID descriptor tool可查看)相对应,否则会导致HID设备配置失败。

HID_Usage()等数值我在usbd_custom_hid_if.h中有定义

__ALIGN_BEGIN static uint8_t CUSTOM_HID_ReportDesc_FS[USBD_CUSTOM_HID_REPORT_DESC_SIZE] __ALIGN_END =
{
  /* USER CODE BEGIN 0 */
  HID_UsagePageVendor(0xa0),   
	HID_Usage(0xa5),
	HID_Collection(0x01),
	HID_Usage(0xa6),
/* input  */
	HID_Usage(0xa7),
	HID_LogicalMin(0x00),
	HID_LogicalMax(0xFF),
	HID_ReportSize(8),
	HID_ReportCount(64),
	HID_Input(HID_Data | HID_Variable | HID_Absolute),



/*  output  */
	HID_Usage(0xa9),
	HID_LogicalMin(0x00),
	HID_LogicalMax(0xff),
	HID_ReportSize(8),
	HID_ReportCount(64),
	HID_Output(HID_Data | HID_Variable | HID_Absolute),
  /* USER CODE END 0 */
  0xC0    /*     END_COLLECTION	             */
};

到这里为止,下载程序后PC机就可以识别出HID设备了

发送数据:

使用USBD_CUSTOM_HID_SendReport,这个函数在usbd_customhid.c中定义,在while循环中调用这个函数

uint8_t USBD_CUSTOM_HID_SendReport     (USBD_HandleTypeDef  *pdev, 
                                 uint8_t *report,
                                 uint16_t len)
{
  USBD_CUSTOM_HID_HandleTypeDef     *hhid = (USBD_CUSTOM_HID_HandleTypeDef*)pdev->pClassData;
  
  if (pdev->dev_state == USBD_STATE_CONFIGURED )
  {
    if(hhid->state == CUSTOM_HID_IDLE)
    {
      hhid->state = CUSTOM_HID_BUSY;
      USBD_LL_Transmit (pdev, 
                        CUSTOM_HID_EPIN_ADDR,                                      
                        report,
                        len);
    }
  }
  return USBD_OK;
}
 /* USER CODE BEGIN WHILE */
  while (1)
  {
    USB_Status = USBD_CUSTOM_HID_SendReport(&hUsbDeviceFS,InReport,64);
		HAL_Delay(100);
  /* USER CODE END WHILE */

  /* USER CODE BEGIN 3 */

  }
  /* USER C

关于端点最大传输包大小和HID报告描述符中的ReportCount,在usbd_customhid.c中有如下定义

#define CUSTOM_HID_EPIN_ADDR                 0x81
#define CUSTOM_HID_EPIN_SIZE                 0x02

#define CUSTOM_HID_EPOUT_ADDR                0x01
#define CUSTOM_HID_EPOUT_SIZE                0x02

这里定义中断传输端点的最大传输包大小为2,但其实实际传输时仍然可以传输64个数据,原因是在HID报告描述符中有对Input和Output的传输数据包大小做定义,实际传输大小应该以这个为标准,即每次传输最大数据量为64个数据,每个数据8位。不过建议将CUSTOM_HID_EPIN_SIZE/CUSTOM_HID_EPOUT_SIZE修改成和报告符中定义的大小一致。

HID_ReportSize(8),
HID_ReportCount(64),

接收数据:

USB中断传输方式中,每次PC机发送数据后USB设备都会产生中断,设备每完成一次从PC机的Out data的接收都会响应一次OutEvent,因此可以通过修改usbd_custom_hid_if.c中的static int8_t CUSTOM_HID_OutEvent_FS(uint8_t event_idx, uint8_t state)来实现对接收到数据做处理。

我这里在OutEvent中置位一次接收完成的标志OutComplete,然后在main函数中处理数据

static int8_t CUSTOM_HID_OutEvent_FS(uint8_t event_idx, uint8_t state)
{
  /* USER CODE BEGIN 6 */
	OutComplete = 1;
  return (USBD_OK);
  /* USER CODE END 6 */
}
  while (1)
  {
    USB_Status = USBD_CUSTOM_HID_SendReport(&hUsbDeviceFS,InReport,256);
		if(OutComplete)
		{
			hhid = (USBD_CUSTOM_HID_HandleTypeDef*)hUsbDeviceFS.pClassData;  //µÃµ½½ÓÊյĵØÖ·
			for(uint16_t i=0;i<64;i++)
			  OutReport[i] = hhid->Report_buf[i];
			HAL_UART_Transmit(&huart2, OutReport, 64, HAL_MAX_DELAY);
			OutComplete = 0;
		}
		
		HAL_Delay(100);
		
  /* USER CODE END WHILE */

  /* USER CODE BEGIN 3 */

  }
  /* USER CODE END 3 */

}

这里有一个小插曲,我调用HAL_UART_Transmit函数时习惯给延时参数直接赋值10,然后发现串口助手每次只接收到OutReport的前12个数据,但我明明指定了传输长度为64,后来才知道是延时参数设置不当,串口还没发送完数据就被迫停止传输了,后来改成HAL_MAX_Delay后数据就正常了。

猜你喜欢

转载自blog.csdn.net/yhl_sophia/article/details/83991153