You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

STM32 ADC DMA双缓冲区(多缓冲区)模式实现方法问询

实现STM32 ADC与DMA双缓冲区协同工作的完整指南

确实,STM32 HAL库提供了HAL_DMAEx_MultiBufferStartHAL_DMAEx_MultiBufferStart_IT这两个双缓冲区DMA函数,但官方SDK里确实没给现成示例,我之前做项目的时候也踩过坑,下面给你一套完整的实现流程和代码示例,亲测在F4、H7系列上都能用。

一、基础配置前提

首先你得在CubeMX里完成ADC和DMA的基础配置:

  • ADC配置:开启连续转换模式、扫描模式(如果是多通道采样),数据对齐选右对齐,触发源按需选软件触发或定时器触发
  • DMA配置:选择对应ADC的数据流,传输方向设为外设到内存,优先级拉高一点,务必关闭循环模式(双缓冲模式会自行处理循环逻辑),数据宽度和ADC配置匹配(比如16位)

二、代码实现步骤

1. 定义双缓冲区和状态变量

在全局或文件私有区域定义两个等长的缓冲区,再加一个标记当前活跃缓冲区的变量:

/* 缓冲区长度根据你的通道数、采样需求调整 */
#define ADC_BUFFER_LEN  1024
uint16_t adc_buffer1[ADC_BUFFER_LEN];
uint16_t adc_buffer2[ADC_BUFFER_LEN];
/* 标记当前DMA正在使用的缓冲区,方便中断里区分处理 */
uint8_t current_active_buffer = 0;

2. 启动双缓冲DMA与ADC

CubeMX会生成MX_ADC1_Init()MX_DMA_Init()这类初始化函数,你只需要在主逻辑里调用双缓冲启动函数:

如果不需要中断(仅后台自动传输),用无中断版本:

// 启动双缓冲DMA传输:参数依次为DMA句柄、ADC数据寄存器地址、缓冲区1地址、缓冲区2地址、传输长度
HAL_DMAEx_MultiBufferStart(&hdma_adc1, (uint32_t)&ADC1->DR, (uint32_t)adc_buffer1, (uint32_t)adc_buffer2, ADC_BUFFER_LEN);
// 启动ADC连续转换
HAL_ADC_Start(&hadc1);

如果需要处理缓冲区满的逻辑(推荐),用带中断的版本:

// 带中断的双缓冲启动,一个缓冲区填满时会触发DMA中断
HAL_DMAEx_MultiBufferStart_IT(&hdma_adc1, (uint32_t)&ADC1->DR, (uint32_t)adc_buffer1, (uint32_t)adc_buffer2, ADC_BUFFER_LEN);
HAL_ADC_Start(&hadc1);

3. 中断回调函数处理

当DMA完成一个缓冲区的传输后,会触发两个不同的回调函数,你需要重写它们来处理数据:

// 第一个缓冲区(Buffer0)传输完成回调
void HAL_DMA_XferCpltCallback(DMA_HandleTypeDef *hdma)
{
  if(hdma->Instance == DMA2_Stream0) // 替换成你的DMA流实例
  {
    current_active_buffer = 0;
    // 这里处理adc_buffer1的数据,比如滤波、上传上位机、计算均值等
    process_adc_data(adc_buffer1, ADC_BUFFER_LEN);
    // 双缓冲模式会自动切换到第二个缓冲区,无需手动重启
  }
}

// 第二个缓冲区(Buffer1)传输完成回调
void HAL_DMA_XferM1CpltCallback(DMA_HandleTypeDef *hdma)
{
  if(hdma->Instance == DMA2_Stream0) // 替换成你的DMA流实例
  {
    current_active_buffer = 1;
    // 处理adc_buffer2的数据
    process_adc_data(adc_buffer2, ADC_BUFFER_LEN);
  }
}

// 可选:DMA错误回调,用于排查传输异常
void HAL_DMA_ErrorCallback(DMA_HandleTypeDef *hdma)
{
  if(hdma->Instance == DMA2_Stream0)
  {
    // 添加错误处理逻辑,比如重启DMA和ADC
    Error_Handler();
  }
}

4. 关键注意事项

  • 两个缓冲区的长度必须完全一致,否则会触发DMA传输错误
  • 确保回调函数里的数据处理耗时小于DMA填满另一个缓冲区的时间,避免数据被覆盖(如果处理耗时过长,可以在回调里仅做数据缓存,主循环再做复杂处理)
  • 不同STM32系列的DMA流、通道对应关系不同,比如F4的ADC1对应DMA2 Stream0 Channel0,H7系列有差异,以CubeMX配置为准
  • 如果用定时器触发ADC,要确保定时器频率和缓冲区长度匹配,避免数据溢出

三、完整流程梳理

  1. 用CubeMX完成ADC和DMA的基础参数配置
  2. 定义两个等长的ADC数据缓冲区
  3. 调用带中断的双缓冲DMA启动函数,同时启动ADC连续转换
  4. 在DMA的两个完成回调里分别处理对应缓冲区的数据
  5. 双缓冲模式下,DMA会自动在两个缓冲区之间循环切换,无需手动重启传输

内容的提问来源于stack exchange,提问作者GAMELASTER

火山引擎 最新活动