代码改变世界

第七章:ADC 模块 —— 给你的开发板一双“慧眼”! - 实践

2026-01-11 17:18  tlnshuju  阅读(16)  评论(0)    收藏  举报

ADC模块详解:从配置到高效数据采集

✅ 适用对象:STM32 初学者
核心目标:掌握 ADC 的 CubeMX 配置 + DMA 数据传输 + 数据处理
特色:用“秤”比喻ADC转换过程,用“自动搬运工”解释DMA工作原理!


一、硬件原理图(以 STM32G431RBT6 为例)

ADC 是什么?

  • 模拟信号转数字信号的桥梁
  • 将外界物理量(如温度、电压)转化为微控制器能理解的数字值

基本连接

信号引脚(ADC1)方向说明
AINxPA0 ~ PA7输入模拟输入通道(可选多个)
VREF+ / VREF-内部参考电压-默认为电源电压

⚠️ 注意:

  • 去耦电容:每个模拟输入前建议加一个0.1uF的去耦电容,减少噪声干扰
  • 地线设计:确保良好的接地,避免引入干扰

⚙️ 二、STM32CubeMX 配置详解

1. ADC 参数设置

基本参数(就像“称重规则”)
参数说明常见值
Resolution (分辨率)转换精度12 bits(最高精度,默认推荐)
Data Alignment (数据对齐)结果存储格式Right Alignment(右对齐,方便处理)
Scan Conversion Mode (扫描转换模式)是否连续转换多个通道Enable(多通道时使用)
Continuous Conversion Mode (连续转换模式)是否持续采样Disable(一般禁用,除非需要实时监控)
Discontinuous Conversion Mode (间歇转换模式)是否分组采样Disable(简单场景禁用)
⚙️ 高级参数(进阶功能)
参数说明
External Trigger Conversion Mode (外部触发转换模式)外部事件触发采样(如定时器中断)
Trigger Source (触发源)触发源选择(如 TIMx_TRGO、EXTI 等)
Channel Configuration (通道配置)配置各通道的采样时间和优先级
Sampling Time (采样时间)提高精度,但增加转换时间
DMA (直接存储器访问)自动传输结果至内存,减轻CPU负担
Watchdog (看门狗)监控结果是否超出预设范围
Analog Watchdog Mode (模拟看门狗模式)单通道或多通道监控

推荐配置:12-bit 右对齐,启用扫描模式(多通道),禁用连续和间歇模式,采样时间适中


2. GPIO 设置

引脚模式说明
PA0 (AIN0)Analog Input连接传感器或其他模拟信号源
PA1 (AIN1)Analog Input同上

注意

  • 确保引脚未被其他外设占用
  • 若有多个通道,重复上述步骤

3. DMA 设置(高效传输核心!)

项目配置建议
DMA RequestADC1_Regular_Channel(常规通道)
DMA Channel自动分配(如 DMA1_Channel1)
DMA ModeCircular(循环模式) ← 接收必须用此模式!
Memory Increment✅ Enable(内存地址递增)
Peripheral Increment❌ Disable(外设地址固定)

为什么?

  • ADC 数据寄存器地址固定 → 外设地址不递增
  • 数据要存入 buffer[0], buffer[1]... → 内存地址必须递增

三、底层代码实现(DMA 模式)

生活比喻:秤与搬运工

想象你有一台智能秤(ADC),每次放上物品都会显示重量(数字值)。为了记录这些重量,你可以:

  1. 手动记录(无DMA):每称一次就记一次数(效率低)
  2. 自动记录(DMA):雇佣一个搬运工(DMA),他会在后台帮你把每次的重量记录下来,完全不需要你操心!

完整代码实现

