11-ADC模数转换.md 4.6 KB


tags: [source-summary] type: source source: "尚硅谷嵌入式技术之STM32单片机(进阶篇)V1.0.1 — ADC章节 + 配套代码32~35" author: "尚硅谷研究院" date: 2026-07-15

created: 2026-07-15

ADC模数转换

用生活理解:ADC 就像一把电压尺子——输入一个模拟电压(0~3.3V),输出一个数字(0~4095,12位精度)。就像用尺子量高度,尺子的刻度数决定了精度(12位=4096个刻度)。


ADC 概述

ADC = Analog-to-Digital Converter(模数转换器)。

STM32F103ZET6 有 3 个 ADC(ADC1/2/3),各有:

特性
分辨率 12 位(输出 0~4095)
通道数 最多 18 个(16 外部 + 2 内部)
转换模式 单次/连续/扫描/间断
触发方式 软件触发 / 定时器触发 / 外部引脚触发
ADC 时钟 来自 APB2,最大 14MHz
采样时间 可编程 1.5~239.5 个周期

SAR ADC 原理

STM32F103 使用逐次逼近型(SAR, Successive Approximation Register)ADC:

1. 采样:对输入电压进行采样保持
2. 比较:从最高位(bit 11)开始,逐位比较
   第1步:VIN > VREF/2 ? → bit11=1 : bit11=0
   第2步:VIN > 调整后的参考值 ? → bit10=1 : bit10=0
   ... 共12步 → 得到12位结果
3. 转换时间 = 采样时间 + 12.5 个 ADC 时钟周期

规则组与注入组

说明 通道数 用途
规则组 常规转换序列 最多16通道 主要数据采集
注入组 可打断规则组的"高优先级"转换 最多4通道 紧急数据采集

ADC 关键寄存器

寄存器 功能
SR 状态(EOC转换结束、STRT开始)
CR1 控制(SCAN扫描模式、RES分辨率)
CR2 控制(ADON开启、CONT连续、EXTTRIG外部触发)
SMPR1/2 采样时间(每个通道单独配置)
SQR1~3 规则序列(通道顺序与数量)
DR 转换结果(12位右对齐)

参考:参考手册 §11(ADC 寄存器描述)


实验:单通道 ADC 采集

软件设计(寄存器版 — 单次采集)

// ADC1 配置:通道0(PA0),软件触发,单次转换
void ADC1_Init(void)
{
    // 1. 开启ADC1时钟 + GPIOA时钟
    RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;
    RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;

    // 2. PA0 模拟输入模式(CNF=00, MODE=00)
    GPIOA->CRL &= ~(GPIO_CRL_MODE0 | GPIO_CRL_CNF0);

    // 3. 复位ADC1配置
    RCC->APB2RSTR |= RCC_APB2RSTR_ADC1RST;  // 复位
    RCC->APB2RSTR &= ~RCC_APB2RSTR_ADC1RST;

    // 4. ADC 时钟分频(APB2=72MHz, ADC_CLK=72/6=12MHz)
    RCC->CFGR |= RCC_CFGR_ADCPRE_DIV6;

    // 5. 配置ADC1
    ADC1->CR2 |= ADC_CR2_ADON;               // 开启ADC1
    Delay_us(10);

    // 6. 校准
    ADC1->CR2 |= ADC_CR2_CAL;                // 开始校准
    while (ADC1->CR2 & ADC_CR2_CAL);         // 等待校准结束

    // 7. 配置规则组序列长度=1,通道0为第1个
    ADC1->SQR1 &= ~ADC_SQR1_L;               // L[3:0]=0: 1个转换
    ADC1->SQR3 &= ~ADC_SQR3_SQ1;             // SQ1[4:0]=0: 通道0

    // 8. 采样时间:239.5周期
    ADC1->SMPR2 |= ADC_SMPR2_SMP0;           // SMP0[2:0]=111: 239.5周期
}

// 单次转换(软件触发)
uint16_t ADC1_Read(void)
{
    ADC1->CR2 |= ADC_CR2_ADON;               // 再次写ADON启动转换
    while (!(ADC1->SR & ADC_SR_EOC));        // 等待转换结束(EOC=1)
    return ADC1->DR;                         // 读取结果(0~4095)
}

int main(void)
{
    ADC1_Init();
    while (1)
    {
        uint16_t val = ADC1_Read();
        float voltage = val * 3.3f / 4095.0f;  // 转为电压值
        printf("ADC: %d, %.2fV\r\n", val, voltage);
        Delay_ms(500);
    }
}

核心速查表

步骤 寄存器操作
开启ADC `CR2
校准 `CR2
配置通道 SQR3.SQ1 = 通道编号
配置采样时间 SMPRx.SMPy = 周期数
启动转换 `CR2
查询EOC while (!(SR & EOC))
读取结果 val = DR(12位右对齐)

常见问题与避坑

  1. ADC 值跳动大 → 电源去耦不良、采样时间太短、建议开启硬件平均
  2. ADC 值始终为 0 → 检查 GPIO 模式(必须模拟输入)、ADC 是否校准
  3. ADC 值始终为 4095 → 输入电压超过 VREF+ 或 ADC 输入引脚悬空
  4. 多通道采样顺序错乱 → 检查 SQR 序列配置(SQ1~SQ16 的顺序)
  5. ADC 时钟超过 14MHz → ADC 转换结果会不准确(ADCPRE 分频必须使 ADC_CLK ≤ 14MHz)