【GD32F303红枫派使用手册】第二十八节 USB-虚拟串口实验

28.1 实验内容

通过本实验主要学习以下内容:

  • CDC虚拟串口协议原理及使用
  • CDC虚拟串口通信操作

28.2 实验原理

USB的CDC类是USB通信设备类 (Communication Device Class)的简称。CDC类是USB组织定义的一类专门给各种通信设备使用的USB子类。该设备类采用批量传输。

本例程中实现了CDC设备类的相关请求,包括SET_LINE_CODING、GET_LINE_CODING、SET_CONTROL_LINE_STATE等。后续将会在代码解析章节进行介绍。

有关CDC协议可以通过以下USB官网下载或者通过红枫派开发板配套资料获取。

大家可以在学习的过程中结合历程代码和协议进行理解。

28.3 硬件设计

USB虚拟键盘实验章节已介绍。

28.4 代码解析

本例程主要实现USB虚拟串口的效果,在PC端可以通过串口调试助手或者设备管理器查到虚拟串口设备,并可实现通过该虚拟串口进行通信的现象。

本例程主函数如下,该函数架构与虚拟键盘例程相似,当USBD设备初始化且枚举完成后,USB设备首先通过cdc_acm_check_ready()函数check是否准备数据发送,如果不需要发送就调用cdc_acm_data_receive()函数接收上位机发送的数据,如果需要发送就调用cdc_acm_data_send()将接收到的数据发送给主机,主机再回显到串口调试助手的接收显示界面中。

C
int main(void)
{/* system clocks configuration */rcu_config();/* GPIO configuration */gpio_config();/* USB device configuration */usbd_init(&usbd_cdc, &cdc_desc, &cdc_class);/* NVIC configuration */nvic_config();/* enabled USB pull-up */usbd_connect(&usbd_cdc);while (USBD_CONFIGURED != usbd_cdc.cur_status) {/* wait for standard USB enumeration is finished */}while (1) {if (0U == cdc_acm_check_ready(&usbd_cdc)) {cdc_acm_data_receive(&usbd_cdc);} else {cdc_acm_data_send(&usbd_cdc);}}
}

下面为大家介绍下虚拟串口设备所使用的设备及配置描述符。

设备描述符如下所示,其中bDevcieClass为0x02,表明当前设备为CDC设备类。

C
usb_desc_dev cdc_dev_desc =
{.header = {.bLength          = USB_DEV_DESC_LEN, .bDescriptorType  = USB_DESCTYPE_DEV,},.bcdUSB                = 0x0200U,.bDeviceClass          = USB_CLASS_CDC,.bDeviceSubClass       = 0x00U,.bDeviceProtocol       = 0x00U,.bMaxPacketSize0       = USBD_EP0_MAX_SIZE,.idVendor              = USBD_VID,.idProduct             = USBD_PID,.bcdDevice             = 0x0100U,.iManufacturer         = STR_IDX_MFC,.iProduct              = STR_IDX_PRODUCT,.iSerialNumber         = STR_IDX_SERIAL,.bNumberConfigurations = USBD_CFG_MAX_NUM,
};

配置描述符如下所示,由配置描述符可知,该USB虚拟串口设备包含两个接口:CMD命令接口和data数据接口。CMD命令接口包含一个IN端点,用于传输命令,该端点采用中断传输方式,轮询间隔为10ms,最大包长为8字节。data数据接口包含一个OUT端点和一个IN端点,这两个端点均采用批量传输方式,最大包长为64字节。另外,该配置描述符中包含了一些类特殊接口描述符,具体请读者参阅CDC类标准协议。

