偶然间看到了CherryUSB的协议栈,觉得还不错,就试试把APM32E103移植上去。
先介绍一下CherryUSB:
地址: https://github.com/sakumisu/CherryUSB。
CherryUSB 是一个小而美的、可移植性高的、用于嵌入式系统(带 USB ip)的 USB 主从协议栈。
- 面向 ip 编程,相同 usb ip 驱动无需重复编写,仅需实现不相同的部分
- 代码树状化编写,轻松理解 usb 概念、枚举过程、class 驱动加载
- Class 驱动模板化,轻松实现复合设备和自定义添加驱动
- 将 usb 的复杂传输简单化,使得用户能够像 uart、dma 一样轻松使用 usb
- 少量的目录结构,少量的 api,少量的 codesize,极致的 usb 带宽
CherryUSB支持设备和主机模式。
Device 协议栈简介CherryUSB Device 协议栈对标准设备请求、CLASS 请求、VENDOR 请求以及 custom 特殊请求规范了一套统一的函数框架,采用面向对象和链表的方式,能够使得用户快速上手复合设备,不用管底层的逻辑。同时,规范了一套标准的 dcd porting 接口,用于适配不同的 USB IP,达到面向 ip 编程。 Host 协议栈简介CherryUSB Host 协议栈对挂载在 roothub、外部 hub 上的设备规范了一套标准的枚举实现,对不同的 Class 类也规范了一套标准接口,用来指示在枚举后和断开连接后该 Class 驱动需要做的事情。同时,规范了一套标准的 hcd porting 接口,用于适配不同的 USB IP,达到面向 IP 编程。最后,协议栈使用 OS 管理,并提供了 osal 用来适配不同的 os。 CherryUSB的目录结构说明。 下面开始移植到APM32E103上。 首先添加CherryUSB的源码。这里是移植USB设备模式,要添加带usbd头的文件。如果是USB主机模式,是带usbh头的文件。 下面是针对apm32e103的usb IP接口fsdev模式。 主要实现usb_dc_fsdev.c里面的的2个函数,这2个函数实现USB的时钟和中断初始化相关。 void usb_dc_low_level_init(void)
{
EINT_Config_T EINT_ConfigStruct;
RCM_ConfigUSBCLK(RCM_USB_DIV_1_5);
RCM_EnableAPB1PeriphClock(RCM_APB1_PERIPH_USB);
EINT_ConfigStruct.mode = EINT_MODE_INTERRUPT;
EINT_ConfigStruct.line = EINT_LINE_18;
EINT_ConfigStruct.trigger = EINT_TRIGGER_RISING;
EINT_ConfigStruct.lineCmd = ENABLE;
EINT_Config(&EINT_ConfigStruct);
#if USB_SELECT == USB1
NVIC_EnableIRQRequest(USBD1_LP_CAN1_RX0_IRQn, 2, 0);
#else
NVIC_EnableIRQRequest(USBD2_LP_CAN2_RX0_IRQn, 2, 0);
#endif
NVIC_EnableIRQRequest(USBDWakeUp_IRQn, 1, 0);
#if USB_SELECT == USB1
USBD2_Disable();
#else
USBD2_Enable();
#endif
}
void usb_dc_low_level_deinit(void)
{
#if USB_SELECT == USB1
NVIC_DisableIRQRequest(USBD1_LP_CAN1_RX0_IRQn);
#else
NVIC_DisableIRQRequest(USBD2_LP_CAN2_RX0_IRQn);
#endif
NVIC_DisableIRQRequest(USBDWakeUp_IRQn);
}
再就是配置usb_config.h头文件,增加一点配置。如下 #define USB1 0
#define USB2 1
#define USB_SELECT USB2
//USBD1 select USBD1_LP_CAN1_RX0_IRQHandler
//USBD2 select USBD2_LP_CAN2_RX0_IRQHandler
#if USB_SELECT == USB1
#define USBD_IRQHandler USBD1_LP_CAN1_RX0_IRQHandler //use actual usb irq name instead
#else
#define USBD_IRQHandler USBD2_LP_CAN2_RX0_IRQHandler //use actual usb irq name instead
#endif
移植非常简单了,这得益于作者的针对各个MCU的USB IP式接口。APM32E103的USB IP是fsdev模式。看看usb_dc_fsdev.c和usb_dc_fsdev.h文件,你就会发现USB的寄存器地址描述和APM32E103文档内关于USB的描述地址功能都是一样的。所以只要MCU的usb寄存器表和fsdev描述的一致都可以使用usb_dc_fsdev.c和usb_dc_fsdev.h就行。 如下测试了3个例子,3个例子设备都能正常枚举。其中U盘是假的,读写会不正常的。效果就不贴了,大家有兴趣就下载程序试试。
程序如下:
|