最近在项目中需要使用APM32F427芯片开发一个Custom HID设备以实现用户自定义通信。虽然官方SDK提供了USB中间件,但我更倾向于寻找一个更轻量、更灵活的解决方案。
经过调研,我发现了CherryUSB这个强大的开源USB协议栈。它最大的优势在于解耦性极强——不需要依赖芯片厂商SDK中的任何USB协议代码,仅需适配底层IP即可运行。本文将详细记录如何将CherryUSB移植到APM32F427上,并实现Custom HID的双向数据收发。
1.为什么选择CherryUSB?
CherryUSB是一个小而美的、可移植性极高的嵌入式USB主从协议栈。对于正在寻找国产MCU USB开发方案的工程师来说,它是一个非常棒的选择。
CherryUSB的核心优势
- 极致轻量:代码精简,资源占用低。
- 高度解耦:它不需要使用到芯片SDK中任何关于USB相关的代码,它是完全和芯片脱离关系的。
- 广泛支持:原生支持多种常见的USB IP,包括DWC2 USB IP(这也是APM32F427所使用的IP核)。
支持的常见USB IP如下:

关于CherryUSB的详细介绍,建议参考以下官方资源:
2.APM32F427移植CherryUSB实战步骤
本章节将手把手教你如何在APM32F427上配置CherryUSB,实现Custom HID功能。
2.1准备工作
在开始移植之前,请确保你已经准备好了以下开发环境和源码:
下载APM32 SDK:前往极海官网下载APM32F427 SDK。
获取CherryUSB源码:
建立基础工程:复制一个SDK中可以正常运行的基础例程(如GPIO或UART例程),我们将在此基础上进行移植。

2.2核心移植:适配底层接口函数
移植CherryUSB的核心在于实现usb_dc_low_level_init和usb_dc_low_level_deinit这两个接口。它们负责处理与芯片硬件相关的初始化,如GPIO引脚配置、USB时钟开启以及中断使能。
由于APM32F427使用的是通用的DWC2 IP,我们可以参考CherryUSB官方提供的STM32移植文件进行修改。
创建接口文件:复制usb_glue_st.c,重命名为usb_glue_apm32f427.c。

适配初始化函数:修改usb_dc_low_level_init,使其适配APM32F427的硬件库(DAL库)。
/* 适配APM32F427的USB底层初始化代码 */
void usb_dc_low_level_init(uint8_t busid)
{
//绑定中断服务函数句柄
if (g_usbdev_bus[busid].reg_base == 0x40040000UL) { // USB_OTG_HS_PERIPH_BASE
g_usb_dwc2_busid[1] = busid;
g_usb_dwc2_irq[1] = USBD_IRQHandler;
} else {
g_usb_dwc2_busid[0] = busid;
g_usb_dwc2_irq[0] = USBD_IRQHandler;
}
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* 1.开启GPIO时钟 */
__DAL_RCM_GPIOA_CLK_ENABLE();
/* 2.配置USB DM, DP引脚(PA11, PA12) */
GPIO_InitStruct.Pin = GPIO_PIN_11 | GPIO_PIN_12;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF10_OTG_FS;
DAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* 3.开启USB OTG FS外设时钟 */
__DAL_RCM_USB_OTG_FS_CLK_ENABLE();
/* 4.配置并使能USB中断 */
DAL_NVIC_SetPriority(OTG_FS_IRQn, 1U, 0U);
DAL_NVIC_EnableIRQ(OTG_FS_IRQn);
}
- 适配去初始化函数:实现
usb_dc_low_level_deinit,用于关闭USB功能。
void usb_dc_low_level_deinit(uint8_t busid)
{
//清除句柄绑定
if (g_usbdev_bus[busid].reg_base == 0x40040000UL) {
g_usb_dwc2_busid[1] = 0;
g_usb_dwc2_irq[1] = NULL;
} else {
g_usb_dwc2_busid[0] = 0;
g_usb_dwc2_irq[0] = NULL;
}
/* 关闭外设时钟 */
__DAL_RCM_USB_OTG_FS_CLK_DISABLE();
/* 复位GPIO */
DAL_GPIO_DeInit(GPIOA, GPIO_PIN_11 | GPIO_PIN_12);
/* 关闭中断 */
DAL_NVIC_DisableIRQ(OTG_FS_IRQn);
}
2.3接管USB中断处理
这是非常关键的一步。我们需要屏蔽SDK原有的中断处理函数,转而调用CherryUSB提供的中断入口。
/* APM32F427 USB OTG FS中断服务函数 */
void OTG_FS_IRQHandler(void)
{
//调用CherryUSB的DWC2驱动中断处理
g_usb_dwc2_irq[0](g_usb_dwc2_busid[0]);
}
//如果使用HS接口,则需要实现OTG_HS_IRQHandler或OTG_FS2_IRQHandler
void OTG_FS2_IRQHandler(void)
{
g_usb_dwc2_irq[1](g_usb_dwc2_busid[1]);
}
2.4Keil工程配置指南
代码修改完成后,需要对Keil MDK工程进行配置,确保编译器能找到CherryUSB的头文件和源文件。
添加源文件:加CherryUSB源码到Keil工程。

