【USB设备设计】-- CDC 设备开发(虚拟串口设备)
在嵌入式系统中,串行异步通信接口(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 设备开发(虚拟串口设备)相关推荐
- STM32 USB使用记录:使用CDC类虚拟串口(VCP)进行通讯
文章目录 目的 基础说明 使用STM32CubeIDE配置生成代码 用户代码分析 回环测试 串口参数设置 USB HS使用与演示 通讯速率测试 测试代码 USB FS测试 USB HS测试 影响速度的 ...
- TI Cortex-M4 USB Host CDC 驱动详解及源代码
1. USB CDC介绍 USB的CDC类是USB通信设备类(Communication Device Class)的简称.CDC类是USB组织定义的一类专门给各种通信设备(电信通信设备和中速网络通信 ...
- linux内核配置usb虚拟串口,Linux USB虚拟串口设备
Linux内核中usb设备侧驱动程序分成3个层次:UDC驱动程序.Gadget API和Gadget驱动程序.UDC驱动程序(USB控制器)直接访问硬件,控制USB设备和主机间的底层通信,向上层提供与 ...
- Linux USB虚拟串口设备
Linux内核中usb设备侧驱动程序分成3个层次:UDC驱动程序.Gadget API和Gadget驱动程序.UDC驱动程序(USB控制器)直接访问硬件,控制USB设备和主机间的底层通信,向上层提供与 ...
- STM32Cube MX USB双设备MSC+CDC 实现虚拟U盘+虚拟串口
前言 在上一篇文章实现USB虚拟U盘之后,项目需要用同一个USB口同时实现MSC和CDC功能,既能进行串口通信又能读取片外FLASH虚拟U盘.对于USB通用串行总线如果要真正搞明白这个协议还是比较困难 ...
- RK3288 添加USB转虚拟串口设备
在系统开启并有日志打印的前提下,插入USB设备,就会打印USB设备和虚拟串口信息. 打印信息如下: 供应商ID(VID):idVendor=1234,产品ID(PID): idProduct=5678 ...
- USB协议学习笔记 - 虚拟串口Virtual Port Com LED控制
前言 STM32 的USB 可以虚拟成一个串口,功能还挺强,感觉比HID好用 这里使用USB 虚拟的串口,做个控制LED的小程序 控制LED这里使用自定义的AT命令方式,如红灯亮:AT+LEDR_ON ...
- stm32 USB HID+CDC 鼠标键盘串口 组合设备配置解析
前言 查阅网上的博客与代码,很多都是关于USB的鼠标配置.USB的键盘配置.USB的虚拟串口配置,稍微深入一点的会将鼠标键盘合在一起,但移植起来就会报很多错误,要么是检测不到,要么是警告,这很正常,因 ...
- Window XP驱动开发(二十四)虚拟串口设备驱动
转载请标明是引用于 http://blog.csdn.net/chenyujing1234 欢迎大家拍砖 在我的一篇文章<<winCE中实现虚拟串口的方法 >>中,讲到在win ...
最新文章
- 低配版AI车神?网友用单个CNN在「极品飞车9」里飙车
- 引用外部jQuery地址
- matlab 几何概率
- 图神经网络 | BrainGNN: 用于功能磁共振成像分析的可解释性脑图神经网络
- qy2格式怎么转成mp3_怎么把常见的视频格式mp4转为音频格式mp3?
- 仓库如何盘点 打印扫描一体PDA盘点机提升库存盘点效率
- oracle11g调整表空间和临时表空间大小
- Linux中w r x数学代码,Linux bc命令实现数学计算器
- JSK-337 汽水瓶【数学+模拟】
- Jenkins+Git+Maven+Nexus+Tomcat
- java 注解开发 解耦_Android java 解耦框架注解Dagger2
- 当U盘内的文件夹都成了1KB的快捷方式的解决方法
- chrome axure 插件安装
- js弹幕脚本(基于油猴)
- [Spark版本更新]--2.3.0发行说明(一)
- mac安装matlab提示libmwlmgrimpl.dylib文件已损坏
- 快速打开cmd的方法(win7) window小合集
- 素数问题 java_JAVA素数问题
- 谷歌11年发展历程 从创新到创新
- 非常棒的13款3DMax渲染器插件推荐给大家