ZZZ-17-ADC

如果说串口是单片机的“嘴巴”,那么 ADC 就是单片机的“眼睛”。它让单片机能够感知真实世界中连续变化的模拟信号(如光照、温度、声音、电压)。

1. 什么是 ADC?(通俗理解)

  • 模拟信号(Analog):大自然中的信号,比如 0.5V, 1.25V, 3.3V,是连续不断的。
  • 数字信号(Digital):单片机的大脑,只能理解 0 和 1(二进制数字)。

ADC 的角色:就像一个带有刻度的量杯。它把杯子里液面的高度(电压)转化成一个具体的数字,告诉单片机:“现在的电压是 2048”。

2. STM32 ADC 的核心参数

在 STM32F103C8T6 中,自带有一个 ADC,ADC 的性能非常强劲:

参数 数值 含义
分辨率 (Resolution) 12 位 (12-bit) 把 0~3.3V 分成 $2^{12} = 4096$ 份。
转换范围 (Range) 0V ~ 3.3V 超过 3.3V 可能烧毁引脚,低于 0V 读不到。
对应关系 0 $\to$ 0V, 4095 $\to$ 3.3V 数值每增加 1,代表电压增加了约 0.8mV。
通道数量 10 个外部通道 PA0PA7, PB0PB1 都可以作为“眼睛”。

3. ADC 的内部构造 (Mermaid)

你会发现 ADC 也有很多 Channel(通道),它的原理和定时器通道类似:一个“测量核心”配合一个“多路开关”。

graph LR subgraph "外部世界 (模拟信号)" In0[PA0: 光敏电阻] In1[PA1: 电位器] In2[PA2: 电池电压] end subgraph "ADC 内部" MUX{多路开关
Multiplexer} Core[ADC 核心
12位量化] DataReg[数据寄存器
DR] end In0 --> MUX In1 --> MUX In2 --> MUX MUX --> Core Core --> DataReg DataReg --> CPU[CPU 或 DMA]

4. 三种常见的“看世界”方式

根据你对实时性的要求,ADC 有三种工作模式:

  1. 单次转换 (Single Conversion):点一下,看一眼。看完了就闭上眼。
  2. 连续转换 (Continuous Conversion):睁着眼不眨,一直看,一直更新数据。
  3. 扫描模式 (Scan Mode):如果有多个通道(比如同时看光敏和电位器),它会挨个看一圈

5. 快速上手实验:读取电位器电压(轮询模式)

我们先用最简单的轮询方式(类似于按键扫描)来感受 ADC。

graph LR subgraph STM32 [STM32F103C8T6] 3V3[3.3V Pin] GND[GND Pin] PA1[PA1 / ADC_IN1] end subgraph POT [电位器 / 传感器] VCC_IN[1号脚: VCC] OUT[2号脚: 信号输出] GND_IN[3号脚: GND] end 3V3 --- VCC_IN PA1 --- OUT GND --- GND_IN

第一步:CubeIDE 配置 (.ioc)

  1. 在左侧点击 Analog -> ADC1
  2. 勾选 IN1(对应引脚 PA1)。
  3. 在下方的 Parameter Settings
    • Continuous Conversion Mode:设为 Enabled(让它不停地转换)。
  4. 保存生成代码。

开启了 ADC 之后,需要注意一下这里的 ADC 分频

第二步:编写代码 (main.c)

main 函数的 while(1) 之前启动 ADC,在 while(1) 里读取。

注意这里也用到了 printf 和串口通讯

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/* USER CODE BEGIN 2 */
HAL_ADC_Start(&hadc1); // 1. 开启 ADC
/* USER CODE END 2 */

/* USER CODE BEGIN WHILE */
while (1)
{
    // 2. 等待转换完成(超时时间 10ms)
    if (HAL_ADC_PollForConversion(&hadc1, 10) == HAL_OK)
    {
        // 3. 读取 12 位的数字量 (0-4095)
        uint32_t adc_val = HAL_ADC_GetValue(&hadc1);

        // 4. 将数字换算成电压 (单位: V)
        float voltage = (float)adc_val * 3.3 / 4096;

        // 5. 通过串口打印出来看看
        printf("ADC Value: %lu, Voltage: %.2fV\r\n", adc_val, voltage);
    }
    HAL_Delay(500); // 每半秒看一眼
    
    /* USER CODE END WHILE */
}

6. 进阶预告:为什么 ADC 最离不开 DMA?

你可能已经发现了:

  • 如果 ADC 开启了连续转换,它每秒钟能产生几十万个数据。
  • 如果 CPU 每次都去 GetValue,那 CPU 真的就什么都不用干了,光在那“看眼睛”了。

