​在嵌入式系统中,串行异步通信接口(UART)使用很频繁的接口,跟主机建立通信往往会用到USB转串口的设备,本章将介绍如何将USB虚拟成串口设备。

前期准备

1.带USB 功能的MCU (笔者使用的NXP RT1062)

2.串口调试助手

虚拟串口为cdc类(Communication Device Class),通常CDC类设备由两个子类接口组成:1个通信接口类接口(Communication Interface Class)和 0到多个数据接口类接口(Data Interface Class)。通常来说CDC设备一般需要至少2个接口。废话不多说,先贴上描述符:

Device Descriptor:
------------------------------
0x12    bLength
0x01    bDescriptorType
0x0200  bcdUSB
0xEF    bDeviceClass      (Miscellaneous device)
0x02    bDeviceSubClass
0x01    bDeviceProtocol
0x40    bMaxPacketSize0   (64 bytes)
0x1FC9  idVendor
0x00A3  idProduct
0x0101  bcdDevice
0x01    iManufacturer   "L17"
0x02    iProduct        "USB VCP"
0x03    iSerialNumber   "0123456789ABCDEF"
0x01    bNumConfigurationsConfiguration Descriptor:
------------------------------
0x09    bLength
0x02    bDescriptorType
0x004B  wTotalLength   (75 bytes)
0x02    bNumInterfaces
0x01    bConfigurationValue
0x00    iConfiguration
0xC0    bmAttributes   (Self-powered Device)
0x32    bMaxPower      (100 mA)Interface Association Descriptor:
------------------------------
0x08    bLength
0x0B    bDescriptorType
0x00    bFirstInterface
0x02    bInterfaceCount
0x02    bFunctionClass      (Communication Device Class)
0x02    bFunctionSubClass   (Abstract Control Model - ACM)
0x00    bFunctionProtocol
0x02    iFunction   "USB VCP"Interface Descriptor:
------------------------------
0x09    bLength
0x04    bDescriptorType
0x00    bInterfaceNumber
0x00    bAlternateSetting
0x01    bNumEndPoints
0x02    bInterfaceClass      (Communication Device Class)
0x02    bInterfaceSubClass   (Abstract Control Model - ACM)
0x01    bInterfaceProtocol   (ITU-T V.250)
0x00    iInterfaceCDC Header Functional Descriptor:
------------------------------
0x05    bFunctionalLength
0x24    bDescriptorType
0x00    bDescriptorSubtype
0x0110  bcdCDCCDC Call Management Functional Descriptor:
------------------------------
0x05    bFunctionalLength
0x24    bDescriptorType
0x01    bDescriptorSubtype
0x01    bmCapabilities
0x01    bDataInterfaceCDC Abstract Control Management Functional Descriptor:
------------------------------
0x04    bFunctionalLength
0x24    bDescriptorType
0x02    bDescriptorSubtype
0x06    bmCapabilitiesCDC Union Functional Descriptor:
------------------------------
0x05    bFunctionalLength
0x24    bDescriptorType
0x06    bDescriptorSubtype
0x00    bControlInterface
0x01    bSubordinateInterface(0)Endpoint Descriptor:
------------------------------
0x07    bLength
0x05    bDescriptorType
0x81    bEndpointAddress  (IN endpoint 1)
0x03    bmAttributes      (Transfer: Interrupt / Synch: None / Usage: Data)
0x0010  wMaxPacketSize    (1 x 16 bytes)
0x07    bInterval         (64 microframes)Interface Descriptor:
------------------------------
0x09    bLength
0x04    bDescriptorType
0x01    bInterfaceNumber
0x00    bAlternateSetting
0x02    bNumEndPoints
0x0A    bInterfaceClass      (CDC Data)
0x00    bInterfaceSubClass
0x00    bInterfaceProtocol
0x00    iInterfaceEndpoint Descriptor:
------------------------------
0x07    bLength
0x05    bDescriptorType
0x82    bEndpointAddress  (IN endpoint 2)
0x02    bmAttributes      (Transfer: Bulk / Synch: None / Usage: Data)
0x0200  wMaxPacketSize    (512 bytes)
0x00    bInterval         Endpoint Descriptor:
------------------------------
0x07    bLength
0x05    bDescriptorType
0x02    bEndpointAddress  (OUT endpoint 2)
0x02    bmAttributes      (Transfer: Bulk / Synch: None / Usage: Data)
0x0200  wMaxPacketSize    (512 bytes)
0x00    bInterval

