STM32 USB VCOM和HID的区别,配置及Echo功能实现(HAL )

STM32的USB功能模块可以配置为虚拟串口(VCOM: Visual Port Com)或人机交互设备(HID: Human Interface Device)两种主要的通讯接口。可以实现PC到STM32的双向通信。在应用方面的区别主要体现为:

  1. 虚拟串口的一帧字节长度可以自行定义,发送和接收的buffer可以开得比较大;
  2. HID的一帧字节长度有限制,如全速USB的HID一帧长度最大64字节;
  3. 虚拟串口需要用户在PC安装操作系统驱动,然后应用软件需要人工或自动扫描和指定串口端口号再进行通讯;
  4. HID采用操作系统自带的驱动,不需要用户再去安装驱动,应用软件可以扫描有效的VID和PID,从而识别对应USB设备有没有正常连接,一般采用自动扫描连接而不采用人工连接。

这里介绍STM32F401CCU6基于STM32CUBIDE开发环境的USB虚拟串口及USB CUSTOM HID的配置及Echo功能实现(HAL库)。

基本工程配置

首先建立工程并配置采用外部HSE晶振,采用默认参数。USB功能需要外部HSE晶振提供时钟。

然后配置时钟树,关键是给USB提供48MHz时钟。

对于虚拟串口,可以根据需要调整堆和栈的大小:

这样就完成了基本的时钟配置,然后使能USB Device功能,采用默认参数即可:


这样就完成了USB Device功能的启用,后面根据实现虚拟串口还是HID进行不同的配置。

虚拟串口配置及Echo功能实现

首先将USB Device选择配置为虚拟串口并采用默认配置:

后面在PC上装STM32提供的虚拟串口驱动就可以进行通讯。这些默认配置信息可以修改,如果做了修改,则需要同步修改STM32提供的虚拟串口驱动里面的信息,实现信息匹配驱动才能对应生效。

注意根据需要配置发送和接收buffer的大小:

保存并生成初始工程代码:

Echo功能实现PC发到STM32的数据,STM32原样返回给PC端。首先找到STM32的接收函数:

然后在函数内增加发送代码,即可实现Echo功能:

static int8_t CDC_Receive_FS(uint8_t* Buf, uint32_t *Len)
{/* USER CODE BEGIN 6 */CDC_Transmit_FS(Buf, *Len);USBD_CDC_SetRxBuffer(&hUsbDeviceFS, &Buf[0]);USBD_CDC_ReceivePacket(&hUsbDeviceFS);return (USBD_OK);/* USER CODE END 6 */
}

