当前位置:首页 > 聚焦财报 > 正文内容

极海APM32F427移植CherryUSB实现自定义USB HID设备

聚亿千财2026-01-21聚焦财报5304

《极海芯得》系列内容为用户使用极海系列产品的经验总结,均转载自21ic论坛极海半导体专区,全文未作任何修改,未经原文作者授权禁止转载。

最近需要使用到APM32F427枚举成Custom HID设备进行用户自定义通信,但我又不想要使用官方的USB中间件去做一个USB Custom HID设备。了解到Cherry USB这个开源的USB代码,而且他最强的就是不需要使用芯片的任何关于USB的代码,就能使用起来。所以,打算使用CherryUSB实现Custom HID设备。

1. Cherry USB简介

CherryUSB是一个小而美的、可移植性高的、用于嵌入式系统(带USB IP)的高性能 USB 主从协议栈。

关于CherryUSB的介绍,大家可以去它的开源仓库和官方提供的介绍文档进行学习。官方提供了很详细的介绍和使用说明。

官方的使用教程:https://cherryusb.readthedocs.io/zh-cn/latest/

CherryUSB代码仓库:https://github.com/cherry-embedded/CherryUSB

CherryUSB厉害之处在于,它不需要使用到芯片SDK中任何关于USB相关的代码,它是完全和芯片脱离关系的。CherryUSB它是基于USB IP写的底层,和芯片完全没有关系,所以移植使用CherryUSB也比较简单。它支持了常见的USB IP。如下:

9ffb7010-f1fa-11f0-92de-92fbcf53809c.png

其中APM32F427使用的USB IP就是 DWC2 USB IP。

2. APM32F427移植CherryUSB实现Custom HID设备

2.1 准备工作

1、下载官网APM32F427 SDK。

下载路径:

https://www.geehy.com/product/fifth/APM32F427_425_423#design

2、下载CherryUSB源码。

CherryUSB代码仓库:

https://github.com/cherry-embedded/CherryUSB

下载的源码暂时放到SDK的中间件目录下备用。

3、复制一个SDK的可以正常使用的例程,然后基于这个例程进行移植CherryUSB。

a0acd9a4-f1fa-11f0-92de-92fbcf53809c.png

2.2 提供CherryUSB的

usb_dc_low_level_init/usb_dc_low_level_deinit函数的实现

要移植实现CherryUSB,我们只需要提供usb_dc_low_level_init/usb_dc_low_level_deinit函数的实现即可。这两个函数只是做了最底层的GPIO引脚初始化,以及开启芯片的USB时钟和中断等与芯片相关的代码。

1、复制usb_glue_st.c文件,修改为usb_glue_apm32f27.c文件。

2、根据原有的usb_dc_low_level_init函数,修改为适应APM32F427芯片的函数。

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;

}

//g_dwc2_instance.Instance = (USB_OTG_GlobalTypeDef *)g_usbdev_bus[busid].reg_base;

//HAL_PCD_MspInit((PCD_HandleTypeDef *)&g_dwc2_instance);

GPIO_InitTypeDef GPIO_InitStruct = {0};

/* Configure USB OTG GPIO */

__DAL_RCM_GPIOA_CLK_ENABLE();

/* USB DM, DP pin configuration */

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);

/* Configure USB OTG */

__DAL_RCM_USB_OTG_FS_CLK_ENABLE();

/* Configure interrupt */

DAL_NVIC_SetPriority(OTG_FS_IRQn, 1U, 0U);

DAL_NVIC_EnableIRQ(OTG_FS_IRQn);

}

该函数主要实现了使用到的USB GPIO初始化,以及打开USB外设时钟和使能USB中断等硬件的初始化工作。

3、提供usb_dc_low_level_deinit函数实现

void usb_dc_low_level_deinit(uint8_t busid)

{

if (g_usbdev_bus[busid].reg_base == 0x40040000UL) { // USB_OTG_HS_PERIPH_BASE

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;

}

//g_dwc2_instance.Instance = (USB_OTG_GlobalTypeDef *)g_usbdev_bus[busid].reg_base;

//HAL_PCD_MspDeInit((PCD_HandleTypeDef *)&g_dwc2_instance);

/* Disable peripheral clock */

__DAL_RCM_USB_OTG_FS_CLK_DISABLE();

/* USB DM, DP pin configuration */

DAL_GPIO_DeInit(GPIOA, GPIO_PIN_11 | GPIO_PIN_12);

/* Disable peripheral interrupt */

DAL_NVIC_DisableIRQ(OTG_FS_IRQn);

}

