通过MCU的USB模块加串口模块,实现一个串口助手的功能。具体操作为把USB接收到的数据通过串口发送,同时实现串口接收的数据回传到USB,从而实现从USB到TTL的电平转换。
1.mcu: 国芯ccm4202
2.开发环境:Keil
1.USB设置为CDC类
USB CDC类的通信部分主要包含三部分:枚举过程、虚拟串口操作和数据通信。其中虚拟串口操作部分并不一定强制需要,因为若跳过这些虚拟串口的操作,实际上USB依然是可以通信的,这也就是为什么上图中,在操作虚拟串口之前会有两条数据通信的数据。之所以会有虚拟串口操作,主要是我们通常使用PC作为Host端,在PC端使用一个串口工具来与其进行通信,PC端的对应驱动将其虚拟成一个普通串口,这样一来,可以方便PC端软件通过操作串口的方式来与其进行通信,但实际上,Host端与Device端物理上是通过USB总线来进行通信的,与串口没有关系,这一虚拟化过程,起决定性作用的是对应驱动,包含如何将每一条具体的虚拟串口操作对应到实际上的USB操作。需要注意的是,Host端与Device端的USB通信速率并不受所谓的串口波特率影响,它就是标准的USB2.0全速(12Mbps)速度,实际速率取决于总线的实际使用率、驱动访问USB外设有效速率(两边)以及外部环境对通信本身造成的干扰率等因素组成。
2.接口映射
初始化MCU的UART,实现通过UART把USB的数据进行收发。
1.USB设置为CDC类
void USB_CDC_Init(void) { //USB1.1, 同时配置USB配置项为USB1.1模式 g_usbVer = 0; //0:internal oscillator; 1:external oscillator USBC_PHY_Init(0); CCM->PHYPA |= 0x0e00; //no need to supply VBUS. /* Global USB Register */ //gUSBC_fifoReg = (USBCFIFO_Reg*)(USBC_BASE_ADDR+0x1A); gUSBC_fifoReg = (USBCFIFO_Reg*)(USBC_BASE_ADDR+0x60); gUSBC_ComReg = (USBCCommonReg*)USBC_BASE_ADDR; gUSBC_IdxReg = (USBCIndexedReg*)(USBC_BASE_ADDR+0x10); //USB data buffer g_databuf = USB_BUFFER_ADDR; //The suspend mode is disable before BULK-Only tranfer start g_suspendMode = 0; /* Setup USB register */ //enable usb common interrupt //0 1 2 3 4 5 6 7 (bit) //Susp Resume Reset SOF Conn Discon SessReq VBusErr gUSBC_ComReg->INTRUSBE = USB_INTERRUPT_RESET|USB_INTERRUPT_CONNECT|USB_INTERRUPT_DISCON|USB_INTERRUPT_SUSPEND|USB_INTERRUPT_RESUME; //enable ep0 and ep1 tx interrupts,clear other tx interrupts gUSBC_ComReg->INTRTXE_L = USB_INTERRUPT_EP0|(1<INTRRXE_L = (1< EINDEX = CONTROL_EP; //Enable Soft connection if(g_usbVer == 1) gUSBC_ComReg->UCSR = USB_POWER_SOFT_CONN|USB_POWER_HS_ENAB; else gUSBC_ComReg->UCSR = USB_POWER_SOFT_CONN; g_USBAddrFlag = 0; g_USBNewAddr = 0; NVIC_Init(3, 3, USBC_IRQn, 2); }
2.初始化串口为中断方式
void Usb_UART_init(void) { UART_InitTypeDef UART_InitStruct; UART_InitStruct.UART_BaudRate = LineCoding.bitrate; UART_InitStruct.UART_Mode = UART_INT_MODE; UART_InitStruct.UART_Parity = UART_PARITY_NONE; if( LineCoding.format == 0) { UART_InitStruct.UART_FrameLength = UART_DATA_FRAME_LEN_10BIT; UART_InitStruct.UART_StopBits = 1; } else { UART_InitStruct.UART_FrameLength = UART_DATA_FRAME_LEN_11BIT; UART_InitStruct.UART_StopBits = 2; } UART_Init(Usb_Uart, &UART_InitStruct); }
3.串口接收数据,通过USB发送到上位机。当数据间隔大于50毫秒默认为两包数据
ret = UART_RecvByte(Usb_Uart, &temp); if(STATUS_OK == ret) { uart_data_buf_[i++] = temp; len = i ; } else { if( Tool_Tc_cnt >= 50) { Tool_Tc_cnt = 0 ; if(len!= 0 ) { usb_cdc_send(INDEX_EP1, (UINT8*)uart_data_buf_, len); i = 0 ; len = 0 ; } } }
4.USB接收数据,并将数据通过UART发出
void USBDev_DoCDCCmd_(void) { UINT16 recvLen = 0; UINT16 len; len = 64; memset(cdc_ctl_buf_, 0x00, sizeof(cdc_ctl_buf_)); recvLen = usb_cdc_receive(INDEX_EP2, (UINT8 *)cdc_ctl_buf_); if (recvLen == 0) return; len = recvLen; UART_SendData(Usb_Uart, cdc_ctl_buf_,len); } void usb_cdc_poll_(void) { if( (g_uchUSBStatus & BIT1) == BIT1 ) //接收到一包数据 { g_uchUSBStatus &= ~BIT1; USBDev_DoCDCCmd_(); } }
五、实验现象:
如图所示,通过MCU模拟出来的串口助手可以实现USB_TTL芯片相同效果,进行数据收发。
1.串口需要采用中断方式,轮训会造成数据接收转发不完全
2.usb虚拟串口部分,需要设置MCU的波特率,数据位,停止位等信息,否则会导致数据乱码。