【兆易GD32H759I-EVAL】--12.USB虚拟串口
[复制链接]
本帖最后由 dirty 于 2024-6-12 09:13 编辑
开发板MCU集成有两个USBFS/HS接口,可用作USB设备/USB主机/OTG设备。本篇讲述USBHS1 被 USB 主机枚举为一个 USB 虚拟串口,可通过USB口回传数据。
一.硬件原理与准备
开发板硬件USB原理图如下图1。这里使用到CN3 USB_HS1接口,跳线帽JP65、JP70打到USB引脚端。从官网可下载虚拟串口驱动并安装如下图2.
图1:USB原理
二.代码准备
虚拟串口 CDC 例程遵循了 USB 通信类相关子协议,可以将 USB 虚拟成 COM 口,可以像普通的串口一样进行操作。官方提供了USBFS固件库实现 USB 的数据通信。USBFS 固件库分为应用层和驱动层,用户层用户可以修改,驱动层包括 USB 主机或设备驱动和 USB 底层驱动。
1.关于CDC库。可以参阅下官方“GD32 USBFS&USBHS 固件库使用指南”文档,做更深入全面了解。
在GD32H7xx_Firmware_Library\GD32H7xx_usbhs_library\device\class\cdc下是CDC设备功能代码。包含了USBD_VID 和USBD_PID的定义、CDC 相关的描述符项。
CDC 本身包含两个接口,一个命令接口、一个数据接口,命令接口相关描述符如下:
图3:CDC命令接口描述符
CDC 设备类接口函数如下,这里主要关注后三个
图4:CDC 设备类接口函数
CDC 设备类请求类如下:
图5:CDC 设备类请求
CDC 用户接口函数如下,这个是我们应用需要的。
图6:CDC 用户接口函数
2.打开CDC_ACM工程,在Options里配置宏USE_USB_HS使用高速USB;配置USE_USBHS1,以使用USB_HS1。
图7:USB宏定义
3.配置USB时钟
- void usb_rcu_config(void)
- {
- pmu_usb_regulator_enable();
- pmu_usb_voltage_detector_enable();
- while (pmu_flag_get(PMU_FLAG_USB33RF) != SET) {
- }
-
- #ifdef USE_USB_FS
-
- #ifndef USE_IRC48M
-
- #ifdef USE_USBHS0
- rcu_usb48m_clock_config(IDX_USBHS0, RCU_USB48MSRC_PLL0R);
- #endif /* USE_USBHS0 */
-
- #ifdef USE_USBHS1
- rcu_usb48m_clock_config(IDX_USBHS1, RCU_USB48MSRC_PLL0R);
- #endif /* USE_USBHS1 */
-
- #else
-
- rcu_osci_on(RCU_IRC48M);
-
-
- while (SUCCESS != rcu_osci_stab_wait(RCU_IRC48M)) {
- }
-
- #ifdef USE_USBHS0
- rcu_usb48m_clock_config(IDX_USBHS0, RCU_USB48MSRC_IRC48M);
- #endif /* USE_USBHS0 */
-
- #ifdef USE_USBHS1
- rcu_usb48m_clock_config(IDX_USBHS1, RCU_USB48MSRC_IRC48M);
- #endif /* USE_USBHS1 */
-
- #endif /* USE_IRC48M */
-
- #endif /* USE_USB_FS */
-
- #ifdef USE_USBHS0
- rcu_periph_clock_enable(RCU_USBHS0);
- #endif /* USE_USBHS0 */
-
- #ifdef USE_USBHS1
- rcu_periph_clock_enable(RCU_USBHS1);
- #endif /* USE_USBHS1 */
-
- #ifdef USE_ULPI_PHY
- rcu_periph_clock_enable(RCU_ULPI);
- #endif /* USE_ULPI_PHY */
- }
4.配置USB delay定时器。这里使用到Timer2,在gd32h7xx_it.c里有其定时中断函数TIMER2_IRQHandler。
- void usb_timer_init (void)
- {
-
- nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2);
-
-
- nvic_irq_enable((uint8_t)TIMER2_IRQn, 1U, 0U);
-
- rcu_periph_clock_enable(RCU_TIMER2);
- }
5.初始化cdc参数。这里使用USBHS1 usb外设,选用高速USB_SPEED_HIGH,通过结构体指针,初始化后将cdc_acm赋值带出。
- void usb_para_init(usb_core_driver *udev, uint32_t usb_periph, uint32_t usb_speed)
- {
- usb_core_basic *usb_basic = &udev->bp;
-
-
- usb_basic->transfer_mode = (uint8_t)USB_USE_FIFO;
-
-
- usb_basic->core_speed = (uint8_t)USB_SPEED_FULL;
-
-
- switch(usb_periph){
- case USBHS0:
- usb_basic->base_reg = (uint32_t)USBHS0_REG_BASE;
- break;
- case USBHS1:
- usb_basic->base_reg = (uint32_t)USBHS1_REG_BASE;
- break;
- default:
- break;
- }
-
-
- usb_basic->num_pipe = USBHS_MAX_CHANNEL_COUNT;
-
-
- usb_basic->num_ep = USBHS_MAX_EP_COUNT;
-
- if (USB_SPEED_HIGH == usb_speed) {
- #ifdef USB_EXTERNAL_ULPI_PHY_ENABLED
- usb_basic->phy_itf = USB_ULPI_PHY_EXTERNAL;
- #endif /* USB_EXTERNAL_ULPI_PHY_ENABLED */
-
- #ifdef USB_EMBEDDED_HS_PHY_ENABLED
- usb_basic->phy_itf = USB_EMBEDDED_PHY_HS;
- #endif /* USB_EMBEDDED_HS_PHY_ENABLED */
- } else if (USB_SPEED_FULL == usb_speed) {
- #ifdef USB_EMBEDDED_FS_PHY_ENABLED
- usb_basic->phy_itf = USB_EMBEDDED_PHY_FS;
- #endif /* USB_EMBEDDED_FS_PHY_ENABLED */
- } else {
-
- }
-
- #ifdef USB_INTERNAL_DMA_ENABLED
- usb_basic->transfer_mode = USB_USE_DMA;
- #endif /* USB_INTERNAL_DMA_ENABLED */
-
- usb_basic->sof_enable = (uint8_t)USB_SOF_OUTPUT;
- usb_basic->low_power = (uint8_t)USB_LOW_POWER;
-
- #if (1U == LPM_ENABLED)
- usb_basic->lpm_enable = 1U;
- #endif /* LPM_ENABLED */
- }
6.初始化USB设备模式,并加载驱动。这里使用到了前面初始化的cdc_acm,初始化usb描述符和USB内核。这里面引用到了CDC库。
- void usbd_init(usb_core_driver *udev, usb_desc *desc, usb_class_core *class_core)
- {
- udev->dev.desc = desc;
-
-
- udev->dev.class_core = class_core;
-
-
- serial_string_get(udev->dev.desc->strings[STR_IDX_SERIAL]);
-
-
- (void)usb_basic_init(&udev->bp, &udev->regs);
-
-
- (void)usb_core_init(udev->bp, &udev->regs);
-
-
- usbd_disconnect(udev);
-
-
- (void)usb_devcore_init(udev);
-
-
- usbd_connect(udev);
-
- udev->dev.cur_status = (uint8_t)USBD_DEFAULT;
- }
7.配置USB中断。宏定义使用USE_USBHS1。在gd32h7xx_it.c里有其中断函数USBHS1_IRQHandler,USBHS1_WKUP_IRQHandler,USBHS1_EP1_OUT_IRQHandler。
-
- void usb_intr_config(void)
- {
- nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2);
-
- #ifdef USE_USBHS0
- nvic_irq_enable((uint8_t)USBHS0_IRQn, 3U, 0U);
- #endif /* USE_USBHS0 */
-
- #ifdef USE_USBHS1
- nvic_irq_enable((uint8_t)USBHS1_IRQn, 3U, 0U);
- #endif /* USE_USBHS0 */
-
-
- rcu_periph_clock_enable(RCU_PMU);
-
- #ifdef USE_USBHS0
-
- exti_interrupt_flag_clear(EXTI_31);
- exti_init(EXTI_31, EXTI_INTERRUPT, EXTI_TRIG_RISING);
- exti_interrupt_enable(EXTI_31);
-
- nvic_irq_enable((uint8_t)USBHS0_WKUP_IRQn, 1U, 0U);
- #endif /* USE_USBHS0 */
-
- #ifdef USE_USBHS1
-
- exti_interrupt_flag_clear(EXTI_32);
- exti_init(EXTI_32, EXTI_INTERRUPT, EXTI_TRIG_RISING);
- exti_interrupt_enable(EXTI_32);
-
- nvic_irq_enable((uint8_t)USBHS1_WKUP_IRQn, 1U, 0U);
- #endif /* USE_USBHS1 */
-
- #ifdef USB_DEDICATED_EP1_ENABLED
-
- #ifdef USE_USBHS0
- nvic_irq_enable((uint8_t)USBHS0_EP1_OUT_IRQn, 1U, 0U);
- nvic_irq_enable((uint8_t)USBHS0_EP1_IN_IRQn, 1U, 0U);
- #endif /* USE_USBHS0 */
-
- #ifdef USE_USBHS1
- nvic_irq_enable((uint8_t)USBHS1_EP1_OUT_IRQn, 1U, 0U);
- nvic_irq_enable((uint8_t)USBHS1_EP1_IN_IRQn, 1U, 0U);
- #endif /* USE_USBHS1 */
-
- #endif /* USB_DEDICATED_EP1_ENABLED */
- }
8.main函数实现USB虚拟串口接收完全后回显。
- int main(void)
- {
-
- while (1)
- {
- if (USBD_CONFIGURED == cdc_acm.dev.cur_status)
- {
- if (0U == cdc_acm_check_ready(&cdc_acm))
- {
- cdc_acm_data_receive(&cdc_acm);
- }
- else
- {
- cdc_acm_data_send(&cdc_acm);
- }
- }
- }
-
- }
三.测验
编译烧录后,复位运行,可以在设备管理器找到虚拟串口。用串口工具打开,发送一串字符,在接收区会接收到一串相同的字符。如下图
图8:USB虚拟串口测试
至此,实现USB 虚拟串口功能。
|