C
usb_cdc_desc_config_set cdc_config_desc = 
{.config = {.header = {.bLength         = sizeof(usb_desc_config), .bDescriptorType = USB_DESCTYPE_CONFIG,},.wTotalLength         = USB_CDC_ACM_CONFIG_DESC_SIZE,.bNumInterfaces       = 0x02U,.bConfigurationValue  = 0x01U,.iConfiguration       = 0x00U,.bmAttributes         = 0x80U,.bMaxPower            = 0x32U},.cmd_itf = {.header = {.bLength         = sizeof(usb_desc_itf), .bDescriptorType = USB_DESCTYPE_ITF },.bInterfaceNumber     = 0x00U,.bAlternateSetting    = 0x00U,.bNumEndpoints        = 0x01U,.bInterfaceClass      = USB_CLASS_CDC,.bInterfaceSubClass   = USB_CDC_SUBCLASS_ACM,.bInterfaceProtocol   = USB_CDC_PROTOCOL_AT,.iInterface           = 0x00U},.cdc_header = {.header ={.bLength         = sizeof(usb_desc_header_func), .bDescriptorType = USB_DESCTYPE_CS_INTERFACE},.bDescriptorSubtype  = 0x00U,.bcdCDC              = 0x0110U},.cdc_call_managment = {.header = {.bLength         = sizeof(usb_desc_call_managment_func), .bDescriptorType = USB_DESCTYPE_CS_INTERFACE},.bDescriptorSubtype  = 0x01U,.bmCapabilities      = 0x00U,.bDataInterface      = 0x01U},.cdc_acm = {.header = {.bLength         = sizeof(usb_desc_acm_func), .bDescriptorType = USB_DESCTYPE_CS_INTERFACE},.bDescriptorSubtype  = 0x02U,.bmCapabilities      = 0x02U,},.cdc_union = {.header = {.bLength         = sizeof(usb_desc_union_func), .bDescriptorType = USB_DESCTYPE_CS_INTERFACE},.bDescriptorSubtype  = 0x06U,.bMasterInterface    = 0x00U,.bSlaveInterface0    = 0x01U,},.cdc_cmd_endpoint = {.header = {.bLength         = sizeof(usb_desc_ep), .bDescriptorType = USB_DESCTYPE_EP,},.bEndpointAddress    = CDC_CMD_EP,.bmAttributes        = USB_EP_ATTR_INT,.wMaxPacketSize      = CDC_ACM_CMD_PACKET_SIZE,.bInterval           = 0x0AU},.cdc_data_interface = {.header = {.bLength         = sizeof(usb_desc_itf), .bDescriptorType = USB_DESCTYPE_ITF,},.bInterfaceNumber    = 0x01U,.bAlternateSetting   = 0x00U,.bNumEndpoints       = 0x02U,.bInterfaceClass     = USB_CLASS_DATA,.bInterfaceSubClass  = 0x00U,.bInterfaceProtocol  = USB_CDC_PROTOCOL_NONE,.iInterface          = 0x00U},.cdc_out_endpoint = {.header = {.bLength         = sizeof(usb_desc_ep), .bDescriptorType = USB_DESCTYPE_EP, },.bEndpointAddress     = CDC_OUT_EP,.bmAttributes         = USB_EP_ATTR_BULK,.wMaxPacketSize       = CDC_ACM_DATA_PACKET_SIZE,.bInterval            = 0x00U},.cdc_in_endpoint = {.header = {.bLength         = sizeof(usb_desc_ep), .bDescriptorType = USB_DESCTYPE_EP },.bEndpointAddress     = CDC_IN_EP,.bmAttributes         = USB_EP_ATTR_BULK,.wMaxPacketSize       = CDC_ACM_DATA_PACKET_SIZE,.bInterval            = 0x00U}
};

为了实现CDC设备类,设备需要支持一些设备类专用请求,这些类专用请求的处理在cdc_acm_req_handler()函数中,该函数的定义如下所示,其中SET_LINE_CODING命令用于响应主机向设备发送设备配置,包括波特率、停止位、字符位数等,收到的数据保存在noti_bu内。GET_LINE_CODING命令用于主机请求设备当前的波特率、停止位、奇偶校验位和字符位数,但在本例程中,主机并未请求该命令,所以设备所设置的串口数据并没有作用,主机可以选择任意波特率与设备进行通信。其他的命令在本例程中并未进行处理,读者可以参考标准CDC类协议。