编译下载后,在PC上用串口工具就可以实现验证。需要先安装STM32虚拟串口驱动(https://www.st.com/zh/development-tools/stsw-stm32102.html):

连接PC的USB口后,在Windows的设备管理器的端口里可以看到识别到的串口设备和端口号:

这里用PortHelper工具的效果,注意Echo设计在STM32里的转发速度比较快,所以只要PC端不是快速连续发送,波特率快慢可以任意调整:

在非Echo功能的实现上,如果有超长的数据发送,则可以分成多个段发送,前一个段发送完后再发下一个段,下面这个函数是每次发送完成之后的回调函数,可以在里面设置标志变化,从而在主程序里进行当前次发送已完成的判断。

HID配置及Echo功能实现

首先将USB Device配置为HID模式:

然后配置如下参数:

Custom_HID_FS_BINTERVAL为响应主机发送数据的延时时间, 最小为1,越小响应度越好。
USBD_CUSTOM_HID_REPORT_DESC_SIZE是报告描述符的字节长度,其与后面将进一步描述的代码关系如下所示:

USBD_CUSTOMHID_OUTREPORT_BUF_SIZE是输出的报文大小,这里设置为64个字节。对于低速设备每一笔事务最大是8字节;对于全速设备每一笔事务最大是64字节;对于高速设备每一笔事务最大是1024字节。
USBD_MAX_NUM_INTERFACES和USBD_MAX_NUM_CONFIGURATION是最大接口和配置的数量,这里只实现一个双向通信,所以是1个配置和1个接口。另外,在接口之下的端点则是3个,一个是默认0端点用于自举控制信息收发,另外两个端点一个用于发送一个用于接收。这些都通过描述符实现,STM32CUBEIDE会自动生成1个收发通讯对应的各级描述符,只有报告描述符需要手动设置。USB HID各级描述符的关系如下图:

USBD_MAX_STR_DESC_SIZ是字符串描述符的空间大小,采用默认即可。
USBD_SELF_POWERED是电源选项,设置为Enable。
USBD_DEBUG_LEVEL是调试选项,这里设置为不用即可。

然后配置描述符信息:

这里VID是厂家信息,正规厂家可以向USB协会申请厂家的代码,如下为ST的代码:

LANGID_STRING是语言识别符,设置为English即可。
MANUFACTURE_STRING是厂商名称,这里是ST公司名称。
PID是产品序列号,默认是22352。
PRODUCT_STRING是产品名称字符串设置。
CONFIGURATION_STRING是配置名称字符串设置。
INTERFACE_STRING是接口名称字符串设置。

实际上,以上的信息都是可以改动的,PC在USB HID自举时,更关键的信息是所采用的通讯协议的设定,从而决定采用的驱动。因此上述信息都是描述性信息,不会影响PC对USB HID的驱动安装,但是如果在PC上通过在线升级方式获取升级驱动,则需要这部分信息和在线驱动信息匹配。这里会将相关信息进行改动为如下所示:

保存后生成初始工程代码:

然后需要对报告描述符进行升级改写:

具体代码:

/** Usb HID report descriptor. */
__ALIGN_BEGIN static uint8_t CUSTOM_HID_ReportDesc_FS[USBD_CUSTOM_HID_REPORT_DESC_SIZE] __ALIGN_END =
{/* USER CODE BEGIN 0 *///0x00,0x05, 0x8c,0x09, 0x01,0xa1, 0x01,0x09, 0x03,0x15, 0x00,0x26, 0x00, 0xFF,0x75, 0x08,//0x95, CUSTOM_HID_EPIN_SIZE,0x95, 0x40,0x81, 0x02,0x09, 0x04,0x15, 0x00,0x26, 0x00, 0xFF,0x75, 0x08,//0x95, CUSTOM_HID_EPOUT_SIZE,0x95, 0x40,0x91, 0x02,/* USER CODE END 0 */0xC0    /*     END_COLLECTION                */
};

为了实现Echo功能,对HID接收中断回调函数进行改写,从而对接收到的数据进行存储。

具体代码:

static int8_t CUSTOM_HID_OutEvent_FS(uint8_t event_idx, uint8_t state)
{/* USER CODE BEGIN 6 */extern uint8_t usb_rx_status;extern uint8_t reportdata[64];USBD_CUSTOM_HID_HandleTypeDef   *hhid;hhid = (USBD_CUSTOM_HID_HandleTypeDef*)hUsbDeviceFS.pClassData;//Get receiving data addressusb_rx_status = 1;for(uint8_t i=0;i<64;i++){reportdata[i]=hhid->Report_buf[i]; //note: event_idx==hhid->Report_buf[0] ; state==hhid->Report_buf[1]}UNUSED(event_idx);UNUSED(state);/* Start next USB packet transfer once data processing is completed */if (USBD_CUSTOM_HID_ReceivePacket(&hUsbDeviceFS) != (uint8_t)USBD_OK){return -1;}return (USBD_OK);/* USER CODE END 6 */
}

然后在main.c文件实现变量定义和Echo功能的发送控制,代码如下:

/* USER CODE BEGIN Header */
/********************************************************************************* @file           : main.c* @brief          : Main program body******************************************************************************* @attention** Copyright (c) 2022 STMicroelectronics.* All rights reserved.** This software is licensed under terms that can be found in the LICENSE file* in the root directory of this software component.* If no LICENSE file comes with this software, it is provided AS-IS.********************************************************************************/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "usb_device.h"/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes *//* USER CODE END Includes *//* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD *//* USER CODE END PTD *//* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD *//* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM *//* USER CODE END PM *//* Private variables ---------------------------------------------------------*//* USER CODE BEGIN PV *//* USER CODE END PV *//* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
/* USER CODE BEGIN PFP *//* USER CODE END PFP *//* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
uint8_t usb_rx_status = 0;
uint8_t reportdata[64]={0};
extern USBD_HandleTypeDef hUsbDeviceFS;/* USER CODE END 0 *//*** @brief  The application entry point.* @retval int*/
int main(void)
{/* USER CODE BEGIN 1 *//* USER CODE END 1 *//* MCU Configuration--------------------------------------------------------*//* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* USER CODE BEGIN Init *//* USER CODE END Init *//* Configure the system clock */SystemClock_Config();/* USER CODE BEGIN SysInit *//* USER CODE END SysInit *//* Initialize all configured peripherals */MX_GPIO_Init();MX_USB_DEVICE_Init();/* USER CODE BEGIN 2 *//* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){if(usb_rx_status==1){usb_rx_status = 0;USBD_CUSTOM_HID_SendReport(&hUsbDeviceFS, reportdata, 64);}/* USER CODE END WHILE *//* USER CODE BEGIN 3 */}/* USER CODE END 3 */
}/*** @brief System Clock Configuration* @retval None*/
void SystemClock_Config(void)
{RCC_OscInitTypeDef RCC_OscInitStruct = {0};RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};/** Configure the main internal regulator output voltage*/__HAL_RCC_PWR_CLK_ENABLE();__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE2);/** Initializes the RCC Oscillators according to the specified parameters* in the RCC_OscInitTypeDef structure.*/RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;RCC_OscInitStruct.HSEState = RCC_HSE_ON;RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;RCC_OscInitStruct.PLL.PLLM = 25;RCC_OscInitStruct.PLL.PLLN = 336;RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV4;RCC_OscInitStruct.PLL.PLLQ = 7;if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK){Error_Handler();}/** Initializes the CPU, AHB and APB buses clocks*/RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK){Error_Handler();}
}/*** @brief GPIO Initialization Function* @param None* @retval None*/
static void MX_GPIO_Init(void)
{/* GPIO Ports Clock Enable */__HAL_RCC_GPIOH_CLK_ENABLE();__HAL_RCC_GPIOA_CLK_ENABLE();}/* USER CODE BEGIN 4 *//* USER CODE END 4 *//*** @brief  This function is executed in case of error occurrence.* @retval None*/
void Error_Handler(void)
{/* USER CODE BEGIN Error_Handler_Debug *//* User can add his own implementation to report the HAL error return state */__disable_irq();while (1){}/* USER CODE END Error_Handler_Debug */
}#ifdef  USE_FULL_ASSERT
/*** @brief  Reports the name of the source file and the source line number*         where the assert_param error has occurred.* @param  file: pointer to the source file name* @param  line: assert_param error line source number* @retval None*/
void assert_failed(uint8_t *file, uint32_t line)
{/* USER CODE BEGIN 6 *//* User can add his own implementation to report the file name and line number,ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) *//* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

编译下载到STM32后,用USB线连接到PC,则可以看到USB HID设备已识别成功:

可以用工具进行通讯测试,STM32提供USB HID Demonstrator作为测试工具。这里还是继续采用PortHelper工具来进行USB HID测试,还是比较方便的。

点击“打开USB"后:

其中自举握手信息显示的厂商号06C7就是十进制的1735,产品号3039就是十进制的12345, 也就是在STM32的USB HID配置界面设置的数字。
PortHelper已经基于获得的64个传输字节大小的信息,已经放置了64个字节在下面的发送区。在界面的辅助位置点上Hex发送和Hex显示,再点击端点2/HID发送,则STM32会将收到的数据回传过来。

需要注意的是,因为设置了64个字节的传输包大小,所以STM32侧发送数据必须以64个字节为一组发送,否则数量不足包不能发送。而在PC侧的软件方面,会自动检测输入的字节数是不是64个字节,如果不是64个字节,就会自动补0到64个字节发送。所以这里将发送区的发送数据改为1个55进行发送,实际上发出的就是1个55和63个00。如下所示:

按照上述设计,就实现了STM32 USB HID的Echo功能。

STM32两种HID区别

STM32可以将USB配置为HID或CUSTOM HID,上述的介绍实际上是针对CUSTOM HID的实现。HID和CUSTOM HID的配置区别为下面的部分:

也即HID相比CUSTOM HID少了报文描述符大小和传输报文大小的设置。实际上HID配置生成工程代码后,直接编译下载,用USB线连接STM32和PC后,PC识别为一个鼠标输入设备并直接使用,当然此时因为STM32里还没有设计输出数据,所以是个不动的鼠标。

范例下载

上述STM32F401 VCOM范例下载
上述STM32F401 CUSTOM HID范例下载

参考资料

Defined Class Codes
Device Class Definition for Human Interface Devices (HID)

–End–

STM32 USB VCOM和HID的区别,配置及Echo功能实现(HAL)相关推荐

  1. STM32 USB组合设备HID+MIDI

    目的:完成一个HID + MIDI的组合设备 准备工作: ·用CUBE生成HID工程: ·复制一份工程修改为MIDI工程:(参考之前博客已完成这两个工程) ·新建USB_User文件夹,将USB相关配 ...

  2. 62 stm32 usb自定义hid复合设备修改实验

    1.引言 最近因为项目需要,我们希望单片机既能有hid键盘功能,又能有hid设备的功能.即单片机的一个usb接口插入电脑后,电脑能识别出键盘设备和hid设备,两者同时存在的. 基于项目只是要求实现功能 ...

  3. stm32 USB HID+CDC 鼠标键盘串口 组合设备配置解析

    前言 查阅网上的博客与代码,很多都是关于USB的鼠标配置.USB的键盘配置.USB的虚拟串口配置,稍微深入一点的会将鼠标键盘合在一起,但移植起来就会报很多错误,要么是检测不到,要么是警告,这很正常,因 ...

  4. 记录一下 开发STM32 USB HID踩过的坑

    记录一下 开发STM32 USB HID踩过的坑 一.前言 二.代码配置 一.前言 MCU: STM32F103C8T6 CubeMX: STM32CubeMX 5.3.0 二.代码配置 引脚配置 时 ...

  5. STM32 USB复合设备(VCP虚拟串口+HID键盘)详解

    USB复合设备 介绍 USB复合设备与组合设备区别 USB描述符修改 修改CustomHID_Reset 修改CustomHID_Data_Setup 介绍 本次使用的是Keil 5+STM32F10 ...

  6. STM32 USB HID Mouse And Keyboard (guide)

    There is a post (STM32 And Custom USB HID Device? Yes Please!) that gets a lot of attention, althoug ...

  7. STM32 USB Host 同时连接多个设备样例(如鼠标和键盘)--原创

    STM32 USB Host 同时连接多个设备样例(如鼠标和键盘) 在网上搜了很多都是USB Host单独连接鼠标或键盘的样例, 而当前很多无线鼠标键盘都是并到一个USB口上的, 也就是同一个USB有 ...

  8. STM32和51单片机有什么区别

    在项目开发过程中,我们经常会遇到单片机选型的需要,所以我们往往会根据项目的功能需求选择合适的品牌和型号.常见的51单片机和STM32单片机也将是我们主要考虑的机型. STM32和51单片机参数区别 核 ...

  9. STM32 USB基础知识

    文章目录 拓展阅读 前言 一.USB基础知识 二.USB2.0 1.usb2.0模式说明 2.USB2.0全速USB和高速USB的识别过程分析 3.usb协议关系 4.USB2.0与USB1.x 硬件 ...

最新文章

  1. mixin network_基于Mixin Network的PHP比特币开发教程: 机器人接受比特币并立即退还用户...
  2. 人脸检测 RetinaFace
  3. Memcached Java客户端2.6.1发布
  4. 如何使用第三方日志记录提供程序替代.NET Core中的内置程序
  5. Wamp下Shopex的安装
  6. 蔡高厅老师 - 高等数学-阅读笔记 - 01 - 前言、函数【视频第01、02、03、】
  7. Madagascar的自定义浮点型函数--取整函数
  8. html论坛注册代码,Discuz!论坛代码大全和HTML代码大全 | 穆小刚营销博客
  9. windows server 2003安装pc套件ovi套件全过程?
  10. Markdown编辑器——Editor.md的使用
  11. lighttpd 配置php,Lighttpd常用配置方法
  12. 《周一清晨的领导课》--司机与乘客 - [读书笔记]
  13. 0-VMware Horizon 2203 虚拟桌面-目录大纲
  14. PDF文件不能编辑,有什么办法能够解决?
  15. 如何卸载有密码保护的Symantec Endpoint Protection v11
  16. Win11设备管理器在哪里打开?
  17. 某宝的登录密码加密--password2
  18. 适合医院、诊所、乡镇卫生院使用的门诊管理系统
  19. 【Proteus仿真】【51单片机】洗衣机控制系统设计
  20. 《PyInstaller打包实战指南》第十一节 其他进阶命令

热门文章

  1. 什么是Android内核
  2. 学计算机了情话,二十句哄女朋友的情话 每句都很管用
  3. 只会用Excel吗?这套全面的数据分析工具打包送你,拿走不谢
  4. Cmd Markdown
  5. i english怎么样,家长讲讲自己的经历!
  6. 面试——讲讲什么是happens-before?
  7. python全栈开发工程师招聘_浅谈Python全栈开发工程师,让程序员都眼红的职业!...
  8. 京东云使用密钥登陆云主机
  9. MySQL进阶:触发器
  10. 荣耀体质称显示网络服务器不可用,华为体脂秤如何重置