添加头文件路径:在"C/C++"选项卡中,添加CherryUSB相关的Include Paths(编译路径)。

配置usb_config.h:从CherryUSB仓库复制cherryusb_config_template.h到工程目录,重命名为usb_config.h。

2.5编写Custom HID应用层代码
移植完成后,我们需要编写应用层代码来验证Custom HID的收发功能。以下是一个简单的回环测试(Loopback)函数:
/* Custom HID数据处理函数 */
void custom_hid_test(uint8_t busid)
{
uint8_t report[64] = {0};
//检查USB设备是否配置完成
if(usb_device_is_configured(busid) == false) {
return;
}
//标记状态为忙,开始发送数据
custom_state = HID_STATE_BUSY;
usbd_ep_start_write(busid, HIDRAW_IN_EP, (uint8_t *)&report, sizeof(report));
//等待发送完成
while (custom_state == HID_STATE_BUSY) {
}
}
2.6主函数调用逻辑
最后,在main.c中初始化USB协议栈,并建立主循环处理逻辑。
int main(void)
{
/* 基础硬件初始化 */
DAL_DeviceConfig();
/* 初始化CherryUSB HID Class */
//0表示busid, USB_OTG_FS_PERIPH_BASE是APM32的USB寄存器基地址
hid_custom_init(0, USB_OTG_FS_PERIPH_BASE);
/* 主循环 */
while (1)
{
//假设usb_receive_flag在接收中断回调中被置位
if (usb_receive_flag == 1)
{
custom_hid_test(0); //执行数据回传
}
}
}
3.USB Custom HID设备通信验证
代码编译下载后,我们需要验证APM32F427是否成功枚举为HID设备,并能正常通信。
3.1设备管理器识别
将开发板连接电脑,打开Windows设备管理器。
这证明枚举过程已经成功,底层驱动和描述符配置无误。
3.2使用上位机工具进行数据收发
为了测试数据通信,推荐使用通用的USB调试工具,例如PortHelper。
工具下载:
测试步骤:
打开PortHelper,选择“USB调试”模式。
在设备列表中找到APM32 Custom HID,点击“打开USB”。

勾选“Hex发送”和“Hex显示”。
输入任意Hex数据并点击发送。

结果观察:
如果一切正常,你将看到上位机接收到了与发送内容完全一致的数据。这表明APM32F427成功接收了数据,并通过CherryUSB协议栈原样返回,实现了完整的Custom HID双向通信。
总结
通过本文的步骤,我们成功在APM32F427上移植了CherryUSB协议栈,并实现了一个自定义HID设备。相比官方SDK,CherryUSB提供了更清晰的逻辑和更好的可移植性,非常适合复杂的嵌入式USB开发项目。
工程源码:
原帖地址:https://bbs.21ic.com/icview-3501154-1-1.html
