STM32F405 ADC+DMA双缓冲规则组采集
📋 项目概述
本项目实现基于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位 |
| 校验 |
无 |
🏗️ 软件架构
✨ 核心功能
- ADC采集:连续采集6个通道的模拟信号
- DMA传输:ADC数据自动传输到内存缓冲区
- 双缓冲机制:两个缓冲区交替使用,实现零等待数据交换
- 串口输出:转换完成后通过串口打印各通道结果
🔀 双缓冲机制原理
复制代码
┌─────────────────────────────────────────────────────────────────┐
│ 双缓冲工作流程 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 缓冲区A ←── DMA写入 ←── ADC转换数据 │
│ │ │
│ │ 转换完成中断触发 │
│ ↓ │
│ 主循环读取缓冲区B ──→ 串口打印输出 │
│ │
│ [中断回调中切换缓冲区] │
│ │
│ 缓冲区B ←── DMA写入 ←── ADC转换数据(下一轮) │
│ │ │
│ │ 转换完成中断触发 │
│ ↓ │
│ 主循环读取缓冲区A ──→ 串口打印输出 │
│ │
└─────────────────────────────────────────────────────────────────┘
工作时序:
- 初始状态:DMA向缓冲区A写入数据,主循环空闲
- 转换完成:触发中断,切换DMA目标为缓冲区B
- 主循环读取缓冲区A的数据并串口输出
- 下一周期:DMA向缓冲区B写入数据,主循环读取缓冲区A
- 循环往复,实现不间断数据采集
⚡ 中断触发流程
复制代码
┌──────────────────────────────────────────────────────────────┐
│ 中断触发流程 │
├──────────────────────────────────────────────────────────────┤
│ 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库
烧录方式:
- 使用ST-Link连接开发板
- 编译工程生成.hex或.bin文件
- 通过烧录工具下载到STM32F405
⚠️ 注意事项
- 电压范围:模拟输入电压必须在0~3.3V范围内
- 参考电压:VDDA引脚必须连接稳定的3.3V电源,建议添加去耦电容
- ADC精度:12位分辨率,电压分辨率约为0.8mV(3.3V/4096)
- 串口设置:调试工具波特率需设置为115200
- 初始化 :主函数中调用
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