在嵌入式开发领域,串口通信是最基础却又最考验工程师功底的技能之一。以下我将分享我在实际项目中的串口开发经验,如何去构建工业级串口通信框架。

一、传统方案

很多初学者停留在基本的轮询或字节中断等低效处理方式,面对复杂场景(高速率、多任务、实时性)时往往就不太适用。

我们的高效在哪里? 不急,先来看看传统方案怎么做的:

1. 轮询

while(1) {
    if(USART_GetFlagStatus(USART1, USART_FLAG_RXNE)) {
        uint8_t data = USART_ReceiveData(USART1);
        // 处理接收到的字节
    }
    // 其他任务...
}

核心弊端:

  • CPU资源浪费:无数据时CPU也在空转
  • 数据丢失风险:轮询间隔若大于串口字节间隔,会导致数据丢失
  • 实时性差:响应时间取决于其他任务执行时间

2. 字节中断

void USART1_IRQHandler(void) {
    if(USART_GetITStatus(USART1, USART_IT_RXNE)) {
        uint8_t data = USART_ReceiveData(USART1);
        // 处理接收到的字节
    }
}

核心弊端:

  • CPU 负载超标:每来一个字节就要进出一次中断,115200波特率下1秒钟岂不是要进出一万多次
  • 数据帧解析复杂:需要一个字节一个字节去拼接来解析协议桢

3. DMA直接处理

void DMA1_Channel5_IRQHandler(void) {
	if (DMA_GetITStatus(DMA1_IT_TC5)) {  
		DMA_ClearITPendingBit(DMA1_IT_TC5);
		
		Process_Received_Data(rx_buf, RX_BUF_SIZE);  //处理DMA缓冲区数据

		DMA_SetCurrDataCounter(DMA1_Channel5, RX_BUF_SIZE); 
		DMA_Cmd(DMA1_Channel5, ENABLE);
	}
}

核心弊端: 如果 数据处理速度 < 数据接收速度,导致数据覆盖丢失

二、本方案设计

1. 方案介绍

DMA(DMA半/全传输中断) + 串口空闲中断 + 环形队列

名词解释:

  1. 串口空闲中断:仅在接收数据后总线变为空闲时触发。 那么空闲多久再触发呢?空闲时间的定义是达到 1 个字节的传输时间,如果是 1 起始位 + 8 数据位 + 1 停止位,那么空闲时间 t = 10bit / 波特率。 9600bps下≈1ms 115200bps≈87us

  2. DMA半传输中断:DMA 传输完成一半数据时触发。例如DMA缓冲区大小为1024,那么在传输到512字节时会触发半传输中断

  3. DMA全传输中断:DMA 完成所有数据传输时触发。

    Tip: 像DMA半/全传输中断更多的常同于双缓冲机制。 什么是双缓冲机制?也就是将DMA缓冲区设定为二维数组buf[2][N],在DMA开始工作是会先将数据存放到buf[0]中,当触发DMA半传输中断时,即表示buf[0]已存满,系统会转到buf[1]中缓存,这时你就可以及时处理buf[0]中的数据了,当触发DMA全传输中断时,即表示buf[1]已存满,系统会转到buf[0]中缓存,这时你就可以及时处理buf[1]中的数据了。即实现无缝数据流采集和并行处理。

现在讲一下:如何接收一帧完整数据?流程是怎么样的?
在这里插入图片描述
分三种情况讲:

  • 第一种: 0 < 一帧的数据量大小 < 512, --------处理方式:串口空闲中断
  • 第二种:512 < 一帧的数据量大小 < 1024, --------处理方式:DMA半传输中断 + 串口空闲中断
  • 第三种:1024 < 一帧的数据量大小 , --------处理方式:DMA半传输中断 + DMA全传输中断 + 串口空闲中断

2. 代码设计

#define DMA_RX_BUF_SIZE 1024
static uint8_t dma_rx_buf[DMA_RX_BUF_SIZE];  //DMA缓冲区
static uint16_t last_dma_rx_size = 0;        //记录上一次接收到的数据量大小

static uint8_t queue_buf[DMA_RX_BUF_SIZE];   //队列缓冲区
static ring_queue_t queue_rx = {             //环形队列
	.wrIdx = 0,
	.rdIdx = 0,
	.size = DMA_RX_BUF_SIZE,
	.buf = queue_buf ,
};


static uint16_t uart_get_dma_rx_buf_remain_size(void)
{
	return DMA_GetCurrDataCounter(DMA1_Channel5);	  //返回DMA缓冲区中未使用的空间大小  
}	


//DMA中断处理函数
void DMA1_Channel5_IRQHandler(void)
{
	if(DMA_GetIntStatus(DMA1_INT_TXC5, DMA1))   //DMA全传输中断
	{
		dmarx_done_isr();  //DMA全传输中断的处理函数
		DMA_ClearFlag(DMA1_FLAG_TC5, DMA1); //清除标志位
	}
	if(DMA_GetIntStatus(DMA1_INT_HTX5, DMA1))  //DMA半传输中断
	{
		dmarx_half_done_isr(); //DMA半传输中断的处理函数
		DMA_ClearFlag(DMA1_FLAG_HT5, DMA1); //清除标志位
	}
}

//串口中断处理函数
void USART1_IRQHandler(void)
{
	if(USART_GetIntStatus(USART1, USART_INT_IDLEF) != RESET)  //串口空闲中断
	{
		uart_idle_isr();	 //串口空闲中断的处理函数
		USART_ClrIntPendingBit(USART1, USART_INT_IDLEF); //清除标志位
	}
}


//DMA全传输中断 处理函数
static void dmarx_done_isr(void)   
{
  	uint16_t handle_size;
	handle_size = DMA_RX_BUF_SIZE - last_dma_rx_size;  

    write_block_queue(&queue_rx, (const uint8_t *)&dma_rx_buf[last_dma_rx_size], handle_size);
 	last_dma_rx_size = 0;
}

//                  0_ _ _ _ _ _ _512_ _ _ _ _ _ _1024 

//DMA半传输中断 处理函数
static void dmarx_half_done_isr(void) 
{
  	uint16_t recv_total_size;    // 总接收大小
  	uint16_t handle_size;        // 本段需要处理的数据量大小     

    recv_total_size = DMA_RX_BUF_SIZE - uart_get_dma_rx_buf_remain_size();	 
    handle_size = recv_total_size - last_dma_rx_size;

	write_block_queue(&queue_rx, (const uint8_t *)&dma_rx_buf[last_dma_rx_size], handle_size);
	last_dma_rx_size = recv_total_size;
}

//串口空闲中断 处理函数
static void uart_idle_isr(void)   
{
  	uint16_t recv_total_size;    // 总接收大小
  	uint16_t handle_size;        // 本段需要处理的数据量大小             
  	
	recv_total_size = DMA_RX_BUF_SIZE - uart_get_dma_rx_buf_remain_size();	 
	handle_size = recv_total_size - last_dma_rx_size;
	write_block_queue(&queue_rx, (const uint8_t *)&dma_rx_buf[last_dma_rx_size], handle_size);
	last_dma_rx_size = recv_total_size;
}

3. 视频讲解

嵌入式串口通信高阶实战(1/3)——DMA+串口空闲中断+环形队列

嵌入式串口通信高阶实战(1/3)——DMA+串口空闲中断+环形队列

三、技术交流

感兴趣同学联系主页wx(Lntt-xbc)进交流群

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