本文主要介绍如何在 RTThread 上使用 CharryUSB
介绍
CharryUSB 试用了一下,感觉还不错,本身实现了多种 USB IP,支持多个
MCU. 许多 MCU 厂商对于 USB 的实现都是直接买现成的 IP,例如 stm32 是的
dwc2, 因此各个厂商的 USB 驱动可以使用同一套 IP 的驱动,这就是为什么
RTThread menuconfig 选项中有选择 IP 选项。
配置
那么开始配置 menuconfig:
1 2 3 4 RT-Thread online packages ---> system packages ---> CherryUSB: tiny and portable USB host/device stack for embedded system with USB IP ---> Enable usb device mode --->
然后 Select usb device ip 记得选择 DWC2, 其他根据需要进行选择.
例如这里选择了 Enable usb msc device --->
并且使用 ram
模拟磁盘 Use msc ram template
设置完成后 `rtconfig.h`` 新增如下内容:
1 2 3 4 5 6 7 8 9 #define PKG_USING_CHERRYUSB #define PKG_CHERRYUSB_DEVICE #define PKG_CHERRYUSB_DEVICE_FS #define PKG_CHERRYUSB_DEVICE_DWC2 #define PKG_CHERRYUSB_DEVICE_DWC2_STM32 #define PKG_CHERRYUSB_DEVICE_DWC2_PORT_FS #define PKG_CHERRYUSB_DEVICE_MSC #define PKG_CHERRYUSB_DEVICE_MSC_TEMPLATE #define PKG_USING_CHERRYUSB_LATEST_VERSION
注意: 不需要在 RT-Thread Components --> Device Drivers --->
中再启动 usb 驱动了, 因为 CharryUSB 直接集成 DWC2 驱动, RT-Thread 默认的
USB 驱动关闭就好了
设置完成后执行 pkgs --update
拉取 CharryUSB 包。
使用
拉取完成后似乎直接就能编译, 那么怎么使用呢?程序执行入口在哪?
启动流程是什么样的?
CharryUSB 唯一需要提供的是一个 usb_dc_low_level_init
函数, 这个函数是用于将外设时钟使能以及和 IO 绑定的操作,实际上就等价于
cubemx 自动生成代码中 xxx_msp 函数, 因此如果使用 cubemx
自动生成则可以直接拿来使用。比如
cubemx 生成代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 void HAL_PCD_MspInit (PCD_HandleTypeDef* hpcd) { GPIO_InitTypeDef GPIO_InitStruct = {0 }; RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0 }; if (hpcd->Instance==USB_OTG_FS) { PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_USB; PeriphClkInitStruct.UsbClockSelection = RCC_USBCLKSOURCE_HSI48; if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK) { Error_Handler(); } HAL_PWREx_EnableUSBVoltageDetector(); __HAL_RCC_GPIOA_CLK_ENABLE(); GPIO_InitStruct.Pin = GPIO_PIN_12|GPIO_PIN_11; 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_OTG1_FS; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); __HAL_RCC_USB_OTG_FS_CLK_ENABLE(); HAL_NVIC_SetPriority(OTG_FS_IRQn, 0 , 0 ); HAL_NVIC_EnableIRQ(OTG_FS_IRQn); } }
可以发现该函数 hpcd 其实没大多用, 去掉即可, 于是有:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 void usb_dc_low_level_init (void ) { GPIO_InitTypeDef GPIO_InitStruct = {0 }; RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0 }; PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_USB; PeriphClkInitStruct.UsbClockSelection = RCC_USBCLKSOURCE_HSI48; if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK) { Error_Handler(); } HAL_PWREx_EnableUSBVoltageDetector(); __HAL_RCC_GPIOA_CLK_ENABLE(); GPIO_InitStruct.Pin = GPIO_PIN_12 | GPIO_PIN_11; 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_OTG1_FS; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); __HAL_RCC_USB_OTG_FS_CLK_ENABLE(); HAL_NVIC_SetPriority(OTG_FS_IRQn, 0 , 0 ); HAL_NVIC_EnableIRQ(OTG_FS_IRQn); }
执行流程如下,首先注册设备描述符, 添加接口, 然后执行
usbd_initialize
初始化 usb
例如 msc 设备:
1 2 3 4 5 6 7 void msc_ram_init (void ) { usbd_desc_register(msc_ram_descriptor); usbd_add_interface(usbd_msc_init_intf(&intf0, MSC_OUT_EP, MSC_IN_EP)); usbd_initialize(); }
而 msc_ram_init 我们可以放到 shell
中手动执行,也可以设置开机后自动执行.
usbd_initialize 会调用 usb_dc_init, 而 usb_dc_init
是移植到不同平台需要实现的部分, 对于 dwc2 平台, CharryUSB
已经有实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 int usbd_initialize (void ) { return usb_dc_init(); } __WEAK void usb_dc_low_level_init (void ) { } __WEAK void usb_dc_low_level_deinit (void ) { } int usb_dc_init (void ) { usb_dc_low_level_pre_init(); memset (&usb_dc_cfg, 0 , sizeof (usb_dc_cfg)); NRF_USBD->EVENTS_USBEVENT = 0 ; NRF_USBD->EVENTCAUSE |= NRF_USBD->EVENTCAUSE; NRF_USBD->INTENCLR = NRF_USBD->INTEN; NRF_USBD->INTENSET = USBD_INTEN_USBRESET_Msk | USBD_INTEN_USBEVENT_Msk | USBD_INTEN_EPDATA_Msk | USBD_INTEN_EP0SETUP_Msk | USBD_INTEN_EP0DATADONE_Msk | USBD_INTEN_ENDEPIN0_Msk | USBD_INTEN_ENDEPOUT0_Msk | USBD_INTEN_STARTED_Msk; usb_dc_low_level_post_init(); return 0 ; }
因此实际需要实现的函数就仅剩下 usb_dc_low_level_pre_init 了. 在
port/dwc2/usb_dc_dwc2.c 中 usb_dc_low_level_init 默认为 weak
这也就是为何即使一开始没实现该函数也能编译成功。
至于 msc_ram_init 函数, 因为勾选了 Use msc ram template
在 third_party/rt-thread-4.1.1/msh_cmd.c
命令也给我们导出来了
1 2 3 4 #if defined(PKG_CHERRYUSB_DEVICE_MSC_TEMPLATE) void msc_ram_init (void ) ;MSH_CMD_EXPORT(msc_ram_init, start msc_ram_init); #endif
下载后通过命令执行 msc_ram_init
即可.
源文件来自于