串口接收中断+DMA操作流程
在嵌入式系统中,串口接收中断结合DMA(直接内存访问)是一种高效的数据接收方式。这种方式允许数据直接从串口外设传输到内存,而无需CPU干预,仅在传输完成或出现错误时触发中断。下面详细介绍其操作流程和实现方法:
一、基本原理
- 串口接收中断当串口接收到数据(如一个字节)时,触发接收中断。CPU暂停当前任务,执行中断服务函数(ISR)处理接收到的数据。
- DMA(直接内存访问)硬件机制,允许外设(如串口)直接与内存交换数据,无需CPU参与。适用于批量数据传输,可显著提高系统效率。
- 结合优势DMA负责数据传输:将接收到的数据直接存入内存缓冲区,不占用CPU资源。中断处理关键事件:仅在传输完成、缓冲区溢出或出错时触发中断,减少CPU负担。
二、操作流程
1. 初始化阶段
- 配置串口设置波特率、数据位、停止位等参数。启用接收功能。
- 配置DMA设置源地址(串口接收缓冲区)和目标地址(内存缓冲区)。配置传输方向(外设→内存)、数据宽度(字节/半字/字)和传输模式(单次/循环)。启用DMA传输完成中断和错误中断。
- 配置中断使能串口接收DMA请求。配置NVIC(嵌套向量中断控制器),设置DMA中断优先级。
2. 数据接收阶段
- DMA传输串口接收到数据后,自动触发DMA传输。DMA将数据从串口接收缓冲区复制到内存缓冲区,无需CPU干预。
- 中断触发条件传输完成中断:当DMA完成预设的传输次数时触发。半传输中断(可选):当传输达到缓冲区一半时触发,适用于双缓冲区实现。错误中断:如溢出、帧错误等异常情况触发。
- 中断服务函数(ISR)处理检查中断标志位,确定中断类型。处理接收数据(如解析协议、更新状态)。清除中断标志位,准备下一次传输。
三、代码实现示例(STM32 HAL库)
以下是基于STM32 HAL库的串口接收中断+DMA实现:
#include "stm32f4xx_hal.h" // 定义接收缓冲区和长度 #define BUFFER_SIZE 256 uint8_t rx_buffer[BUFFER_SIZE]; // 串口和DMA句柄 UART_HandleTypeDef huart1; DMA_HandleTypeDef hdma_usart1_rx; // 初始化函数 void SystemInit(void) { // 系统时钟配置 // ... } void MX_USART1_UART_Init(void) { // 配置串口 huart1.Instance = USART1; huart1.Init.BaudRate = 115200; huart1.Init.WordLength = UART_WORDLENGTH_8B; huart1.Init.StopBits = UART_STOPBITS_1; huart1.Init.Parity = UART_PARITY_NONE; huart1.Init.Mode = UART_MODE_RX; huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE; huart1.Init.OverSampling = UART_OVERSAMPLING_16; HAL_UART_Init(&huart1); } void MX_DMA_Init(void) { // 配置DMA __HAL_RCC_DMA2_CLK_ENABLE(); hdma_usart1_rx.Instance = DMA2_Stream2; hdma_usart1_rx.Init.Channel = DMA_CHANNEL_4; hdma_usart1_rx.Init.Direction = DMA_PERIPH_TO_MEMORY; hdma_usart1_rx.Init.PeriphInc = DMA_PINC_DISABLE; hdma_usart1_rx.Init.MemInc = DMA_MINC_ENABLE; hdma_usart1_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; hdma_usart1_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; hdma_usart1_rx.Init.Mode = DMA_CIRCULAR; // 循环模式,接收满后自动从头开始 hdma_usart1_rx.Init.Priority = DMA_PRIORITY_LOW; hdma_usart1_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE; HAL_DMA_Init(&hdma_usart1_rx); __HAL_LINKDMA(&huart1, hdmarx, hdma_usart1_rx); } // 中断服务函数 void USART1_IRQHandler(void) { HAL_UART_IRQHandler(&huart1); } void DMA2_Stream2_IRQHandler(void) { HAL_DMA_IRQHandler(&hdma_usart1_rx); } // HAL库回调函数:DMA传输完成时调用 void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if (huart == &huart1) { // 处理接收到的数据 // ... // 继续接收(如果使用非循环模式) // HAL_UART_Receive_DMA(&huart1, rx_buffer, BUFFER_SIZE); } } // HAL库回调函数:DMA半传输完成时调用(可选) void HAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart) { if (huart == &huart1) { // 处理前半部分数据 // ... } } // HAL库回调函数:错误处理 void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart) { if (huart == &huart1) { // 处理错误(如溢出、帧错误等) uint32_t error = HAL_UART_GetError(&huart1); // ... } } int main(void) { HAL_Init(); SystemInit(); MX_USART1_UART_Init(); MX_DMA_Init(); // 启动DMA接收 HAL_UART_Receive_DMA(&huart1, rx_buffer, BUFFER_SIZE); // 使能串口接收中断 __HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); // 可选:启用空闲线中断 while (1) { // 主循环处理其他任务 // ... } }
四、关键配置说明
- DMA模式选择循环模式(Circular Mode):传输完成后自动从头开始,适合连续接收数据。单次模式(Normal Mode):传输完成后停止,需手动重启(如在中断服务函数中调用 HAL_UART_Receive_DMA())。
- 缓冲区管理单缓冲区:简单但可能丢失数据(如处理不及时)。双缓冲区/环形缓冲区:通过半传输中断和传输完成中断切换缓冲区,避免数据覆盖。
- 中断触发方式空闲线中断(IDLE):检测到串口空闲时触发,适用于一帧数据接收完成的判断。DMA传输完成中断:按预设长度接收完成后触发。
五、应用场景
- 大数据量传输:如传感器数据采集、文件传输等。
- 低功耗设计:减少CPU唤醒次数,降低系统功耗。
- 实时性要求高的场景:快速响应数据,减少处理延迟。
通过结合串口接收中断和DMA,可以高效地实现数据接收,同时最大限度减少CPU资源占用,提升系统整体性能。
更多内容全在下方专栏
全网最受欢迎的嵌入式笔试专栏
笔试专栏包含全部最新的笔试必考考点,非常适合在找工作面经薄弱的同学
3000+订阅还会涨价,提前订阅提前享受,持续更新中。
专栏链接:https://www.nowcoder.com/creation/manager/columnDetail/mPZ4kk