STM32F405 ADC+DMA双缓冲规则组采集

STM32F405 ADC+DMA双缓冲规则组采集

  • ✨本文基于AI辅助生成整理。

📋 项目概述

本项目实现基于STM32F405的6通道ADC数据采集系统,采用DMA传输和双缓冲机制,实现高效、稳定的数据采集和串口输出。

主要特性:

  • 6通道同步采集(PA0~PA5)
  • DMA自动数据传输,降低CPU占用
  • 双缓冲机制,提高数据吞吐量
  • 串口实时输出采集结果

⚙️ 硬件配置

🖥️ MCU
参数 规格
型号 STM32F405RGT6
内核 ARM Cortex-M4
主频 168MHz
⏱️ 时钟配置
参数 设置
时钟源 HSE (8MHz 外部晶振)
PLL_M 4
PLL_N 168
PLL_P 2
PLL_Q 4
SYSCLK 168MHz
HCLK (AHB) 168MHz
PCLK1 (APB1) 42MHz (最大42MHz)
PCLK2 (APB2) 84MHz (最大84MHz)
ADC时钟 21MHz (PCLK2/4)
  • 🔖ADC 的时钟频率: ADC 模块的工作时钟,由系统主时钟经分频得到,由 APB2 时钟分频,分频因子可选 2/4/6/8)。


  • ADC工作频率最高:36MHz (超过上限会导致转换稳定性与精度下降)
📊 ADC配置
参数 设置
使用ADC ADC2(独立模式)
模拟通道 PA0 ~ PA5(6通道)
分辨率 12位(0~4095)
采样时间 28个ADC时钟周期
转换模式 连续扫描模式

ADC初始化配置代码(adc.c):

c 复制代码
hadc2.Instance = ADC2;                                   // 使用ADC2
hadc2.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;    // 时钟分频:PCLK2/4 = 21MHz
hadc2.Init.Resolution = ADC_RESOLUTION_12B;              // 分辨率:12位
hadc2.Init.ScanConvMode = ENABLE;                        // 扫描模式:使能(多通道)
hadc2.Init.ContinuousConvMode = ENABLE;                  // 连续转换模式:使能
hadc2.Init.DiscontinuousConvMode = DISABLE;              // 间断转换模式:禁用
hadc2.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;  // 外部触发边沿:无
hadc2.Init.ExternalTrigConv = ADC_SOFTWARE_START;        // 触发源:软件触发
hadc2.Init.DataAlign = ADC_DATAALIGN_RIGHT;              // 数据对齐:右对齐
hadc2.Init.NbrOfConversion = 6;                          // 转换通道数:6个
hadc2.Init.DMAContinuousRequests = ENABLE;               // DMA连续请求:使能
hadc2.Init.EOCSelection = ADC_EOC_SEQ_CONV;              // EOC选择:序列转换完成
  • 🎉:STM32CubeMX里 ADC 12bit "15 ADC clock cycles",并非代表您设置的"采样时间"为 15 个时钟周期,而是指在当前配置下,ADC 完成一次完整转换,最少所需的总时间。结合下面具体下面的通道采样时间,最小设置的采样时间:3 Cycles
  • 🔧ADC 时钟 = 21 MHz,总转换时间 = (采样周期 + 12.5 处理周期) / 21 MHz
🔄 DMA配置
参数 设置
DMA通道 DMA2_Stream2,通道1
数据宽度 半字(16位)
传输模式 正常模式
传输方向 外设到内存
内存增量模式 使能

DMA初始化配置代码(dma.c):

c 复制代码
hdma_adc2.Instance = DMA2_Stream2;                       // DMA流:DMA2_Stream2
hdma_adc2.Init.Channel = DMA_CHANNEL_1;                  // DMA通道:通道1
hdma_adc2.Init.Direction = DMA_PERIPH_TO_MEMORY;         // 传输方向:外设到内存
hdma_adc2.Init.PeriphInc = DMA_PINC_DISABLE;             // 外设地址增量:禁用
hdma_adc2.Init.MemInc = DMA_MINC_ENABLE;                 // 内存地址增量:使能
hdma_adc2.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;  // 外设数据宽度:半字(16位)
hdma_adc2.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;     // 内存数据宽度:半字(16位)
hdma_adc2.Init.Mode = DMA_NORMAL;                        // 传输模式:正常模式
hdma_adc2.Init.Priority = DMA_PRIORITY_MEDIUM;           // 优先级:中等
hdma_adc2.Init.FIFOMode = DMA_FIFOMODE_DISABLE;          // FIFO模式:禁用
📡 串口配置(用于调试输出)
参数 设置
串口 USART1
波特率 115200
数据位 8位
停止位 1位
校验

