目录

1 模数转换器 (ADC) 

1.1 概述

1.2 逐次逼近型 (SAR) 工作原理

1.2.1 核心组成部分及其职能

1.2.2 详细转换流程解析

2 STM32 ADC 硬件架构与工作机制

2.1 整体架构解析

2.1.1 电压输入引脚

2.1.2 输入通道与内部信号源

2.1.3 规则组与注入组

2.1.3.1 核心差异对比

2.1.3.2 规则组

2.1.3.3 注入组

2.1.3.4 抢占、寄存器修改与互操作行为

2.1.4 触发源控制器

2.1.5 ADC 时钟与转换时间

2.1.6 数据寄存与存储机制

2.1.6.1 数据对齐方式

2.1.6.2 规则组的数据存储与转运

2.1.6.3 注入组的数据存储

2.1.7 中断与预警机制

2.1.7.1 中断源分类

2.1.7.2 模拟看门狗 (Analog Watchdog) 机制

2.1.7.3 DMA 请求触发

3 ADC 转换模式

3.1 控制位功能定义

3.1.1 连续转换控制位:CONT (Continuous Conversion)

3.1.2 扫描模式控制位:SCAN (Scan Mode)

3.2 单次非扫描模式 (SCAN = 0, CONT = 0)

3.3 连续非扫描模式 (SCAN = 0, CONT = 1)

3.4 单次扫描模式 (SCAN = 1, CONT = 0)

3.5 连续扫描模式 (SCAN = 1, CONT = 1)

3.6 模式配置总结与对比

4 底层驱动配置与工作流剖析

4.1 初始化基准配置流程

4.1.1 总线时钟使能

4.1.2 ADC 时钟域预分频配置

4.1.3 GPIO 模式定义

4.1.4 规则组通道配置(静态初始化)

4.1.5 外设结构体实例化

4.1.6 模块上电与内部校准

4.2 单通道采集逻辑与实现方案

4.3 多通道分时复用(软件同步方案)

4.4 数据量化与真实电压换算

5 相关元器件简介

5.1 光敏电阻传感器模块

5.1.1 工作原理

5.1.2 模块功能与接口

5.2 3362P 电位器

5.2.1 器件简介

5.2.1 接口描述

5.2.2 工作原理

5.3 4线制 NTC 热敏电阻传感器模块

5.3.1 器件简介

5.3.2 接口描述

5.3.3 硬件结构

5.3.4 工作原理

5.4 TCRT5000红外反射传感器

5.4.1 器件简介

5.4.2 引脚定义

5.4.3 硬件结构

5.4.4 工作原理

6. 章节实验与应用实践

6.1 ADC 单通道采集(电位器电压监测)

6.1.1 实验目标

6.1.2 硬件设计

6.1.3 软件设计

6.1.4 实验现象

6.2 ADC 多通道采集(多传感器并行监测)

6.2.1 实验目标

6.2.2 硬件设计

6.2.3 软件设计

6.2.4 实验现象


1 模数转换器 (ADC) 

1.1 概述

          模数转换器(Analog-to-Digital Converter, ADC)是嵌入式系统中连接连续模拟信号与离散数字信号的核心接口。微控制器(如 STM32)内部逻辑电路仅能处理以高、低电平表征的数字信号,无法直接解析传感器输出的连续变化电压。

          ADC 的核心职能在于通过**采样(Sampling)、保持(Holding)、量化(Quantizing)与编码(Encoding)**四个物理过程,将模拟电压转换为处理器可识别的二进制数值。这些数值存储于特定的硬件寄存器中,为后续的数字信号处理、闭环控制及数据存储提供基础数据支撑。

1.2 逐次逼近型 (SAR) 工作原理

          逐次逼近型(Successive Approximation Register, SAR)ADC 是目前嵌入式微控制器中应用最广泛的模数转换架构。理解其内部组件及其协同工作逻辑,对于配置 STM32 的采样周期、电压参考及输入阻抗匹配具有重要的工程指导意义。大致架构框图如下所示:

1.2.1 核心组成部分及其职能

          根据硬件架构分析,SAR ADC 主要由以下五个核心模块构成:

  • 保持电路(Sample and Hold, S&H):负责在转换周期起始阶段捕捉瞬时模拟输入电压 $V_{IN}$,并将其锁定在内部采样电容上,以确保在整个量化过程中输入信号电平的稳定性。
  • 电压比较器(Comparator):作为模拟与数字域的判定核心,用于比较外部输入电压 $V_{IN}$ 与内部数模转换器(DAC)产生的参考反馈电压 $V_{DAC}$ 的大小关系,并输出逻辑电平作为数字判定依据。
  • 逐次逼近寄存器(SAR Register):用于存放量化中间试探结果及最终 12 位数字编码的移位寄存器。它在控制逻辑驱动下,按照二进制权重从最高位至最低位依次进行状态试探。
  • 数模转换器(DAC):根据 SAR 寄存器当前的二进制数值,将其还原为对应的内部模拟反馈电压 $V_{DAC}$,为比较器提供下一步对比的基准电平。
  • 控制电路与时序模块(Control Logic):责产生内部时钟节拍,精确协调采样保持与逐次比较的状态切换,并在转换结束后置位硬件标志位(如 STM32 中的 EOC 标志)。

1.2.2 详细转换流程解析

          SAR ADC 的量化过程基于硬件实现的二分查找算法(Binary Search Algorithm),其工作逻辑可类比为使用一套二进制权重的砝码在天平上测量未知重量。

          以 STM32 常用的 12 位分辨率 ADC 为例,其详细转换流程如下:

(1)采样保持阶段

          控制电路闭合采样开关,外部模拟信号向内部采样电容充电。随后开关断开,电容捕获并锁定该瞬时的电压值 $V_{IN}$,将其输入至比较器的同相端。这一阶段确保了在后续长达 12 次的比较过程中,被测信号电平保持恒定,避免产生量化误差。

(2)逐位试探循环(MSB —> LSB)

          这是 ADC 的核心工作阶段。控制逻辑驱动 SAR 寄存器按照“由高位到低位”的顺序,对 12 个二进制位依次进行状态判定:

  • 初始试探:控制电路首先将 SAR 寄存器的最高有效位(MSB,即 Bit 11)设为 1,其余位暂时清零。此时 DAC 根据寄存器状态,输出一个等效于满量程一半的参考电压 $V_{DAC}$ = 0.5 X $V_{REF}$

  • 比较与判定:比较器对 $V_{IN}$$V_{DAC}$ 进行实时对比:

    • $V_{IN} \ge V_{DAC}$:说明当前权重未超过输入信号,该位保留为 1。

    • $V_{IN} < V_{DAC}$:说明当前权重过大,该位清零为 0。

  • 连续迭代:完成 MSB 判定后,控制逻辑指向下一位(Bit 10)并将其设为 1,同时保持此前已确定的位(如已保留的 MSB)状态不变。系统再次通过 DAC 转换和比较判定来决定该位的去留。此过程重复执行 12 次,直至确定最低有效位(LSB,即 Bit 0)。例如,假设待测模拟电压对应的理想数字量为 10(二进制 1010)

    步骤 试探操作 寄存器状态 判定结果 (与 1010 比较) 寄存器最终动作
    1 (MSB) 设 Bit3=1 1000 (8) 10 > 8,权重不足 保留该位的 1
    2 设 Bit2=1 1100 (12) 10 < 12,权重过大 清零该位
    3 设 Bit1=1 1010 (10) 10 = 10,权重吻合 保留该位的 1
    4 (LSB) 设 Bit0=1 1011 (11) 10 < 11,权重过大 清零该位

(3)迭代完成与数据锁存

          当 12 次比较循环全部结束后,SAR 寄存器内最终驻留的 12 位二进制序列即为该模拟电压的数字量化结果。硬件逻辑随即将其从移位寄存器转存至 ADC 数据寄存器(DR)中,以供用户读取。

(4)状态反馈

          在数据锁存完成的同时,硬件会自动置位转换结束标志位(EOC, End of Conversion)。该信号可用于触发中断、告知 CPU 读取数据或直接驱动 DMA 进行自动化数据转运。

备注

          虽然逐位比较由硬件自动完成,但理解此原理有助于精确计算转换耗时。STM32 的 12 位 ADC 总转换时间由下式决定:

$T_{conv} = $ 采样时间 + 12.5 x T_{ADCCLK}

          其中固定的 12.5 个周期包含了 12 次位比较(每位 1 周期)以及 0.5 个周期的内部同步与时序开销。这不仅解释了 SAR 架构的运行节拍,也决定了系统在特定频率下的最大采样率上限。


2 STM32 ADC 硬件架构与工作机制