C
static uint8_t cdc_acm_req_handler (usb_dev *udev, usb_req *req)
{uint8_t status = REQ_NOTSUPP, noti_buf[10] = {0U};usb_cdc_handler *cdc = (usb_cdc_handler *)udev->class_data[CDC_COM_INTERFACE];acm_notification *notif = (void *)noti_buf;switch (req->bRequest) {case SEND_ENCAPSULATED_COMMAND:break;case GET_ENCAPSULATED_RESPONSE:break;case SET_COMM_FEATURE:break;case GET_COMM_FEATURE:break;case CLEAR_COMM_FEATURE:break;case SET_LINE_CODING:/* set the value of the current command to be processed */udev->class_core->req_cmd = req->bRequest;usb_transc_config(&udev->transc_out[0U], (uint8_t *)&cdc->line_coding, req->wLength, 0U);status = REQ_SUPP;break;case GET_LINE_CODING:usb_transc_config(&udev->transc_in[0U], (uint8_t *)&cdc->line_coding, 7U, 0U);status = REQ_SUPP;break;case SET_CONTROL_LINE_STATE:notif->bmRequestType = 0xA1U;notif->bNotification = USB_CDC_NOTIFY_SERIAL_STATE;notif->wIndex = 0U;notif->wValue = 0U;notif->wLength = 2U;noti_buf[8] = (uint8_t)req->wValue & 3U;noti_buf[9] = 0U;status = REQ_SUPP;break;case SEND_BREAK:break;default:break;}return status;
}

下面为大家介绍USBD虚拟串口设备数据的收发。

数据接收通过cdc_acm_data_receive()函数实现,该函数的程序如下所示。在该函数中,首先将packet_receive标志位设置为0,表明接下来将进行接收数据,当接收完成时,在cdc_acm_data_out()函数中,将packet_receive标志位置1,表明数据接收完成。usbd_ep_recev()用于配置接收操作,利用CDC_OUT_EP端点,将接收到的数据放置在cdc->data用户缓冲区中。

C
void cdc_acm_data_receive(usb_dev *udev)
{usb_cdc_handler *cdc = (usb_cdc_handler *)udev->class_data[CDC_COM_INTERFACE];cdc->packet_receive = 0U;cdc->pre_packet_send = 0U;usbd_ep_recev(udev, CDC_OUT_EP, (uint8_t*)(cdc->data), USB_CDC_RX_LEN);
}
static void cdc_acm_data_out (usb_dev *udev, uint8_t ep_num)
{usb_cdc_handler *cdc = (usb_cdc_handler *)udev->class_data[CDC_COM_INTERFACE];cdc->packet_receive = 1U;cdc->receive_length = udev->transc_out[ep_num].xfer_count;
}

数据发送通过cdc_acm_data_send()函数实现,该函数的程序如下所示。在该函数中,首先将packet_sent标志位设置为0,表明接下来将进行发送数据,当数据发送完成时,在cdc_acm_data_in()函数中,将packet_sent标志位设置为1,表明数据发送完成。usbd_ep_send()用于配置发送操作,利用CDC_IN_EP端点,将以cdc->data地址为起始data_len长度的数据发送给主机。

C
void cdc_acm_data_send (usb_dev *udev)
{usb_cdc_handler *cdc = (usb_cdc_handler *)udev->class_data[CDC_COM_INTERFACE];uint32_t data_len = cdc->receive_length;if ((0U != data_len) && (1U == cdc->packet_sent)) {cdc->packet_sent = 0U;usbd_ep_send(udev, CDC_IN_EP, (uint8_t*)(cdc->data), (uint16_t)data_len);cdc->receive_length = 0U;}
}
static void cdc_acm_data_in (usb_dev *udev, uint8_t ep_num)
{usb_transc *transc = &udev->transc_in[ep_num];usb_cdc_handler *cdc = (usb_cdc_handler *)udev->class_data[CDC_COM_INTERFACE];if (transc->xfer_count == transc->max_len) {usbd_ep_send(udev, EP_ID(ep_num), NULL, 0U);} else {cdc->packet_sent = 1U;cdc->pre_packet_send = 1U;}
}

28.5 实验结果

将本例程烧录到红枫派开发板中,并通过TypeC数据线连接USB通信接口和PC,在WIN7上虚拟串口需要安装驱动,在WIN8 WIN10以及后续版本的系统上不需要安装驱动。