专业做法:开启 ADC + DMA。 让 DMA 这个“快递员”自动把 ADC 转换好的数据搬运到数组里,CPU 只需要在想看的时候去数组里翻一下就行了。

常见避坑指南

  1. 引脚悬空:如果你不接任何东西到 PA1,读到的数值会乱跳。这是正常的,因为引脚在感知空气中的静电电场(这叫“天线效应”)。
  2. 采样时间:如果信号变化很快,需要调整 Sampling Time(采样时间),让量杯有足够的时间装满水(充好电)。

扩展资料

在 CubeIDE 中选择 ADC1 时,能看到 IN0、IN1…,这些是频道选择。就像电视机有很多个频道,但只有一个屏幕,ADC 内部通常只有一个“测量器”,通过一个开关来决定现在看哪个引脚的电压

1. 什么是 IN0, IN1…?

STM32F103C8T6 中,ADC1 拥有 10 个外部引脚通道。它们的对应关系是固定的:

  • IN0 $\to$ PA0
  • IN1 $\to$ PA1
  • IN9 $\to$ PB1

你在 CubeIDE 里勾选了哪个 IN,就代表你告诉单片机:“我要把这个引脚当成模拟输入来用”。

2. ADC 内部框架图 (Mermaid)

为了理解那些复杂的配置项,我们来看看电压信号进入单片机后经历了什么:

graph TD subgraph "外部引脚 (Input Channels)" P0[PA0/IN0] P1[PA1/IN1] Pn[...其他引脚] end subgraph "ADC 内部核心" MUX{多路开关
Multiplexer} subgraph "采样保持与转化" SH[采样保持器
Sampling Time] SAR[12位逐次逼近器
Successive Approx] end Logic[控制逻辑
Continuous/Scan Mode] end P0 --> MUX P1 --> MUX Pn --> MUX MUX --> SH SH --> SAR SAR --> DR[数据寄存器
Data Register] Logic -.-> MUX Logic -.-> SAR DR --> CPU[CPU读取/中断] DR --> DMA[DMA搬运] style SAR fill:#f96,stroke:#333 style Logic fill:#bbf,stroke:#333

3. 配置参数详细拆解

当你点击 ADC1Parameter Settings 时,最核心的配置分为三部分:

A. 基础设置 (Basic Settings)

  • Data Alignment (数据对齐):通常选 Right Alignment(右对齐)。
    • 解释:ADC 结果是 12 位的,但寄存器是 16 位的。右对齐就像我们在本子上写数字,个位对齐,方便直接读取数值。
  • Scan Conversion Mode (扫描模式):
    • 如果你勾选了多个 IN 通道(比如同时看 PA0 和 PA1),就必须 Enable。它会自动按顺序测量选中的所有通道。
  • Continuous Conversion Mode (连续转换模式):
    • Enable:它会像监控摄像头一样,拍完一张(转完一次)立刻拍下一张,数据永不停歇地更新。
    • Disable:拍一张就闭眼,直到你再次命令它。

B. 规则组设置 (ADC_Regular_ConversionMode)

这里决定了“谁先测,测多久”。

  • Number of Conversion:你要测几个通道?如果你选了 IN0 和 IN1,这里就填 2。
  • Rank (排名):这是具体的顺序。
    • Rank 1Channel 0,代表第一个测 PA0。
    • Rank 2Channel 1,代表第二个测 PA1。
  • Sampling Time (采样时间):
    • 这是最容易被忽视的参数! 电压进入单片机需要给内部电容充电。
    • 如果电压信号“很虚”(阻抗高),你需要把这个时间调大(比如选 55.5 Cycles 或更高),测出来的电压才准。

C. 外部触发 (External Trigger)

  • External Trigger Conversion Source:通常选 Regular Conversion launched by software。
    • 意思是:由你在代码里写 HAL_ADC_Start 来手动开启转换。你也可以设置为由定时器触发(比如每秒精准采集 1000 次)。

4. 为什么配置这么多?

STM32 的 ADC 设计理念是:尽可能不需要 CPU 介入

  • 最简单的配置:单通道、单次转换(你之前的实验)。
  • 最专业的配置:
    1. 开启 Scan Mode(看多个引脚)。
    2. 开启 Continuous Mode(不停地看)。
    3. 开启 DMA(让搬运工把看的结果自动写进数组)。
    • 结果:CPU 只需要在启动时下个令,然后随时去查数组就行了,完全不浪费 CPU 时间。
Licensed under CC BY-NC-SA 4.0