STM32 USB 设备中间件 tinyusb

整个 STM32 USB 设备中间件确实是基于 USB中断机制 来驱动的,详细说来:

  • USB硬件控制器 通过中断通知 MCU 有事件发生,比如:

    • 总线事件(连接、断开、重置、挂起、唤醒等)
    • 数据包传输完成(端点IN/OUT完成数据传输)
    • 控制请求到达(SETUP包)
  • 中断服务程序(ISR) 捕获这些事件,调用 USB中间件的处理函数

    • 这部分代码就在 usbd_core.cusbd_ctlreq.cusbd_ioreq.c 中处理各种USB事件和请求
    • 并根据事件类型调用对应的类回调函数(比如 Audio类的 DataInDataOut 回调)
  • 上层USB类代码 通过实现这些回调函数,完成具体的数据处理和传输逻辑(如音频数据采集发送)


所以,USB中间件不是轮询模式,而是基于中断触发和事件驱动的,响应速度快且效率高,非常适合USB这种实时性要求高的通信。


USB通信的核心就是:

  1. 中断触发

    • USB硬件收到主机的数据包或事件(比如控制请求、数据传输完成)后产生中断。
    • MCU进入USB中断服务程序。
  2. 控制端点(Endpoint 0)处理控制请求

    • usbd_ctlreq.c 负责解析并响应标准USB请求(Get Descriptor、Set Configuration等)。
    • 控制端点也处理类特定请求,比如音频控制接口的音量调节命令。
  3. 读写缓冲区

    • 非控制端点的数据传输(如音频数据的IN/OUT端点)通过 usbd_ioreq.c 管理。
    • 中断触发后,USB核心库调用对应类的回调函数(如音频的 DataInDataOut),让上层代码读写缓冲区。
    • 上层类代码负责填充要发送的数据,或处理接收到的数据。
  4. 数据发送和接收

    • 通过 USBD_LL_Transmit() 等底层函数启动端点数据传输。
    • 传输完成后触发中断,再次调用回调,形成循环。

TinyUSB

基于 STM32F103(比如 STM32F103C8)+ STM32CubeMX + HAL 使用 TinyUSB 。下面是最完整的集成步骤,可以让你保留 STM32CubeMX 的结构,同时接入 TinyUSB 来实现如 USB CDC、HID、MSC 等设备功能


🧱 基础条件

确保你已经完成:

✅ STM32CubeMX 工程创建,启用了 USB(Device 模式)

✅ 使用 HAL 库(非 LL)

✅ 项目使用 Makefile 或 CMake 构建(你用的是 CMake)

✅ 下载了 TinyUSB 源码


🧩 集成步骤(CubeMX + HAL + TinyUSB)


✅ 1. STM32CubeMX 设置

  • Pinout

    • USB_DP: PA12 ✅
    • USB_DM: PA11 ✅
  • Middleware

    • 不启用 USB_DEVICE 中的 CDC/HID(我们用 TinyUSB 实现)
  • Peripherals

    • 启用 USB 外设(Device 模式)
    • 其他系统外设如时钟、SysTick、RCC、USART 自行配置

然后生成代码,结构大概是:

复制代码
Core/
  └── Src/
  └── Inc/
Drivers/
  └── STM32F1xx_HAL_Driver/
  └── CMSIS/

✅ 2. 引入 TinyUSB 到项目中

把 TinyUSB 下载并放在项目目录中,例如:

复制代码
third_party/tinyusb/

✅ 3. 添加 tusb_config.h 文件

Core/Inc/ 中添加配置文件:

📄 Core/Inc/tusb_config.h

c 复制代码
#ifndef _TUSB_CONFIG_H_
#define _TUSB_CONFIG_H_

#define CFG_TUSB_MCU             OPT_MCU_STM32F1
#define CFG_TUSB_RHPORT0_MODE    OPT_MODE_DEVICE
#define CFG_TUSB_OS              OPT_OS_NONE

#define CFG_TUD_ENDPOINT0_SIZE   64

// 启用 TinyUSB 的子功能(例如 CDC)
#define CFG_TUD_CDC              1
#define CFG_TUD_MSC              0
#define CFG_TUD_HID              0
#define CFG_TUD_MIDI             0
#define CFG_TUD_VENDOR           0

#endif // _TUSB_CONFIG_H_

✅ 4. 初始化 TinyUSB(在 main.c 中)

main.c 中手动初始化 USB:

c 复制代码
#include "tusb.h"

int main(void)
{
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_USB_PCD_Init(); // CubeMX 生成的 USB 初始化

  tusb_init(); // 初始化 TinyUSB 核心

  while (1)
  {
    tud_task();     // TinyUSB 轮询任务(必须)
  }
}

✅ 5. 添加 CDC 回调(比如 echo)

📄 usb_descriptors.c

c 复制代码
#include "tusb.h"

// USB 描述符定义(可以参考 tinyusb/examples/device/cdc_msc)

📄 cdc_device.c

c 复制代码
void tud_cdc_rx_cb(uint8_t itf) {
  uint8_t buf[64];
  uint32_t count = tud_cdc_read(buf, sizeof(buf));
  tud_cdc_write(buf, count);  // Echo 回去
  tud_cdc_write_flush();
}

