本文实现了STM32G4模拟 usb hid 设备作为鼠标

CherryUSB版本为v1.4.3

ST的固件库为STM32Cube_FW_G4_V1.6.1

CherryUSB介绍(摘自CherryUSB的readme)

CherryUSB 是一个小而美的、可移植性高的、用于嵌入式系统的 USB 主从协议栈。同时 CherryUSB 具有以下优点:

易于学习 USB

为了方便用户学习 USB 基本知识、枚举、驱动加载、IP 驱动,因此,编写的代码具备以下优点:

  • 代码精简,逻辑简单,无复杂 C 语言语法
  • 树状化编程,代码层层递进
  • Class 驱动和 porting 驱动模板化、精简化
  • API 分类清晰(从机:初始化、注册类、命令回调类、数据收发类;主机:初始化、查找类、数据收发类)

易于使用 USB

为了方便用户使用 USB 接口,考虑到用户学习过 uart 和 dma,因此,设计的数据收发类接口具备以下优点:

  • 等价于使用 uart tx dma/uart rx dma
  • 收发长度没有限制,用户不需要关心 USB 分包过程(porting 驱动做分包过程)

易于发挥 USB 性能

考虑到 USB 性能问题,尽量达到 USB 硬件理论带宽,因此,设计的数据收发类接口具备以下优点:

  • Porting 驱动直接对接寄存器,无抽象层封装
  • Memory zero copy
  • IP 如果带 DMA 则使用 DMA 模式(DMA 带硬件分包功能)
  • 长度无限制,方便对接硬件 DMA 并且发挥 DMA 的优势
  • 分包功能在中断中处理

从机协议栈整体执行流程

usb-device

主机协议栈整体执行流程

usb-host

项目地址

cherry-embedded/CherryUSB

开始移植

  1. 使用 STM32CubeMX 创建工程,配置基本的 RCC、UART (作为 log 使用,但是我们移植的 G4 系列的 usb ip 核为 fsdev,基本没有配置项,故无 log)

image-20250314221305123

image-20250314221318104

  1. 我们这里使用的是 fsdev ip,勾选 USB。开启 USB 中断,其他配置对我们没用,cherry 的代码中不会使用任何 ST 的 USB 库

image-20250314221336698

  1. 检查时钟树,保证 USB 时钟为 48MHz

    发现一个现象,如果用的是PLL出来的48M,后面注释MX自动生成的 usb init 似乎可有可无,但是用HSI的48M,则必须注释掉MX生成的 usb init,否则无法枚举

image-20250315111137942

  1. Generate CODE

image-20250314221525860

在项目目录里拷进CherryUSB源码

image-20250314221959758

  1. 添加 CherryUSB 必须要的源码,以及想要使用的 class 驱动,可以将对应 class 的 demo template 添加方便测试,我这里就添加 hid 的 class 和 demo template 来测试。

image-20250314235630850

  1. 将 ./CherryUSB/cherryusb_config_template.h 复制到 ./Core/Inc 并重命名为 usb_config.h

image-20250314234255893

  1. 如果使用 fsdev ip,在 usb_config.h 中实现以下宏
1
2
3
// 以下细节不同芯片可能有出入,要改!!
#define CONFIG_USBDEV_EP_NUM 8
#define CONFIG_USBDEV_FSDEV_PMA_ACCESS 2
  1. 拷贝 usb_xxx.c 里面的 HAL_PCD_MspInit 函数中的内容到 usb_dc_low_level_init 函数中,并屏蔽 ST 生成的 USB 初始化
1
2
3
void usb_dc_low_level_init(uint8_t busid) {
add something...
}

image-20250315111125946

image-20250314234942250

  1. 在中断函数中调用 USBD_IRQHandler,并传入 busid

image-20250314235159981

  1. 调用 template 的内容初始化,并填入 busid 和 USB IP 的 reg base,busid 从 0 开始,不能超过CONFIG_USBDEV_MAX_BUS,这里 reg base 在 stm32xxx.h 里找。

image-20250315111248946

image-20250314235723312

顺利的话,应该接上 USB 鼠标就开始转圈了,但是这里出现了没法枚举的情况。

image-20250315110450418

由于 fsdev ip 本来就只有一个配置项,故怀疑是

1
#define CONFIG_USBDEV_FSDEV_PMA_ACCESS 2 // maybe 1 or 2, many chips may have a difference

这里的 define 不太合适,改为“1”后解决问题,鼠标开始转圈圈了。

注意注意:配置USB时钟的时候如果用HSI的48MHz频率,则必须不要忘了注释MX生成的 usb init。

参考资料

  1. CherryUSB 使用指南——遇事不决先看官方文档,这是能找到的最好的资料,99%的问题文档里都提到过,感谢@sakumisu
  2. APM32F407VGT6 DWC2移植 Port.A Full-Speed + Por.B High-Speed——另一位大佬的帖子,看完受益匪浅