【兆易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
/* enable IRC48M clock */
rcu_osci_on(RCU_IRC48M);
/* wait till IRC48M is ready */
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)
{
/* configure the priority group to 2 bits */
nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2);
/* enable the TIM2 global interrupt */
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;
/* configure USB default transfer mode as FIFO mode */
usb_basic->transfer_mode = (uint8_t)USB_USE_FIFO;
/* USB default speed is full-speed */
usb_basic->core_speed = (uint8_t)USB_SPEED_FULL;
/* USB basic register address setting */
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;
}
/* set the host channel numbers */
usb_basic->num_pipe = USBHS_MAX_CHANNEL_COUNT;
/* set the device endpoint numbers */
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 {
/* no operation */
}
#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;
/* class callbacks */
udev->dev.class_core = class_core;
/* create serial string */
serial_string_get(udev->dev.desc->strings[STR_IDX_SERIAL]);
/* configure USB capabilities */
(void)usb_basic_init(&udev->bp, &udev->regs);
/* initializes the USB core*/
(void)usb_core_init(udev->bp, &udev->regs);
/* set device disconnect */
usbd_disconnect(udev);
/* initializes device mode */
(void)usb_devcore_init(udev);
/* set device connect */
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。
/*!
\brief configure USB interrupt
\param[in] none
\param[out] none
\retval none
*/
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 */
/* enable the power module clock */
rcu_periph_clock_enable(RCU_PMU);
#ifdef USE_USBHS0
/* USB wakeup EXTI line configuration */
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
/* USB wakeup EXTI line configuration */
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)
{
/*
//USB HS1 CDC初始化
......
*/
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 虚拟串口功能。
|