🏗️ 软件架构

✨ 核心功能
  1. ADC采集:连续采集6个通道的模拟信号
  2. DMA传输:ADC数据自动传输到内存缓冲区
  3. 双缓冲机制:两个缓冲区交替使用,实现零等待数据交换
  4. 串口输出:转换完成后通过串口打印各通道结果
🔀 双缓冲机制原理
复制代码
┌─────────────────────────────────────────────────────────────────┐
│                        双缓冲工作流程                           │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  缓冲区A ←── DMA写入 ←── ADC转换数据                             │
│       │                                                         │
│       │ 转换完成中断触发                                         │
│       ↓                                                         │
│  主循环读取缓冲区B ──→ 串口打印输出                              │
│                                                                 │
│  [中断回调中切换缓冲区]                                           │
│                                                                 │
│  缓冲区B ←── DMA写入 ←── ADC转换数据(下一轮)                    │
│       │                                                         │
│       │ 转换完成中断触发                                         │
│       ↓                                                         │
│  主循环读取缓冲区A ──→ 串口打印输出                              │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

工作时序:

  1. 初始状态:DMA向缓冲区A写入数据,主循环空闲
  2. 转换完成:触发中断,切换DMA目标为缓冲区B
  3. 主循环读取缓冲区A的数据并串口输出
  4. 下一周期:DMA向缓冲区B写入数据,主循环读取缓冲区A
  5. 循环往复,实现不间断数据采集
⚡ 中断触发流程
复制代码
┌──────────────────────────────────────────────────────────────┐
│                   中断触发流程                               │
├──────────────────────────────────────────────────────────────┤
│  1. HAL_ADC_Start_DMA()                                     │
│         ↓                                                   │
│  2. ADC开始连续转换                                          │
│         ↓                                                   │
│  3. DMA将数据传输到内存缓冲区                                 │
│         ↓                                                   │
│  4. 6通道转换完成 → EOC中断 + DMA TC中断                     │
│         ↓                                                   │
│  5. HAL_ADC_ConvCpltCallback() 回调函数                      │
│         ↓                                                   │
│  6. 设置 new_data_flag = 1                                   │
│         ↓                                                   │
│  7. 切换缓冲区索引 current_buf = 1 - current_buf             │
│         ↓                                                   │
│  8. 重新启动DMA转换                                          │
│         ↓                                                   │
│  9. 主循环检测到标志 → 读取数据 → 串口输出                   │
└──────────────────────────────────────────────────────────────┘
🔑 关键变量
变量名 类型 说明
adc_buf[2][6] uint16_t 双缓冲区,存储两组6通道ADC数据
current_buf uint8_t 当前DMA写入的缓冲区索引(0或1)
new_data_flag uint8_t 新数据可用标志(0=无新数据,1=有新数据)

📖 使用说明

🔌 硬件连接
信号 MCU引脚 说明
ADC_IN0 PA0 模拟输入通道0
ADC_IN1 PA1 模拟输入通道1
ADC_IN2 PA2 模拟输入通道2
ADC_IN3 PA3 模拟输入通道3
ADC_IN4 PA4 模拟输入通道4
ADC_IN5 PA5 模拟输入通道5
USART1_TX PA9 串口发送(连接USB转TTL模块的RX)
USART1_RX PA10 串口接收(可选,连接USB转TTL模块的TX)
VREF+ VDDA 参考电压输入(连接稳定3.3V)
GND GND 共地
📤 串口输出格式
复制代码
------------------------
ADC Result:

ADC2 CH0 (PA0): 1270
ADC2 CH1 (PA1): 1287
ADC2 CH2 (PA2): 1262
ADC2 CH3 (PA3): 1249
ADC2 CH4 (PA4): 1259
ADC2 CH5 (PA5): 1306
------------------------
⚙️ 软件配置

编译环境

  • STM32CubeIDE / Keil MDK
  • STM32Cube HAL库

烧录方式

  1. 使用ST-Link连接开发板
  2. 编译工程生成.hex或.bin文件
  3. 通过烧录工具下载到STM32F405
⚠️ 注意事项
  1. 电压范围:模拟输入电压必须在0~3.3V范围内
  2. 参考电压:VDDA引脚必须连接稳定的3.3V电源,建议添加去耦电容
  3. ADC精度:12位分辨率,电压分辨率约为0.8mV(3.3V/4096)
  4. 串口设置:调试工具波特率需设置为115200
  5. 初始化 :主函数中调用 HAL_ADC_Start_DMA()启动采集

💻 核心代码说明

