MM32 MCU USB的CDC功能
0 2023-03-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)

{

usbd_init();  // USB Device Initialization and connect

usbd_connect(__TRUE);

while (!usbd_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 \n"

#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 GCR = 3  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;

}

usb_buf_busy_flag = 1;

for(i = 0;i rEP2_FIFO = *(EP2TXBuff   i);

}

if((USB ->rEP2_AVIL&0x3f) == count)

{

USB->rEP2_CTRL = 0x80|count;

}      

else

{

USB->rTOP |= 1rTOP &= ~(1rEP2_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

如上,我们就完成MM32 MCU的CDC功能,将程序下载到MCU中,插上USB线,然后在电脑的设备管理器的端口栏就可以找到对应的USB CDC枚举模拟串口设备。

图1 PC设备管理器列表

用串口助手打开虚拟串口,TX接RX测试发送数据,结果如下:

图2 串口收发数据通信
声明: 本文转载自其它媒体或授权刊载,目的在于信息传递,并不代表本站赞同其观点和对其真实性负责,如有新闻稿件和图片作品的内容、版权以及其它问题的,请联系我们及时删除。(联系我们,邮箱:evan.li@aspencore.com )
0
评论
  • 【6.29直播】西门子EDA工具在3D IC设计中的应用


  • 相关技术文库
  • 单片机
  • 嵌入式
  • MCU
  • STM
  • 怎样才能快速学习8051单片机

    [导读]单片机是微机的一种,现时有8051、AVR、ARM7、ARM9等系列,其中 “8051”是源自于Intel公司的MCS-51系列芯片,但目前不同生产厂商

    前天
  • 什么是DDR?DDR的分类有哪些?

    存储器可分为易失性存储器和非易失性存储器两类,前者在掉电后会失去记忆的数据,后者即使在切断电源也可以保持数据

    05-31
  • 51单片机RAM 数据存储区、位寻址区、数据缓冲区

    [导读]1.RAM keil C语言编程RAM是程序运行中存放随机变量的数据空间。在keil中编写程序,如果当前模式为small模式,如果总的变量大小未超过12

    05-30
  • STM32使用库函数驱动LED灯编写程序步骤

    [导读] 一、熟悉GPIO结构体以下这个结构体是我从官方手册中获取的:[cpp] view plain copy print?typedef struct{u1

    05-30
  • 你知道Linux下的ds18b20驱动吗?

    [导读]今天在各位前辈已有成就的基础上花了两天时间终于把这个驱动给搞定了,从开始编译成模块看效果,进行调试,再到编译进内核,最后又编译了一个界面出来,虽说大多数

    05-29
  • 一文区分AT89C51和AT89C52

    [导读]AT89C51和AT89C52是单片机的两种型号。主要区别是容量不同。at89c51最多支持4KB的程序,at89c52则最多支持8KB的程序。

    05-29
  • 功能强大的时钟中断应用分析

    [导读]在单片机程序设计中,设置一个好的时钟中断,将能使一个CPU发挥两个CPU的功效,大大方便和简化程序的编制,提高系统的效率与可操作性。我们可以把一些例行的

    05-29
  • 一文告诉你MCS-51单片机有几个工作寄存器?

    [导读]工作寄存器有4组,每组都是8个工作寄存器R0~R7,通过PSW中的RS1、RS0两位来选择使用哪一组,如果不选,默认是选择第0组。

    05-29
  • 一文详解STC89C52单片机

    [导读]STC89C52单片机简介

    05-29
  • AVR单片机时熔丝位配置出现的一些问题分析

    [导读]AVR单片机的熔丝位配置是AVR单片机初学者很容易出错的地方,其实只要注意一些事项,还是能够尽量避免单片机被锁死,即使单片机被锁死,也可以使用一些方法解

    05-29
  • 一文详解STC89C52处理芯片

    [导读]主要性能: 与MCS-51单片机产品兼容 、8K字节在系统可编程Flash存储器、 1000次擦写周期、全静态操作:0Hz~33Hz 、三级加密程序存储

    05-29
  • 一文详细分析51单片机中断问题

    [导读]该寄存器用于设置定时/计数器的工作方式,低四位用于定时器0,高四位用于定时器1。 GATE:门控位。GATE=0时,只要用软件使TCON中的TR0或

    05-29
下载排行榜
更多
广告