2.1 整体架构解析

          STM32F103C8T6 微控制器集成了 2 个独立的高性能 12 位逐次逼近型(SAR)模数转换器(ADC1 和 ADC2)。其硬件设计采用模块化并行架构,可划分为以下 7 个核心功能单元:

  • 电压参考与模拟电源单元:定义转换的满量程范围。
  • 多路复用输入通道网络:包含 16 个外部通道与 2 个内部信号源。
  • 通道组控制器:分为规则组(Regular Group)与注入组(Injected Group)。
  • 触发源选择器:支持软件触发与多种硬件同步触发。
  • 时钟预分频系统:源自 APB2 总线的专用 ADCCLK。
  • 数据寄存器与对齐单元:负责 12 位量化结果的存储与格式化。
  • 监控与中断矩阵:包含模拟看门狗、DMA 请求及转换结束标志。


2.1.1 电压输入引脚

          在 STM32 的模拟前端设计中,ADC 的工作依赖于两组核心电压引脚:电源引脚($V_{DDA}/V_{SSA}$参考电平引脚($V_{REF+}/V_{REF-}$

  • VDDA(模拟供电):芯片的模拟电源,引脚为模拟域供电。VDDA 为 ADC、比较器及其它模拟模块提供电源;在没有外部参考的情况下,VDDA 常作为 VREF+ 的来源。
  • VSSA(模拟地):模拟地,引脚用于模拟地回流与参考负端的连接,应与数字地(GND)在 PCB 设计中按良好实践处理以减少噪声耦合。
  • VREF+(参考正端):ADC 的正向参考电压。ADC 的最大可量化电平由 VREF+ 决定。部分 STM32 器件此引脚可外接精密参考电源,也可以直接连接到模拟电源 VDDA。
  • VREF-(参考负端):ADC 的负向参考电压。一般与模拟地 VSSA 相连,决定 ADC 的最小可量化电平(参考下限)。

          ADC 输入电压范围为:$V_{REF-} \le V_{IN} \le V_{REF+}$,由VREF-、 VREF+ 、 VDDA 、 VSSA这四个外部引脚严格限定。

  • 典型配置:在常规低功耗电路设计中,通常将 VREF- 与 VSSA 接地(GND),而 VREF+ 与 VDDA 连接至 3.3V。此时 ADC 的理想量化范围为 0 ~ 3.3V(前提 VREF+=VDDA=3.3V)。
  • 信号调理需求:若输入信号包含负压或高于 ADC 量化上限(超出 0 ~ $V_{REF+}$ 范围),必须通过外部电路(如电平移位运放、差分放大器、衰减分压或缓冲器)将信号线性映射到 ADC 的参考区间内;直接将超出范围的电压接入 ADC 会导致测量错误或损伤器件。
  • 电气可靠性约束:严禁引入超过 VREF+(或 VDDA)+0.3V 或低于 VREF-(或 VSSA)−0.3V 的瞬时过压(具体数值请参考所用 STM32 器件的绝对最大额定值,典型值为 ±0.3V)。超限电压会触发片上 ESD/保护二极管导通,造成过流、局部发热甚至模拟前端永久损坏。

注意:独立的 VREF+ 和 VREF- 引脚仅存在于 100 脚或以上(如 VQFP100)的大容量微控制器封装中。对于常见的 48 脚(如 STM32F103C8T6)或 64 脚封装,由于管脚数量限制,芯片在内部已经将 VREF+ 物理连接到了 VDDA,将 VREF- 连接到了 VSSA。因此,在这些小封装芯片上,ADC 的参考电压直接受限于模拟供电电压。


2.1.2 输入通道与内部信号源

          STM32F103 系列ADC 控制器的输入网络支持 18 个多路复用通道,涵盖外部信号采集与内部系统监控两大类:

  • 外部通道(External Channels):具备 16 个 GPIO 复用模拟输入引脚。受限于封装管脚密度(如典型的 LQFP48 封装),实际引出的外部通道通常为 10 个(ADC12_IN0 ~ ADC12_IN9),对应引脚为 PA0~PA7 及 PB0~PB1。如下图所示:

  • 内部通道(Internal Channels,仅 ADC1 支持):其中 ADC1 集成 2 个专用内部信号源,分别为芯片内部温度传感器(用于粗略监测芯片结温)与内部参考基准电压($V_{REFINT}$)。该电压具有极高的热稳定性,不随 $V_{DDA}$ 波动。通过先测量 $V_{REFINT}$,软件可以反向推算出真实的 $V_{DDA}$ 电压,从而对其他通道的测量结果进行电源增益补偿,消除供电波动引起的测量误差。

          STM32F103 的 ADC 通道与 GPIO 引脚的映射关系如下表所示。请注意,ADC1 和 ADC2 的通道引脚完全重合,支持双 ADC 同步采样模式;而 ADC3 在部分通道上使用了不同的引脚映射(如 PF 系列引脚)。

通道编号 ADC1 映射引脚 ADC2 映射引脚 ADC3 映射引脚
通道 0 PA0 PA0 PA0
通道 1 PA1 PA1 PA1
通道 2 PA2 PA2 PA2
通道 3 PA3 PA3 PA3
通道 4 PA4 PA4 PF6
通道 5 PA5 PA5 PF7
通道 6 PA6 PA6 PF8
通道 7 PA7 PA7 PF9
通道 8 PB0 PB0 PF10
通道 9 PB1 PB1 -
通道 10 PC0 PC0 PC0
通道 11 PC1 PC1 PC1
通道 12 PC2 PC2 PC2
通道 13 PC3 PC3 PC3
通道 14 PC4 PC4 -
通道 15 PC5 PC5 -
通道 16 内部温度传感器 - -
通道 17 内部参考电压 ($V_{REFINT}$) - -

:在 ADC2 与 ADC3 中,通道 16、17 全部连接到了内部的 VSS,不可用于内部信号监测。


2.1.3 规则组与注入组

          STM32 引入了组(Group)的概念,将 16 个外部通道划分为两类不同的转换序列:规则通道组注入通道组。这种机制类似于操作系统的任务调度,赋予了模拟信号采集“优先级”的概念,用以实现常规采样与高优先级/突发采样的并存与调度。

2.1.3.1 核心差异对比

          规则组最多可配置 16 个通道;注入组最多可配置 4 个通道。两组各自有独立的“序列寄存器”来指定转换顺序与长度,注入组具有优先权(可抢占规则组)。下表总结了它们的物理特性与应用场景:

特性 规则通道组 (Regular Group) 注入通道组 (Injected Group)
通道数量 最多 16 路 最多 4 路
逻辑比喻 类似于“主程序”,按序执行 类似于“中断服务”,插队执行
数据存储 共用一个 16 位数据寄存器 (ADC_DR) 独立四个 16 位数据寄存器 (ADC_JDRx)
覆盖风险 后一通道会覆盖前一通道,必须配合 DMA 各通道互不干扰,无需担心覆盖
优先级 高(可打断规则组)

2.1.3.2 规则组

          用途:用于常规、周期性或批量扫描采样:用户把想要轮询的通道按顺序放入规则序列,ADC 按序逐个转换;多通道扫描时,由于规则组只有一个共享的 16 位数据寄存器,因此在多通道扫描场景下需要配合 DMA 将每次转换结果搬到内存,以避免每次转换的数据被下一个序列转换的数据覆盖。

          寄存器(SQRx)映射:规则序列由 3 个 32bit 寄存器控制:ADC_SQR3、ADC_SQR2、ADC_SQR1。字段与序号对应关系(每个 SQ 字段占 5 位,存放通道号)如下:

  • SQR3:SQ1 … SQ6 → 控制规则序列的第 1 ~ 6 次转换(SQ1 为第一次转换)。
  • SQR2:SQ7 … SQ12 → 控制第 7 ~ 12 次转换。
  • SQR1:SQ13 … SQ16 + L[3:0] → 控制第 13 ~ 16 次转换;L[3:0] 指定规则序列的长度(实际转换次数 = L + 1,最多 16)。如图所示:

          配置示例:若希望通道 3 首次转换,则把通道号 3 写入 ADC_SQR3 中的 SQ1[4:0] 字段,即规则序列中的第1个转换;若希望通道 1 为第 8 次转换,则把 1 写入 ADC_SQR2 的 SQ8[4:0],即序列8中为通道 1 。


2.1.3.3 注入组

          用途:用于高优先级、事件触发或时间敏感的采样:它有独立的 4 个数据寄存器 ADC_JDRx (x= 1..4),因此注入组的多通道转换不会互相覆盖结果。并且可以在规则组正在转换时“插队”——一旦注入组触发,ADC 会暂停当前的规则组转换、先执行完整的注入序列再回到规则组。

          寄存器(JSQR)映射:注入组的转换序列由 ADC 注入序列寄存器(ADC_JSQR) 控制。寄存器中的 JL[1:0] 字段用于配置注入序列的转换次数,具体对应关系为:

  • JL = 00 → 1 次转换
  • JL = 01 → 2 次转换
  • JL = 10 → 3 次转换
  • JL = 11 → 4 次转换

          寄存器中还包含 JSQ1 ~ JSQ4(各为 5 位,JSQx[4:0]) 四个字段,用于配置每一次注入转换所对应的通道号。需要注意的是,当注入序列长度小于 4 时,序列在寄存器中采用 右对齐存储结构,即 N 个有效条目占据 JSQ(4−N+1) … JSQ4。执行顺序按寄存器索引从小到大读取,举例说明:

  • JL = 11(4 次转换) 时,ADC 的转换顺序为JSQ1 → JSQ2 → JSQ3 → JSQ4
  • JL = 10(3 次转换) 时,转换顺序为 JSQ2 → JSQ3 → JSQ4
  • JL = 01(2 次转换) 时,转换顺序为 JSQ3 → JSQ4
  • JL = 00(1 次转换) 时,仅执行 JSQ4

          因此在配置注入序列时,若注入序列长度小于 4,在配置通道时应按寄存器布局从 JSQ4 向前填充(即有效条目右对齐到 JSQ4)。

注意:这里 “从 JSQ4 开始填充” 是指寄存器字段在逻辑上应占据的位(布局),并不是要求在程序中先写 JSQ4 才写其它字段——写寄存器的先后顺序对最终执行无影响;只要最终 JSQx 字段内容正确,ADC 就会按上面列出的执行顺序工作。若仍按 JSQ1 起始顺序填写(即左对齐写入),会导致实际转换顺序与预期不一致——这是注入序列配置中最常见的错误之一。

          配置示例

  • 若注入序列只进行 1 次转换,且希望采样通道 3,则设置 JL = 00,并将通道号 3 写入 JSQ4[4:0];
  • 若注入序列进行 2 次转换,希望按顺序转换 通道 A → 通道 B,则设置 JL = 01,并将 A 写入 JSQ3[4:0],B 写入 JSQ4[4:0](最终寄存器布局为 JSQ3=A、JSQ4=B);
  • 若进行 3 次转换,希望按顺序转换 A → B → C,则设置 JL = 10,并将 A 写入 JSQ2[4:0],B 写入 JSQ3[4:0],C 写入 JSQ4[4:0]。
  • 当 JL = 11 表示 4 次注入转换,此时转换顺序按 JSQ1 → JSQ2 → JSQ3 → JSQ4 依次执行。

2.1.3.4 抢占、寄存器修改与互操作行为
  • 抢占(优先级):当规则组正在进行序列转换时,若触发了注入组转换,ADC 会立即暂停(挂起)规则组的当前流程、先完成注入组的全部序列,再恢复规则组后续转换。此机制可用于实时读取关键采样点(例如同步 PWM 辅助测量等)。
  • 数据寄存器:规则组的转换结果进入单一共享的规则数据寄存器 ADC_DR,注入组的结果分别写入注入数据寄存器 ADC_JDRx(x= 1..4),因此注入组不会出现“结果覆盖”问题。
  • 寄存器修改的即时影响:若在正在转换期间修改 SQRx 或 JSQR,ADC 会复位当前转换并对新选择的序列发送新的启动脉冲;通常应保证在安全时机(如 ADSTART=0 或先停止转换)修改序列寄存器以避免意外中断或重启。

2.1.4 触发源控制器

          在完成 ADC 输入通道选择和转换序列配置之后,接下来就需要确定 ADC 何时开始执行转换。STM32 为此提供了一个 触发源控制机制,用于控制 ADC 转换的启动方式。

          ADC 的转换启动主要有两种方式:软件触发外部事件触发

  • 软件触发:通过向 ADC_CR2 寄存器中的 SWSTART(规则组) 或 JSWSTART(注入组) 位写入指令,可以直接启动一次 ADC 转换。这种方式由 CPU 主动控制采样时刻,实现简单,适用于对采样时间精度要求不高的场景,例如读取电池电压、采集电位器位置等。

  • 外部事件触发:ADC 也可以由片上其他外设产生的事件触发启动,例如 定时器事件(如 TIM1_TRGO、TIM8_TRGO 等) 或 外部中断事件(EXTI)。在这种模式下,ADC 的采样时刻由硬件事件自动控制,可以实现与系统时序的严格同步。例如在电机控制系统中,常利用 定时器事件触发 ADC 在 PWM 周期的特定时刻进行采样,从而减少 CPU 轮询负担并提高系统实时性。如图所示:

          为了对这些触发信号进行管理,STM32 在 ADC_CR2 寄存器中提供了对应的配置位:

配置维度

规则通道

组控制位

注入通道

组控制位

功能说明
触发源选择 EXTSEL[2:0] JEXTSEL[2:0] 选择具体的外部触发源(如定时器事件或 EXTI 信号)。
触发使能 EXTTRIG JEXTTRIG 使能对应通道组的外部触发功能。只有使能后 ADC 才会响应外部事件。

          需要注意的是,在 STM32F1 系列中,ADC 外部触发机制仅负责 选择触发源并使能触发,并不提供触发信号的边沿检测配置(如上升沿或下降沿选择)。触发事件本身由外设产生,一旦对应事件发生,ADC 即会启动相应的转换序列。

          通过这种触发控制机制,ADC 可以与系统中的其他外设形成 硬件级联动,实现精确、稳定的同步采样。


2.1.5 ADC 时钟与转换时间

          ADC 的运行由专用模拟时钟 ADCCLK 驱动。该时钟由复位和时钟控制单元(RCC)通过 APB2 总线时钟(PCLK2)预分频生成。

(1)时钟频率配置与限制

          根据电气特性要求,ADCCLK 的最大工作频率不得超过 14MHz。其预分频因子由时钟配置寄存器 RCC_CFGR 的 ADCPRE[1:0] 位进行设置。

  • 预分频选项:支持 2、4、6、8 分频。
  • 配置约束:该预分频器不提供 1 分频选项。
  • 典型应用:在系统主频 PCLK2 = 72MHz 的常规配置下,通常选择 6 分频,产生的 ADCCLK 为 12MHz。若选择 4 分频(18MHz),将超出硬件设计的频率上限,导致采样精度下降或量化异常。

(2)可编程采样时间

          ADC 对输入电压的采样过程需要占用若干个 ADCCLK 周期。每个通道的采样时间可独立配置,通过采样时间寄存器 ADC_SMPR1(通道 10~17)和 ADC_SMPR2(通道 0~9)中的 SMP[2:0] 位进行设置。

  • 采样周期控制:可配置范围包括 1.5、7.5、13.5、28.5、41.5、55.5、71.5 及 239.5 个周期。

  • 最小采样周期:采样周期最小可设为 1.5 个 ADCCLK 周期,此时 ADC 具备最高的采样速率。

(3)总转换时间计算

          单次 ADC 转换的总时间 $T_{conv}$ 取决于采样周期与固定量化周期的总和。其计算公式为:

$T_{conv} = T_{SMPL} + 12.5 \times T_{ADCCLK}$

          其中:

  • $T_{SMPL}$:可编程的采样周期(最小为 1.5)。
  • 12.5 周期:逐次逼近(SAR)算法完成量化转换所需的固定硬件开销。

(4)计算示例

          以 STM32F103 系列典型的 72MHz 系统频率为例,计算最短转换时间:

          时钟频率:设置预分频因子为 6,则

$ADCCLK = \frac{72\text{MHz}}{6} = 12\text{MHz}$

          采样时间:配置采样周期为最小的 1.5 个周期。

          计算结果

$T_{conv} = (1.5 + 12.5) \times \frac{1}{12\text{MHz}} = \frac{14}{12 \times 10^6} \approx 1.17\mu\text{s}$

          若在 ADCCLK 极限频率 14MHz 下运行且采样周期设为 1.5,则总转换时间理论上可缩短至 1us。但在实际工程中,通常以 1.17us 作为标准的最快转换周期。


2.1.6 数据寄存与存储机制

          ADC转换结果的存储方式由寄存器架构和数据对齐配置共同决定。STM32的ADC分辨率为12位,而其硬件数据寄存器为16位,因此支持左对齐和右对齐两种数据排列方式。

2.1.6.1 数据对齐方式

          硬件支持以下两种对齐模式,以适应不同的软件处理逻辑,对齐方式通过 ADC_CR2 寄存器的 ALIGN 位(位 11)设置:

          右对齐(Right Alignment):

  • 排列逻辑:转换结果占据寄存器的 [11:0] 位,高位 [15:12] 自动补 0。
  • 应用场景:此模式下读取的数值直接对应量化后的原始数据(0 ~ 4095),是工程开发中最常用的配置。

          左对齐(Left Alignment):

  • 排列逻辑:转换结果占据寄存器的 [15:4] 位,低位 [3:0] 自动补 0。
  • 应用场景:若仅读取数据寄存器的高 8 位(即字节读取),可实现精度的快速截断(等效于 8 位 ADC),适用于对处理速度要求极高且可容忍精度损失的实时控制场景。

2.1.6.2 规则组的数据存储与转运

          规则组具备多通道扫描能力,但其硬件架构存在严格的数据通道限制:

(1)寄存器约束:规则组的 16 路通道共用一个 16 位数据寄存器 ADC_DR。

(2)覆盖风险:在扫描模式下,ADC 会按序列依次转换各通道。由于只有一个存储窗口,后一通道的转换结果会即时覆盖前一通道的数据。

(3)自动转运机制(DMA):为防止数据丢失,必须在下一通道转换完成前将当前数据移出。

  • 常规模式:通过查询 ADC_SR 状态寄存器中的标志位或触发中断,由 CPU 手动读取。
  • 高效模式:配置 直接存储器访问(DMA)控制器。每当 ADC_DR 更新时,硬件自动将数据转运至指定的 SRAM 存储区,这是多通道采集的标准化解决方案。
2.1.6.3 注入组的数据存储

          注入组在寄存器架构上提供了更高的独立性与数据安全性:

  • 寄存器配置:注入组配备了 4 个独立的 16 位数据寄存器 ADC_JDR1 ~ ADC_JDR4。
  • 并行存储逻辑:每个注入通道的转换结果均有固定的专用寄存器存放。
  • 优势:在最大 4 通道的配置下,各通道数据互不干扰,不存在数据覆盖风险,软件可随时读取任意通道的最新采样值,无需配合 DMA 即可确保数据的一致性。

2.1.7 中断与预警机制

          ADC 的运行状态监控与数据转运主要依赖于中断机制、DMA 请求以及转换模式的组合配置。如图所示:

2.1.7.1 中断源分类

          当特定的硬件事件发生且相应的中断使能位被置位时,ADC 会向内核发送中断请求。主要包含以下三类:

  • 规则组转换结束中断(EOC): 当规则组序列中的所有通道转换完成后,状态寄存器 ADC_SR 中的 EOC 位被置位。若使能了 EOCIE 位,则触发中断。
  • 注入组转换结束中断(JEOC): 当注入组序列转换完成后,JEOC 位被置位。若使能了 JEOCIE 位,则产生中断。
  • 模拟看门狗中断(AWD): 硬件自动监测受控通道的转换数值。若数值超出预设的阈值范围,则触发 AWD 中断。
2.1.7.2 模拟看门狗 (Analog Watchdog) 机制

          模拟看门狗是一种硬件级的电压监控机制,无需 CPU 干预即可实现实时报警。

  • 阈值设定:通过 ADC_HTR(高阈值寄存器)和 ADC_LTR(低阈值寄存器)预设 12 位数字量限值。
  • 运行逻辑:当转换结果 $D_{out}$ 满足 $D_{out} > HTR$$D_{out} < LTR$ 时,硬件自动生成全局中断。
  • 应用价值:适用于系统过压保护、欠压报警等高优先级故障处理场景,能显著降低 CPU 轮询监控的资源开销。
2.1.7.3 DMA 请求触发

          为了应对规则组多通道转换时的数据覆盖问题,ADC 具备触发 DMA(直接存储器访问)请求的能力:

  • 功能描述:每当一个规则通道转换结束,ADC 可产生一个 DMA 请求,将数据从 ADC_DR 寄存器自动转运至预设的 SRAM 存储区。
  • 硬件约束:STM32F103C8T6(中容量型号)中,只有ADC1支持通过DMA1通道1触发DMA请求,ADC2不具备此功能。此外,该型号不包含ADC3模块(ADC3仅在大容量产品中提供)
  • 设计建议:在执行多通道扫描采样时,开启 DMA 模式是确保数据完整性的标准化方案。

3 ADC 转换模式

          通过组合 ADC_CR1 寄存器的 SCAN 位与 ADC_CR2 寄存器的 CONT 位,规则组可衍生出四种典型的模拟量采集模式;而注入组因硬件限制,不支持由 CONT 位驱动的独立连续转换。

3.1 控制位功能定义

          ADC 的运行模式由以下两个核心控制位共同定义,分别决定了转换序列的深度(通道数量)频次(重复执行)

3.1.1 连续转换控制位:CONT (Continuous Conversion)

          该位位于控制寄存器 ADC_CR2 中,用于定义转换周期的执行频次:

  • 单次模式 (CONT = 0):ADC 在完成预设的转换序列后,硬件会自动停止运行,不再发起下一次采样。若需再次转换,必须由软件或硬件重新触发。
  • 连续模式 (CONT = 1):ADC 在完成当前序列转换后,硬件立即自动启动下一轮转换。系统将保持循环采样状态,直至软件强制关闭 ADC。

3.1.2 扫描模式控制位:SCAN (Scan Mode)

          该位位于控制寄存器 ADC_CR1 中,用于定义单次触发任务所涵盖的通道范围:

  • 非扫描模式 (SCAN = 0):ADC 仅对序列列表中的首个通道(即 SQ1 或 JSQ1)进行采样转换。即使序列寄存器中配置了多个通道,硬件也会忽略后续任务。

  • 扫描模式 (SCAN = 1):ADC 将按照序列寄存器(SQRx 或 JSQR)中定义的顺序,依次对所有使能的通道进行扫描转换。

          这两个控制位的正交组合决定了 ADC 工作的四种基本架构。其中,CONT 位决定了何时停止,而 SCAN 位决定了转换多少个通道

3.2 单次非扫描模式 (SCAN = 0, CONT = 0)

          这是最基础的触发采集模式,系统仅对单一目标执行一次动作。

  • 执行逻辑:ADC 接收到触发信号(软件或硬件)后,仅对规则组或注入组序列中的第一个通道(SQ1 或 JSQ1)进行一次量化转换。
  • 结束状态:转换完成后,硬件将结果存入 ADC_DR(或 ADC_JDR1),置位 EOC(转换结束)标志,随后 ADC 模块停止运行,等待下一次触发。
  • 典型应用:按需采集场景,例如系统唤醒后单次读取电池电压,或通过按键触发单次读取温度值。

3.3 连续非扫描模式 (SCAN = 0, CONT = 1)

          此模式使 ADC 在单一通道上形成自循环,常用于高频独立监控。

  • 执行逻辑:触发启动后,ADC 同样仅针对序列中的第一个通道(SQ1)进行量化。但在本次转换结束后,系统不会停止,而是立即自动发起下一轮针对该通道的转换。
  • 结束状态:每次单通道转换完成都会更新 ADC_DR 并触发 EOC 标志。除非软件清零 CONT 位或关闭 ADC,否则转换永不停止。
  • 典型应用:单一模拟信号的连续后台监测,例如配合内部温度传感器进行实时的热保护监控。

3.4 单次扫描模式 (SCAN = 1, CONT = 0)

          当需要对多个传感器状态获取一个“时间切片”时,通常采用此模式。

  • 执行逻辑:触发启动后,ADC 会根据 SQR1 寄存器中配置的通道数量(L 位),依次对 SQ1 到 SQx 的通道进行一轮逐个转换。
  • 结束状态:当序列列表中最后一个通道转换完成后,ADC 停止运行并置位 EOC 标志。
  • 数据约束:由于规则组所有通道共用一个 ADC_DR 寄存器,后续通道的数据会原子性地覆盖前一通道。因此,必须开启 DMA 传输,在每次单通道转换间隙将数据转运至内存。
  • 典型应用:硬件定时器触发的多路传感器同步采样,如电机控制中的三相电流瞬时快照。

3.5 连续扫描模式 (SCAN = 1, CONT = 1)

          这是自动化程度最高的数据采集模式,CPU 参与度降至最低。

  • 执行逻辑:触发启动后,ADC 按照序列列表完成一轮多通道转换(如 SQ1 —> SQ2 —> SQ3)。到达最后一个通道后,硬件会自动将指针复位至 SQ1,立即开始下一轮列表转换,周而复始。
  • 数据约束:必须配合 DMA 使用,且通常将 DMA 配置为循环模式 (Circular Mode)。硬件会自动维护一块内存数组,实时映射各通道的最新电压值。
  • 典型应用:多路独立传感器的全自动后台采集,例如同时连接多路光敏电阻、电位器和操纵杆的多通道数据收集终端。

3.6 模式配置总结与对比

为了在工程实践中快速选型,以下表格梳理了四种模式的核心约束条件:

模式名称 SCAN 位 CONT 位 转换目标 触发需求 规则组 DMA 依赖
单次非扫描 0 0 仅 SQ1 (执行 1 次) 每次均需触发 不需要 (CPU 直接读取)
连续非扫描 0 1 仅 SQ1 (无限循环) 仅启动时需 1 次触发 可选 (推荐使用以降低中断频率)
单次扫描 1 0 SQ1 至 SQx (执行 1 轮) 每轮均需触发 强制需要 (防止数据覆盖)
连续扫描 1 1 SQ1 至 SQx (无限循环) 仅启动时需 1 次触发 强制需要 (需配置 DMA 循环模式)

4 底层驱动配置与工作流剖析

4.1 初始化基准配置流程

          驱动 ADC 的底层构建顺序需严格遵循时序依赖关系,以确保外设能够从复位状态平稳过渡到工作状态。配置流程大致如下图所示:

4.1.1 总线时钟使能

          首先需开启 ADCx 以及目标复用 GPIO 的 APB2 硬件时钟。在 STM32 架构中,外设时钟默认关闭以降低功耗,所有配置操作前必须先行激活时钟总线。

/*开启时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);    //开启ADC1的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);   //开启GPIOA的时钟

4.1.2 ADC 时钟域预分频配置

          调用 RCC_ADCCLKConfig() 函数,将 APB2 的 72MHz 时钟进行分频。根据硬件手册,ADC 的输入时钟不得超过 14MHz,因此通常选择 6 分频,生成 12MHz 的 ADCCLK。

/*设置ADC时钟*/
RCC_ADCCLKConfig(RCC_PCLK2_Div6);                       //选择时钟6分频,ADCCLK = 72MHz / 6 = 12MHz

4.1.3 GPIO 模式定义

          对应引脚必须初始化为 GPIO_Mode_AIN(模拟输入状态)。在此模式下,引脚内部的施密特触发器和输出驱动电路断开,使引脚呈现高阻态。这种高阻态配置能够最大限度地减少数字电路对模拟总线的干扰,确保电压采样的真实性。

/*GPIO初始化*/
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;           //将引脚初始化为模拟输入模式
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;               //选定PA0引脚
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);                  //执行GPIOA的底层配置

4.1.4 规则组通道配置(静态初始化)

          利用 ADC_RegularChannelConfig() 函数将目标通道映射至规则组序列。在本步骤中,需定义通道在 16 个序列位中的排布次序及其采样时间周期。

/* 规则组通道配置 */
// 参数:ADC1, 通道0, 序列排名1, 采样周期55.5
// 将通道 0 映射至规则组序列 1 的位置,设定采样时间为 55.5 个 ADC 周期
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);