下面介绍WIN7系统的驱动安装过程。

在WIN7系统上,将Tyep C数据线连接到PC后,将会在设备管理器中发现一个未知设备,通过以下连接可以下载官方提供的虚拟串口驱动:https://www.gd32mcu.com/download/down/document_id/44/path_type/1

下载驱动并进行安装,之后将会在设备管理器中发现虚拟串口设备已经识别。

之后即可通过串口调试助手与MCU进行CDC通信,在串口调试助手中打开对应虚拟串口的端口,然后输入任意字符,进行发送,将会在接收窗口中看到MCU返回的接收数据,具体现象如下所示。

由聚沃科技原创,来源于【红枫派开发板】第二十八讲 USB-虚拟串口实验 - 苏州聚沃电子科技有限公司 (gd32bbs.com) 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/367133.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

大模型技术在辅助学习中的应用

大模型技术在辅助学习中的应用场景非常广泛,以下是一些典型示例。大模型技术在辅助学习中具有广阔的应用前景,可以为学生提供更加个性化、智能化和高效的学习体验。随着大模型技术的不断发展,我们可以期待在未来看到更多创新应用。北京木奇移…

Linux中的库

什么是库? 库是一组预先编译好的方法/函数的集合,其他程序想要使用源文件中的函数时,只需在编译可执行程序时,链接上该源文件生成的库文件即可。 库分为两类:静态库和动态库 在Linux系统中,以.a为后缀的…

day09了 加油

浅拷贝 指向同一个地址空间 右边不可取地址 左边一定是到了具体的位置 右值引用std&#xff1a;&#xff1a; move 相信大家默认构造函数都没有问题&#xff0c;所以就不贴例子了 浅拷贝构造函数 只负责复制地址&#xff0c;而不是真的把完整的内存给它 #include <iostre…

Nginx主配置文件---Nginx.conf

nginx主配置文件的模块介绍 全局块&#xff1a; 全局块是配置文件从开始到 events 块之间的部分&#xff0c;其中指令的作用域是 Nginx 服务器全局。主要指令包括&#xff1a; user&#xff1a;指定可以运行 Nginx 服务的用户和用户组&#xff0c;只能在全局块配置。例如&…

怎么解决C++不支持字符串枚举?

首先&#xff0c;有两种方法&#xff1a;使用命名空间和字符串常量与使用 enum class 和辅助函数。 表格直观展示 特性使用命名空间和字符串常量使用 enum class 和辅助函数类型安全性低 - 编译器无法检查字符串有效性&#xff0c;运行时发现错误高 - 编译期类型检查&#xf…

基于正点原子FreeRTOS学习笔记——时间片调度实验

目录 一、时间片调度介绍 二、实验演示 1、宏修改 1.1、滴答定时器宏 1.2、调度器宏 2、实验程序 2.1.1、任务1&#xff0c;任务2不加临界区程序 2.1.2 实验现象 2.2.1、任务1&#xff0c;任务2加临界区程序 2.2.2 实验现象 一、时间片调度介绍 时间片&#xff1a;同…

[Cloud Networking] BGP

1. AS (Autonomous System) 由于互联网规模庞大&#xff0c;所以网络会被分为许多 自治系统&#xff08;AS-Autonomous system&#xff09;。 所属类型ASN名称IPv4 数量IPv6数量运营商ISPAS3356LEVEL3 - Level 3 Parent, LLC, US29,798,83273,301,954,048互联网企业AS15169GO…

vue+element-ui简洁完美实现个人博客“​响石潭 ​”

目录 一、项目介绍 二、项目截图 1.项目结构图 2.首页 3.生活 ​编辑 4.文章详情 ​编辑 5.关于我 ​编辑 ​编辑 三、源码实现 1.项目依赖package.json 2.项目启动 3.首页源码 四、总结 一、项目介绍 本项目在线预览&#xff1a;点击访问 参考官网&#xff1…

数据库操作语言(DML)

数据库操作语言&#xff08;DML&#xff09; 文章目录 数据库操作语言&#xff08;DML&#xff09;一、四种操作二、数据的插入&#xff08;增&#xff09;三、数据的删除&#xff08;删&#xff09;四、数据的修改&#xff08;改&#xff09;五、数据的查询&#xff08;查&…

