![wKgaomZVdiiAfR9BAB3mDFhHnZc972.png](https://file1.elecfans.com/web2/M00/EA/B7/wKgaomZVdiiAfR9BAB3mDFhHnZc972.png)
28.USB-虛擬串口實驗
28.1實驗內容
通過本實驗主要學習以下內容:
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
![wKgaomaEu1mAXrSFAAA70EeD8L4398.png](https://file1.elecfans.com/web2/M00/F8/64/wKgaomaEu1mAXrSFAAA70EeD8L4398.png)
下載驅動并進行安裝,之后將會在設備管理器中發現虛擬串口設備已經識別。
![wKgaomaEu2aAG9zMAAAg_sC1I0w295.png](https://file1.elecfans.com/web2/M00/F8/64/wKgaomaEu2aAG9zMAAAg_sC1I0w295.png)
之后即可通過串口調試助手與MCU進行CDC通信,在串口調試助手中打開對應虛擬串口的端口,然后輸入任意字符,進行發送,將會在接收窗口中看到MCU返回的接收數據,具體現象如下所示。
![wKgaomaEu3iAEMjYAACIVzxZxww116.png](https://file1.elecfans.com/web2/M00/F8/65/wKgaomaEu3iAEMjYAACIVzxZxww116.png)
-
usb
+關注
關注
60文章
7980瀏覽量
266067 -
串口
+關注
關注
14文章
1557瀏覽量
77033 -
開發板
+關注
關注
25文章
5121瀏覽量
98189 -
虛擬串口
+關注
關注
3文章
62瀏覽量
13941 -
GD32
+關注
關注
7文章
413瀏覽量
24467
發布評論請先 登錄
相關推薦
STM32CUBEMX開發GD32F303(9)----USART通過DMA收發
![STM32CUBEMX<b class='flag-5'>開發</b><b class='flag-5'>GD32F303</b>(9)----USART通過DMA收發](https://file.elecfans.com/web2/M00/7E/46/poYBAGOEerqAe0LjAADILAsHIgY275.png)
【星空派GD32F303開發板試用體驗】開箱+環境搭建
【星空派GD32F303開發板試用體驗】開箱+環境搭建
【星空派GD32F303開發板試用體驗】+板卡概覽
GD32F303開發板介紹
GD32F303固件庫開發
![<b class='flag-5'>GD32F303</b>固件庫<b class='flag-5'>開發</b>](https://file.elecfans.com/web2/M00/7E/B7/poYBAGOG-mGAZl3hABOR8D6E0tU278.png)
【GD32F303紅楓派開發板使用手冊】第二講 GPIO-流水燈實驗
![【<b class='flag-5'>GD32F303</b><b class='flag-5'>紅楓</b><b class='flag-5'>派</b><b class='flag-5'>開發板</b><b class='flag-5'>使用手冊</b>】<b class='flag-5'>第二</b><b class='flag-5'>講</b> GPIO-流水燈<b class='flag-5'>實驗</b>](https://file1.elecfans.com/web2/M00/EA/0A/wKgZomZWjIGAa8HvAAAfwpvxoD4203.png)
【GD32F303紅楓派開發板使用手冊】第三講 GPIO-按鍵查詢檢測實驗
![【<b class='flag-5'>GD32F303</b><b class='flag-5'>紅楓</b><b class='flag-5'>派</b><b class='flag-5'>開發板</b><b class='flag-5'>使用手冊</b>】第三<b class='flag-5'>講</b> GPIO-按鍵查詢檢測<b class='flag-5'>實驗</b>](https://file1.elecfans.com/web2/M00/EA/54/wKgZomZX3jiAGq0tAAAqa9bz-8E477.png)
【GD32F303紅楓派開發板使用手冊】第五講 FMC-片內Flash擦寫讀實驗
![【<b class='flag-5'>GD32F303</b><b class='flag-5'>紅楓</b><b class='flag-5'>派</b><b class='flag-5'>開發板</b><b class='flag-5'>使用手冊</b>】第五<b class='flag-5'>講</b> FMC-片內Flash擦寫讀<b class='flag-5'>實驗</b>](https://file1.elecfans.com/web2/M00/EB/E3/wKgaomZb036AbrwmAAAy6saV5XI905.png)
【GD32F303紅楓派開發板使用手冊】第十六講 USART-DMA串口收發實驗
![【<b class='flag-5'>GD32F303</b><b class='flag-5'>紅楓</b><b class='flag-5'>派</b><b class='flag-5'>開發板</b><b class='flag-5'>使用手冊</b>】第十六<b class='flag-5'>講</b> USART-DMA<b class='flag-5'>串口</b>收發<b class='flag-5'>實驗</b>](https://file1.elecfans.com/web2/M00/EF/11/wKgaomZs9FuAR6kDAAAeVi2N6To941.png)
【GD32F303紅楓派開發板使用手冊】第二十講 SPI-SPI NAND FLASH讀寫實驗
![【<b class='flag-5'>GD32F303</b><b class='flag-5'>紅楓</b><b class='flag-5'>派</b><b class='flag-5'>開發板</b><b class='flag-5'>使用手冊</b>】<b class='flag-5'>第二十</b><b class='flag-5'>講</b> SPI-SPI NAND FLASH讀寫<b class='flag-5'>實驗</b>](https://file1.elecfans.com/web2/M00/F1/AD/wKgaomZzisiAamI-AABGtAQwDgo073.png)
【GD32F303紅楓派開發板使用手冊】第二十七講 USB-虛擬鍵盤實驗
![【<b class='flag-5'>GD32F303</b><b class='flag-5'>紅楓</b><b class='flag-5'>派</b><b class='flag-5'>開發板</b><b class='flag-5'>使用手冊</b>】<b class='flag-5'>第二十</b>七<b class='flag-5'>講</b> <b class='flag-5'>USB-</b><b class='flag-5'>虛擬</b>鍵盤<b class='flag-5'>實驗</b>](https://file1.elecfans.com/web2/M00/F3/75/wKgZomZ8w4iAQmoOAAA6VG2wAvw007.png)
評論