4.1.5 外设结构体实例化

          通过 ADC_InitTypeDef 结构体配置 ADC 的运行参数,包括工作模式(独立模式)、数据对齐机制(右对齐)、触发源选择(软件触发)以及转换模式(单次且非扫描模式)。

ADC_InitTypeDef ADC_InitStructure;                      

/* 1. 工作模式:独立模式 (Independent) */
// 说明:STM32 内部有多个 ADC,该参数决定 ADC1 和 ADC2 是各自为战(独立),
// 还是同步采样(双模式)。单 ADC 应用中均选择独立模式。
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;      

/* 2. 扫描模式 (Scan Mode) */
// 说明:涉及多通道切换。DISABLE 表示单通道转换;ENABLE 表示按序列遍历所有使能通道。
ADC_InitStructure.ADC_ScanConvMode = DISABLE;           

/* 3. 连续转换模式 (Continuous Mode) */
// 说明:决定触发后的执行频次。DISABLE 表示单次转换后停止;ENABLE 表示自动循环。
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;     

/* 4. 触发源选择 (ExternalTrigConv) */
// 说明:决定谁来“扣动扳机”。ADC_ExternalTrigConv_None 表示不使用外部硬件信号,
// 而是通过调用软件指令(SWSTART)来手动触发转换。
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; 

