文章目录
-
- 一、开发背景与环境准备
-
- [1.1 硬件准备](#1.1 硬件准备)
- [1.2 软件环境](#1.2 软件环境)
- [1.3 核心原理说明](#1.3 核心原理说明)
- 二、STM32CubeMX配置步骤
-
- [2.1 新建工程](#2.1 新建工程)
- [2.2 基础配置](#2.2 基础配置)
-
- [2.2.1 时钟配置](#2.2.1 时钟配置)
- [2.2.2 USB配置](#2.2.2 USB配置)
- [2.2.3 HID配置](#2.2.3 HID配置)
- [2.2.4 调试串口配置(可选,用于调试)](#2.2.4 调试串口配置(可选,用于调试))
- [2.3 工程生成](#2.3 工程生成)
- 三、代码编写与实现
-
- [3.1 代码文件结构说明](#3.1 代码文件结构说明)
- [3.2 关键代码编写](#3.2 关键代码编写)
-
- [3.2.1 文件名:usbd_custom_hid_if.h](#3.2.1 文件名:usbd_custom_hid_if.h)
- [3.2.2 文件名:usbd_custom_hid_if.c](#3.2.2 文件名:usbd_custom_hid_if.c)
- [3.2.3 文件名:main.c](#3.2.3 文件名:main.c)
- 四、编译与烧录
-
- [4.1 编译工程](#4.1 编译工程)
- [4.2 烧录程序](#4.2 烧录程序)
- 五、硬件调试与通信验证
-
- [5.1 驱动安装](#5.1 驱动安装)
- [5.2 通信测试](#5.2 通信测试)
-
- [5.2.1 工具准备](#5.2.1 工具准备)
- [5.2.2 测试步骤](#5.2.2 测试步骤)
- [5.3 常见问题排查](#5.3 常见问题排查)
- 六、功能扩展与优化
-
- [6.1 数据分包与校验](#6.1 数据分包与校验)
- [6.2 低功耗优化](#6.2 低功耗优化)
- 七、总结
一、开发背景与环境准备
你想要基于STM32F105芯片实现USB HID设备的开发,并完成虚拟串口通信的实战开发,本教程会从环境搭建到代码实现、硬件调试,全程详细讲解,确保零基础的你也能一步步完成开发落地。
1.1 硬件准备
- 核心板:STM32F105RBT6/STM32F105VCT6开发板(需带USB OTG/FS引脚)
- 外设:USB Type-A转Micro/Type-C数据线
- 辅助工具:USB转串口模块(用于调试)、杜邦线、万用表(可选)
- 烧录工具:ST-Link V2
1.2 软件环境
- 开发IDE:STM32CubeIDE 1.15.0(兼容主流版本)
- 固件库:STM32CubeF1 V1.8.5
- 调试工具:串口助手(SecureCRT/串口调试助手)、USB设备查看器(Windows)
- 驱动:STM32虚拟串口驱动(VCP Driver)
1.3 核心原理说明
STM32F105内置USB 2.0 FS控制器,支持HID(人机接口设备)类协议,虚拟串口本质是将HID协议封装成串口数据交互格式,实现PC与MCU之间的双向数据通信。整体开发流程如下:
成功
失败
环境搭建
STM32CubeMX配置
代码编写与移植
USB HID协议封装
虚拟串口数据交互
编译烧录
硬件调试
通信验证
功能扩展
问题排查
二、STM32CubeMX配置步骤
2.1 新建工程
- 打开STM32CubeMX,点击
Access to MCU Selector - 在搜索框输入
STM32F105RBT6,选择对应芯片后点击Start Project - 弹出
Project Manager窗口,先点击Cancel,后续统一配置
2.2 基础配置
2.2.1 时钟配置
- 点击左侧
Clock Configuration - 选择
HSE为Crystal/Ceramic Resonator(外部晶振) - 将
PLLMUL设置为x9,AHB Prescaler为/1,APB1 Prescaler为/2,APB2 Prescaler为/1 - 最终配置:
System Clock Mux为PLLCLK,SYSCLK=72MHz,HCLK=72MHz,PCLK1=36MHz,PCLK2=72MHz
2.2.2 USB配置
- 点击左侧
Pinout & Configuration - 在
Categories中选择Connectivity,找到USB_OTG_FS - 模式选择
Device Only(仅设备模式) - 启用
USB Global Settings中的VBUS Sense(根据硬件是否有VBUS引脚选择,无则关闭) - 自动分配USB引脚:PA11(DM)、PA12(DP),确认引脚无冲突
2.2.3 HID配置
- 在
Middleware中选择USB_DEVICE Class For FS IP选择HID Class- 点击
Parameter Settings,配置HID参数:HID_IN_EP_SIZE:64(输入端点大小,最大64字节)HID_OUT_EP_SIZE:64(输出端点大小)HID_POLLING_INTERVAL:10ms(轮询间隔)HID_REPORT_DESC_SIZE:64(报告描述符大小)
2.2.4 调试串口配置(可选,用于调试)
- 在
Categories中选择Connectivity,找到USART1 - 模式选择
Asynchronous(异步模式) - 配置参数:波特率115200、8位数据位、1位停止位、无校验
- 分配引脚:PA9(TX)、PA10(RX)
2.3 工程生成
- 点击左侧
Project Manager Project Name:输入STM32F105_USB_HID_VCOMProject Location:选择本地保存路径Toolchain/IDE:选择STM32CubeIDE- 点击
Code Generator,勾选Generate peripheral initialization as a pair of '.c/.h' files per peripheral - 点击
Generate Code,等待工程生成完成后,点击Open Project打开STM32CubeIDE
三、代码编写与实现
3.1 代码文件结构说明
生成的工程核心文件如下,后续主要修改/新增以下文件:
Core/Src/main.c:主函数,实现数据交互逻辑Middlewares/ST/STM32_USB_Device_Library/Class/HID/Src/usbd_hid.c:HID类核心函数(少量修改)Middlewares/ST/STM32_USB_Device_Library/Class/HID/Inc/usbd_hid.h:HID头文件(新增宏定义)Core/Inc/usbd_custom_hid_if.h:HID接口头文件Core/Src/usbd_custom_hid_if.c:HID接口实现(核心修改文件)
3.2 关键代码编写
3.2.1 文件名:usbd_custom_hid_if.h
c
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : usbd_custom_hid_if.h
* @brief : Header for usbd_custom_hid_if.c file.
******************************************************************************
* @attention
*
* Copyright (c) 2024 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 */
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __USBD_CUSTOM_HID_IF_H
#define __USBD_CUSTOM_HID_IF_H
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ------------------------------------------------------------------*/
#include "usbd_hid.h"
/* USER CODE BEGIN INCLUDE */
#include "stm32f1xx_hal.h"
/* USER CODE END INCLUDE */
/* Exported types ------------------------------------------------------------*/
/* Exported constants --------------------------------------------------------*/
// 自定义HID报告描述符大小
#define CUSTOM_HID_REPORT_DESC_SIZE 64
// 虚拟串口数据缓冲区大小
#define VCOM_DATA_BUFFER_SIZE 64
// HID报告描述符(适配虚拟串口)
extern const uint8_t CUSTOM_HID_ReportDesc_FS[CUSTOM_HID_REPORT_DESC_SIZE];
/* Exported macro ------------------------------------------------------------*/
/* Exported functions prototypes ---------------------------------------------*/
extern USBD_HID_ItfTypeDef USBD_CustomHID_fops_FS;
/* USER CODE BEGIN EXPORTED_FUNCTIONS */
// 发送数据到PC
uint8_t USBD_CUSTOM_HID_SendData_FS(uint8_t *pData, uint16_t Length);
// 接收PC发送的数据(回调函数)
void USBD_CUSTOM_HID_ReceiveData_FS(uint8_t *pData, uint16_t Length);
/* USER CODE END EXPORTED_FUNCTIONS */
#ifdef __cplusplus
}
#endif
#endif /* __USBD_CUSTOM_HID_IF_H */
3.2.2 文件名:usbd_custom_hid_if.c
c
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : usbd_custom_hid_if.c
* @brief : USB Device Custom HID interface file
******************************************************************************
* @attention
*
* Copyright (c) 2024 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 "usbd_custom_hid_if.h"
/* USER CODE BEGIN INCLUDE */
#include <string.h>
/* USER CODE END INCLUDE */
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
USBD_HID_ItfTypeDef USBD_CustomHID_fops_FS =
{
CUSTOM_HID_ReportDesc_FS,
CUSTOM_HID_Init_FS,
CUSTOM_HID_DeInit_FS,
CUSTOM_HID_OutEvent_FS
};
// 接收数据缓冲区
static uint8_t HID_ReceiveBuffer[VCOM_DATA_BUFFER_SIZE] = {0};
// 发送数据缓冲区
static uint8_t HID_SendBuffer[VCOM_DATA_BUFFER_SIZE] = {0};
/* USER CODE BEGIN PV */
/* Private variables ---------------------------------------------------------*/
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
static int8_t CUSTOM_HID_Init_FS(void);
static int8_t CUSTOM_HID_DeInit_FS(void);
static int8_t CUSTOM_HID_OutEvent_FS(uint8_t event_idx, uint8_t state);
/**
* @brief CUSTOM_HID_ReportDesc_FS
* Custom HID report descriptor
* @param None
* @retval pointer to report descriptor
*/
__ALIGN_BEGIN static uint8_t CUSTOM_HID_ReportDesc_FS[CUSTOM_HID_REPORT_DESC_SIZE] __ALIGN_END =
{
// 虚拟串口HID报告描述符
0x06, 0x00, 0xFF, // USAGE_PAGE (Vendor Defined Page 1)
0x09, 0x01, // USAGE (Vendor Usage 1)
0xA1, 0x01, // COLLECTION (Application)
0x09, 0x02, // USAGE (Vendor Usage 2)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x26, 0xFF, 0x00, // LOGICAL_MAXIMUM (255)
0x75, 0x08, // REPORT_SIZE (8)
0x95, 0x40, // REPORT_COUNT (64)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x09, 0x03, // USAGE (Vendor Usage 3)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x26, 0xFF, 0x00, // LOGICAL_MAXIMUM (255)
0x75, 0x08, // REPORT_SIZE (8)
0x95, 0x40, // REPORT_COUNT (64)
0x91, 0x02, // OUTPUT (Data,Var,Abs)
0xC0 // END_COLLECTION
};
/* Private functions ---------------------------------------------------------*/
/**
* @brief Initializes the CUSTOM HID media low layer
* @retval USBD_OK if all operations are OK else USBD_FAIL
*/
static int8_t CUSTOM_HID_Init_FS(void)
{
/* USER CODE BEGIN 4 */
// 初始化缓冲区
memset(HID_ReceiveBuffer, 0, VCOM_DATA_BUFFER_SIZE);
memset(HID_SendBuffer, 0, VCOM_DATA_BUFFER_SIZE);
return (USBD_OK);
/* USER CODE END 4 */
}
/**
* @brief DeInitializes the CUSTOM HID media low layer
* @retval USBD_OK if all operations are OK else USBD_FAIL
*/
static int8_t CUSTOM_HID_DeInit_FS(void)
{
/* USER CODE BEGIN 5 */
return (USBD_OK);
/* USER CODE END 5 */
}
/**
* @brief Manage the CUSTOM HID class events
* @param event_idx: Event index
* @param state: Event state
* @retval USBD_OK if all operations are OK else USBD_FAIL
*/
static int8_t CUSTOM_HID_OutEvent_FS(uint8_t event_idx, uint8_t state)
{
/* USER CODE BEGIN 6 */
// 读取PC发送的数据到接收缓冲区
USBD_HID_GetReport(&hUsbDeviceFS, HID_ReceiveBuffer, 0, VCOM_DATA_BUFFER_SIZE);
// 调用接收回调函数
USBD_CUSTOM_HID_ReceiveData_FS(HID_ReceiveBuffer, VCOM_DATA_BUFFER_SIZE);
return (USBD_OK);
/* USER CODE END 6 */
}
/* USER CODE BEGIN 7 */
/**
* @brief 发送数据到PC端
* @param pData: 待发送数据指针
* @param Length: 数据长度
* @retval 发送状态(0成功,其他失败)
*/
uint8_t USBD_CUSTOM_HID_SendData_FS(uint8_t *pData, uint16_t Length)
{
uint8_t result = USBD_FAIL;
if((pData != NULL) && (Length <= VCOM_DATA_BUFFER_SIZE))
{
// 拷贝数据到发送缓冲区
memset(HID_SendBuffer, 0, VCOM_DATA_BUFFER_SIZE);
memcpy(HID_SendBuffer, pData, Length);
// 发送数据
result = USBD_HID_SendReport(&hUsbDeviceFS, HID_SendBuffer, VCOM_DATA_BUFFER_SIZE);
}
return result;
}
/**
* @brief 接收PC端数据的回调函数(可在main.c中重定义)
* @param pData: 接收数据指针
* @param Length: 数据长度
* @retval None
*/
__attribute__((weak)) void USBD_CUSTOM_HID_ReceiveData_FS(uint8_t *pData, uint16_t Length)
{
// 默认实现:回显数据(将接收的数据直接发送回去)
if(Length > 0)
{
USBD_CUSTOM_HID_SendData_FS(pData, Length);
}
}
/* USER CODE END 7 */
3.2.3 文件名:main.c
c
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2024 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 */
#include "usbd_custom_hid_if.h"
#include <stdio.h>
#include <string.h>
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
// 串口打印缓冲区大小
#define UART_BUFFER_SIZE 128
// 测试数据发送间隔(ms)
#define SEND_TEST_DATA_INTERVAL 1000
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
UART_HandleTypeDef huart1;
/* USER CODE BEGIN PV */
// 串口打印缓冲区
char UART_Buffer[UART_BUFFER_SIZE];
// 系统滴答计数
uint32_t SysTick_Count = 0;
// 测试发送数据
uint8_t Test_Send_Data[] = "STM32F105 USB HID VCOM Test: Hello PC!\r\n";
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART1_UART_Init(void);
/* USER CODE BEGIN PFP */
// 重定向printf到USART1
int fputc(int ch, FILE *f);
// 自定义HID数据接收回调函数(覆盖弱函数)
void USBD_CUSTOM_HID_ReceiveData_FS(uint8_t *pData, uint16_t Length);
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
// 重定向printf到USART1
int fputc(int ch, FILE *f)
{
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, HAL_MAX_DELAY);
return ch;
}
// 自定义HID数据接收回调函数
void USBD_CUSTOM_HID_ReceiveData_FS(uint8_t *pData, uint16_t Length)
{
if((pData != NULL) && (Length > 0))
{
// 1. 串口打印接收的数据(调试用)
snprintf(UART_Buffer, UART_BUFFER_SIZE, "Receive PC Data: ");
HAL_UART_Transmit(&huart1, (uint8_t *)UART_Buffer, strlen(UART_BUFFER_SIZE), HAL_MAX_DELAY);
HAL_UART_Transmit(&huart1, pData, Length, HAL_MAX_DELAY);
snprintf(UART_Buffer, UART_BUFFER_SIZE, "\r\n");
HAL_UART_Transmit(&huart1, (uint8_t *)UART_Buffer, strlen(UART_BUFFER_SIZE), HAL_MAX_DELAY);
// 2. 回显数据到PC(虚拟串口)
USBD_CUSTOM_HID_SendData_FS(pData, Length);
}
}
/* 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_USART1_UART_Init();
MX_USB_DEVICE_Init();
/* USER CODE BEGIN 2 */
// 初始化完成,串口打印提示信息
printf("STM32F105 USB HID Virtual COM Port Init OK!\r\n");
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
// 每隔1秒发送一次测试数据到PC
if(HAL_GetTick() - SysTick_Count >= SEND_TEST_DATA_INTERVAL)
{
SysTick_Count = HAL_GetTick();
// 发送测试数据
USBD_CUSTOM_HID_SendData_FS(Test_Send_Data, strlen((char *)Test_Send_Data));
// 串口打印发送状态
printf("Send Test Data to PC: %s", Test_Send_Data);
}
/* 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};
RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
/** 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.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
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();
}
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USB;
PeriphClkInit.UsbClockSelection = RCC_USBCLKSOURCE_PLL_DIV1_5;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
{
Error_Handler();
}
}
/**
* @brief USART1 Initialization Function
* @param None
* @retval None
*/
static void MX_USART1_UART_Init(void)
{
/* USER CODE BEGIN USART1_Init 0 */
/* USER CODE END USART1_Init 0 */
/* USER CODE BEGIN USART1_Init 1 */
/* USER CODE END USART1_Init 1 */
huart1.Instance = USART1;
huart1.Init.BaudRate = 115200;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart1) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN USART1_Init 2 */
/* USER CODE END USART1_Init 2 */
}
/**
* @brief GPIO Initialization Function
* @param None
* @retval None
*/
static void MX_GPIO_Init(void)
{
/* USER CODE BEGIN MX_GPIO_Init_1 */
/* USER CODE END MX_GPIO_Init_1 */
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOA_CLK_ENABLE();
/* USER CODE BEGIN MX_GPIO_Init_2 */
/* USER CODE END MX_GPIO_Init_2 */
}
/* 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)
{
// 错误处理:可添加LED闪烁等提示
}
/* 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 */
四、编译与烧录
4.1 编译工程
- 在STM32CubeIDE中,点击顶部菜单栏
Project->Build Project - 等待编译完成,控制台显示
0 errors, 0 warnings即为编译成功 - 编译完成后,在工程目录
Debug文件夹下会生成STM32F105_USB_HID_VCOM.elf和STM32F105_USB_HID_VCOM.bin文件
4.2 烧录程序
- 连接ST-Link V2到STM32开发板的SWD接口(SWDIO、SWCLK、GND、VCC)
- 连接开发板到电脑USB口,打开STM32CubeIDE的
Run->Debug Configurations - 选择
STM32 Cortex-M C/C++ Application,点击New创建新配置 Project选择当前工程,C/C++ Application选择生成的.elf文件Debugger选择ST-Link Debugger,点击Apply->Debug- 程序会自动烧录到STM32芯片中,并进入调试模式,点击
Resume运行程序
五、硬件调试与通信验证
5.1 驱动安装
- 将STM32开发板的USB接口(PA11/PA12对应的USB口)连接到电脑
- Windows会自动检测新设备,若未自动安装驱动,需手动安装STM32 HID驱动:
- 下载STM32虚拟串口驱动(VCP Driver)
- 设备管理器中找到"STM32 HID Device",右键更新驱动,选择下载的驱动目录
5.2 通信测试
5.2.1 工具准备
- USB设备查看器:确认设备枚举成功,设备名称为"STM32 HID Virtual COM Port"
- HID调试工具:如
HID Terminal或USBlyzer - 串口助手:用于查看USART1的调试信息
5.2.2 测试步骤
- 打开串口助手,选择USART1对应的串口(如COM3),波特率115200,打开串口,可看到"STM32F105 USB HID Virtual COM Port Init OK!"提示
- 每隔1秒,串口助手会打印"Send Test Data to PC: STM32F105 USB HID VCOM Test: Hello PC!",说明MCU正在向PC发送数据
- 打开HID调试工具,选择对应的STM32 HID设备,发送任意字符(如"Test Data from PC")
- 串口助手会打印"Receive PC Data: Test Data from PC",同时HID调试工具会收到回显数据,说明双向通信成功
5.3 常见问题排查
- 设备枚举失败 :
- 检查USB引脚连接(PA11/DM、PA12/DP)是否正确,有无虚焊
- 确认时钟配置正确,USB时钟需为48MHz(72MHz/1.5)
- 检查USB线缆是否正常,尝试更换线缆
- 数据发送失败 :
- 检查HID报告描述符是否正确,端点大小是否匹配
- 确认
USBD_CUSTOM_HID_SendData_FS函数调用参数正确 - 检查USB设备是否处于挂起状态,可添加唤醒逻辑
- 数据接收无回调 :
- 确认
CUSTOM_HID_OutEvent_FS函数中是否调用USBD_HID_GetReport - 检查HID输出端点配置是否正确
- 确认
六、功能扩展与优化
6.1 数据分包与校验
对于超过64字节的数据,需实现分包发送,并添加CRC校验,避免数据丢失或错误:
c
// 文件名:usbd_custom_hid_if.c(新增函数)
/**
* @brief 分包发送数据
* @param pData: 待发送数据指针
* @param TotalLength: 数据总长度
* @retval 发送状态
*/
uint8_t USBD_CUSTOM_HID_SendData_Pack_FS(uint8_t *pData, uint16_t TotalLength)
{
uint8_t result = USBD_OK;
uint16_t SendLen = 0;
uint16_t RemainLen = TotalLength;
uint8_t *pSendBuf = pData;
while(RemainLen > 0)
{
SendLen = (RemainLen > VCOM_DATA_BUFFER_SIZE) ? VCOM_DATA_BUFFER_SIZE : RemainLen;
result = USBD_CUSTOM_HID_SendData_FS(pSendBuf, SendLen);
if(result != USBD_OK)
{
return result;
}
// 等待发送完成(可添加延时或等待标志位)
HAL_Delay(1);
pSendBuf += SendLen;
RemainLen -= SendLen;
}
return result;
}
6.2 低功耗优化
添加USB挂起/唤醒处理,降低功耗:
c
// 文件名:main.c(新增回调函数)
void HAL_PCD_SuspendCallback(PCD_HandleTypeDef *hpcd)
{
// USB挂起处理:关闭外设、进入低功耗模式
printf("USB Suspend, Enter Low Power Mode!\r\n");
HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON, PWR_SLEEPENTRY_WFI);
}
void HAL_PCD_ResumeCallback(PCD_HandleTypeDef *hpcd)
{
// USB唤醒处理:恢复外设、退出低功耗模式
printf("USB Resume, Exit Low Power Mode!\r\n");
HAL_PWR_ExitSLEEPMode();
}
七、总结
关键点回顾
- STM32F105实现USB HID虚拟串口的核心是配置USB_DEVICE为HID类,并自定义符合虚拟串口的HID报告描述符;
- 数据交互的核心函数为
USBD_CUSTOM_HID_SendData_FS(发送)和CUSTOM_HID_OutEvent_FS(接收回调),需保证缓冲区和端点大小匹配; - 调试时需先确认USB设备枚举成功,再通过串口助手和HID调试工具验证双向通信,常见问题集中在引脚连接、时钟配置和报告描述符错误。
本教程从环境搭建到代码实现、调试优化,覆盖了STM32F105 USB HID虚拟串口开发的全流程,代码可复制使用,零基础用户按照步骤操作即可完成实战开发。