【STM32】使用STM32CubeMX配置串口DMA实现高效不定长数据接收与IDLE检测
本文主要讲解如何配置串口使能IDLE空闲帧中断使用DMA来搬运接收到的数据。
一、大致原理
当接收器连续接收到一个完整的数据帧后(例如在8N1模式下,即1个起始位、8个数据位、1个停止位),如果接收线上没有立即出现下一个起始位,且信号保持空闲状态(逻辑高电平)超过至少一个字符的传输时间(即“帧时间”),则会触发IDLE中断。
DMA的Normal模式和circular模式的区别:
Normal模式(普通模式):
在Normal模式下,DMA会按照预先设定的数据传输长度(NDTR寄存器设置的值)进行传输,传输完毕后停止工作,不再自动重启。也就是说,完成一次完整的数据搬运后,DMA停止传输,需要软件重新启动DMA才能进行下一次传输。
Circular模式(循环模式):
在Circular模式下,DMA在完成一次数据传输后,会自动重新加载NDTR寄存器的初始值,从而重新开始传输。这意味着DMA会以循环的方式不断地搬运数据,适合用于连续数据流的应用场景(如音频采集、连续串口接收、ADC连续采样等)。
二、操作步骤
1.stm32cubemx操作
1.1使能串口
创建项目、时钟配置和项目管理配置步骤我就跳过了,大家根据自己的型号来进行配置。
首先先使能串口为异步模式。选择步骤2的对应串口,配置步骤3的串口模式为异步模式,第4个框的内容不用修改。
第四个框的配置内容解释如下:
Basic Parameters(基本参数)
Baud Rate(波特率):115200 Bits/s
Word Length(数据位长度): 8 Bits (including Parity)
Parity(奇偶校验位):无
Stop Bits(停止位) : 1位
Advanced Parameters(高级参数)
Data Direction(数据方向)
Receive and Transmit(接收和发送)
说明该串口既可以进行数据接收,也可以进行数据发送。
适用于全双工或半双工通信场景。
Over Sampling(过采样率)
16 Samples
说明 UART 采用 16 倍过采样率(通常有 8 或 16 可选)。
16 倍过采样能提供更好的抗噪能力,使数据采样更加稳定。
但在某些低功耗应用中,可能会选择 8 倍过采样,以降低功耗和提高吞吐率。
1.2使能串口接收DMA
1.3选择循环模式
使能串口中断, 然后生成代码
2.keil代码编写
1.1在main.h文件中添加缓存大小#define
/* Exported constants --------------------------------------------------------*/
/* USER CODE BEGIN EC */
#define RX_BUFFER_SIZE 256
/* USER CODE END EC */
1.2在main.c文件的代码块块中分别添加
{
/* USER CODE BEGIN PV */
uint8_t uart_rx_buffer[RX_BUFFER_SIZE]; // DMA搬运目标缓存
/* USER CODE END PV */
}
{
/* USER CODE BEGIN 2 */
__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); // 使能串口IDLE中断
HAL_UART_Receive_DMA(&huart1, uart_rx_buffer, RX_BUFFER_SIZE); // 开启串口接收DMA
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
1.3在stm32f4xx_it.c添加代码
/**
* @brief This function handles USART1 global interrupt.
*/
void USART1_IRQHandler(void)
{
/* USER CODE BEGIN USART1_IRQn 0 */
if(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE) != RESET){ // 判断是否产生IDLE中断
__HAL_UART_CLEAR_IDLEFLAG(&huart1); // 清除IDLE中断标志位
HAL_UART_DMAStop(&huart1); // 停止DMA接收
uint16_t received_length = RX_BUFFER_SIZE - __HAL_DMA_GET_COUNTER(huart1.hdmarx); // 计算实际接收到的数据长度
// 业务代码
HAL_UART_Transmit(&huart1, uart_rx_buffer, received_length, 100); // 发送接收到的数据
HAL_UART_Receive_DMA(&huart1, uart_rx_buffer, RX_BUFFER_SIZE); // 开启DMA接收
}
/* USER CODE END USART1_IRQn 0 */
HAL_UART_IRQHandler(&huart1);
/* USER CODE BEGIN USART1_IRQn 1 */
/* USER CODE END USART1_IRQn 1 */
}
以下是实验结果:
作者:来块小鱼饼干(≧^.^≦)