/* 5. 数据对齐方式 (DataAlign) */
// 说明:12 位数据放入 16 位寄存器的方式。选择“右对齐”后,读取的数值即为原始量化值。
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;  

/* 6. 通道数量 (NbrOfChannel) */
// 说明:在扫描模式下决定需要转换的序列长度。本例仅采样 1 路通道。
ADC_InitStructure.ADC_NbrOfChannel = 1;                 

ADC_Init(ADC1, &ADC_InitStructure);                     // 将配置写入寄存器

4.1.6 模块上电与内部校准

          在完成所有参数配置后,必须通过一系列严格的硬件初始化序列来确保采样的精度。

(1)为什么要进行校准?

          ADC 内部采用的是精密的逐次逼近型电容网络。由于半导体制造过程中的微小公差、环境温度波动以及供电电压的漂移,这些电容会产生一定的失调误差(Offset Error)。调用自校准函数,能够让硬件自动测量这些偏差并生成补偿因子,从而在后续的转换中自动抵消误差,保证量化结果的线性度。

(2)前提条件:模块使能(ADON)

          ADC 的校准电路必须在模块带电的状态下才能工作。因此,校准之前必须先调用 ADC_Cmd 函数使能 ADC。此时,内部模拟电路开始上电,通常需要几个微秒的稳定期(硬件自动处理)后,方可启动校准。