通信接口

通信接口下拥有一个输入端点,用于报告主机一些状态。对于实现USB虚拟串口功能,需要将USB通信接口类的类型设置为:Abstract Control Model子类和Common AT Commands传输协议

通信接口描述符后面会跟,功能描述符(Functional Descriptors),用于描述接口功能

描述符子类 基本功能信息
00h 功能描述符头
01h Call Management 功能描述符
02h 抽象控制管理 功能描述符
03h Direct Line Management 功能描述符
04h Telephone Ringer 功能描述符
05h Telephone Call and Line State Reporting Capability 功能描述符
06h Union 功能描述符
07h Country Selection 功能描述符
08h Telephone Operation Mode 功能描述符
09h USB Terminal 功能描述符
0Ah Network Channel 功能描述符
0Bh 协议单元功能描述符
0Ch 扩展单元功能描述符
0Dh 多通道管理功能描述符
0Eh CAPI控制管理功能描述符
0Fh 以太网网络功能描述符
10h ATM功能描述符
11-FFh 保留

数据接口

数据接口,没啥好说的,接口类定义为:0x0A 。端点描述符,注意传输类型为:Bulk 块传输

虚拟串口相关类请求

  • GET LINE CODING

主机获取当前串口属性请求,包括波特率、停止位、校验位及数据位的位数。CTL请求编码格式

返回7Byte参数,分别为: [0:3]波特率,[4]停止位,[5]校验位,[6]数据位。可知上图获取波特率为:115200

  • SET LINE CODING

类似GET LINE CODING,用于主机设置从机当前属性,可修改波特率、停止位、校验位及数据位

  • SET CTRL LINE STATE

该请求没有数据输出阶段,作用是设置设备的DTR和RTS引脚电平,D0位表示DTR,D1位表示RTS

类请求回调函数内容如下:

