26.STM32H743 USB
STM32H743集成了高速USB 2.0 OTG(On-The-Go) 外设,支持Host(主机)、Device(设备)、OTG(双模)三种模式,最高传输速率达480Mbps(高速模式),兼容USB 1.1(低速/全速12Mbps)。USB是嵌入式系统中实现与PC/外设数据交互的核心接口,可实现U盘读写、虚拟串口、USB摄像头、自定义HID设备等功能。
一、STM32H743 USB 核心硬件特性
STM32H743的USB OTG外设分为USB_OTG_FS(全速,12Mbps) 和USB_OTG_HS(高速,480Mbps),其中HS可兼容FS模式,核心特性如下:
|
特性 |
说明 |
|
传输速率 |
低速(1.5Mbps)、全速(12Mbps)、高速(480Mbps) |
|
工作模式 |
Device(设备):如虚拟串口、U盘、HID键盘;Host(主机):读写U盘、连接USB外设;OTG:双模切换 |
|
接口类型 |
USB_OTG_HS:D+(PA12)、D-(PA11)、VBUS(PA9)、ID(PA10);USB_OTG_FS:同HS(引脚复用) |
|
传输方式 |
批量传输(Bulk)、中断传输(Interrupt)、同步传输(Isochronous)、控制传输(Control) |
|
硬件加速 |
内置DMA控制器,支持高速数据传输,降低CPU占用 |
|
电源管理 |
支持VBUS检测(Host模式)、软/硬件电源控制 |
|
协议兼容 |
USB 2.0规范,支持CDC(虚拟串口)、MSC(大容量存储,U盘)、HID(人机交互)、自定义类 |
|
典型应用 |
虚拟串口(CDC)、USB U盘(MSC)、USB转CAN、自定义HID设备、USB数据采集 |
USB 核心信号
|
信号名称 |
功能 |
典型引脚(STM32H743) |
备注 |
|
USB_DP |
差分数据+线 |
PA12(OTG_HS/FS) |
全速/高速模式必备,低速模式D+接1.5kΩ上拉 |
|
USB_DM |
差分数据-线 |
PA11(OTG_HS/FS) |
所有模式必备 |
|
USB_VBUS |
总线电压检测 |
PA9(OTG_HS) |
Host模式下检测外设是否接入,Device模式下检测供电 |
|
USB_ID |
OTG模式识别 |
PA10(OTG_HS) |
区分Host/Device模式(接地=Host,悬空=Device) |
关键:
- Device模式:D+需接1.5kΩ上拉电阻(全速/高速),D-接1.5kΩ上拉(低速);
- Host模式:需外接5V电源给USB外设供电(如U盘),并检测VBUS电平;
- STM32H743的USB_OTG_HS默认使用内部PHY,无需外接USB PHY芯片(简化硬件)。
二、USB 工作原理
USB通信的核心是“主机(Host)发起请求,设备(Device)响应”,STM32可作为Host或Device,核心概念和流程如下:
1. 核心概念
- 端点(Endpoint):USB设备的通信端口,每个端点对应一种传输类型:
-
- 控制端点(Endpoint 0):所有设备必备,用于设备枚举、配置;
- 批量端点(Bulk):用于大量数据传输(如U盘、虚拟串口),容错重传;
- 中断端点(Interrupt):用于小数据高频传输(如HID键盘、鼠标);
- 同步端点(Isochronous):用于实时数据传输(如音频、视频),不重传。
- 枚举(Enumeration):Device接入Host后,Host通过控制端点获取设备信息(厂商ID、产品ID、设备类),并分配地址的过程。
- USB类(Class):标准化的设备类型,无需自定义驱动:
-
- CDC(通信设备类):虚拟串口,PC端识别为COM口;
- MSC(大容量存储类):U盘,PC端识别为可移动磁盘;
- HID(人机交互类):键盘、鼠标,免驱;
- 自定义类:需编写专用驱动(新手不推荐)。
2. 典型工作流程
(1)Device模式(以CDC虚拟串口为例)
- 初始化:配置USB引脚、时钟、端点,注册CDC类回调函数;
- 枚举:PC检测到USB设备,通过Endpoint 0获取设备信息,安装驱动;
- 数据传输:
-
- PC→STM32:Host通过批量IN端点发送数据,STM32中断接收;
- STM32→PC:STM32通过批量OUT端点发送数据,PC端COM口接收。
(2)Host模式(以读写U盘为例)
- 初始化:配置USB Host,启用VBUS检测,初始化USB库(如FatFs);
- 设备接入:检测到U盘接入后,枚举U盘,获取设备信息;
- 数据传输:通过批量端点读写U盘扇区,结合FatFs实现文件级操作。
三、STM32H743 USB 配置步骤(HAL库+CubeMX)
推荐从Device模式-CDC虚拟串口入手(最简单,免驱),CubeMX配置步骤如下:
步骤1:硬件准备
- 核心板:STM32H743(引出PA11/PA12/PA9/PA10);
- USB线:Micro-USB/Type-C线(连接STM32和PC);
- 硬件接线:
-
- PA11(DM)、PA12(DP)接USB座子的DM/DP;
- PA12接1.5kΩ上拉电阻到3.3V(Device模式必备);
- VBUS(PA9)可选接分压电阻检测5V供电。
步骤2:CubeMX 配置
- 配置系统时钟:
-
- H743主频设为280MHz,USB时钟源设为PLL3Q(480MHz,需精准);
- 确保USB_OTG_HS时钟=480MHz(高速模式)或48MHz(全速模式);
- 开启USB_OTG_HS外设:
-
- 进入“Connectivity → USB_OTG_HS”:
-
-
- Mode:Device Only(仅设备模式,新手推荐);
- PHY Selection:Internal PHY(内部PHY,无需外接);
- VBUS Detection:Disabled(新手先禁用,简化配置);
- Tick the FS Only Mode(可选,仅全速模式,降低复杂度);
-
- 配置USB设备类:
-
- 进入“Middleware → USB_DEVICE”:
-
-
- Class For FS IP:Communication Device Class (CDC)(虚拟串口);
- 其他默认(无需修改);
-
- 配置GPIO:
-
- PA11/PA12自动配置为USB_OTG_HS_DM/DP(Alternate Function模式);
- PA5配置为LED(推挽输出,用于指示状态);
- 生成代码:选择HAL库,生成MDK/STM32CubeIDE工程。
步骤3:HAL库核心API
|
函数 |
功能 |
|
MX_USB_DEVICE_Init() |
初始化USB Device(CubeMX自动生成) |
|
CDC_Transmit_FS() |
CDC模式发送数据到PC(核心函数) |
|
CDC_Receive_FS() |
CDC模式接收PC发送的数据(需注册回调) |
|
HAL_PCD_IRQHandler() |
USB Device中断服务函数 |
|
HAL_PCD_DataOutStageCallback() |
数据发送完成回调函数 |
|
HAL_PCD_DataInStageCallback() |
数据接收完成回调函数 |
CDC模式关键宏定义:
#define CDC_TX_BUF_SIZE 512 // 发送缓冲区大小
#define CDC_RX_BUF_SIZE 512 // 接收缓冲区大小
extern uint8_t UserRxBufferFS[CDC_RX_BUF_SIZE]; // 接收缓冲区
四、实战代码示例
示例1:CDC虚拟串口(Device模式)
实现STM32作为USB虚拟串口,与PC双向通信(PC端识别为COM口):
1. 核心代码(main.c)
#include "stm32h7xx_hal.h"
#include "usbd_cdc.h"
#include "usbd_core.h"
#include <stdio.h>
#include <string.h>
/* USB Device句柄(CubeMX自动生成) */
USBD_HandleTypeDef hUsbDeviceFS;
/* LED引脚定义(PA5) */
#define LED_PIN GPIO_PIN_5
#define LED_PORT GPIOA
/* 接收缓冲区(CubeMX生成在usbd_cdc_if.c中) */
extern uint8_t UserRxBufferFS[CDC_RX_BUF_SIZE];
extern uint32_t UserRxLengthFS;
/* 错误处理函数 */
void Error_Handler(void)
{
while(1)
{
HAL_GPIO_TogglePin(LED_PORT, LED_PIN); // LED快闪表示错误
HAL_Delay(200);
}
}
/**
* @brief 重定向printf到USB虚拟串口
*/
int fputc(int ch, FILE *f)
{
uint8_t temp[1] = {ch};
CDC_Transmit_FS(temp, 1);
return ch;
}
/**
* @brief CDC接收回调函数(usbd_cdc_if.c中重定义)
* 当PC发送数据到虚拟串口时,触发此函数
*/
void CDC_ReceiveCallback(uint8_t *Buf, uint32_t Len)
{
// 1. 点亮LED表示接收到数据
HAL_GPIO_SetPin(LED_PORT, LED_PIN);
// 2. 回显接收到的数据到PC
CDC_Transmit_FS(Buf, Len);
// 3. 打印调试信息(通过虚拟串口输出)
printf("Received Data: ");
for(uint32_t i=0; i<Len; i++)
{
printf("%02X ", Buf[i]);
}
printf("\r\n");
// 4. 延时后熄灭LED
HAL_Delay(500);
HAL_GPIO_ResetPin(LED_PORT, LED_PIN);
// 5. 重新启用接收(必须!否则无法接收下一次数据)
USBD_CDC_ReceivePacket(&hUsbDeviceFS);
}
int main(void)
{
/* 初始化HAL库、系统时钟、GPIO、USB Device */
HAL_Init();
SystemClock_Config();
MX_GPIO_Init(); // 初始化LED引脚
MX_USB_DEVICE_Init(); // 初始化USB CDC设备
// 初始化LED为熄灭状态
HAL_GPIO_ResetPin(LED_PORT, LED_PIN);
printf("STM32H743 USB CDC Virtual COM Port Test\r\n");
printf("Send Data from PC to COM Port to Test...\r\n");
while (1)
{
// 每隔1秒发送测试数据到PC
printf("Hello USB CDC! Time: %lu ms\r\n", HAL_GetTick());
HAL_Delay(1000);
}
}
2. 回调函数修改(usbd_cdc_if.c)
// 找到USBD_CDC_Receive_FS函数,修改为:
static int8_t USBD_CDC_Receive_FS(uint8_t* Buf, uint32_t *Len)
{
UserRxLengthFS = *Len;
memcpy(UserRxBufferFS, Buf, UserRxLengthFS);
// 调用自定义接收回调
CDC_ReceiveCallback(UserRxBufferFS, UserRxLengthFS);
// 重新启用接收
USBD_CDC_ReceivePacket(&hUsbDeviceFS);
return (USBD_OK);
}
// 声明回调函数(头文件中添加)
void CDC_ReceiveCallback(uint8_t *Buf, uint32_t Len);
3. 测试步骤
- 编译下载代码到STM32H743;
- 用USB线连接STM32和PC,PC自动识别为“USB Serial Device”(COM口);
- 打开串口助手(如SecureCRT、串口调试助手),选择对应的COM口,波特率任意(CDC无需波特率匹配);
- 发送数据,STM32回显数据并点亮LED,同时STM32每隔1秒发送“Hello USB CDC!”到PC。
示例2:MSC大容量存储(U盘模式)
实现STM32模拟U盘,PC可读写STM32内部Flash/外部SD卡(此处以内部RAM模拟U盘为例):
1. CubeMX配置修改
- Middleware → USB_DEVICE → Class For FS IP:Mass Storage Class (MSC);
- 配置MSC参数:Storage Size=1MB(RAM模拟),LUN(逻辑单元)=1;
2. 核心代码(main.c)
#include "stm32h7xx_hal.h"
#include "usbd_msc.h"
#include "usbd_core.h"
USBD_HandleTypeDef hUsbDeviceFS;
// 模拟U盘的RAM缓冲区(1MB)
uint8_t MSC_Buffer[1024*1024] __attribute__((aligned(4)));
// MSC设备描述符(CubeMX自动生成,需修改容量)
extern USBD_StorageTypeDef USBD_MSC_FS_fops;
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USB_DEVICE_Init();
// 初始化模拟U盘缓冲区(填充测试数据)
memset(MSC_Buffer, 0xAA, sizeof(MSC_Buffer));
printf("STM32H743 USB MSC U Disk Test\r\n");
printf("Connect to PC, U Disk Size: 1MB\r\n");
while (1)
{
// MSC模式无需主循环操作,硬件自动处理读写
HAL_Delay(1000);
}
}
3. 测试效果
- PC识别到“可移动磁盘”(1MB),可新建文件、写入数据;
- 数据实际存储在STM32的RAM中(掉电丢失),如需持久化可改为外部Flash/SD卡。
示例3:USB Host模式(读写U盘)
实现STM32作为USB主机,读写外接U盘(需结合FatFs文件系统):
1. CubeMX配置
- USB_OTG_HS Mode:Host Only;
- Middleware → USB_HOST → Class Support:MSC Class;
- 启用FatFs文件系统,绑定USB Host;
2. 核心代码(main.c)
#include "stm32h7xx_hal.h"
#include "usb_host.h"
#include "ff.h"
#include "stdio.h"
FATFS fs; // 文件系统对象
FIL file; // 文件对象
char buffer[512]; // 读写缓冲区
/* USB Host状态回调 */
void USBH_UserProcess(USBH_HandleTypeDef *phost, uint8_t id)
{
switch(id)
{
case HOST_USER_SELECT_CONFIGURATION:
break;
case HOST_USER_DISCONNECTION:
f_mount(NULL, "", 0); // 卸载文件系统
printf("USB Disk Disconnected!\r\n");
break;
case HOST_USER_CLASS_ACTIVE:
// U盘接入,挂载文件系统
if(f_mount(&fs, "", 0) == FR_OK)
{
printf("USB Disk Mounted!\r\n");
// 新建并写入文件
if(f_open(&file, "test.txt", FA_CREATE_ALWAYS | FA_WRITE) == FR_OK)
{
f_write(&file, "Hello USB Host!", 14, NULL);
f_close(&file);
printf("Write to test.txt Success!\r\n");
// 读取文件
if(f_open(&file, "test.txt", FA_READ) == FR_OK)
{
f_read(&file, buffer, 512, NULL);
printf("Read from test.txt: %s\r\n", buffer);
f_close(&file);
}
}
}
break;
default:
break;
}
}
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USB_HOST_Init(); // 初始化USB Host
printf("STM32H743 USB Host Test\r\n");
printf("Insert USB Disk to Test...\r\n");
while (1)
{
// USB Host主循环(必须调用)
USBH_Process(&hUsbHostHS);
HAL_Delay(10);
}
}
五、常见问题与避坑指南
1. USB Device枚举失败(PC识别不到设备)
- 原因1:时钟配置错误:
USB时钟不是精准的48MHz(全速)/480MHz(高速),H743需配置PLL3Q=480MHz;
→ 解决:CubeMX中“Clock Configuration”确保USB时钟=480MHz(HS)或48MHz(FS); - 原因2:上拉电阻缺失:
Device模式下D+未接1.5kΩ上拉电阻,PC无法检测到设备;
→ 解决:PA12接1.5kΩ上拉到3.3V(全速/高速); - 原因3:引脚配置错误:
PA11/PA12未设为USB_OTG_HS_DM/DP模式,或被其他外设占用;
→ 解决:核对引脚模式为Alternate Function 10(USB_OTG_HS); - 原因4:USB库初始化顺序错误:
先初始化GPIO,再初始化USB Device,否则引脚未配置导致枚举失败;
→ 解决:按“时钟→GPIO→USB”顺序初始化。
2. CDC虚拟串口能发不能收/能收不能发
- 原因1:未重新启用接收:
接收回调函数中未调用USBD_CDC_ReceivePacket(&hUsbDeviceFS),导致仅能接收一次数据;
→ 解决:每次接收完成后必须重新启用接收; - 原因2:缓冲区溢出:
接收缓冲区过小(如64字节),PC发送大数据时溢出;
→ 解决:增大缓冲区(如512/1024字节); - 原因3:发送函数阻塞:
CDC_Transmit_FS()返回忙(USBD_BUSY),未等待发送完成就再次发送;
→ 解决:添加发送状态检查:
while(CDC_Transmit_FS(buf, len) == USBD_BUSY);
3. USB Host无法识别U盘
- 原因1:VBUS供电不足:
Host模式下未给U盘提供5V供电,或供电电流不足(U盘需≥100mA);
→ 解决:外接5V/1A电源给USB接口供电,避免STM32 3.3V引脚直接供电; - 原因2:未调用USBH_Process():
主循环中未执行USBH_Process(&hUsbHostHS),USB Host无法处理枚举流程;
→ 解决:主循环中高频调用此函数(间隔≤10ms); - 原因3:U盘格式不支持:
U盘格式为NTFS/exFAT,FatFs默认仅支持FAT32;
→ 解决:将U盘格式化为FAT32,簇大小≤32KB。
4. 高速模式(480Mbps)传输卡顿
- 原因1:未启用DMA:
USB传输使用CPU轮询,未启用DMA,导致高速传输时CPU占用率100%;
→ 解决:CubeMX中启用USB DMA(默认已启用); - 原因2:总线带宽不足:
同时进行多个USB传输(如CDC+MSC),导致带宽耗尽;
→ 解决:单模式测试(仅CDC或仅MSC),降低传输频率; - 原因3:缓存未对齐:
传输缓冲区未按32位对齐,DMA传输出错;
→ 解决:用__attribute__((aligned(4)))修饰缓冲区。
5. USB设备频繁断开重连
- 原因1:电源纹波过大:
USB供电电源纹波>100mV,导致设备复位;
→ 解决:USB接口添加100uF电解电容+0.1uF陶瓷电容滤波; - 原因2:数据线过长/屏蔽差:
USB线超过2米,或无屏蔽,信号干扰导致断开;
→ 解决:使用≤1米的屏蔽USB线,靠近接口处接磁环; - 原因3:中断优先级冲突:
USB中断优先级低于其他高优先级中断(如SysTick),导致枚举超时;
→ 解决:将USB中断优先级设为最高(NVIC优先级0)。
六、调试顺序
1. 入门步骤
- 第一步:CDC虚拟串口(Device模式),实现双向通信(最简单,免驱);
- 第二步:MSC U盘模式(Device),模拟RAM/Flash为U盘;
- 第三步:USB Host模式,读写外接U盘(结合FatFs);
- 第四步:OTG双模切换,实现Device/Host动态切换;
- 第五步:自定义HID设备(如USB按键、USB数据采集器)。
2. 调试技巧
- USB分析仪调试:
用USB协议分析仪(如Beagle USB)抓取枚举/传输数据包,定位枚举失败原因; - 串口日志调试:
在USB初始化、枚举、传输等关键节点打印日志,定位问题环节; - 分步测试:
- 先测试全速模式(12Mbps),再切换到高速模式(480Mbps);
- 先测试小数据传输(1字节),再测试大数据(512字节);
- 硬件排查:
用万用表测D+/DM电压(Device模式:D+≈3.3V,DM≈0V),VBUS电压(Host模式≈5V)。
3. 重点注意事项
- 时钟精准性:USB时钟必须是48MHz/480MHz精准值(误差≤0.25%),否则枚举失败;
- 电源设计:Host模式下USB外设供电必须稳定,避免电流不足导致U盘断开;
- 回调函数:USB依赖回调函数处理事件,必须正确实现(如CDC接收回调、Host插拔回调);
- 兼容性:不同U盘/PC的USB兼容性不同,测试时优先用知名品牌U盘(如金士顿)。
4. 实际应用练习
- 数据采集:通过CDC虚拟串口将传感器数据(温湿度、ADC)实时上传到PC;
- 固件升级:通过USB MSC模式,将bin文件写入STM32 Flash,实现USB升级;
- USB转串口:STM32作为USB Host,连接USB转串口模块,实现多串口扩展;
- 自定义HID设备:实现USB游戏手柄、USB数据记录仪,免驱接入PC。
更多推荐



所有评论(0)