✅ 6. 修改 CMake 以包含 TinyUSB

你已有的 CMakeLists.txt 加上:

cmake 复制代码
# 添加 TinyUSB 子目录
add_subdirectory(third_party/tinyusb)

# 添加 tusb_config.h 和 tinyusb 头文件
target_include_directories(${PROJECT_NAME} PRIVATE
    ${CMAKE_SOURCE_DIR}/Core/Inc
    ${CMAKE_SOURCE_DIR}/third_party/tinyusb/src
)

# 链接 TinyUSB 设备库
target_link_libraries(${PROJECT_NAME} PRIVATE
    tusb
)

✅ 整体结构

复制代码
Project/
├── Core/
│   └── Inc/tusb_config.h
│   └── Src/main.c
├── third_party/
│   └── tinyusb/
├── CMakeLists.txt

🔍 验证结果(CDC 示例)

连接 USB 到电脑后,你会在设备管理器看到一个 USB COM 口("TinyUSB CDC Device"),使用串口工具发送内容时能回显回来。


🧩 可选:使用 USB 断电重启技巧(为 Win7 兼容)

STM32F1 没有 USB ID 引脚,有时需要拉低 D+ 来实现"USB 插拔"效果:

c 复制代码
// 拉低 USB D+(PA12)一会儿再放开
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_12, GPIO_PIN_RESET);
HAL_Delay(10);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_12, GPIO_PIN_SET);

可以放在 main() 开头。


✅ 总结

步骤 内容
1 CubeMX 生成基本工程
2 引入 TinyUSB 代码
3 添加 tusb_config.h
4 main.c 中调用 tusb_init()
5 编写描述符、回调函数
6 CMake 添加 TinyUSB 库

很关键的问题!如果你使用 STM32F103 + HAL + TinyUSB中断函数(ISR)是必须配置的,否则 USB 无法正常通信。


✅ 必要中断处理函数(USB)

对于 STM32F103,使用的是 USB (Device FS) 外设 ,它的 中断向量是:USB_LP_CAN1_RX0_IRQHandler

🔧 你需要在中断中调用 TinyUSB 的中断处理函数:

c 复制代码
void USB_LP_CAN1_RX0_IRQHandler(void)
{
  tud_int_handler(0);  // 通知 TinyUSB 发生 USB 中断,端口 0
}

🔍 具体配置方法

1. CubeMX 生成的中断处理代码在 stm32f1xx_it.c

默认它会生成这个空函数:

c 复制代码
void USB_LP_CAN1_RX0_IRQHandler(void)
{
  HAL_PCD_IRQHandler(&hpcd_USB_FS); // CubeMX 自动生成的 HAL USB 中断处理
}

你要把它替换或追加为:

c 复制代码
#include "tusb.h"

void USB_LP_CAN1_RX0_IRQHandler(void)
{
  // 直接跳过 HAL 的处理,交给 TinyUSB
  tud_int_handler(0);
}

🟡 注意:如果你用的是 LL 驱动或没用 HAL,可以自己配置 NVIC 和中断优先级。


🔧 NVIC 中断开启

MX_USB_PCD_Init() 或 CubeMX 中自动会开启中断:

c 复制代码
HAL_NVIC_SetPriority(USB_LP_CAN1_RX0_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(USB_LP_CAN1_RX0_IRQn);

如果没有,请手动添加。


💡 如果你使用 CMake 项目结构:

确保你把 stm32f1xx_it.cstm32f1xx_it.h 添加到了 target_sources() 列表中,并在其中定义上述中断函数。


✅ 总结

步骤 内容说明
中断函数名称 USB_LP_CAN1_RX0_IRQHandler()
中断函数实现 调用 tud_int_handler(0);
HAL 中断是否保留? 否(TinyUSB 自己处理)
NVIC 中断优先级/使能 必须启用,优先级可设为 0
文件放在 Core/Src/stm32f1xx_it.c

相关推荐
范纹杉想快点毕业11 分钟前
基于 C 语言视角:流程图中分支与循环结构的深度解析
c语言·stm32·单片机·设计模式·架构·流程图·uml
玄酒1 小时前
51单片机入门:模块化编程
单片机·嵌入式硬件·51单片机
Ronin-Lotus9 小时前
嵌入式硬件篇---ESP32稳压板
嵌入式硬件·esp32·稳压板
芯片小熊11 小时前
RTC时钟详解
单片机·嵌入式硬件·实时音视频·时钟·rtc
雨中来客12 小时前
STM32移植LVGL9.2.1教程
stm32·单片机·嵌入式硬件
不吃鱼的羊13 小时前
关于tresos Studio(EB)的MCAL配置之GPT
单片机·嵌入式硬件·gpt
CC呢14 小时前
基于单片机恒温控制系统/温度控制系统/恒温箱设计
单片机·嵌入式硬件·恒温控制·恒温箱
jingshaoqi_ccc14 小时前
stm32中优先使用原子操作的具体实现方式
stm32·单片机·嵌入式硬件
小猪写代码15 小时前
大白话畅谈:stm32中断和FreeRTOS的中断
stm32·单片机·嵌入式硬件
自激振荡器16 小时前
8,FreeRTOS时间片调度
stm32·单片机·嵌入式硬件·freertos