串口接收中断+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
