完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
在上一节我们介绍了MM32 MCU的WINUSB功能,对于WINUSB来说,还是需要开发对应的上位机来测试收发数据,相对来说较麻烦,如果仅做MCU与PC的数据通信功能,开发USB CDC功能可以直接用串口助手等工具即可测试数据收发。 本节我们来讲解如何在MM32 MCU实现CDC功能。USB CDC类是USB通信设备类 (Communication Device Class)的简称。CDC类是USB组织定义的一类专门给各种通信设备(电信通信设备和中速网络通信设备)使用的USB子类。根据CDC类所针对通信设备的不同,CDC类又被分成以下不同的模型:USB传统纯电话业务(POTS)模型,USB ISDN模型和USB网络模型。其中,USB传统纯电话业务模型,有可分为直接线控制模型(Direct Line Control Model)、抽象控制模型(Abstract Control Model)和USB电话模型(USB Telephone Model),本节我们所讨论的虚拟串口就属于USB传统纯电话业务模型下的抽象控制模型。 通常一个CDC类又是由两个接口子类组成通信接口类(Communication Interface Class)和数据接口类(Data Interface Class)。主要通过通信接口类对设备进行管理和控制,而通过数据接口类传送数据。这两个接口子类占有不同数量和类型的终端点(Endpoints),对于前面所述的不同CDC类模型,其所对应的接口的终端点需求也是不同的。如所需要讨论的抽象控制模型对终端点的需求,通信接口类需要一个控制终端点(Control Endpoint)和一个可选的中断(Interrupt)型终端点,数据接口子类需要一个方向为输入(IN)的周期性(Isochronous)型终端 点和一个方向为输出(OUT)的周期性型终端点。其中控制终端点主要用于USB设备的枚举和虚拟串口的波特率和数据类型(数据位数、停止位和起始位)设置 的通信。输出方向的非同步终端点用于主机(Host)向从设备(Slave)发送数据,相当于传统物理串口中的TXD线(如果从单片机的角度看),输入方向的非同步终端点用于从设备向主机发送数据,相当于传统物理串口中的RXD线。 USB CDC类的通信部分主要包含三部分:枚举过程、虚拟串口操作和数据通信。其中虚拟串口操作部分并不一定强制需要,因为若跳过这些虚拟串口的操作,实际上USB依然是可以通信的,因为在操作虚拟串口之前会有两条数据通信的数据。之所以会有虚拟串口操作,主要是我们通常使用PC作为Host端,在PC端使用一个串口工具来与其进行通信,PC端的对应驱动将其虚拟成一个普通串口,这样一来,可以方便PC端软件通过操作串口的方式来与其进行通信,但实际上,Host端与Device端物理上是通过USB总线来进行通信的,与串口没有关系,这一虚拟化过程,起决定性作用的是对应驱动,包含如何将每一条具体的虚拟串口操作对应到实际上的USB操作。这里需要注意地是,Host端与Device端的USB通信速率并不受所谓的串口波特率影响,它就是标准的USB2.0全速(12Mbps)速度,实际速率取决于总线的实际使用率、驱动访问USB外设有效速率(两边)以及外部环境对通信本身造成的干扰率等等因素组成。 本次我们采用MM32L373 miniboard作为测试开发板。为了方便大家使用MM32 MCU的CDC功能,我们已经封装好全部代码,用户不需要自己配置以上的那些描述符等参数,只需要知道如何处理CDC的数据接收和发送即可。 软件资源如下: 函数初始化配置及相关全局变量定义内容,参考之前文章代码即可,在此不作过多赘述。 对于MM32 MCU的CDC功能来说,在使用CDC功能之前先调用USB初始化函数来初始化USB协议栈。 int main(void) { u***d_init(); // USB Device Initialization and connect u***d_connect(__TRUE); while (!u***d_configured()) // Wait for USB Device to configure { } while (1) { } } 然后就是CDC数据收发处理函数,USB数据处理函数如下: // For usart #define CDC_UART UART1 #define CDC_UART_ENABLE() RCC_APB2PeriphClockCmd(RCC_APB2Periph_UART1, ENABLE) #define CDC_UART_DISABLE() RCC_APB2PeriphClockCmd(RCC_APB2Periph_UART1, DISABLE) #define CDC_UART_IRQn UART1_IRQn #define CDC_UART_IRQn_Handler UART1_IRQHandler #define UART_PINS_PORT_ENABLE() RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE) #define UART_PINS_PORT_DISABLE() RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , DISABLE) #define UART_TX_PORT GPIOA #define UART_TX_PIN GPIO_Pin_9 #define UART_TX_PIN_SOURCE GPIO_PinSource9 #define UART_RX_PORT GPIOA #define UART_RX_PIN GPIO_Pin_10 #define UART_RX_PIN_SOURCE GPIO_PinSource10 #define USART_BUFFER_SIZE (256) static struct { volatile uint16_t idx_in; volatile uint16_t idx_out; volatile int16_t cnt_in; volatile int16_t cnt_out; uint8_t data[USART_BUFFER_SIZE]; } WrBuffer, RdBuffer; /* Control Lines */ #define UART_CONTROL_LINE_RTS_Pos 0 /* request to send control line */ #define UART_CONTROL_LINE_RTS_Msk (1 << UART_CONTROL_LINE_RTS_Pos) #define UART_CONTROL_LINE_DTR_Pos 0 /* request to send control line */ #define UART_CONTROL_LINE_DTR_Msk (1 << UART_CONTROL_LINE_DTR_Pos) /* Status Lines */ #define UART_STATUS_LINE_CTS_Pos 0 /* clear to send control line */ #define UART_STATUS_LINE_CTS_Msk (1 << UART_STATUS_LINE_CTS_Pos) #define UART_STATUS_LINE_DCD_Pos 1 /* data carrier detect */ #define UART_STATUS_LINE_DCD_Msk (1 << UART_STATUS_LINE_DCD_Pos) #define UART_STATUS_LINE_DSR_Pos 2 /* data set ready */ #define UART_STATUS_LINE_DSR_Msk (1 << UART_STATUS_LINE_DSR_Pos) #define UART_STATUS_LINE_RI_Pos 3 /* ring indicator */ #define UART_STATUS_LINE_RI_Msk (1 << UART_STATUS_LINE_RI_Pos) /* Communication Errors */ #define UART_FRAMING_ERROR_Pos 0 #define UART_FRAMING_ERROR_Msk (1 << UART_FRAMING_ERROR_Pos) #define UART_PARITY_ERROR_Pos 1 #define UART_PARITY_ERROR_Msk (1 << UART_PARITY_ERROR_Pos) #define UART_OVERRUN_ERROR_Pos 2 #define UART_OVERRUN_ERROR_Msk (1 << UART_OVERRUN_ERROR_Pos) static uint32_t StatusRegister; static uint32_t BreakFlag; extern uint8_t EP2ReceiveFlag; extern uint8_t EP2TransferFlag; extern uint8_t RxBufLen; extern uint8_t TxBufLen; extern uint8_t u***_buf_busy_flag; extern uint8_t EP2RXBuff[64]; extern uint8_t EP2TXBuff[1024]; #define RX_OVRF_MSG " #define RX_OVRF_MSG_SIZE (sizeof(RX_OVRF_MSG) - 1) #define BUFFER_SIZE (512) circ_buf_t write_buffer; uint8_t write_buffer_data[BUFFER_SIZE]; static uint32_t tx_in_progress = 0; circ_buf_t read_buffer; uint8_t read_buffer_data[BUFFER_SIZE]; static UART_Configuration configuration = { .Baudrate = 9600, .DataBits = UART_DATA_BITS_8, .Parity = UART_PARITY_NONE, .StopBits = UART_STOP_BITS_1, .FlowControl = UART_FLOW_CONTROL_NONE, }; extern uint32_t SystemCoreClock; void CDC_UART_CallBack(void); static void clear_buffers(void) { circ_buf_init(&write_buffer, write_buffer_data, sizeof(write_buffer_data)); circ_buf_init(&read_buffer, read_buffer_data, sizeof(read_buffer_data)); } int32_t uart_initialize(void) { uint16_t data_bits; uint16_t parity; uint16_t stop_bits; GPIO_InitTypeDef GPIO_InitStructure; UART_InitTypeDef UART_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; UART_ITConfig(CDC_UART, UART_IT_RXIEN|UART_IT_TXIEN, DISABLE); clear_buffers(); CDC_UART_ENABLE(); UART_PINS_PORT_ENABLE(); //TX pin GPIO_InitStructure.GPIO_Pin = UART_TX_PIN; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_Init(UART_TX_PORT, &GPIO_InitStructure); //RX pin GPIO_InitStructure.GPIO_Pin = UART_RX_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(UART_RX_PORT, &GPIO_InitStructure); //Only 8 bit support data_bits = UART_WordLength_8b; // parity if(configuration.Parity == UART_PARITY_ODD) parity = UART_Parity_Odd; else if(configuration.Parity == UART_PARITY_EVEN) parity = UART_Parity_Even; else parity = UART_Parity_No; // stop bits if(configuration.StopBits == UART_STOP_BITS_2) stop_bits = UART_StopBits_2; else if(configuration.StopBits == UART_STOP_BITS_1) stop_bits = UART_StopBits_1; else stop_bits = UART_StopBits_1; // UART_InitStructure.UART_BaudRate = configuration.Baudrate; UART_InitStructure.UART_WordLength = data_bits; UART_InitStructure.UART_StopBits = stop_bits; UART_InitStructure.UART_Parity = parity; UART_InitStructure.UART_HardwareFlowControl = UART_HardwareFlowControl_None; UART_InitStructure.UART_Mode = UART_Mode_Rx | UART_Mode_Tx; UART_Init(CDC_UART, &UART_InitStructure); // NVIC_InitStructure.NVIC_IRQChannel = CDC_UART_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); // Enable RX interrupt UART_ITConfig(CDC_UART, UART_IT_RXIEN, ENABLE); // Initially disable TxEmpty Interrupt UART_ITConfig(CDC_UART, UART_IT_TXIEN, DISABLE); UART_ITConfig(CDC_UART, UART_OVER_ERR|UART_IT_ERR|UART_IT_PE|0x0040, ENABLE); NVIC_ClearPendingIRQ(CDC_UART_IRQn); UART_Cmd(CDC_UART, ENABLE); return 1; } int32_t uart_uninitialize(void) { UART_Cmd(CDC_UART, DISABLE); UART_ITConfig(CDC_UART, UART_IT_RXIEN|UART_IT_TXIEN, DISABLE); clear_buffers(); return 1; } int32_t uart_reset(void) { uart_initialize(); tx_in_progress = 0; clear_buffers(); return 1; } int32_t uart_set_configuration(UART_Configuration *config) { uint16_t data_bits; uint16_t parity; uint16_t stop_bits; UART_InitTypeDef UART_InitStructure; // Disable uart and tx/rx interrupter UART_Cmd(CDC_UART, DISABLE); UART_ITConfig(CDC_UART, UART_IT_RXIEN|UART_IT_TXIEN, DISABLE); clear_buffers(); //Only 8 bit support data_bits = UART_WordLength_8b; configuration.DataBits = UART_DATA_BITS_8; // parity configuration.Parity = config->Parity; if(config->Parity == UART_PARITY_ODD) parity = UART_Parity_Odd; else if(config->Parity == UART_PARITY_EVEN) parity = UART_Parity_Even; else if(config->Parity == UART_PARITY_NONE) parity = UART_Parity_No; else { //Other not support parity = UART_Parity_No; configuration.Parity = UART_PARITY_NONE; } // stop bits configuration.StopBits = config->StopBits; if(config->StopBits == UART_STOP_BITS_2) stop_bits = UART_StopBits_2; else if(config->StopBits == UART_STOP_BITS_1) stop_bits = UART_StopBits_1; else { stop_bits = UART_StopBits_1; configuration.StopBits = UART_STOP_BITS_1; } configuration.Baudrate = config->Baudrate; configuration.FlowControl = UART_FLOW_CONTROL_NONE; // UART_InitStructure.UART_BaudRate = config->Baudrate; UART_InitStructure.UART_WordLength = data_bits; UART_InitStructure.UART_StopBits = stop_bits; UART_InitStructure.UART_Parity = parity; UART_InitStructure.UART_HardwareFlowControl = UART_HardwareFlowControl_None; UART_InitStructure.UART_Mode = UART_Mode_Rx | UART_Mode_Tx; UART_Init(CDC_UART, &UART_InitStructure); // Enable RX interrupt UART_ITConfig(CDC_UART, UART_IT_RXIEN, ENABLE); // Initially disable TxEmpty Interrupt UART_ITConfig(CDC_UART, UART_IT_TXIEN, DISABLE); UART_Cmd(CDC_UART, ENABLE); return 1; } int32_t uart_get_configuration(UART_Configuration *config) { config->Baudrate = configuration.Baudrate; config->DataBits = configuration.DataBits; config->Parity = configuration.Parity; config->StopBits = configuration.StopBits; config->FlowControl = UART_FLOW_CONTROL_NONE; return 1; } int32_t uart_write_free(void) { return circ_buf_count_free(&write_buffer); } int32_t uart_write_data(uint8_t *data, uint16_t size) { uint32_t cnt; cnt = circ_buf_write(&write_buffer, data, size); // Atomically enable TX if(!tx_in_progress) { // Wait for tx is free //while(USART_GetITStatus(CDC_UART, UART_IT_TXIEN) == RESET); tx_in_progress = 1; UART_SendData(CDC_UART, circ_buf_pop(&write_buffer)); // Enale tx interrupt UART_ITConfig(CDC_UART, UART_IT_TXIEN, ENABLE); } return cnt; } int32_t uart_read_data(uint8_t *data, uint16_t size) { return circ_buf_read(&read_buffer, data, size); } void uart_enable_flow_control(bool enabled) { // Flow control not implemented for this platform } void CDC_UART_IRQn_Handler(void) { CDC_UART_CallBack(); } int32_t UART_GetCommunicationErrorStatus (void) { int32_t err = 0; if (StatusRegister & UART_IT_PE) err |= UART_PARITY_ERROR_Msk; if (StatusRegister & UART_OVER_ERR) err |= UART_OVERRUN_ERROR_Msk; if (BreakFlag == 0 && (StatusRegister & UART_IT_ERR)) err |= UART_PARITY_ERROR_Msk; return (err); } int32_t UART_GetStatusLineState (void) { return (0); } int32_t UART_GetBreak (void) { return (BreakFlag); } /* Check if status has changed and if so, send notify to USB Host on Int EP */ void NotifyOnStatusChange (void) { static int32_t old_notify = -1; int32_t status, notify = 0; status = UART_GetCommunicationErrorStatus(); if (status & UART_OVERRUN_ERROR_Msk) notify |= CDC_SERIAL_STATE_OVERRUN; if (status & UART_PARITY_ERROR_Msk ) notify |= CDC_SERIAL_STATE_OVERRUN; if (status & UART_FRAMING_ERROR_Msk) notify |= CDC_SERIAL_STATE_FRAMING; status = UART_GetStatusLineState(); if (status & UART_STATUS_LINE_RI_Msk ) notify |= CDC_SERIAL_STATE_RING; if (status & UART_STATUS_LINE_DSR_Msk) notify |= CDC_SERIAL_STATE_TX_CARRIER; if (status & UART_STATUS_LINE_DCD_Msk) notify |= CDC_SERIAL_STATE_RX_CARRIER; if (UART_GetBreak()) notify |= CDC_SERIAL_STATE_BREAK; #ifdef CDC_ENDPOINT if (notify ^ old_notify) // If notify changed { if (USBD_CDC_ACM_Notify (notify)) // Send new notification old_notify = notify; } #endif } int32_t UART_ReadData (uint8_t *data, uint16_t size) { int32_t cnt = 0; while (size != 0) { --size; if (RdBuffer.cnt_in != RdBuffer.cnt_out) { *data++ = RdBuffer.data[RdBuffer.idx_out++]; RdBuffer.idx_out &= (USART_BUFFER_SIZE - 1); RdBuffer.cnt_out++; cnt++; } } return (cnt); } void Uart_Put_Char (char ch) { while((CDC_UART->CSR&UART_IT_TXIEN)==0); CDC_UART->TDR = (ch & (uint16_t)0x00FF); } void Uart_PutBuff (uint8_t *buff, uint32_t len) { while(len--) { Uart_Put_Char(*buff); buff++; } } void USBD_CDC_TASK(void) { uint8_t i,count; NotifyOnStatusChange(); if(CDC_UART ->ISR &0x08) { CDC_UART ->GCR &= ~(3 << 3); CDC_UART ->GCR = 3 << 3; UART_ClearITPendingBit(CDC_UART,UART_OVER_ERR); } // USB -> UART if(EP2ReceiveFlag == 1) { EP2ReceiveFlag = 0; Uart_PutBuff(EP2RXBuff,RxBufLen); } // UART -> USB if(EP2TransferFlag == 1) { if(TxBufLen > 0) { while(USB->rEP2_CTRL & 0x80); if(TxBufLen > 64) { UART_ReadData(EP2TXBuff,64); count = 64; TxBufLen -= 64; } else { UART_ReadData(EP2TXBuff,TxBufLen); count = TxBufLen; TxBufLen = 0; } u***_buf_busy_flag = 1; for(i = 0;i < count;i++) { USB->rEP2_FIFO = *(EP2TXBuff + i); } if((USB ->rEP2_AVIL&0x3f) == count) { USB->rEP2_CTRL = 0x80|count; } else { USB->rTOP |= 1<<3; USB->rTOP &= ~(1<<3); } USB->rEP2_CTRL = 0x80|count; if(0 == TxBufLen) EP2TransferFlag = 0; } } } void CDC_UART_CallBack(void) { uint8_t ch; int16_t len_in_buf; StatusRegister = CDC_UART->ISR; if(StatusRegister&0x78) { CDC_UART->ICR = 0xFF; CDC_UART->ICR; GPIOA ->ODR ^= 1; CDC_UART->GCR &= 0xFFE7; CDC_UART->GCR |= 0x18; } if (UART_GetITStatus(CDC_UART, UART_IT_RXIEN) != RESET) { UART_ClearITPendingBit(CDC_UART,UART_IT_RXIEN); len_in_buf = RdBuffer.cnt_in - RdBuffer.cnt_out; if (len_in_buf < USART_BUFFER_SIZE) { ch = (uint8_t)UART_ReceiveData(CDC_UART); TxBufLen ++; RdBuffer.data[RdBuffer.idx_in++] = ch; if ((ch == 0)&& UART_GetFlagStatus(CDC_UART, UART_CSR_RXAVL)) { BreakFlag = 1; } else { BreakFlag = 0; } RdBuffer.idx_in &= (USART_BUFFER_SIZE - 1); RdBuffer.cnt_in++; } else { RdBuffer.cnt_in =0; RdBuffer.cnt_out=0; UART_ReceiveData(CDC_UART); } } } 如上,我们就完成MM32 MCU的CDC功能,将程序下载到MCU中,插上USB线,然后在电脑的设备管理器的端口栏就可以找到对应的USB CDC枚举模拟串口设备。 |
|
相关推荐
|
|
只有小组成员才能发言,加入小组>>
2249个成员聚集在这个小组
加入小组灵动微电子MM32全系列MCU产品应用手册,库函数和例程和选型表
11693 浏览 3 评论
【MM32 eMiniBoard试用连载】+基于OLED12864的GUI---U8G2
5927 浏览 1 评论
【MM32 eMiniBoard试用连载】移植RT-Thread至MM32L373PS
10963 浏览 0 评论
【MM32 eMiniBoard测评报告】+ 开箱 + 初探
4576 浏览 1 评论
灵动微课堂(第106讲) | MM32 USB功能学习笔记 —— WinUSB设备
4300 浏览 1 评论
[MM32软件] MM32F002使用内部flash存储数据怎么操作?
974浏览 1评论
798浏览 0评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-26 07:14 , Processed in 0.396115 second(s), Total 39, Slave 31 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号