(3)标准校准流程(“复位”+“执行”)

          校准过程分为两个阶段,每一步都必须配合“硬件标志位轮询”来确保动作已切实完成:

  • 第一步:复位校准(Reset Calibration) 调用 ADC_ResetCalibration。其作用是清除之前可能存在的校准参数,将校准寄存器初始化。由于这是一个异步硬件动作,必须通过 while 循环检查复位标志位,直到硬件自动将其清零(RESET),表示初始化完成。
  • 第二步:启动自校准(Start Calibration) 调用 ADC_StartCalibration。此时 ADC 开始内部测量逻辑。同理,我们需要再次利用 while 循环等待校准标志位清零,这标志着硬件已计算并锁存了最新的补偿参数。
/* --- 第一步:上电使能 --- */
ADC_Cmd(ADC1, ENABLE);                                  // 启动 ADC1 电源

/* --- 第二步:复位校准寄存器 --- */
ADC_ResetCalibration(ADC1);                             // 发起复位请求
while (ADC_GetResetCalibrationStatus(ADC1) == SET);     // 等待硬件自动清零,确认复位成功

/* --- 第三步:执行正式自校准 --- */
ADC_StartCalibration(ADC1);                             // 发起正式校准请求
while (ADC_GetCalibrationStatus(ADC1) == SET);          // 等待硬件自动清零,确认校准完成

4.2 单通道采集逻辑与实现方案

          在“单次非扫描”模式下,单路信号的采集周期被封装为一个闭环的函数执行流,确保每次转换均能得到确定的量化结果。

          单通道采集实现逻辑:

  1. 软件启动:向控制寄存器写入启动指令。
  2. 状态轮询:通过 while 循环监测 EOC(End of Conversion)标志位,确保量化过程完成。
  3. 结果读取:读取数据寄存器 DR。根据寄存器硬件特性,读取 DR 的操作会自动清除 EOC 标志。
/**
  * 函    数:获取AD转换的值(单通道方案)
  * 返 回 值:AD转换的值,范围:0~4095
  */
uint16_t AD_GetValue(void)
{
    ADC_SoftwareStartConvCmd(ADC1, ENABLE);                 //软件触发一次AD转换
    while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET); //轮询等待EOC标志位置位
    return ADC_GetConversionValue(ADC1);                    //读取并返回转换数据,同时清除EOC标志
}

4.3 多通道分时复用(软件同步方案)

          在缺乏 DMA 支持的前提下采集多路信号(如电位器、光敏、热敏等传感器共存),常规的“扫描模式”因规则通道的单一寄存器覆盖问题无法直接使用。

          针对此缺陷的系统级解决方案为:复用“单次非扫描”模式,采用软件分时重配置策略。核心重构逻辑在于:将通道配置步骤延迟至具体采样函数调用时刻。

  • 在全局初始化阶段,激活所有待采样的对应 GPIO,但不绑定具体的规则组列表。
  • 在轮询数据时,向封装好的采样函数AD_GetValue传递具体的通道枚举参量(如 ADC_Channel_0)。
  • 在软件触发转换动作执行的前一指令周期,动态覆写规则组序列 1 的通道映射。

          借由以上时序隔离处理,不同物理通道在时间轴上实现了分时多路复用转换,安全规避了规则寄存器的数据重写冲刷现象。

/**
  * 函    数:获取AD转换的值(多通道分时复用方案)
  * 参    数:ADC_Channel 指定AD转换的通道
  * 返 回 值:AD转换的值,范围:0~4095
  */
uint16_t AD_GetValue(uint8_t ADC_Channel)
{
    //在触发前动态配置规则组序列1的位置为指定通道
    ADC_RegularChannelConfig(ADC1, ADC_Channel, 1, ADC_SampleTime_55Cycles5);
    
    ADC_SoftwareStartConvCmd(ADC1, ENABLE);                 //启动转换
    while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET); //等待转换完成
    return ADC_GetConversionValue(ADC1);                    //返回结果
}

