tags: [source-summary] type: source source: "尚硅谷嵌入式技术之STM32单片机(进阶篇)V1.0.1 — ADC章节 + 配套代码32~35" author: "尚硅谷研究院" date: 2026-07-15
用生活理解:ADC 就像一把电压尺子——输入一个模拟电压(0~3.3V),输出一个数字(0~4095,12位精度)。就像用尺子量高度,尺子的刻度数决定了精度(12位=4096个刻度)。
ADC = Analog-to-Digital Converter(模数转换器)。
STM32F103ZET6 有 3 个 ADC(ADC1/2/3),各有:
| 特性 | 值 |
|---|---|
| 分辨率 | 12 位(输出 0~4095) |
| 通道数 | 最多 18 个(16 外部 + 2 内部) |
| 转换模式 | 单次/连续/扫描/间断 |
| 触发方式 | 软件触发 / 定时器触发 / 外部引脚触发 |
| ADC 时钟 | 来自 APB2,最大 14MHz |
| 采样时间 | 可编程 1.5~239.5 个周期 |
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通道 | 紧急数据采集 |
| 寄存器 | 功能 |
|---|---|
| SR | 状态(EOC转换结束、STRT开始) |
| CR1 | 控制(SCAN扫描模式、RES分辨率) |
| CR2 | 控制(ADON开启、CONT连续、EXTTRIG外部触发) |
| SMPR1/2 | 采样时间(每个通道单独配置) |
| SQR1~3 | 规则序列(通道顺序与数量) |
| DR | 转换结果(12位右对齐) |
参考:参考手册 §11(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位右对齐) |