STM32 使用 TinyUSB
基于 STM32F103(比如 STM32F103C8)+ STM32CubeMX + HAL 使用 TinyUSB。
🧱 基础条件
确保你已经完成:
✅ STM32CubeMX 工程创建,启用了 USB(Device 模式)
✅ 使用 HAL 库(非 LL)
✅ 项目使用 cubeIDE 或 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
#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:
#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
#include "tusb.h"// USB 描述符定义(可以参考 tinyusb/examples/device/cdc_msc)
📄 cdc_device.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
加上:
# 添加 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} PRIVATEtusb
)
✅ 整体结构
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 插拔”效果:
// 拉低 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 库 |
✅ 必要中断处理函数(USB)
对于 STM32F103,使用的是 USB (Device FS) 外设,它的 中断向量是:USB_LP_CAN1_RX0_IRQHandler
。
🔧 需要在中断中调用 TinyUSB 的中断处理函数:
void USB_LP_CAN1_RX0_IRQHandler(void)
{tud_int_handler(0); // 通知 TinyUSB 发生 USB 中断,端口 0
}
🔍 具体配置方法
1. CubeMX 生成的中断处理代码在 stm32f1xx_it.c
:
默认它会生成这个空函数:
void USB_LP_CAN1_RX0_IRQHandler(void)
{HAL_PCD_IRQHandler(&hpcd_USB_FS); // CubeMX 自动生成的 HAL USB 中断处理
}
你要把它替换或追加为:
#include "tusb.h"void USB_LP_CAN1_RX0_IRQHandler(void)
{// 直接跳过 HAL 的处理,交给 TinyUSBtud_int_handler(0);
}
🟡 注意:如果你用的是 LL 驱动或没用 HAL,可以自己配置 NVIC 和中断优先级。
🔧 NVIC 中断开启
在 MX_USB_PCD_Init()
或 CubeMX 中自动会开启中断:
HAL_NVIC_SetPriority(USB_LP_CAN1_RX0_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(USB_LP_CAN1_RX0_IRQn);
如果没有,请手动添加。
💡 如果你使用 CMake 项目结构:
确保你把 stm32f1xx_it.c
和 stm32f1xx_it.h
添加到了 target_sources()
列表中,并在其中定义上述中断函数。
✅ 总结
步骤 | 内容说明 |
---|---|
中断函数名称 | USB_LP_CAN1_RX0_IRQHandler() |
中断函数实现 | 调用 tud_int_handler(0); |
HAL 中断是否保留? | 否(TinyUSB 自己处理) |
NVIC 中断优先级/使能 | 必须启用,优先级可设为 0 |
文件放在 | Core/Src/stm32f1xx_it.c |