提示: 此方案虽然灵活且容易理解,但由于每次转换都需要 CPU 介入重新配置寄存器并死循环等待(阻塞运行),因此极其消耗 CPU 资源,仅适用于低频的非实时采样。若对多通道采样率要求较高,强烈建议首选“连续扫描模式 + DMA”架构

4.4 数据量化与真实电压换算

          ADC 输出的数据寄存器值 $D_{ADC}$ 是离散的量化编码,在人机交互展示阶段,必须建立比例映射模型以回归物理电压值。

          量化结果与输入电压呈线性比例关系。当输入电压达到基准电压 $V_{REF}$(通常为 3.3V)时,12 位 ADC 输出满量程值 4095。

$V_{actual} = \frac{D_{ADC}}{4095} \times V_{REF}$

          在执行除法运算前,由于 C 编译器底层的数据类型运算边界问题,寄存器整型数据 ADValue 在除以 4095 之前,必须被强制类型转换为单精度浮点数 float。若不执行强制转换,整数除法将直接截断小数域产生硬性归零错误。

// 定义浮点型电压变量
float Voltage;

// 执行线性变换逻辑
// 显式转换ADValue为float类型,以保证除法运算具备浮点精度
Voltage = (float)ADValue / 4095 * 3.3;

          在实际工程中,ADC 采样结果可能受热噪声影响产生微小的数值波动。为提升系统稳定性,建议在上述驱动层之上引入算术平均滤波(Moving Average Filter)或滞回比较(Hysteresis Comparison)算法。

提示(关于 4095 还是 4096):

          代码中除以 4095 是一种工程上的折中习惯,能让满量程的数值对应出绝对精确的 3.3V。但从严格的 ADC 量化原理来看,12 位 ADC 将量程划分了 2^{12} = 4096 个阶梯,真实的 LSB(最低有效位)电压步进应为 3.3 / 4096。在绝大多数的一般应用中,两者的计算差异仅为千分之一级,可忽略不计。


5 相关元器件简介

5.1 光敏电阻传感器模块

         光敏电阻传感器模块是一种用于检测环境光照强度的常用模块。其核心原理是模块上的光敏电阻阻值会随外界光照变化,通过内部电路将此模拟量变化转换为可供单片机直接读取的数字信号或模拟信号。模块集成电压比较器与电位器,使用便捷,实物如下图所示。

5.1.1 工作原理

          模块的核心传感元件是光敏电阻,其由硫化镉等半导体材料制成,工作原理基于内光电效应。当无光照时,内部载流子极少,电阻值非常高(可达兆欧级);当受到光照时,光子能量激发产生大量电子-空穴对,使其电阻值迅速降低。光照越强,电阻值越小。

          模块将此阻值变化通过电路转换为两种输出:

  • 模拟电压输出 (AO):光敏电阻与一个定值电阻构成分压电路。其分压点(即AO引脚)的电压值随光敏电阻阻值(即光照强度)连续变化。此电压可接入单片机的ADC引脚进行采样,从而获得精确的环境光强数值。
  • 数字开关输出 (DO):AO的电压被送入一个LM393电压比较器,与一个由可调电位器设定的参考电压进行比较。当环境光强低于设定阈值(即AO电压高于参考电压)时,DO输出高电平;当环境光强超过设定阈值时,DO输出低电平。模块上的“DO输出指示灯”会在输出低电平时点亮。

          本实验主要关注模块的数字开关输出 (DO)输出即可:

  • 当环境光强低于设定阈值时,DO输出高电平;
  • 当环境光强超过设定阈值时,DO输出低电平,同时模块上的DO输出指示灯点亮。

5.1.2 模块功能与接口

          模块提供以下四个引脚:

  • VCC:接电源正极(3.3V - 5V)
  • GND:接电源负极
  • AO:模拟信号输出。输出与光强成比例的电压信号。
  • DO:TTL数字信号输出。输出高低电平,可作为开关信号。


          模块上包含两个可调部分:

  • 灵敏度调节电位器:用于顺时针(增强)或逆时针(减弱)调节数字输出(DO)的动作阈值。
  • 电源指示灯:指示模块已通电。

5.2 3362P 电位器

5.2.1 器件简介

          3362P 系列电位器是一款小型化、单圈、顶部调节的密封式微调电位器。在电路中,它本质上是一个可调电阻或滑动变阻器,主要用于电路参数的静态校准,例如运算放大器的增益补偿、比较器的阈值设定等。实物如图所示:

          核心技术参数:

  • 阻值范围$100\Omega \sim 2\text{M}\Omega$
  • 额定功耗:0.25W(在 70° 环境下)。
  • 阻值偏差$\pm 10\%$
  • 机械行程$240 \pm 10^\circ$

5.2.1 接口描述

          3362P 采用标准的直插式(DIP)3 引脚结构。其引脚定义如下:

引脚编号 标识 功能定义与技术说明
引脚 1 & 3 CCW / CW 固定端:连接内部电阻体的两个端点。在分压应用中,通常分别接 VCC 与GND。
引脚 2 WIPER 动触点(滑片):连接内部电刷,作为信号输出端。其电位随旋钮转动而连续变化。

          引脚与内部结构的对应关系如下图所示:

5.2.2 工作原理

          电位器的调节逻辑基于机械位移与电阻分配。内部电刷通过中心轴与顶部的十字调节旋钮相连。当旋转旋钮时,电刷在圆弧形电阻体上滑动,从而改变引脚 2 到两个固定端之间的电阻比例。

          根据电路接入方式,3362P 可实现以下两种典型功能:

(1)分压器模式(三端连接)

          将引脚 1 和 3 分别接入 VCC 与 GND。随着旋钮转动,电刷在电阻体上的位置改变,引脚 2 即可获得与机械位移成比例的输出电压。其输出公式满足:

$V_{out} = V_{in} \times \frac{R_{23}}{R_{13}}$

(2)变阻器模式(二端连接)

          将动触点(引脚 2)与其中一个固定端(引脚 1 或 3)短接或悬空。此时电位器作为两端元件接入电路,可获得平滑、连续变化的电阻值,用于控制回路电流或设定时间常数。

  • 分压器模式(三端连接):将引脚 1、3 接入 VCC 和 GND,引脚 2 输出可调电平。电刷通过中心旋转轴与外部调节旋钮相连,当旋转顶部的十字槽时,电刷在电阻体上滑动,所以,引脚的电平或电阻值也随旋钮转动而连续变化,所以引脚 2 即可获得与机械位移成比例的输出电压。其输出公式满足:

  • 变阻器模式(二端连接):将动触点(引脚 2)与其中一个固定端(引脚 1 或 3)短接或悬空。此时电位器作为两端器件使用,可获得平滑连续变化的电阻值。

注意事项:电位器的额定功率(0.25W)是指全电阻接入时的极限。若仅部分电阻接入(变阻器模式),允许功耗应按阻值比例降低。


5.3 4线制 NTC 热敏电阻传感器模块

5.3.1 器件简介

          4 线制 NTC 热敏电阻模块是一款高灵敏度的环境温度感知单元。该模块采用 NTC(负温度系数)热敏电阻作为探测核心,其电阻值随周围环境温度的升高而显著减小。

          相较于常见的 3 线制模块,该模块额外引出了模拟电压接口(AO),实现了数字量预警与模拟量精确采集的双重功能。实物如图所示:

          核心特性:

  • 高灵敏度探头:集成高灵敏度 NTC 热敏电阻,典型温度检测范围为$20^\circ\text{C} \sim 80^\circ\text{C}$
  • 双输出模式:支持数字开关量(DO)和线性模拟量(AO)输出。
  • 驱动能力强:搭载宽电压 LM393 比较器,输出信号波形平整,电流驱动能力 >15 mA 。
  • 灵活配置:板载精密电位器,可无级调节温度触发阈值。

5.3.2 接口描述

          模块采用 4 线制引出方式,兼容 3.3V 与 5V 系统逻辑电平,可直接适配主流单片机。

引脚 名称 描述 连接建议
1 VCC 电源正极(3.3V ~ 5V) 接微控制器的电源轨
2 GND 电源地 接系统公共地线
3 DO 数字量输出接口(0 和 1) 接 GPIO(输入模式)
4 AO 模拟量输出接口 接 ADC 采样通道

5.3.3 硬件结构

          模块 PCB 尺寸仅为 $3.2\text{cm} \times 1.4\text{cm}$,布局紧凑。其主要硬件结构如图所示:

  • 传感探头:前端 NTC 热敏电阻。
  • 信号处理中心:LM393 比较器,负责将连续变化的模拟信号转化为离散的开关量信号。
  • 调节机构:灵敏度调节电位器,用于手动设定触发温控的“临界点”。
  • 指示系统:包含电源指示灯(接通后常亮)与开关信号指示灯(DO 触发时亮)。

