STM32 单片机 USB 通讯原理与 HAL 库实战详解

一、USB 基础与 STM32 硬件架构

1.1 USB 核心概念

USB (Universal Serial Bus) 是一种主从架构的串行总线标准:

  • 主机 (Host):控制总线,发起所有传输
  • 设备 (Device):响应主机请求
  • OTG(On-The-Go):设备可在主机和设备模式间切换
  • 传输速度:STM32 支持 USB 2.0 全速 (FS, 12Mbps) 和高速 (HS, 480Mbps)
  • 端点 (Endpoint) :USB 通讯的基本单元,每个设备最多 16 个 IN 端点和 16 个 OUT 端点
    • 端点 0:控制传输专用,双向,所有 USB 设备必须具备
    • 端点 1~15:批量、中断、同步传输,单向

1.2 STM32 USB 外设架构

STM32 的 USB OTG 外设主要由以下部分组成:

  • OTG 核心:处理 USB 协议底层逻辑
  • 设备控制器 (PCD):设备模式下的控制逻辑
  • 主机控制器 (HCD):主机模式下的控制逻辑
  • 端点 FIFO:每个端点有独立的 FIFO 缓冲区
  • DMA 控制器:用于高速数据传输
  • 中断控制器:处理 USB 相关中断

二、USB 传输类型详解

传输类型 用途 特点 典型应用
控制传输 设备枚举、配置、命令控制 双向,可靠,低带宽 设备识别、驱动安装
批量传输 大量非实时数据 单向,可靠,带宽可变 U 盘、打印机
中断传输 少量实时数据 单向,低延迟,固定周期 键盘、鼠标
同步传输 实时数据流 单向,不可靠,固定带宽 音频、视频

三、STM32 USB 关键寄存器详解

3.1 全局控制与状态寄存器

  • USB_OTG_GOTGCTL:OTG 控制寄存器,控制主从模式切换
  • USB_OTG_GINTSTS:全局中断状态寄存器,指示所有中断源
  • USB_OTG_GINTMSK:全局中断屏蔽寄存器,使能 / 禁用特定中断
  • USB_OTG_GRXSTSP:接收状态弹出寄存器,读取 OUT 端点接收状态

3.2 设备模式专用寄存器

  • USB_OTG_DCFG:设备配置寄存器,设置设备速度、端点 0 最大包长
  • USB_OTG_DCTL:设备控制寄存器,控制设备复位、远程唤醒
  • USB_OTG_DSTS:设备状态寄存器,指示设备当前状态
  • USB_OTG_DAINT:设备所有端点中断寄存器,指示哪个端点产生中断

3.3 端点控制寄存器

  • USB_OTG_DIEPCTLx:设备 IN 端点 x 控制寄存器,配置端点类型、最大包长
  • USB_OTG_DOEPCTLx:设备 OUT 端点 x 控制寄存器
  • USB_OTG_DIEPTSIZx:设备 IN 端点 x 传输大小寄存器,设置要发送的字节数
  • USB_OTG_DOEPTSIZx:设备 OUT 端点 x 传输大小寄存器,设置要接收的字节数

四、STM32CubeMX USB 配置步骤

4.1 基础时钟配置 (关键!)

USB 时钟必须精确为48MHz(FS)480MHz(HS)

  1. 启用外部高速晶振 (HSE)
  2. 配置 PLL:
    • 对于 HSE=8MHz:PLLM=8, PLLN=336, PLLP=2, PLLQ=7 → USB 时钟 = 48MHz
    • 对于 HSE=25MHz:PLLM=25, PLLN=336, PLLP=2, PLLQ=7 → USB 时钟 = 48MHz
  3. 在 "Clock Configuration" 中确认 "USB Clock" 为 48MHz

4.2 USB 外设配置

  1. 在 "Pinout & Configuration" 中选择 USB_OTG_FS 或 USB_OTG_HS
  2. 选择模式:
    • Device_Only:仅设备模式 (最常用)
    • Host_Only:仅主机模式
    • OTG:支持主从切换
  3. 配置 GPIO:
    • USB_DM 和 USB_DP 自动配置为复用功能
    • 如需 VBUS 检测,配置 VBUS 引脚为输入模式