usb_status_t USB_DeviceCdcVcomCallback(class_handle_t handle, uint32_t event, void *param)
{uint32_t len;uint8_t *uartBitmap;usb_cdc_acm_info_t *acmInfo;usb_device_cdc_acm_request_param_struct_t *acmReqParam;usb_device_endpoint_callback_message_struct_t *epCbParam;volatile usb_cdc_vcom_struct_t *vcomInstance;usb_status_t error = kStatus_USB_InvalidRequest;acmReqParam = (usb_device_cdc_acm_request_param_struct_t *)param;epCbParam   = (usb_device_endpoint_callback_message_struct_t *)param;vcomInstance = &g_deviceComposite->cdcVcom;acmInfo      = vcomInstance->usbCdcAcmInfo;switch (event){case kUSB_DeviceCdcEventSendResponse:{if ((epCbParam->length != 0) && (!(epCbParam->length % vcomInstance->bulkInEndpointMaxPacketSize))){/* If the last packet is the size of endpoint, then send also zero-ended packet,** meaning that we want to inform the host that we do not have any additional** data, so it can flush the output.*/error = USB_DeviceCdcAcmSend(handle, vcomInstance->bulkInEndpoint, NULL, 0);}else if ((1 == vcomInstance->attach) && (1 == vcomInstance->startTransactions)){if ((epCbParam->buffer != NULL) || ((epCbParam->buffer == NULL) && (epCbParam->length == 0))){/* User: add your own code for send complete event *//* Schedule buffer for next receive event */error = USB_DeviceCdcAcmRecv(handle, vcomInstance->bulkOutEndpoint, vcomInstance->currRecvBuf,vcomInstance->bulkOutEndpointMaxPacketSize);}}else{}}break;case kUSB_DeviceCdcEventRecvResponse:{if ((1 == vcomInstance->attach) && (1 == vcomInstance->startTransactions)){vcomInstance->recvSize = epCbParam->length;if (!vcomInstance->recvSize){/* Schedule buffer for next receive event */error = USB_DeviceCdcAcmRecv(handle, vcomInstance->bulkOutEndpoint, vcomInstance->currRecvBuf,vcomInstance->bulkOutEndpointMaxPacketSize);}}}break;case kUSB_DeviceCdcEventSerialStateNotif:((usb_device_cdc_acm_struct_t *)handle)->hasSentState = 0;error                                                 = kStatus_USB_Success;break;case kUSB_DeviceCdcEventSendEncapsulatedCommand:break;case kUSB_DeviceCdcEventGetEncapsulatedResponse:break;case kUSB_DeviceCdcEventSetCommFeature:if (USB_DEVICE_CDC_FEATURE_ABSTRACT_STATE == acmReqParam->setupValue){if (1 == acmReqParam->isSetup){*(acmReqParam->buffer) = vcomInstance->abstractState;*(acmReqParam->length) = COMM_FEATURE_DATA_SIZE;}else{/* no action, data phase, s_abstractState has been assigned */}error = kStatus_USB_Success;}else if (USB_DEVICE_CDC_FEATURE_COUNTRY_SETTING == acmReqParam->setupValue){if (1 == acmReqParam->isSetup){*(acmReqParam->buffer) = vcomInstance->countryCode;*(acmReqParam->length) = COMM_FEATURE_DATA_SIZE;}else{/* no action, data phase, s_countryCode has been assigned */}error = kStatus_USB_Success;}else{/* no action, return kStatus_USB_InvalidRequest */}break;case kUSB_DeviceCdcEventGetCommFeature:if (USB_DEVICE_CDC_FEATURE_ABSTRACT_STATE == acmReqParam->setupValue){*(acmReqParam->buffer) = vcomInstance->abstractState;*(acmReqParam->length) = COMM_FEATURE_DATA_SIZE;error                  = kStatus_USB_Success;}else if (USB_DEVICE_CDC_FEATURE_COUNTRY_SETTING == acmReqParam->setupValue){*(acmReqParam->buffer) = vcomInstance->countryCode;*(acmReqParam->length) = COMM_FEATURE_DATA_SIZE;error                  = kStatus_USB_Success;}else{/* no action, return kStatus_USB_InvalidRequest */}break;case kUSB_DeviceCdcEventClearCommFeature:break;case kUSB_DeviceCdcEventGetLineCoding:*(acmReqParam->buffer) = vcomInstance->lineCoding;*(acmReqParam->length) = LINE_CODING_SIZE;error                  = kStatus_USB_Success;break;case kUSB_DeviceCdcEventSetLineCoding:{if (1 == acmReqParam->isSetup){*(acmReqParam->buffer) = vcomInstance->lineCoding;*(acmReqParam->length) = LINE_CODING_SIZE;}else{/* no action, data phase, s_lineCoding has been assigned */}error = kStatus_USB_Success;}break;case kUSB_DeviceCdcEventSetControlLineState:{error                     = kStatus_USB_Success;vcomInstance->usbCdcAcmInfo->dteStatus = acmReqParam->setupValue;/* activate/deactivate Tx carrier */if (acmInfo->dteStatus & USB_DEVICE_CDC_CONTROL_SIG_BITMAP_CARRIER_ACTIVATION){acmInfo->uartState |= USB_DEVICE_CDC_UART_STATE_TX_CARRIER;}else{acmInfo->uartState &= (uint16_t)~USB_DEVICE_CDC_UART_STATE_TX_CARRIER;}/* activate carrier and DTE. Com port of terminal tool running on PC is open now */if (acmInfo->dteStatus & USB_DEVICE_CDC_CONTROL_SIG_BITMAP_DTE_PRESENCE){acmInfo->uartState |= USB_DEVICE_CDC_UART_STATE_RX_CARRIER;}/* Com port of terminal tool running on PC is closed now */else{acmInfo->uartState &= (uint16_t)~USB_DEVICE_CDC_UART_STATE_RX_CARRIER;}/* Indicates to DCE if DTE is present or not */acmInfo->dtePresent = (acmInfo->dteStatus & USB_DEVICE_CDC_CONTROL_SIG_BITMAP_DTE_PRESENCE) ? true : false;/* Initialize the serial state buffer */acmInfo->serialStateBuf[0] = NOTIF_REQUEST_TYPE;                /* bmRequestType */acmInfo->serialStateBuf[1] = USB_DEVICE_CDC_NOTIF_SERIAL_STATE; /* bNotification */acmInfo->serialStateBuf[2] = 0x00;                              /* wValue */acmInfo->serialStateBuf[3] = 0x00;acmInfo->serialStateBuf[4] = 0x00; /* wIndex */acmInfo->serialStateBuf[5] = 0x00;acmInfo->serialStateBuf[6] = UART_BITMAP_SIZE; /* wLength */acmInfo->serialStateBuf[7] = 0x00;/* Notify to host the line state */acmInfo->serialStateBuf[4] = acmReqParam->interfaceIndex;/* Lower byte of UART BITMAP */uartBitmap    = (uint8_t *)&acmInfo->serialStateBuf[NOTIF_PACKET_SIZE + UART_BITMAP_SIZE - 2];uartBitmap[0] = acmInfo->uartState & 0xFFu;uartBitmap[1] = (acmInfo->uartState >> 8) & 0xFFu;len           = (uint32_t)(NOTIF_PACKET_SIZE + UART_BITMAP_SIZE);if (0 == ((usb_device_cdc_acm_struct_t *)handle)->hasSentState){error = USB_DeviceCdcAcmSend(handle, vcomInstance->interruptEndpoint, acmInfo->serialStateBuf, len);if (kStatus_USB_Success != error){//                    usb_echo("kUSB_DeviceCdcEventSetControlLineState error!");}((usb_device_cdc_acm_struct_t *)handle)->hasSentState = 1;}/* Update status */if (acmInfo->dteStatus & USB_DEVICE_CDC_CONTROL_SIG_BITMAP_CARRIER_ACTIVATION){/*  To do: CARRIER_ACTIVATED */}else{/* To do: CARRIER_DEACTIVATED */}if (acmInfo->dteStatus & USB_DEVICE_CDC_CONTROL_SIG_BITMAP_DTE_PRESENCE){/* DTE_ACTIVATED */if (1 == vcomInstance->attach){vcomInstance->startTransactions = 1;}}else{/* DTE_DEACTIVATED */if (1 == vcomInstance->attach){vcomInstance->startTransactions = 0;}}}break;case kUSB_DeviceCdcEventSendBreak:break;default:break;}return error;
}

测试自发自收功能

串口调试工具使用MobaXterm,底层自发自收,测试结果如下

【USB设备设计】-- CDC 设备开发(虚拟串口设备)相关推荐

  1. STM32 USB使用记录:使用CDC类虚拟串口(VCP)进行通讯

    文章目录 目的 基础说明 使用STM32CubeIDE配置生成代码 用户代码分析 回环测试 串口参数设置 USB HS使用与演示 通讯速率测试 测试代码 USB FS测试 USB HS测试 影响速度的 ...

  2. TI Cortex-M4 USB Host CDC 驱动详解及源代码

    1. USB CDC介绍 USB的CDC类是USB通信设备类(Communication Device Class)的简称.CDC类是USB组织定义的一类专门给各种通信设备(电信通信设备和中速网络通信 ...

  3. linux内核配置usb虚拟串口,Linux USB虚拟串口设备

    Linux内核中usb设备侧驱动程序分成3个层次:UDC驱动程序.Gadget API和Gadget驱动程序.UDC驱动程序(USB控制器)直接访问硬件,控制USB设备和主机间的底层通信,向上层提供与 ...

  4. Linux USB虚拟串口设备

    Linux内核中usb设备侧驱动程序分成3个层次:UDC驱动程序.Gadget API和Gadget驱动程序.UDC驱动程序(USB控制器)直接访问硬件,控制USB设备和主机间的底层通信,向上层提供与 ...

  5. STM32Cube MX USB双设备MSC+CDC 实现虚拟U盘+虚拟串口

    前言 在上一篇文章实现USB虚拟U盘之后,项目需要用同一个USB口同时实现MSC和CDC功能,既能进行串口通信又能读取片外FLASH虚拟U盘.对于USB通用串行总线如果要真正搞明白这个协议还是比较困难 ...

  6. RK3288 添加USB转虚拟串口设备

    在系统开启并有日志打印的前提下,插入USB设备,就会打印USB设备和虚拟串口信息. 打印信息如下: 供应商ID(VID):idVendor=1234,产品ID(PID): idProduct=5678 ...

  7. USB协议学习笔记 - 虚拟串口Virtual Port Com LED控制

    前言 STM32 的USB 可以虚拟成一个串口,功能还挺强,感觉比HID好用 这里使用USB 虚拟的串口,做个控制LED的小程序 控制LED这里使用自定义的AT命令方式,如红灯亮:AT+LEDR_ON ...

  8. stm32 USB HID+CDC 鼠标键盘串口 组合设备配置解析

    前言 查阅网上的博客与代码,很多都是关于USB的鼠标配置.USB的键盘配置.USB的虚拟串口配置,稍微深入一点的会将鼠标键盘合在一起,但移植起来就会报很多错误,要么是检测不到,要么是警告,这很正常,因 ...

  9. Window XP驱动开发(二十四)虚拟串口设备驱动

    转载请标明是引用于 http://blog.csdn.net/chenyujing1234 欢迎大家拍砖 在我的一篇文章<<winCE中实现虚拟串口的方法 >>中,讲到在win ...

最新文章

  1. 低配版AI车神?网友用单个CNN在「极品飞车9」里飙车
  2. 引用外部jQuery地址
  3. matlab 几何概率
  4. 图神经网络 | BrainGNN: 用于功能磁共振成像分析的可解释性脑图神经网络
  5. qy2格式怎么转成mp3_怎么把常见的视频格式mp4转为音频格式mp3?
  6. 仓库如何盘点 打印扫描一体PDA盘点机提升库存盘点效率
  7. oracle11g调整表空间和临时表空间大小
  8. Linux中w r x数学代码,Linux bc命令实现数学计算器
  9. JSK-337 汽水瓶【数学+模拟】
  10. Jenkins+Git+Maven+Nexus+Tomcat
  11. java 注解开发 解耦_Android java 解耦框架注解Dagger2
  12. 当U盘内的文件夹都成了1KB的快捷方式的解决方法
  13. chrome axure 插件安装
  14. js弹幕脚本(基于油猴)
  15. [Spark版本更新]--2.3.0发行说明(一)
  16. mac安装matlab提示libmwlmgrimpl.dylib文件已损坏
  17. 快速打开cmd的方法(win7) window小合集
  18. 素数问题 java_JAVA素数问题
  19. 谷歌11年发展历程 从创新到创新
  20. 非常棒的13款3DMax渲染器插件推荐给大家

热门文章

  1. 【Python第3篇】如何在Python中对代码进行注释
  2. Critical Scenarios definition
  3. @sun.misc.Contended
  4. CentOS - NFS实现共享文件夹及开机自动挂载功能
  5. js交换两个变量的值
  6. Maven面试题总结
  7. C语言之全局变量和局部变量习题
  8. 【数据库】索引的基本原理
  9. audiotrack java_Android音视频之AudioTrack播放音频(二)
  10. javascript自执行函数