5.3.4 工作原理

          该模块的核心逻辑是由电阻分压网络电压比较电路以及状态指示逻辑构成的闭环检测系统。其电路拓扑结构如下图所示:

(1)模拟信号采集(AO 链路)

          模块的模拟采集电路基于电阻分压原理构建:

  • 电路构成:精密电阻 R1(10KΩ)与 NTC 热敏电阻 N1 串联分压。
  • 电压映射模型:模拟输出引脚 AO(图中标识为 AC)的电平受 N1 阻值实时调节。根据分压方程:

$V_{AO} = V_{CC} \times \frac{R_{N1}}{R_1 + R_{N1}}$

  • 负温度系数逻辑:由于 N1 具有负温度系数特性,其阻值 $R_{N1}$ 随环境温度 T 的升高而减小,导致 $V_{AO}$ 的电平发生线性偏移(即温度越高,模拟电压越低)。该信号可接入单片机的 ADC 转换通道,通过校准公式换算出精确的摄氏度数值。

(2)开关信号生成(DO 逻辑)

          数字量输出电路利用 LM393 电压比较器 实现信号的阈值判定:

  • 基准判定:电位器 R2 接入 VCC 与 GND 之间,为比较器 U1(LM393)的反相输入端(INA-)提供一个可调的基准电压 $V_{ref}$

  • 比较逻辑:比较器的同相输入端(INA+)实时监测 AO 电平。

    • 低温状态:当环境温度低于设定阈值时,$R_{N1}$ 阻值较大,满足 $V_{AO} > V_{ref}$,比较器输出高阻态,经外部电路(或内部逻辑)输出为高电平,D2熄灭。

    • 高温状态:当环境温度超过设定阈值时,$R_{N1}$ 阻值下降使 $V_{AO} < V_{ref}$,比较器翻转,输出拉低至低电平,D2点亮。

补充:DO 接口支持直接驱动 5V 继电器模块(驱动电流 >15mA),可作为硬件级温控开关独立运行。


5.4 TCRT5000红外反射传感器

5.4.1 器件简介

          TCRT5000 是一款集红外发射与接收于一体的光电探测器,主要用于非接触式物体检测或表面反射率特征辨识。在嵌入式开发中,该模块常作为寻线机器人的光电导航单元(执行黑白线循迹任务),或部署于工业流水线作为位置传感器(通过物体遮挡触发计数逻辑)。实物如图所示:

          核心选型参数:

  • 检测距离:有效反射距离为 1mm ~ 25mm。
  • 工作电压:支持 3.3V ~ 5V 宽电压输入,兼容主流单片机逻辑。
  • 驱动特性:板载 LM393 比较器,提供干净的方波信号,驱动能力超过 15mA。
  • 输出形式:支持数字开关量(DO)与模拟电压(AO)双输出。
  • 调节机制:配有多圈可调精密电位器,用于精细调节检测灵敏度(触发阈值)。

5.4.2 引脚定义

          该模块通常采用 4 线制接口,支持数字量(DO)与模拟量(AO)双输出模式。

引脚标识 功能定义 电气说明
VCC 电源正极 接 3.3V ~ 5V  电源
GND 电源负极 接系统参考地
DO 数字量输出 TTL 开关信号(0 或 1),通常连接单片机外部中断引脚
AO 模拟量输出 原始电压信号,随反射强度连续变化,需接 ADC 采样通道

5.4.3 硬件结构

          TCRT5000 的核心硬件由一个 940nm 红外发射二极管和一个 NPN 红外光电三极管 组成。模块结构如下图所示:

          TCRT5000 的内部结构包含两大核心部分:

  • 红外发射单元:一个砷化镓(GaAs)红外发光二极管,负责发射 950nm 近红外光。
  • 红外接收单元:一个硅 NPN 光电晶体管,其基极电流受入射光强度调制。当接收到反射红外光时,光生电流导致晶体管导通,集电极-发射极间电阻急剧下降。

          两个单元并排封装于一个不透明的塑料外壳内,壳体顶部开有窗口并集成可见光滤光片,用于减少环境可见光的干扰。

5.4.4 工作原理

          TCRT5000 基于红外光的反射特性进行物体检测,其工作流程包含三个阶段:

(1)发射阶段

          当红外发射管阳极(引脚1)接正、阴极(引脚2)接地时,发射管正向导通,持续向外探测空间发射 950nm 红外光束。

(2)反射阶段

          若探测范围内出现物体,发射的红外光会照射到物体表面,若物体表面具有较高的红外反射率(如白色、浅色、金属镜面),则部分红外光被反射回传感器窗口,并被光电晶体管接收。

(3)接收与转换逻辑

  • 无反射/强度不足:接收管处于关断状态,DO 输出高电平,板载指示灯熄灭。
  • 强反射(检测到物体):红外线使接收管饱和导通,DO 输出低电平,指示灯点亮。
  • 模拟线性变化:光电晶体管接收到的反射光强度越强,其导通程度越高,集电极-发射极间等效电阻越小,导致 AO 端电压随反射光强度产生波动。该电压的变化直接反映了反射光强度,进而可推断物体的有无、距离远近或表面灰度。反射面颜色越浅(反射率高)、距离越近,AO 输出电压变化越显著。

6. 章节实验与应用实践

6.1 ADC 单通道采集(电位器电压监测)

6.1.1 实验目标

  • 掌握单通道 ADC 基础配置:深入理解单次转换、非扫描模式下的寄存器配置流程及硬件触发机制。
  • 理解软件触发与轮询逻辑:通过软件置位启动转换,并利用轮询标志位(EOC)的方式确保数据提取的同步性。
  • 实现线性量化与物理映射:建立离散数字量与连续模拟电压之间的数学转换模型,实现 0 ~ 3.3V 的电压实时监测。
  • 验证硬件自校准时序:通过代码实施完整的校准流程,降低由于内部电容制造偏差带来的量化误差。

6.1.2 硬件设计

6.1.3 软件设计

          本实验采用单次转换非扫描模式,通过主循环轮询触发的方式获取量化数据,具体架构如下:

(1)ADC 底层初始化模块(基于 ADC1)

  • 时钟与引脚配置:使能 ADC1 与 GPIOA 时钟。通过 RCC 分频器将 PCLK2 降频至 12MHz 以满足硬件采样频率要求。将 PA0 配置为模拟输入(GPIO_Mode_AIN)。
  • 工作模式设定:初始化 ADC 结构体,配置为独立模式、右对齐、软件触发、单次转换且不启用扫描模式。
  • 硬件自校准执行:在上电使能后,依次执行复位校准与自主校准时序,通过 while 循环确保校准寄存器状态机归零,完成硬件初始化。

(2)单次转换提取模块

  • 触发机制实现:在封装函数 AD_GetValue 中,通过 ADC_SoftwareStartConvCmd 下发转换指令。
  • 状态位轮询:利用 ADC_GetFlagStatus 持续监测规则组转换结束标志位(EOC)。当该位置位时,表明逐次逼近过程结束。
  • 数据读取:提取数据寄存器(DR)中的 12 位量化结果。

(3)应用层数据解算逻辑

  • 物理量映射:主程序获取量化值后,利用公式 $V_{actual} = \frac{D_{ADC}}{4095} \times 3.3$  将 0 ~ 4095 的数值线性转换为浮点电压值。
  • 分位显示处理:由于标准 OLED 驱动缺乏浮点显示支持,通过取整与取余算法,将电压值的整数部分与两位小数部分拆分输出。

          具体代码如下:

          main.c文件:

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "AD.h"

uint16_t ADValue;			//定义AD值变量
float Voltage;				//定义电压变量

int main(void)
{
	/*模块初始化*/
	OLED_Init();			//OLED初始化
	AD_Init();				//AD初始化
	
	/*显示静态字符串*/
	OLED_ShowString(1, 1, "ADValue:");
	OLED_ShowString(2, 1, "Voltage:0.00V");
	
	while (1)
	{
		ADValue = AD_GetValue();					//获取AD转换的值
		Voltage = (float)ADValue / 4095 * 3.3;		//将AD值线性变换到0~3.3的范围,表示电压
		
		OLED_ShowNum(1, 9, ADValue, 4);				//显示AD值
		OLED_ShowNum(2, 9, Voltage, 1);				//显示电压值的整数部分
		OLED_ShowNum(2, 11, (uint16_t)(Voltage * 100) % 100, 2);	//显示电压值的小数部分
		
		Delay_ms(100);			//延时100ms,手动增加一些转换的间隔时间
	}
}

          AD.c文件:

#include "stm32f10x.h"                  // Device header

/**
  * 函    数:AD初始化
  * 参    数:无
  * 返 回 值:无
  */