4.3 USB 中间件配置

  1. 切换到 "Middleware" 选项卡
  2. 选择 "USB_DEVICE"
  3. 选择设备类:
    • Communication Device Class (CDC):虚拟串口
    • Mass Storage Class (MSC):U 盘
    • Human Interface Device (HID):人机接口设备
    • Audio Class:音频设备
  4. 配置设备描述符参数 (VID、PID、产品名称等)

五、USB 设备状态机

STM32 USB 设备在运行过程中会经历以下状态:

  1. 连接状态 (Attached):设备物理连接到主机
  2. 上电状态 (Powered):设备从 USB 总线获得电源
  3. 默认状态 (Default):设备收到复位信号后
  4. 地址状态 (Address):主机为设备分配唯一地址
  5. 配置状态 (Configured):主机完成设备配置,设备可正常工作
  6. 挂起状态 (Suspended):总线空闲超过 3ms,设备进入低功耗模式

六、常用 USB 模式 HAL 库应用示例

6.1 虚拟串口 (CDC) 模式

应用场景:PC 与单片机之间的串口通讯,替代传统 RS232 接口

CubeMX 配置

  • USB_DEVICE → Class for FS IP → Communication Device Class(CDC)
  • 生成代码后,在usbd_cdc_if.c文件中实现数据收发

发送数据示例

cpp 复制代码
#include "usbd_cdc_if.h"

// 发送字符串
uint8_t tx_buffer[] = "Hello USB CDC!\r\n";
CDC_Transmit_FS(tx_buffer, sizeof(tx_buffer)-1);

// 发送二进制数据
uint16_t adc_value = 1234;
uint8_t data[2];
data[0] = (adc_value >> 8) & 0xFF;
data[1] = adc_value & 0xFF;
CDC_Transmit_FS(data, 2);

接收数据示例 :在usbd_cdc_if.cCDC_Receive_FS函数中添加处理代码:

cpp 复制代码
static int8_t CDC_Receive_FS(uint8_t* Buf, uint32_t *Len)
{
  // Buf: 接收到的数据缓冲区
  // Len: 接收到的数据长度
  
  // 回显接收到的数据
  CDC_Transmit_FS(Buf, *Len);
  
  // 准备下一次接收
  USBD_CDC_SetRxBuffer(&hUsbDeviceFS, &Buf[0]);
  USBD_CDC_ReceivePacket(&hUsbDeviceFS);
  
  return (USBD_OK);
}

6.2 大容量存储 (MSC) 模式

应用场景:实现 U 盘功能,可访问内部 Flash 或外部 SD 卡

CubeMX 配置

  • USB_DEVICE → Class for FS IP → Mass Storage Class(MSC)
  • 生成代码后,在usbd_storage_if.c文件中实现存储介质操作

内部 Flash 模拟 U 盘示例 :修改usbd_storage_if.c中的以下函数:

cpp 复制代码
#define STORAGE_LUN_NBR                  1
#define STORAGE_BLK_NBR                  64  // 64个块
#define STORAGE_BLK_SIZ                  512 // 每个块512字节

// 内部Flash地址(根据实际芯片调整)
#define FLASH_START_ADDR                 0x08020000

int8_t STORAGE_Read_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)
{
  uint32_t i;
  uint32_t flash_addr = FLASH_START_ADDR + blk_addr * STORAGE_BLK_SIZ;
  
  for(i=0; i<blk_len*STORAGE_BLK_SIZ; i++)
  {
    buf[i] = *(__IO uint8_t*)(flash_addr + i);
  }
  
  return (USBD_OK);
}

int8_t STORAGE_Write_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)
{
  uint32_t i;
  uint32_t flash_addr = FLASH_START_ADDR + blk_addr * STORAGE_BLK_SIZ;
  HAL_StatusTypeDef status;
  
  // 解锁Flash
  HAL_FLASH_Unlock();
  
  // 擦除扇区
  FLASH_EraseInitTypeDef erase_init;
  erase_init.TypeErase = FLASH_TYPEERASE_SECTORS;
  erase_init.Sector = FLASH_SECTOR_5; // 根据实际地址调整
  erase_init.NbSectors = 1;
  erase_init.VoltageRange = FLASH_VOLTAGE_RANGE_3;
  uint32_t sector_error;
  HAL_FLASHEx_Erase(&erase_init, &sector_error);
  
  // 写入数据
  for(i=0; i<blk_len*STORAGE_BLK_SIZ; i+=4)
  {
    status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, 
                              flash_addr + i, 
                              *(uint32_t*)(buf + i));
    if(status != HAL_OK) break;
  }
  
  // 锁定Flash
  HAL_FLASH_Lock();
  
  return (USBD_OK);
}

