stm32 ADC DMA 循环模式配合双缓冲分块(Ping-Pong)

ADC DMA 循环模式配合双缓冲分块(Ping-Pong)处理,核心是利用 DMA 的"半传输完成(HT)"和"全传输完成(TC)"中断,将一块连续的内存逻辑上切分为前后两半。 DMA 在后台循环填充缓冲区时,CPU 可以安全地处理已经填满的那一半,从而实现"采集"与"处理"的并行,避免数据丢失。

核心机制:如何实现 Ping-Pong

你不需要手动去切换 DMA 的目标地址,只需配置一个大小为 2 * N 的数组,并开启 DMA 的循环模式以及 HT/TC 中断:

  1. Ping 阶段(前半缓冲就绪) :当 DMA 填满数组的前 N 个元素时,触发半传输完成中断 。此时 DMA 硬件会自动继续向数组的后半部分写入数据,而 CPU 可以在中断或主循环中安全地读取并处理前半部分 [0] ~ [N-1] 的数据。
  2. Pong 阶段(后半缓冲就绪) :当 DMA 填满整个数组(即后半部分也满了)时,触发全传输完成中断 。此时由于配置了循环模式,DMA 会自动绕回数组开头重新覆盖写入。CPU 此时去处理后半部分 [N] ~ [2N-1] 的数据即可。

关键配置与代码逻辑(以 STM32 HAL 库为例)

1. CubeMX / 初始化配置要点
  • DMA 模式 :必须选择 Circular(循环模式)。
  • 中断 :务必勾选 DMA 的 Half Transfer 和 Transfer Complete 中断。
  • 数据宽度:外设与内存通常都设为 Half Word(16位,对应 12 位 ADC 数据)。
2. 代码实现框架

核心在于回调函数中只做"标记",把耗时的数据处理放在主循环或任务中,确保中断快速返回。

c 复制代码
#define BUF_SIZE 512
uint16_t adc_dma_buf[BUF_SIZE * 2]; // 总大小是 2 倍,逻辑上分两半
volatile uint8_t data_ready_flag = 0; // 0:无数据 1:前半就绪 2:后半就绪

// 半传输完成回调(前半 Ping 就绪)
void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef* hadc) {
    data_ready_flag = 1; 
}

// 全传输完成回调(后半 Pong 就绪)
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) {
    data_ready_flag = 2;
}

int main(void) {
    // ... 初始化 ADC 和 DMA ...
    HAL_ADC_Start_DMA(&hadc1, (uint32_t*)adc_dma_buf, BUF_SIZE * 2);

    while (1) {
        if (data_ready_flag == 1) {
            // 安全处理前半部分:adc_dma_buf[0] 到 adc_dma_buf[BUF_SIZE-1]
            process_data(&adc_dma_buf[0], BUF_SIZE);
            data_ready_flag = 0;
        } 
        else if (data_ready_flag == 2) {
            // 安全处理后半部分:adc_dma_buf[BUF_SIZE] 到 adc_dma_buf[2*BUF_SIZE-1]
            process_data(&adc_dma_buf[BUF_SIZE], BUF_SIZE);
            data_ready_flag = 0;
        }
        // 执行其他任务
    }
}

必须注意的"坑"与优化点

  1. 处理速度必须跟上采集速度 :这是最核心的约束。如果你在处理前半部分数据时耗时过长,导致 DMA 已经填满了后半部分并绕回来开始覆盖前半部分,就会发生数据覆盖 。如果算法复杂,请增大 BUF_SIZE 或降低 ADC 采样率。
  2. 中断标志的清除:在 HAL 库中,回调函数内部通常已经处理了标志位清除。如果你使用寄存器或 LL 库开发,务必在中断服务函数中手动清除 HT 和 TC 标志,否则会无限进中断。
  3. Cache 一致性(针对 Cortex-M7 等高性能系列) :如果你的芯片带有 D-Cache(如 STM32H7),DMA 直接写内存不会更新 Cache。在 CPU 读取 adc_dma_buf 之前,需要调用 SCB_InvalidateDCache_by_Addr 使对应内存区域的 Cache 失效,以确保读到的是 DMA 写入的最新数据。
  4. 硬件双缓冲 vs 软件双缓冲 :部分高端系列(如 STM32F4/G4/H7)的 DMA 支持硬件双缓冲(DBM),可以配置两个独立的内存地址(M0AR/M1AR)自动切换。但上述的"半传输/全传输中断"法兼容性最好,几乎在所有带 DMA 的 STM32 上都通用。
相关推荐
POMAGTOR3 小时前
【磁吸式连接器选型工程解析:结构原理、应用场景与选型可靠性要点】
网络·嵌入式硬件
RFID科技的魅力4 小时前
2026 年 RFID 资产标签批量制作怎么做?RFID 标签打印机实操指南
运维·人工智能·嵌入式硬件·物联网
Sakura_j_h_6 小时前
STM32F103 定时器的三种用法:PWM 输出、输入捕获与编码器接口
stm32·嵌入式·输入捕获·定时器·pwm·编码器·1024程序员节
Lxx_Zw6 小时前
机器周期与共地:时钟基准和电压参考基准
stm32·单片机·嵌入式硬件
我叫洋洋8 小时前
用 Qoder(AI 编程助手)把 Cadence 16.6 从零配到能画板
人工智能·单片机·嵌入式硬件·电路·1024程序员节
Zw-awa8 小时前
LED 灯珠与限流电阻
stm32
深圳老胡9 小时前
STM32 开发中 Trae 内置模型怎么选:Seed、MiniMax、GLM、DeepSeek、Kimi、Qwen 全对比
笔记·stm32·单片机·嵌入式硬件·代码规范·1024程序员节
传感器与混合集成电路9 小时前
多计算节点怎么融合?全国产通用融合计算机三子卡架构与任务配置方法
嵌入式硬件·1024程序员节
西安同步电子11 小时前
时统卡驱动踩坑:Windows 和 Linux 下时间同步的底层差异
linux·stm32·单片机