STM32 中断编程入门

目录 一、中断系统 1、中断的原理 2、中断类型 外部中断 定时器中断 DMA中断 3、中断处理函数 中断标志位清除 中断服务程序退出 二、实际应用 中断控制LED 任务要求 代码示例 中断控制串口通信 任务要求1 代码示例 任务要求2 代码示例 总结 学习目标&…

如何理解MySql的MVCC机制

MVCC是什么 MySQL的MVCC机制&#xff0c;全称为多版本并发控制&#xff08;Multi-VersionConcurrency Control&#xff09;&#xff0c;是一种提高数据库并发性能的技术。MVCC的主要目的是在保证数据一致性的同时&#xff0c;提高数据库的并发性能。 它通过为每个读操作创建数…

技术赋能教育:校园3D电子地图与AR导航解决方案

随着高考的落幕&#xff0c;又一批新鲜血液即将注入大学校园。面对陌生的环境&#xff0c;如何快速适应、准确找到目标地点&#xff0c;成为新生们的一大难题。同时&#xff0c;对于学校而言&#xff0c;如何向报考人员直观展示校园环境&#xff0c;提供沉浸式参观体验&#xf…

Vue跨域获取ip和ip位置城市等归属地信息

由于端口设置与查询服务器不一致&#xff0c;所以不能直接从ip138网上抓取&#xff0c;只能跨域查询。实现跨域查询&#xff0c;简单的方法是使用jsonp方式&#xff0c;只支持get请求&#xff0c;同时也需要查询的服务器支持jsonp。这时找到了腾讯位置服务。参考文章&#xff0…

第 27 篇 : 搭建maven私服nexus

官网文档 1. 下载应该很慢, 最好是能翻墙 nexus-3.69.0-02-java8-unix.tar.gz 2. 上传到/usr/local/src, 解压及重命名 tar -zxvf nexus-3.69.0-02-java8-unix.tar.gz rm -rf nexus-3.69.0-02-java8-unix.tar.gz mv nexus-3.69.0-02 nexus ls3. 修改配置 cd /usr/local/sr…

2024最新版Redis常见面试题包含详细讲解

Redis适用于哪些场景&#xff1f; 缓存分布式锁降级限流消息队列延迟消息队 说一说缓存穿透 缓存穿透的概念 用户频繁的发起恶意请求查询缓存中和数据库中都不存在的数据&#xff0c;查询积累到一定量级导致数据库压力过大甚至宕机。 缓存穿透的原因 比如正常情况下用户发…

生命在于学习——Python人工智能原理(3.1.2)

一、概率基本知识 1.3 常见概型 1.3.1 古典概型 定义1 古典概型 若随机事件E满足如下两个条件&#xff1a; &#xff08;1&#xff09;样本空间S中只有有限个样本点。 &#xff08;2&#xff09;样本空间S中每个样本点发生都是等可能的。 这样的随机试验称为古典概型。 P(A)…

暑期大数据人工智能学习-企业项目试岗实训开营

暑期企业项目-试岗实训活动全面开启啦 跟张良均老师学大数据人工智能 不仅可以提供实习证明&#xff0c;有需要话也可以提供实习鉴定报告 √54个热门案例拆解 √40项目实战课程 √27个项目可选 √4个项目方向

企业本地大模型用Ollama+Open WebUI+Stable Diffusion可视化问答及画图

最近在尝试搭建公司内部用户的大模型&#xff0c;可视化回答&#xff0c;并让它能画图出来&#xff0c; 主要包括四块&#xff1a; Ollama 管理和下载各个模型的工具Open WebUI 友好的对话界面Stable Diffusion 绘图工具Docker 部署在容器里&#xff0c;提高效率 以上运行环境…

to_json 出现乱码的解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…

基于MIMO系统的预编码matlab性能仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 4.1 最小均方误差&#xff08;MMSE&#xff09;准则 4.2 量化准则 5.算法完整程序工程 1.算法运行效果图预览 (完整程序运行后无水印) 2.算法运行软件版本 MATLAB2022a 3.部分核心程序 …