📦 全局变量定义
c 复制代码
#define ADC_CHANNEL_NUM    6              // ADC通道数量
uint16_t adc_buf[2][ADC_CHANNEL_NUM];    // 双缓冲区(2个缓冲区,每区6个通道)
uint8_t current_buf = 0;                  // 当前DMA写入的缓冲区索引
uint8_t new_data_flag = 0;                // 新数据标志
▶️ 启动ADC DMA转换
c 复制代码
// 启动ADC DMA转换,使用缓冲区0作为初始目标
HAL_ADC_Start_DMA(&hadc2, (uint32_t*)adc_buf[current_buf], ADC_CHANNEL_NUM);
🔔 中断回调函数(main.c)
c 复制代码
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
    if(hadc->Instance == ADC2)
    {
        new_data_flag = 1;                // 标记有新数据可用
        current_buf = 1 - current_buf;    // 切换到另一个缓冲区
        // 重新启动DMA,使用新的缓冲区
        HAL_ADC_Start_DMA(&hadc2, (uint32_t*)adc_buf[current_buf], ADC_CHANNEL_NUM);
    }
}
🔄 主循环数据处理
c 复制代码
while (1)
{
    if(new_data_flag)
    {
        uint8_t read_buf = 1 - current_buf;  // 读取非当前写入的缓冲区

        printf("------------------------\r\n");
        printf("ADC Result:\r\n\r\n");
        printf("ADC2 CH0 (PA0): %d\r\n", adc_buf[read_buf][0]);
        printf("ADC2 CH1 (PA1): %d\r\n", adc_buf[read_buf][1]);
        printf("ADC2 CH2 (PA2): %d\r\n", adc_buf[read_buf][2]);
        printf("ADC2 CH3 (PA3): %d\r\n", adc_buf[read_buf][3]);
        printf("ADC2 CH4 (PA4): %d\r\n", adc_buf[read_buf][4]);
        printf("ADC2 CH5 (PA5): %d\r\n", adc_buf[read_buf][5]);

        new_data_flag = 0;  // 清除新数据标志
    }
    // 其他任务...
}

⚡ 性能参数

参数 数值 说明
ADC时钟 21MHz PCLK2=84MHz,4分频
单通道采样时间 ~1.33μs 28个ADC时钟周期
6通道总转换时间 ~10μs 含扫描延迟和采样时间
理论采样率 ~100kSps 6通道扫描模式下的最大速率
实际采样率 ~80-90kSps 受串口输出速度影响
CPU占用率 <5% DMA自动传输,CPU仅处理数据输出

🔧 故障排除

问题现象 可能原因 解决方法
串口无输出 串口波特率不匹配 确认串口工具波特率为115200
ADC值始终为0 模拟输入未连接或接地 检查模拟输入信号连接
ADC值始终为4095 模拟输入超过3.3V或短路到VDD 检查输入电压范围
ADC值波动大 电源不稳定或未加滤波 增加电源去耦电容,使用RC滤波
数据不更新 DMA配置错误 检查DMA通道、流配置和中断使能
中断不触发 NVIC优先级配置错误 确认DMA中断已正确配置
缓冲区数据错误 缓冲区索引逻辑错误 检查 current_buf切换逻辑

📚测试源码

c 复制代码
通过网盘分享的文件:STM32F405_ADC2_DMA.rar
链接: https://pan.baidu.com/s/1H1axZGeUS-1BkPDaBL2eNA?pwd=9tj5 提取码: 9tj5
相关推荐
念恒123063 小时前
STM(GPIO)上篇
stm32·单片机·嵌入式硬件
yanlaifan6 小时前
STM32L011中map文件中内存分析
stm32
朴人7 小时前
【stm32无感FOC理论与实践:滑模观测器】【02 PLL】
stm32·foc·永磁同步电机·pmsm·无感·滑模
念恒123067 小时前
STM32---新建工程
linux·stm32·嵌入式硬件
振南的单片机世界7 小时前
全双工vs半双工vs单工:电话、对讲机、广播
stm32·单片机·嵌入式硬件
笨笨饿7 小时前
#72_聊聊I2C以及他们的变体
linux·c语言·网络·stm32·单片机·算法·个人开发
ghie90908 小时前
4轴运动控制源代码(STM32 + GRBL 1.1移植版)
stm32·单片机·嵌入式硬件
0南城逆流09 小时前
【STM32】RTT-Studio中HAL库开发教程十一:WS2812彩色RGB模块使用
stm32·单片机·嵌入式硬件
恶魔泡泡糖9 小时前
stm32F103C8T6标准库外部中断点灯
stm32·单片机·嵌入式硬件