int8_t STORAGE_GetCapacity_FS(uint8_t lun, uint32_t *block_num, uint16_t *block_size)
{
  *block_num = STORAGE_BLK_NBR;
  *block_size = STORAGE_BLK_SIZ;
  return (USBD_OK);
}

6.3 人机接口 (HID) 模式

应用场景:无需安装驱动的设备,如键盘、鼠标、游戏手柄

CubeMX 配置

  • USB_DEVICE → Class for FS IP → Human Interface Device(HID)
  • 生成代码后,在usbd_hid.c文件中修改报告描述符

USB 鼠标示例

  1. 修改usbd_hid.c中的报告描述符:
cpp 复制代码
__ALIGN_BEGIN static uint8_t HID_MOUSE_ReportDesc[HID_MOUSE_REPORT_DESC_SIZE] __ALIGN_END =
{
  0x05, 0x01,        // Usage Page (Generic Desktop)
  0x09, 0x02,        // Usage (Mouse)
  0xA1, 0x01,        // Collection (Application)
  0x09, 0x01,        //   Usage (Pointer)
  0xA1, 0x00,        //   Collection (Physical)
  0x05, 0x09,        //     Usage Page (Buttons)
  0x19, 0x01,        //     Usage Minimum (0x01)
  0x29, 0x03,        //     Usage Maximum (0x03)
  0x15, 0x00,        //     Logical Minimum (0)
  0x25, 0x01,        //     Logical Maximum (1)
  0x75, 0x01,        //     Report Size (1)
  0x95, 0x03,        //     Report Count (3)
  0x81, 0x02,        //     Input (Data,Var,Abs)
  0x75, 0x05,        //     Report Size (5)
  0x95, 0x01,        //     Report Count (1)
  0x81, 0x03,        //     Input (Const,Var,Abs)
  0x05, 0x01,        //     Usage Page (Generic Desktop)
  0x09, 0x30,        //     Usage (X)
  0x09, 0x31,        //     Usage (Y)
  0x09, 0x38,        //     Usage (Wheel)
  0x15, 0x81,        //     Logical Minimum (-127)
  0x25, 0x7F,        //     Logical Maximum (127)
  0x75, 0x08,        //     Report Size (8)
  0x95, 0x03,        //     Report Count (3)
  0x81, 0x06,        //     Input (Data,Var,Rel)
  0xC0,              //   End Collection
  0xC0               // End Collection
};
  1. 发送鼠标报告:
cpp 复制代码
#include "usbd_hid.h"

// 鼠标报告结构
typedef struct {
  uint8_t buttons; // 位0:左键, 位1:右键, 位2:中键
  int8_t x;        // X轴移动(-127~127)
  int8_t y;        // Y轴移动(-127~127)
  int8_t wheel;    // 滚轮移动(-127~127)
} MouseReport;

// 移动鼠标
MouseReport report = {0};
report.x = 10;  // 向右移动10个单位
report.y = -5;  // 向上移动5个单位
USBD_HID_SendReport(&hUsbDeviceFS, (uint8_t*)&report, sizeof(report));

// 点击左键
report.buttons = 0x01;
USBD_HID_SendReport(&hUsbDeviceFS, (uint8_t*)&report, sizeof(report));
HAL_Delay(100);
report.buttons = 0x00;
USBD_HID_SendReport(&hUsbDeviceFS, (uint8_t*)&report, sizeof(report));

6.4 复合设备模式

应用场景:同时实现多个 USB 功能,如虚拟串口 + U 盘

CubeMX 配置

  • USB_DEVICE → Class for FS IP → Composite Device
  • 添加需要的设备类 (如 CDC 和 MSC)
  • 生成代码后,在usbd_composite.c文件中配置接口

七、HAL 库 USB 核心函数详解

