【USB设备设计】

您所在的位置:网站首页 win7usb虚拟串口cdc驱动 【USB设备设计】

【USB设备设计】

2024-07-17 16:14| 来源: 网络整理| 查看: 265

​在嵌入式系统中,串行异步通信接口(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 bNumConfigurations Configuration 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 iInterface CDC Header Functional Descriptor: ------------------------------ 0x05 bFunctionalLength 0x24 bDescriptorType 0x00 bDescriptorSubtype 0x0110 bcdCDC CDC Call Management Functional Descriptor: ------------------------------ 0x05 bFunctionalLength 0x24 bDescriptorType 0x01 bDescriptorSubtype 0x01 bmCapabilities 0x01 bDataInterface CDC Abstract Control Management Functional Descriptor: ------------------------------ 0x04 bFunctionalLength 0x24 bDescriptorType 0x02 bDescriptorSubtype 0x06 bmCapabilities CDC 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 iInterface Endpoint 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功能描述符头01hCall Management 功能描述符02h抽象控制管理 功能描述符03hDirect Line Management 功能描述符04hTelephone Ringer 功能描述符05hTelephone Call and Line State Reporting Capability 功能描述符06hUnion 功能描述符07hCountry Selection 功能描述符08hTelephone Operation Mode 功能描述符09hUSB Terminal 功能描述符0AhNetwork Channel 功能描述符0Bh协议单元功能描述符0Ch扩展单元功能描述符0Dh多通道管理功能描述符0EhCAPI控制管理功能描述符0Fh以太网网络功能描述符10hATM功能描述符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,底层自发自收,测试结果如下

请添加图片描述



【本文地址】


今日新闻


推荐新闻


    CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3