🎯 准备工作
-
获取源码:从上一节视频配套源码中复制基础工程
-
硬件平台:STM32H563xx系列(根据实际芯片调整)
-
开发环境:STM32CubeMX + Keil MDK-ARM(或其他IDE)
-
USBX源码:从ST官方获取或已有usbx文件夹
📝 详细移植步骤
第一步:CubeMX配置USB硬件
-
打开现有工程和对应的
.iocCubeMX文件 -
配置USB模块:
-
在"Connectivity"中选择USB
-
模式选择:Device_Only
-
-
配置NVIC中断:
- 勾选USB中断(确保中断使能)
-
生成代码:
- 点击"Generate Code"更新工程文件
第二步:准备USBX组件目录
-
找到USBX源码:定位到电脑上的usbx文件夹
-
移植到工程:
cs源位置:任意位置/usbx 目标位置:<工程目录>/Middlewares/Third_Party/usbx -
复制app文件夹:
cs源位置:程序源码/usbx/app 目标位置:<工程目录>/Middlewares/Third_Party/usbx/app
第三步:创建工程文件结构 
在MDK-ARM工程中创建四个组(Group)对应USBX四层架构:
| 组名 | 对应层级 | 物理路径 |
|---|---|---|
| USBX/class_layer | Class Layer | Middlewares/USBX/class_layer |
| USBX/stack_layer | Stack Layer | Middlewares/USBX/stack_layer |
| USBX/usb_device_controller_layer | Controller Layer | Middlewares/USBX/usb_device_controller_layer |
| USBX/app | Application | Middlewares/USBX/app |
第四步:添加各层源文件
4.1 Class Layer (CDC ACM类)
-
源文件位置 :
usbx/common/usbx_device_classes/src/ -
添加文件:
-
添加所有以
ux_device_class_cdc_acm开头的.c文件 -
使用通配符:
ux_device_class_cdc_acm*
-
4.2 Stack Layer (设备栈)
-
源文件位置 :
usbx/common/core/src/ -
添加文件:
-
添加所有以
ux_device_stack开头的.c文件 -
使用通配符:
ux_device_stack* -
注意:编译时可能会缺其他辅助文件,后续根据错误添加
-
4.3 Controller Layer (STM32控制器驱动)
-
源文件位置 :
usbx/common/usbx_stm32_device_controllers/ -
添加文件:添加该目录下的所有源文件
4.4 Application Layer (应用层)
-
源文件位置 :
usbx/app/(之前复制的) -
添加文件:添加该目录下的所有源文件
第五步:修改usb.c文件
5.1 添加头文件
cs
#include "ux_port.h"
#include "ux_device_descriptors.h"
#include "ux_dcd_stm32.h"
5.2 添加函数声明
cs
UINT MX_USBX_Device_Init(void);
MX_USBX_Device_Init();
5.3 在MX_USB_DRD_FS_Init()函数中添加以下代码:
电源使能:
cs
HAL_PWREx_EnableVddUSB();
HAL_PWREx_EnableUSBVoltageDetector();
端点Packet Buffer Memory配置:
cs
// 配置各个端点的缓冲区地址
HAL_PCDEx_PMAConfig(&hpcd_USB_DRD_FS, 0x00, PCD_SNG_BUF, 0x14);
HAL_PCDEx_PMAConfig(&hpcd_USB_DRD_FS, 0x80, PCD_SNG_BUF, 0x54);
HAL_PCDEx_PMAConfig(&hpcd_USB_DRD_FS, USBD_CDCACM_EPINCMD_ADDR, PCD_SNG_BUF, 0x94);
HAL_PCDEx_PMAConfig(&hpcd_USB_DRD_FS, USBD_CDCACM_EPOUT_ADDR, PCD_SNG_BUF, 0xD4);
HAL_PCDEx_PMAConfig(&hpcd_USB_DRD_FS, USBD_CDCACM_EPIN_ADDR, PCD_SNG_BUF, 0x114);
USBX初始化和启动:
cs
// 初始化USBX的STM32设备控制器
ux_dcd_stm32_initialize((ULONG)USB_DRD_FS, (ULONG)&hpcd_USB_DRD_FS);
// 调用USBX设备初始化函数
MX_USBX_Device_Init();
// 启动PCD
HAL_PCD_Start(&hpcd_USB_DRD_FS);
第六步:创建USBX任务(独立模式)
6.1 修改app_freertos.c
添加头文件:
cs
#include "stdio.h"
#include "draw.h"
#include "ux_api.h"
在默认任务循环中添加:
cs
void StartDefaultTask(void *argument)
{
/* 初始化代码... */
for(;;)
{
// 运行USBX系统任务(独立模式必须定期调用)
ux_system_tasks_run();
osDelay(1); // 适当延迟
}
}
第七步:MDK-ARM工程配置详解
7.1 预处理宏定义配置
在 Options for Target → C/C++ → Preprocessor Symbols 中添加以下宏:
cs
USE_HAL_DRIVER
STM32H563xx
UX_INCLUDE_USER_DEFINE_FILE
各宏作用说明:
-
USE_HAL_DRIVER:启用STM32 HAL库 -
STM32H563xx:指定芯片型号(根据实际调整) -
UX_INCLUDE_USER_DEFINE_FILE:关键!允许USBX使用用户自定义配置文件(ux_user.h)
7.2 编译器选项设置
在 Language / Code Generation 选项卡中:
-
Language C :选择
c11标准 -
Optimization :调试时建议选
-O0,发布可选-O1或-O2 -
关键勾选项:
-
☑ One ELF Section per Function(便于代码优化和调试)
-
☑ Plain Char is Signed(char默认为有符号)
-
7.3 头文件包含路径配置
在 Include Paths 中添加以下路径(注意相对路径):
cs
./Core/Inc
./Drivers/STM32H5xx_HAL_Driver/Inc
./Drivers/STM32H5xx_HAL_Driver/Inc/Legacy
./Drivers/CMSIS/Device/ST/STM32H5xx/Include
./Drivers/CMSIS/Include
./Drivers/Module_driver
./Middlewares/Third_Party/FreeRTOS/Source/include/
./Middlewares/Third_Party/FreeRTOS/Source/portable/GCC/ARM_CM33_NTZ/non_secure/
./Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS_V2/
./Middlewares/Third_Party/CMSIS/RTOS2/include/
./Middlewares/Third_Party/usbx/common/core/inc
./Middlewares/Third_Party/usbx/ports/generic/inc
./Middlewares/Third_Party/usbx/app
./Middlewares/Third_Party/usbx/common/usbx_stm32_device_controllers
./Middlewares/Third_Party/usbx/common/usbx_device_classes/inc
路径说明:
-
前9个是原有的FreeRTOS和CMSIS路径
-
后5个是新增的USBX相关路径,确保USBX各层头文件能被正确找到
第八步:添加USB串口测试代码
8.1 修改app_freertos.c添加发送任务
在 Core/Src/app_freertos.c 文件中进行以下修改:
1. 添加头文件(USER CODE BEGIN Includes段)
cs
/* USER CODE BEGIN Includes */
#include "stdio.h"
#include "draw.h"
#include "ux_api.h"
/* USER CODE END Includes */
2. 添加USB串口发送函数声明
如果函数未在其他头文件中声明,需要在任务函数前添加:
cs
/* 声明USB串口发送函数 */
int ux_device_cdc_acm_send(uint8_t *datas, uint32_t len, uint32_t timeout);
3. 创建SPILCD任务发送测试数据
在合适位置添加以下任务函数(或在现有任务中修改):
cs
static void SPILCDTaskFunction(void *pvParameters)
{
char buf[100];
int cnt = 0;
while (1)
{
/* 格式化测试字符串 */
sprintf(buf, "USB Serial Send Test : %d\r\n", cnt++);
/* 可选:在LCD上显示(如果LCD已初始化) */
// Draw_String(0, 0, buf, 0x0000ff00, 0);
/* 通过USB串口发送数据 */
ux_device_cdc_acm_send((uint8_t *)buf, strlen(buf), 1000);
/* 延迟1秒 */
vTaskDelay(1000);
}
}
第九步:编译与问题解决
首次编译常见错误及解决
| 错误类型 | 可能原因 | 解决方案 |
|---|---|---|
| 未找到头文件 | 包含路径不正确 | 检查第七步中的路径,确保所有路径都存在 |
| 未定义符号 | 源文件未添加完整 | 回到第四步,确保每层所需文件都已添加 |
| 内存不足 | 堆栈配置太小 | 在FreeRTOSConfig.h中增大堆栈大小 |
| 链接错误 | 函数重复定义 | 检查是否重复包含了源文件 |
编译顺序建议
-
先编译基础工程:确保不带USBX时能编译通过
-
分层添加编译:按Controller→Stack→Class→App顺序添加文件并编译
-
解决依赖关系:根据错误提示逐步添加缺少的文件
第十步:烧录与测试验证
10.1 硬件连接
-
使用USB数据线连接开发板USB口到电脑
-
确保开发板供电正常
-
连接调试器(ST-Link/J-Link等)
10.2 烧录程序
-
在MDK-ARM中点击 Download 按钮
-
确认程序烧录成功,无错误提示
-
重启开发板或按下复位键
10.3 电脑端验证
设备识别检查
-
打开 设备管理器
-
查看 端口(COM和LPT) 下是否有新设备
-
正常识别应显示类似:
USB 串行设备 (COM3)
串口通信测试
-
打开串口调试工具(如Putty、SecureCRT等)
-
选择正确的COM端口
-
设置参数:波特率115200、8数据位、1停止位、无校验
-
发送测试:在串口工具中发送任意字符
- 观察开发板LCD是否显示发送的内容
-
接收测试:查看串口工具是否每秒收到测试数据:
csUSB Serial Send Test : 0 USB Serial Send Test : 1 USB Serial Send Test : 2 ...
10.4 常见问题排查
| 现象 | 可能原因 | 排查方法 |
|---|---|---|
| 电脑无法识别 | USB线问题/供电不足 | 更换USB线,确保D+/D-线连接正常 |
| 识别为未知设备 | 驱动问题/描述符错误 | 安装STM32 USB驱动,检查描述符配置 |
| 无法打开串口 | 端口被占用 | 关闭其他占用该端口的程序 |
| 无数据接收 | 发送任务未运行 | 检查任务是否创建,USBX任务是否定期调用 |
| 数据乱码 | 波特率不匹配 | 确认串口工具和代码中波特率一致 |
📋 完整配置检查清单
配置项检查
-
CubeMX中USB配置为Device_Only
-
USB中断已使能
-
四层文件结构完整
-
各层源文件已添加到工程
-
usb.c中的初始化和配置代码已添加
-
头文件路径已正确配置
-
预处理宏已定义
-
发送任务已创建并运行
-
接收回调函数已修改
代码检查
-
ux_system_tasks_run()在任务中定期调用 -
USB电源使能函数已调用
-
端点缓冲区配置正确
-
描述符配置符合CDC ACM规范
-
内存地址无冲突
🎯 功能验证清单
| 功能 | 测试方法 | 预期结果 |
|---|---|---|
| USB设备识别 | 查看设备管理器 | 出现新串口设备 |
| 数据发送 | 串口工具接收 | 每秒收到递增的测试数据 |
| 数据接收 | 串口工具发送 | LCD显示发送的内容 |
| 稳定性 | 长时间运行 | 无断连,数据无丢失 |
| 热插拔 | 拔插USB线 | 重新连接后功能正常 |
🔧 调试技巧
-
使用调试器单步调试:在USB初始化代码处设置断点
-
查看USB状态寄存器:通过STM32CubeMonitor等工具监控USB状态
-
修改测试间隔:缩短发送间隔测试实时性
-
添加调试输出:通过串口打印调试信息(注意不要与虚拟串口冲突)
-
使用USB分析仪:如果有条件,使用USB协议分析仪抓包分析