目录
前言
一、固定长度数据帧
1.原理
2.代码示例(STM32 HAL库)
3.优缺点
二、特定结束符
1.原理
2.代码示例(STM32 HAL库)
3.优缺点
三、使用UART IDLE中断 + DMA
1.原理
2.代码示例(STM32 HAL库)
总结
前言
在STM32的开发过程中,使用DMA(直接内存访问)配合串口进行数据传输是一种高效的方式。然而,对于串口从机而言,准确判断一条数据接收完成是数据处理的关键缓解。本文将介绍几种常见的判断方法,帮助大家更好地理解和应用。
一、固定长度数据帧
1.原理
这种方法基于预先约定好的固定长度数据帧。从机在启动DMA接收时,会配置DMA接收指定数量的数据。当DMA接收到的数据量达到这个预设值时,就可以认为一条数据接收完成。这种方式简单直接,适用于数据长度固定的应用场景。
2.代码示例(STM32 HAL库)
#define DATA_LENGTH 10 //假设固定长度为10字节
u8 rx_buffer[DATA_LENGTH];//初始化DMA接收
HAL_UART_Receive_DMA(&huart1,rx_buffer,DATA_LENGTH);//检查DMA是否传输完成
if(HAL_DMA_GetState(&hdma_usart1_rx))==HAL_DMA_STATE_READY){//数据接收完成,进行处理//...
}
if(HAL_DMA_GetState(&hdma_usart1_rx))==HAL_DMA_STATE_READY) 用于判断USART1的接收DMA通道是否已经完成了数据接收任务,并且准备好进行下一次接收操作。如果条件成立,说明DMA通道已经就绪,可以进行新的数据接收;如果条件不成立,则表示DMA通道可能还在进行数据传输或者处于其它状态。
3.优缺点
优点:实现简单,不需要额外的协议或者标志位。只要数据长度固定,就可以稳定的判断数据接收完成。
缺点:缺乏灵活性。如果数据长度需要动态变化,这种方法就无法满足需求。
二、特定结束符
1.原理
在数据帧的末尾添加一个特定的结束符,从机在接收数据时,通过DMA不断将数据接收到缓存区,同时检查是否接收到结束符。一旦接收到结束符,就认为一条数据接收完成。这种方式适用于长度不固定的场景。
2.代码示例(STM32 HAL库)
#define BUFFER_SIZE 100
u8 rx_buffer[BUFFER_SIZE];
u8 end_char = '\n'; // 结束符
volatile u8 data_ready = 0; // 数据接收完成标志// 启动DMA接收
HAL_UART_Receive_DMA(&huart1, rx_buffer, BUFFER_SIZE);// DMA接收完成回调
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {// 遍历缓冲区检查结束符for (int i = 0; i < BUFFER_SIZE; i++) {if (rx_buffer[i] == end_char) {// 处理数据(例如:前i+1个字节)ProcessReceivedData(rx_buffer, i + 1);data_ready = 1;break;}}// 如果未找到结束符,处理整个缓冲区if (!data_ready) {ProcessReceivedData(rx_buffer, BUFFER_SIZE);}// 重置标志并重新启动DMA接收data_ready = 0;HAL_UART_Receive_DMA(&huart1, rx_buffer, BUFFER_SIZE);
}// 处理数据函数
void ProcessReceivedData(u8 *data, u16 length) {// 实现数据处理逻辑
}
3.优缺点
优点:
减少DMA重启次数,提高性能。
支持不定长数据(通过结束符判断)。
缺点:需要遍历整个缓冲区,可能增加处理延迟,如果数据中可能包含与结束符相同的字符,需要进行额外的转义处理。
三、使用UART IDLE中断 + DMA
1.原理
IDLE中断是UART的一个特性,当UART总线在一段时间内(通常是一个字节的传输时间)没有接收到新的数据时,会触发IDLE中断。当UART检测到总线空闲时,触发IDLE中断。在中断中,可以计算接收到的数据长度,并处理数据。处理完数据后,重新启动DMA接收,继续接收下一帧数据。
2.代码示例(STM32 HAL库)
#define BUFFER_SIZE 100
u8 rx_buffer[BUFFER_SIZE];void UART_INIT(void){//配置UART参数//...//启动UART IDLE中断__HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE);//启用IDLE中断//启动DMA接收HAL_UART_Receive_DMA(&huart1,rx_buffer,BUFFER_SIZE);}void HAL_UART_IdleCallback(UART_HandleTypeDef *huart){if(huart->Instance==USART1){//检查是哪个UART触发的IDLE中断//计算已接收的数据长度 hurt1.hdmarx->Instance->NDTR 为剩余的传输长度uint16_t data_length=BUFFER_SIZE-hurt1.hdmarx->Instance->NDTR;//处理接收到的数据ProcessReciveData(rx_buffer,data_length);//重新启动DMA接收HAL_UART_Receive_DMA(&huart1,rx_buffer,BUFFER_SIZE);}}void USART1_IRQHandler(void) {HAL_UART_IRQHandler(&huart1); // 调用HAL库的中断处理函数// 检查是否是IDLE中断if (__HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE)) {__HAL_UART_CLEAR_IDLEFLAG(&huart1); // 清除IDLE标志HAL_UART_IdleCallback(&huart1); // 调用IDLE回调函数}
}
优点:
实时性高,总线空闲时立即处理数据。
无需频繁重启DMA。
缺点:
需要配置IDLE中断,增加代码复杂度。
总结
总体来说,我更倾向于第三种方法,因为好多时候我们对接第三方协议不能够保证数据接收的长度以及对方提供结束符,IDLE中断的方式不管接收哪种数据都能接收完整,注意接收不要超出数组的长度就可以了。