该函数是usb_dc_low_level_init函数的反操作,解除GPIO初始化和失能时钟等。

2.3 提供CherryUSB中断函数的实现

调用USB中断函数时,已经不能调用SDK提供的中断处理函数了,需要调用Cherry提供的中断处理函数。如下:

void OTG_FS_IRQHandler(void)

{

g_usb_dwc2_irq[0](g_usb_dwc2_busid[0]);

}

//void OTG_HS_IRQHandler(void)

void OTG_FS2_IRQHandler(void)

{

g_usb_dwc2_irq[1](g_usb_dwc2_busid[1]);

}

到这里就已经修改完了需要把CHerryUSB移植到APM32F427的底层的实现。修改实现的代码,都保存到usb_glue_apm32f427.c这个文件里。

2.4 修改Keil工程配置

前面已经把该准备的代码实现了,下面我们把复制的SDK一个例程,修改一下实现CherryUSB的例程。

1、添加CherryUSB源码到Keil工程

a16bde80-f1fa-11f0-92de-92fbcf53809c.png

2、添加CherryUSB的编译路径

a1ccc9b6-f1fa-11f0-92de-92fbcf53809c.png

3、CherryUSB仓库代码,把cherryusb_config_template.h复制这个文件到我们的工程目录下,并修改名称为 usb_config.h

a22ef9d8-f1fa-11f0-92de-92fbcf53809c.png

2.5 实现CherryUSB Custom HID收发测试函数

前面的步骤已经相当于全部移植完了CherryUSB了。现在实现CherryUSB Custom HID应用层的收发测试函数。

void custom_hid_test(uint8_t busid)

{

uint8_t report[64] = {0};

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) {

}

}

这个函数就是把接收到数据返回到PC端上位机

2.6 实现main函数的调用

在main函数,我们首先需要调用 hid_custom_init 函数进行CherryUSB的初始化,然后再主循环检测到接收USB数据,就调用2.5小节实现的CherryUSB收发测试函数。如下:

int main(void)

{

// uint8_t mouse_cfg[4] = {};

/* Device configuration */

DAL_DeviceConfig();

// /* Output a message on Hyperterminal using printf function */

// LOG_Print(" UART Printf Example: retarget the C library printf function to the UART ");

// LOG_Print(" ** Test finished successfully. ** ");

hid_custom_init(0, USB_OTG_FS_PERIPH_BASE);

/* Infinite loop */

while (1)

{

if (usb_receive_flag == 1)

{

custom_hid_test(0);

}

}

}

3. USB Custom HID设备通信的测试验证

关于测试验证,和我之前写的一篇文章很相似的,因为代码实现都是和上位机进行收发测试。

3.1 在电脑的设备管理器查看

在第二节修改完代码之后,编译下载到APM32F427芯片,运行起来后,我们可以在windows系统的设备管理器的人体学输入设备,查看到我们自己实现的USB Custom HID的。如下:

a28b30b8-f1fa-11f0-92de-92fbcf53809c.png

又或者通过控制面板的设备和打印机的选项,可以查看到我们实现的USB HID设备,如下:

a2e744c0-f1fa-11f0-92de-92fbcf53809c.png

可以看到我们实现的Cherry HID DEMO设备,说明修改的代码已经正常运行。

3.2 通过PC端上位机工具进行数据收发测试

我们需要使用USB HID的调试工具,进行数据收发测试。关于这样的上位机工具,网上有很多的。我这里使用的是PortHelper工具。这个工具大家可以网上搜一下下载。

1、打开PortHelper 上位机,然后选择USB调试,然后找到

APM32 Custom HID 设备,然后打开USB

a34752a2-f1fa-11f0-92de-92fbcf53809c.png

2、PortHelper 测试与APM32F427进行USB数据收发

打开USB之后,然后我们勾选hex发送,hex显示。然后点击发送即可,如下图:

a3a9ca2c-f1fa-11f0-92de-92fbcf53809c.png

然后可以PC端上位机发送的数据给APM32F427,芯片原样返回数据给该上位机。

到这里就实现的我们自定义的USB Custom HID设备,该设备主要是接收PC端上位机发送过来的数据,然后APM32F427接收进行处理。在实际项目我是需要基于这个通信进行更复杂的项目开发。

注:文章作者在原帖中提供了代码文件,有需要请至原文21ic论坛

原文地址:https://bbs.21ic.com/icview-3501154-1-1.html?_dsign=67f2f385

或点击下方阅读原文跳转