7.1 设备控制函数

  • HAL_PCD_Init(PCD_HandleTypeDef *hpcd):初始化 USB 设备控制器
  • HAL_PCD_DeInit(PCD_HandleTypeDef *hpcd):反初始化 USB 设备控制器
  • HAL_PCD_Start(PCD_HandleTypeDef *hpcd):启动 USB 设备
  • HAL_PCD_Stop(PCD_HandleTypeDef *hpcd):停止 USB 设备
  • HAL_PCD_SetAddress(PCD_HandleTypeDef *hpcd, uint8_t address):设置设备地址

7.2 端点操作函数

  • HAL_PCD_EP_Open(PCD_HandleTypeDef *hpcd, uint8_t ep_addr, uint16_t ep_mps, uint8_t ep_type):打开端点
  • HAL_PCD_EP_Close(PCD_HandleTypeDef *hpcd, uint8_t ep_addr):关闭端点
  • HAL_PCD_EP_Transmit(PCD_HandleTypeDef *hpcd, uint8_t ep_addr, uint8_t *pbuf, uint32_t len):发送数据
  • HAL_PCD_EP_Receive(PCD_HandleTypeDef *hpcd, uint8_t ep_addr, uint8_t *pbuf, uint32_t len):接收数据
  • HAL_PCD_EP_SetStall(PCD_HandleTypeDef *hpcd, uint8_t ep_addr):设置端点暂停
  • HAL_PCD_EP_ClearStall(PCD_HandleTypeDef *hpcd, uint8_t ep_addr):清除端点暂停

7.3 中断回调函数

  • HAL_PCD_ResetCallback(PCD_HandleTypeDef *hpcd):USB 复位回调
  • HAL_PCD_SuspendCallback(PCD_HandleTypeDef *hpcd):USB 挂起回调
  • HAL_PCD_ResumeCallback(PCD_HandleTypeDef *hpcd):USB 恢复回调
  • HAL_PCD_ConnectCallback(PCD_HandleTypeDef *hpcd):USB 连接回调
  • HAL_PCD_DisconnectCallback(PCD_HandleTypeDef *hpcd):USB 断开回调

八、常见问题与解决方案

  1. USB 枚举失败

    • 检查 USB 时钟是否精确为 48MHz
    • 检查 USB_DM 和 USB_DP 引脚是否正确连接
    • 检查设备描述符和配置描述符是否正确
    • 确保 VBUS 检测功能正常 (如果启用)
  2. 数据传输不稳定

    • 增加 USB 线的屏蔽性
    • 缩短 USB 线长度
    • 检查电源稳定性
    • 调整端点 FIFO 大小
  3. 驱动安装问题

    • CDC 设备:Windows 10 及以上系统自带驱动
    • HID 设备:所有系统都自带驱动
    • 自定义设备:需要使用 WinUSB 或 libusb 驱动
  4. 中断冲突

    • 调整 USB 中断优先级,确保高于其他低优先级中断
    • 避免在中断服务程序中执行耗时操作
相关推荐
资深流水灯工程师1 小时前
STM32 I2C 通讯原理与三种实现模式详解
stm32·单片机·嵌入式硬件
zlinear数据采集卡2 小时前
电源纹波杀手:LDO线性稳压电路的“降噪哲学”——基于ZLinear数据采集卡的深度解析
单片机·嵌入式硬件·fpga开发·硬件架构
资深流水灯工程师2 小时前
STM32 USART 通讯原理与三种模式详解
stm32·单片机·嵌入式硬件
资深流水灯工程师2 小时前
STM32 单片机 SPI 通讯原理详解
stm32·单片机·嵌入式硬件
EMTime2 小时前
玲珑GUI-工程设置
单片机·mcu·ui·用户界面
不做无法实现的梦~2 小时前
MAVLink 协议教程
linux·stm32·嵌入式硬件·算法
QiLinkOS3 小时前
【用呼吸重构创造价值关系——QiLink生态】
c语言·数据结构·c++·人工智能·单片机·嵌入式硬件·算法
sxstj3 小时前
STM32F103 串口数量 + 对应 GPIO
单片机·嵌入式硬件
嵌入式ZYXC3 小时前
第4章:MCU最小系统设计——从一颗光杆芯片到它能跑起来
stm32·单片机·嵌入式硬件·物联网