void AD_Init(void)
{
	/*开启时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);	//开启ADC1的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	//开启GPIOA的时钟
	
	/*设置ADC时钟*/
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);						//选择时钟6分频,ADCCLK = 72MHz / 6 = 12MHz
	
	/*GPIO初始化*/
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);					//将PA0引脚初始化为模拟输入
	
	/*规则组通道配置*/
	ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);		//规则组序列1的位置,配置为通道0
	
	/*ADC初始化*/
	ADC_InitTypeDef ADC_InitStructure;						//定义结构体变量
	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;		//模式,选择独立模式,即单独使用ADC1
	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;	//数据对齐,选择右对齐
	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;	//外部触发,使用软件触发,不需要外部触发
	ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;		//连续转换,失能,每转换一次规则组序列后停止
	ADC_InitStructure.ADC_ScanConvMode = DISABLE;			//扫描模式,失能,只转换规则组的序列1这一个位置
	ADC_InitStructure.ADC_NbrOfChannel = 1;					//通道数,为1,仅在扫描模式下,才需要指定大于1的数,在非扫描模式下,只能是1
	ADC_Init(ADC1, &ADC_InitStructure);						//将结构体变量交给ADC_Init,配置ADC1
	
	/*ADC使能*/
	ADC_Cmd(ADC1, ENABLE);									//使能ADC1,ADC开始运行
	
	/*ADC校准*/
	ADC_ResetCalibration(ADC1);								//固定流程,内部有电路会自动执行校准
	while (ADC_GetResetCalibrationStatus(ADC1) == SET);
	ADC_StartCalibration(ADC1);
	while (ADC_GetCalibrationStatus(ADC1) == SET);
}

/**
  * 函    数:获取AD转换的值
  * 参    数:无
  * 返 回 值:AD转换的值,范围:0~4095
  */
uint16_t AD_GetValue(void)
{
	ADC_SoftwareStartConvCmd(ADC1, ENABLE);					//软件触发AD转换一次
	while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);	//等待EOC标志位,即等待AD转换结束
	return ADC_GetConversionValue(ADC1);					//读数据寄存器,得到AD转换的结果
}

6.1.4 实验现象

          当电位器调节旋钮处于不同位置时,OLED 屏幕显示结果如下:

  • 下限点:逆时针旋转到底时,量化值趋近于 0,电压显示为 0.00V。
  • 线性调节:随着顺时针旋转,ADValue 与 Voltage 数值平滑增大。
  • 上限点:顺时针旋转到底时,量化值达到 4095(受限于硬件精度可能存在微小波动),电压显示为 3.30V。

6.2 ADC 多通道采集(多传感器并行监测)

6.2.1 实验目标

  • 实现多通道模拟量获取:在不引入 DMA 机制的情况下,掌握利用单次模式手动切换通道的开发技巧。
  • 掌握通道动态重映射方法:学习通过 ADC_RegularChannelConfig 函数在运行期实时覆写规则组序列器的机制。
  • 验证多路信号一致性:通过接入不同类型的传感器(电位器、光敏、热敏、红外),验证分时复用方案在多信号环境下的隔离度与准确性。

6.2.2 硬件设计

6.2.3 软件设计

          由于规则组仅有一个数据寄存器,本实验弃用扫描模式,通过主循环分时切换规则序列 1 的通道映射来实现多路采集,逻辑如下:

(1)多路 IO 与静态初始化

  • IO 阵列配置:将 PA0、PA1、PA2、PA3 四个引脚统一初始化为模拟输入模式,建立多路硬件接入层。
  • 公共参数设定:初始化 ADC 模块时,依然采用非扫描、单次转换配置,保持底层架构的通用性。

(2)分时复用采样模块

  • 通道重映射逻辑:对采样函数 AD_GetValue 进行重构,引入 ADC_Channel 参数。在触发转换前,首先调用通道配置函数,将传入的通道 ID 写入规则序列 1 位置。
  • 顺序轮询机制:主程序按序调用函数,依次指定通道 0 至通道 3。每一路采样均遵循 “重映射 —> 触发 —> 等待 —> 读取” 的闭环,确保数据在主程序层面完成空间分离。

(3)数据缓冲与刷新

  • 独立变量存储:定义四个独立的 uint16_t 型变量,分别承载不同传感器的量化结果。
  • 界面分层刷新:OLED 屏幕分四行实时显示各通道的 AD 数值,利用 100ms 的循环周期维持显示稳定性。

          具体代码如下:

          main.c文件:

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "AD.h"

uint16_t AD0, AD1, AD2, AD3;	//定义AD值变量

int main(void)
{
	/*模块初始化*/
	OLED_Init();				//OLED初始化
	AD_Init();					//AD初始化
	
	/*显示静态字符串*/
	OLED_ShowString(1, 1, "AD0:");
	OLED_ShowString(2, 1, "AD1:");
	OLED_ShowString(3, 1, "AD2:");
	OLED_ShowString(4, 1, "AD3:");
	
	while (1)
	{
		AD0 = AD_GetValue(ADC_Channel_0);		//单次启动ADC,转换通道0
		AD1 = AD_GetValue(ADC_Channel_1);		//单次启动ADC,转换通道1
		AD2 = AD_GetValue(ADC_Channel_2);		//单次启动ADC,转换通道2
		AD3 = AD_GetValue(ADC_Channel_3);		//单次启动ADC,转换通道3
		
		OLED_ShowNum(1, 5, AD0, 4);				//显示通道0的转换结果AD0
		OLED_ShowNum(2, 5, AD1, 4);				//显示通道1的转换结果AD1
		OLED_ShowNum(3, 5, AD2, 4);				//显示通道2的转换结果AD2
		OLED_ShowNum(4, 5, AD3, 4);				//显示通道3的转换结果AD3
		
		Delay_ms(100);			//延时100ms,手动增加一些转换的间隔时间
	}
}

          AD.c文件:

#include "stm32f10x.h"                  // Device header

/**
  * 函    数:AD初始化
  * 参    数:无
  * 返 回 值:无
  */
void AD_Init(void)
{
	/*开启时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);	//开启ADC1的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	//开启GPIOA的时钟
	
	/*设置ADC时钟*/
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);						//选择时钟6分频,ADCCLK = 72MHz / 6 = 12MHz
	
	/*GPIO初始化*/
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);					//将PA0、PA1、PA2和PA3引脚初始化为模拟输入
	
	/*不在此处配置规则组序列,而是在每次AD转换前配置,这样可以灵活更改AD转换的通道*/
	
	/*ADC初始化*/
	ADC_InitTypeDef ADC_InitStructure;						//定义结构体变量
	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;		//模式,选择独立模式,即单独使用ADC1
	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;	//数据对齐,选择右对齐
	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;	//外部触发,使用软件触发,不需要外部触发
	ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;		//连续转换,失能,每转换一次规则组序列后停止
	ADC_InitStructure.ADC_ScanConvMode = DISABLE;			//扫描模式,失能,只转换规则组的序列1这一个位置
	ADC_InitStructure.ADC_NbrOfChannel = 1;					//通道数,为1,仅在扫描模式下,才需要指定大于1的数,在非扫描模式下,只能是1
	ADC_Init(ADC1, &ADC_InitStructure);						//将结构体变量交给ADC_Init,配置ADC1
	
	/*ADC使能*/
	ADC_Cmd(ADC1, ENABLE);									//使能ADC1,ADC开始运行
	
	/*ADC校准*/
	ADC_ResetCalibration(ADC1);								//固定流程,内部有电路会自动执行校准
	while (ADC_GetResetCalibrationStatus(ADC1) == SET);
	ADC_StartCalibration(ADC1);
	while (ADC_GetCalibrationStatus(ADC1) == SET);
}

/**
  * 函    数:获取AD转换的值
  * 参    数:ADC_Channel 指定AD转换的通道,范围:ADC_Channel_x,其中x可以是0/1/2/3
  * 返 回 值:AD转换的值,范围:0~4095
  */
uint16_t AD_GetValue(uint8_t ADC_Channel)
{
	ADC_RegularChannelConfig(ADC1, ADC_Channel, 1, ADC_SampleTime_55Cycles5);	//在每次转换前,根据函数形参灵活更改规则组的通道1
	ADC_SoftwareStartConvCmd(ADC1, ENABLE);					//软件触发AD转换一次
	while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);	//等待EOC标志位,即等待AD转换结束
	return ADC_GetConversionValue(ADC1);					//读数据寄存器,得到AD转换的结果
}

6.2.4 实验现象

          屏幕同时显示 AD0 至 AD3 四路独立的量化数据:

  • 独立响应:遮挡光敏传感器仅引起 AD1 数值变化,调节电位器仅引起 AD0 数值变化,证明了软件分时切换通道方案的有效隔离。
  • 物理反馈一致性:光线增强时 AD1 减小(光敏模块特性),温度升高时 AD2 减小,红外触发时 AD3 产生电平突变,系统能够稳定识别各传感器的实时模拟状态。

Logo

openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。

更多推荐