1. 全局变量定义
uint32_t dma_buff[2][30]; // DMA接收缓存,支持两个ADC通道,每通道30个样本
float adc_value[2];       // ADC采样值数组
2. 初始化函数
void adc_dma_init(void) {
    HAL_ADC_Start_DMA(&hadc1, (uint32_t*)&dma_buff[0][0], 30); // 启动ADC1的DMA传输
    HAL_ADC_Start_DMA(&hadc2, (uint32_t*)&dma_buff[1][0], 30); // 启动ADC2的DMA传输
}
3. 数据处理函数
void adc_proc(void) {
    // 计算平均值
    adc_value[0] = adc_value[1] = 0;
    for(uint8_t i = 0; i < 30; i++) {
        adc_value[0] += (float)dma_buff[0][i];
        adc_value[1] += (float)dma_buff[1][i];
    }
    adc_value[0] /= 30 * 4096; // 归一化处理
    adc_value[1] /= 30 * 4096;
    // 转换为实际电压值(假设参考电压为3.3V)
    adc_value[0] *= 3.3f;
    adc_value[1] *= 3.3f;
    printf("ADC1 Value: %.2fV\n", adc_value[0]);
    printf("ADC2 Value: %.2fV\n", adc_value[1]);
}

修复细节

  • i +) → i++)(循环变量自增)
  • 添加了归一化处理(除以4096)和电压转换公式

四、HAL 库核心 API 详解

HAL_ADC_Start_DMA()

功能

启动 ADC 的 DMA 模式转换。通过此函数,ADC 可以将转换后的数据直接存储到内存中,而无需 CPU 的干预,从而提高数据处理效率。

描述
HAL_StatusTypeDef HAL_ADC_Start_DMA(ADC_HandleTypeDef *hadc, uint32_t *pData, uint32_t Length);
参数说明
hadc指向 ADC 句柄的指针
pData指向数据存储缓冲区的指针
Length要转换的数据量
返回值说明
HAL_OK函数执行成功
HAL_ERROR函数执行时发生错误
HAL_BUSYADC处于忙碌状态,无法启动新的转换
HAL_TIMEOUT函数执行超时
使用场景
  • 连续数据采集:在需要连续采集大量ADC数据并将其存储到内存中时,使用DMA可以有效减少CPU负担。
  • 实时数据处理:应用程序可以在后台持续采集数据,同时前台处理其他任务,提高系统响应速度。
  • 多通道ADC转换:在多通道ADC转换场景中,使用DMA可以自动将各通道的数据存储到指定的缓冲区中。

五、使用示例(main.c)

int main(void) {
    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();
    MX_ADC1_Init(); // 初始化ADC1
    MX_ADC2_Init(); // 初始化ADC2
    MX_DMA_Init();
    adc_dma_init(); // 启动DMA传输
    while (1) {
        adc_proc(); // 处理ADC数据
        HAL_Delay(1000); // 每秒处理一次
    }
}

✅ 总结:关键点回顾

分辨率与精度的关系

  • 分辨率越高(如12位),精度越高,但转换时间越长
  • 计算公式实际电压 = ADC值 * Vref / 4096
    • Vref:参考电压(通常为3.3V)
    • 4096:12位ADC的最大值(2^12)

DMA的作用

  • 自动搬运工:将ADC转换结果直接传输到内存,释放CPU资源
  • 循环模式:特别适合连续采集,避免数据丢失

数据处理流程

  1. 初始化:配置ADC和DMA
  2. 启动DMA:开始数据采集
  3. 处理数据:定期读取DMA缓冲区,计算平均值或进行其他处理

本章口诀(背下来!)

ADC 配置四要素:分辨率、对齐方式、采样时间、DMA使能!
模拟输入需去耦,干净电源稳如山!
DMA是搬运工,CPU从此轻松松!
⏸️ 采样时间定精度,时间越长越精准!
数据处理别马虎,归一化后才准确!
printf输出真方便,调试信息全知晓!


这份笔记完整覆盖了ADC所有内容,包括:

  • CubeMX 配置细节
  • DMA 数据传输实现
  • 数据处理逻辑
  • HAL 库 API 详解
  • 硬件连接说明

现在,你不仅能配置 ADC,还能利用 DMA 实现高效的数据采集和处理!