串口接收中断+DMA操作流程

在嵌入式系统中,串口接收中断结合DMA(直接内存访问)是一种高效的数据接收方式。这种方式允许数据直接从串口外设传输到内存,而无需CPU干预,仅在传输完成或出现错误时触发中断。下面详细介绍其操作流程和实现方法:

一、基本原理

  1. 串口接收中断当串口接收到数据(如一个字节)时,触发接收中断。CPU暂停当前任务,执行中断服务函数(ISR)处理接收到的数据。
  2. DMA(直接内存访问)硬件机制,允许外设(如串口)直接与内存交换数据,无需CPU参与。适用于批量数据传输,可显著提高系统效率。
  3. 结合优势DMA负责数据传输:将接收到的数据直接存入内存缓冲区,不占用CPU资源。中断处理关键事件:仅在传输完成、缓冲区溢出或出错时触发中断,减少CPU负担。

二、操作流程

1. 初始化阶段

  1. 配置串口设置波特率、数据位、停止位等参数。启用接收功能。
  2. 配置DMA设置源地址(串口接收缓冲区)和目标地址(内存缓冲区)。配置传输方向(外设→内存)、数据宽度(字节/半字/字)和传输模式(单次/循环)。启用DMA传输完成中断和错误中断。
  3. 配置中断使能串口接收DMA请求。配置NVIC(嵌套向量中断控制器),设置DMA中断优先级。

2. 数据接收阶段

  1. DMA传输串口接收到数据后,自动触发DMA传输。DMA将数据从串口接收缓冲区复制到内存缓冲区,无需CPU干预。
  2. 中断触发条件传输完成中断:当DMA完成预设的传输次数时触发。半传输中断(可选):当传输达到缓冲区一半时触发,适用于双缓冲区实现。错误中断:如溢出、帧错误等异常情况触发。
  3. 中断服务函数(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) {
    // 主循环处理其他任务
    // ...
  }
}

四、关键配置说明

  1. DMA模式选择循环模式(Circular Mode):传输完成后自动从头开始,适合连续接收数据。单次模式(Normal Mode):传输完成后停止,需手动重启(如在中断服务函数中调用 HAL_UART_Receive_DMA())。
  2. 缓冲区管理单缓冲区:简单但可能丢失数据(如处理不及时)。双缓冲区/环形缓冲区:通过半传输中断和传输完成中断切换缓冲区,避免数据覆盖。
  3. 中断触发方式空闲线中断(IDLE):检测到串口空闲时触发,适用于一帧数据接收完成的判断。DMA传输完成中断:按预设长度接收完成后触发。

五、应用场景

  • 大数据量传输:如传感器数据采集、文件传输等。
  • 低功耗设计:减少CPU唤醒次数,降低系统功耗。
  • 实时性要求高的场景:快速响应数据,减少处理延迟。

通过结合串口接收中断和DMA,可以高效地实现数据接收,同时最大限度减少CPU资源占用,提升系统整体性能。

更多内容全在下方专栏

全网最受欢迎的嵌入式笔试专栏

笔试专栏包含全部最新的笔试必考考点,非常适合在找工作面经薄弱的同学

3000+订阅还会涨价,提前订阅提前享受,持续更新中。

专栏链接:https://www.nowcoder.com/creation/manager/columnDetail/mPZ4kk

全部评论

相关推荐

点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务