
10_STM32_模拟篇
本文最后更新于 2025-06-10,学习久了要注意休息哟
第一章 模数转换 - ADC
1.1 ADC 基本概念
1.1.1 模拟信号 vs 数字信号
1、模拟信号
模拟信号的特点是连续变化的,在任意时刻都可以取任意电压值(理论上无限精度)。它可以完整表达自然界的物理信息,例如:
-
温度、光强、电压、电流
-
声音波形(例如人声、音乐)
-
图像中每个像素的亮度
例如,电位器输出的电压范围在 0V ~ 3.3V 之间,它可以是 1.25V、2.01V、3.29V 这样的任意值。
举例:
下图所示为一个正弦波信号,就是典型的模拟信号:
2、数字信号
数字信号是离散的、有限的值,通常只包含0 或 1。它不关心波形的连续性,而是以“高电平”和“低电平”两种状态表达信息。
-
数字电路逻辑门处理的就是数字信号
-
STM32 控制器内部和通信数据几乎都是数字信号
举例:
比如我们使用 STM32 控制一个 LED 灯,发送一个数字信号 1
表示“亮”,0
表示“灭”。
3、模拟与数字的对比
项目 | 模拟信号 | 数字信号 |
---|---|---|
表达方式 | 连续电压值 | 仅两个离散状态(0/1) |
精度 | 理论上无限 | 受位数限制(如8位、12位) |
易受干扰 | 是 | 否,抗干扰性强 |
处理方式 | 模拟电路(放大、滤波) | 数字电路(逻辑、运算) |
应用举例 | 温度采集、电位器、电压传感器 | LED 控制、串口通信 |
4、为什么要用 ADC?
STM32 是数字处理器,只能处理“数字信号”,但是外部世界大多数传感器输出的是模拟信号。
所以我们需要:
- ADC(Analog to Digital Converter):把模拟信号转换成数字信号,方便 STM32 读取、处理、控制。
1.1.2 什么是模数转换器
模数转换器(ADC,Analog to Digital Converter)是一种将模拟信号转换为数字信号的电子元件或功能模块。
它的作用是把连续的电压信号,转换成 MCU 能够处理的离散数字值。
例如:
我们将一个 0~3.3V 的电压输入 STM32 内部的 ADC,得到一个范围在 0 ~ 4095
(假设 12 位精度)之间的整数值,STM32 就可以对这个值进行逻辑判断、数值计算或显示。
STM32 是数字设备,而 ADC 就是它连接真实模拟世界的“眼睛”。
有了 ADC,STM32 不再只是“逻辑判断工具”,而是具备了感知外部世界的能力,是物联网、工业控制、环境感知等项目中不可或缺的一部分。
1、工作流程
-
输入:外部电路(如电位器、传感器)输出一个模拟电压,例如 1.65V。
-
采样保持:ADC 在一个固定时间点采样这个电压,并保持它。
-
量化:把这个模拟值映射成一个最接近的数字值(基于分辨率)。
-
编码输出:输出对应的数字数据,例如 2048(假设是 12 位 ADC,1.65V = 3.3V/2)。
STM32 内部的 ADC 就是完成这套转换流程的核心模块。
2、STM32 中 ADC 的特点
以 STM32F407 为例:
-
最多有 3 个 ADC 模块(ADC1/2/3)
-
每个 ADC 支持多达 16 个外部通道输入
-
支持 单通道采集、多通道扫描
-
支持 DMA 自动搬运数据
-
支持 内部通道采集(如芯片温度、参考电压)
-
精度可选:12 位 / 10 位 / 8 位 / 6 位
3、ADC 的典型应用场景
场景 | 描述 |
---|---|
采集电位器电压 | 电位器电压变化,对应 ADC 数值变化 |
检测光敏电阻输入 | 光强变化 → 电阻变化 → 模拟电压变化 |
测量电池电压 | 电池电压送入 ADC → 判断电量 |
温度传感器读取 | 热敏电阻输出模拟电压 → 转换为温度 |
音频波形分析 | 麦克风输出模拟信号 → 转为数字供算法使用 |
1.1.3 常见ADC类型
ADC 的设计方式不同,决定了它在转换速度、精度、功耗和电路复杂度等方面的表现也不同。
不同的应用场景,对这些性能的要求也不同,因此出现了多种类型的 ADC。
类型 | 工作原理概述 | 特点 | 应用场景 |
---|---|---|---|
并联比较型 | 使用 2ⁿ 个比较器一次性比较所有值 | 速度非常快、硬件复杂 | 高频波形采集(如视频、雷达) |
逐次逼近型 | 二分查找方式逐位逼近目标电压 | 速度快、精度高、结构适中 | 通用 MCU(STM32)、中高速应用 |
双积分型 | 计时积分方式,抗干扰性强 | 精度高、速度慢 | 数字万用表、工业仪器 |
Δ-Σ(增量型) | 高速采样+滤波平均 | 高精度、低带宽、高延迟 | 音频处理、高精度慢速信号 |
1.1.4 并联比较型
1、工作原理
并联比较型 ADC(Flash ADC)是最快速的一类 ADC,它的核心思想是使用多个比较器同时并行比较输入电压和参考电压的不同等级,最后通过编码器将比较结果转换为二进制输出。
2、内部结构
其结构通常包括三个部分:
✅ 分压部分 + 比较部分 + 编码部分
3、工作流程
-
分压器网络(左侧垂直电阻链)
-
将参考电压
V_REF
均匀划分成若干等级电压(例如 8 个电平) -
每个电平作为一个比较器的参考输入
-
-
比较器阵列
-
所有比较器的正端输入为
Vx
(待转换的模拟电压) -
负端输入为对应的参考电压电平
-
比较器输出为高电平表示
Vx > Vref/等级
-
-
编码器
- 将比较器的输出组合,转化为最终的数字编码值(例如
D0 D1 D2
)
- 将比较器的输出组合,转化为最终的数字编码值(例如
4、优缺点
项目 | 描述 |
---|---|
✅ 优点 | 转换速度极快(几乎实时),无延迟 |
❌ 缺点 | 成本高(2ⁿ 个比较器)、功耗高、扩展性差 |
适用场景 | 高频应用如视频信号采集、雷达系统等 |
1.1.5 逐次逼近型
1、工作原理
逐次逼近型 ADC(SAR:Successive Approximation Register ADC)是目前嵌入式芯片中最常见的 ADC 类型。
它的原理类似于“二分查找”法,通过一位一位地尝试逼近输入电压值,直到找到最接近的数字编码为止。
2、内部结构
主要模块如下:
-
控制电路
- 控制转换过程的开始、步进和终止逻辑
-
逐次逼近寄存器(SAR寄存器)
- 暂存当前试探的数字结果,按位从高到低调整值
-
D/A 转换器(DAC)
- 将当前寄存器中的数字值转换为模拟电压
V0
- 将当前寄存器中的数字值转换为模拟电压
-
比较器(Comparator)
- 将
Vx
(输入电压)与V0
(DAC 输出)进行比较,得出下一步逼近方向(大了还是小了)
- 将
3、工作流程
假设为 3 位 ADC,目标是找出 Vx ≈ 5V
(满量程为 8V)对应的数字值:
-
首先试探最高位 D2 = 1(即尝试 100),DAC 输出 4V,比 5V 小 → 保留
-
试探 D1 = 1,即 110(= 6V),比 5V 大 → 清除该位
-
最终尝试 D0 = 1,即 101(= 5V),与输入电压相符
-
得出最终编码:
101
→ 十进制 5
这个过程一般只需 n
次比较,适合中高速转换。
4、优缺点
项目 | 描述 |
---|---|
✅ 优点 | 结构简单,功耗低,适用于集成在 MCU 内部 |
❌ 缺点 | 转换速度慢于并联比较型,受制于每位逼近时间 |
矛盾点 | 分辨率越高,需要的逼近次数越多,速度越慢 |
应用 | STM32、Arduino、ESP32 等微控制器内部 ADC |
1.1.6 ADC的核心参数
ADC 性能的好坏主要由以下几个核心参数决定,不同的参数会直接影响到采集数据的精度、速度、稳定性等。
参数 | 建议设置或注意事项 |
---|---|
分辨率 | STM32 默认 12 位,适用大多数应用 |
采样时间 | 高频信号短采样,慢速信号延长采样 |
参考电压 | 尽量稳定,推荐使用稳压源或滤波电容 |
输入范围 | 严格控制在 0~Vref 之间 |
通道选择 | 多通道可用扫描模式+DMA 提高效率 |
1、分辨率
-
表示 ADC 将模拟信号转换为数字信号时能分成多少级。
-
STM32 中常见:12位(即 2¹² = 4096 个等级)
-
分辨率越高,转换结果越精细,但也意味着速度降低、转换时间更长。
📌 示例:
3.3V 的参考电压下,12 位分辨率,最小电压变化可检测为:
$$
\frac{3.3\text{V}}{2^{12}} = 0.000805\text{V} ≈ 0.805\text{mV}
$$
2、参考电压
-
ADC 使用
Vref
作为满量程的最大电压参考,转换结果是相对于它来衡量的。 -
STM32 默认使用
Vref = Vdda
,一般为 3.3V。 -
若模拟电压超过
Vref
,将被“截断”为最大值。
📌 例子:
若 Vref = 3.3V
,输入 3.3V 得到最大值 4095,输入 1.65V 得到约 2048。
3、采样时间
-
指 ADC 采样输入电压的时间,单位为 ADC 时钟周期。
-
STM32 支持多种采样时间设置(如 3/15/56/480 cycles 等)。
-
输入电阻大时或电压变化快时应设置更长采样时间以保证准确性。
📌 建议:对高阻信号源或内部通道(如温度)设置较长采样时间。
4、转换时间
-
包含:采样时间 + 转换时间(一般为 12.5 ADC 时钟周期)
-
决定了 ADC 每秒能完成多少次转换(即最大采样率)
📌 STM32F4 ADC 最快转换速度约为 2.4 MSPS(百万次/秒)
5、输入通道数
-
STM32F407 内部的 ADC 最多支持 16 个外部通道 和若干内部通道(温度传感器、电源监测等)
-
可按顺序配置多个通道采样,称为“扫描模式(Scan Mode)”
7、输入范围
-
一般为
0V ~ Vref
(如 0 ~ 3.3V),不可超出,否则精度失真或损坏 ADC。 -
若采集外部高于 3.3V 的电压,应使用 电阻分压或缓冲电路。
8、数据对齐方式
-
STM32 可选择左对齐(高位对齐)或右对齐(低位对齐)
-
影响读取 ADC 寄存器的方式,不影响实际精度
9、转换方式
-
单次模式(Single):每次手动启动转换
-
连续模式(Continuous):自动反复采样
-
间断模式(Discontinuous):分批采样
-
DMA模式:与 DMA 联动实现自动搬运数据
-
中断模式:采样结束触发中断,适合事件驱动处理
10、采样率
-
每秒钟可以采集多少个数据点,受采样时间、ADC时钟、通道数量影响
-
STM32 单通道理论采样率可达 2~2.4 MSPS
1.1.7 STM32中ADC的特性
主要特性 | 参数 |
---|---|
ADC类型 | 逐次逼近型 |
分辨率 | 6/8/10/12位 |
ADC时钟频率 | 36MHz(max) |
采样时间 | 采样时间越长, 转换结果相对越准确, 但是转换速度就越慢 |
转换时间 | 与ADC时钟频率、分辨率和采样时间等有关 |
供电电压 | VSSA :0V,VDDA :2.4V~3.6V(全速运行) |
参考电压 | VREF– :0V,VREF+一般为3.3V |
输入电压 | VREF–≤VIN≤VREF+ |
1.2 ADC工作原理
1.2.1 ADC 框图
1.2.2 参考电压/模拟部分电压
ADC供电电源:VSSA、 VDDA (2.4V≤VDDA≤3.6V )
ADC输入电压范围:VREF–≤VIN≤VREF+(即0V≤VIN≤3.3V )
1.2.3 输入通道
1.2.4 转换序列
A/D转换被组织为两组:规则组(常规转换组)和注入组(注入转换组)
规则组最多可以有16个转换,注入组最多有4个转换
优先级
住单间
规则序列寄存器控制关系汇总
寄存器位 | 功能 | 取值 |
SQ1 [ 4 : 0 ] | 设置第1个转换的通道 | 通道0~17 |
SQ2 [ 4 : 0 ] | 设置第2个转换的通道 | 通道0~17 |
SQ3 [ 4 : 0 ] | 设置第3个转换的通道 | 通道0~17 |
SQ4 [ 4 : 0 ] | 设置第4个转换的通道 | 通道0~17 |
SQ5 [ 4 : 0 ] | 设置第5个转换的通道 | 通道0~17 |
SQ6 [ 4 : 0 ] | 设置第6个转换的通道 | 通道0~17 |
SQ7 [ 4 : 0 ] | 设置第7个转换的通道 | 通道0~17 |
SQ8 [ 4 : 0 ] | 设置第8个转换的通道 | 通道0~17 |
SQ9 [ 4 : 0 ] | 设置第9个转换的通道 | 通道0~17 |
SQ10 [ 4 : 0 ] | 设置第10个转换的通道 | 通道0~17 |
SQ11 [ 4 : 0 ] | 设置第11个转换的通道 | 通道0~17 |
SQ12 [ 4 : 0 ] | 设置第12个转换的通道 | 通道0~17 |
SQ13 [ 4 : 0 ] | 设置第13个转换的通道 | 通道0~17 |
SQ14 [ 4 : 0 ] | 设置第14个转换的通道 | 通道0~17 |
SQ15 [ 4 : 0 ] | 设置第15个转换的通道 | 通道0~17 |
SQ16 [ 4 : 0 ] | 设置第16个转换的通道 | 通道0~17 |
SQL [ 3 : 0 ] | 设置规则序列要转换的通道数 | 0~15 |
注入序列寄存器控制关系汇总
寄存器位 | 功能 | 取值 |
JSQ1 [ 4 : 0 ] | 设置第1个转换的通道 | 通道0~17 |
JSQ2 [ 4 : 0 ] | 设置第2个转换的通道 | 通道0~17 |
JSQ3 [ 4 : 0 ] | 设置第3个转换的通道 | 通道0~17 |
JSQ4 [ 4 : 0 ] | 设置第4个转换的通道 | 通道0~17 |
JL [ 1 : 0 ] | 设置注入序列要转换的通道数 | 0~3 |
注入序列的转换顺序是从JSQx[ 4 : 0 ](x=4-JL[1:0])开始
1.2.5 触发源
1.2.6 转换时间
1、ADC时钟设定
2、ADC采样时间
ADC转换时间(12位分辨率): TCONV = 采样时间 + 12个周期
采样时间可通过 SMPx[2:0]位设置,x=0~18
SMP = 000:3个ADC时钟周期
SMP = 001:15个ADC时钟周期
SMP = 010:28个ADC时钟周期
SMP = 011:56个ADC时钟周期
SMP = 100:84个ADC时钟周期
SMP = 101:112个ADC时钟周期
SMP = 110:144个ADC时钟周期
SMP = 111:480个ADC时钟周期
举个例子:ADC时钟频率为21MHz时,ADC最短的转换时间是多少?
TCONV = 采样时间 + 12个周期 = 3个周期 + 12个周期 = 15个周期 = (1/21000000)∗15 s = 0.71us
1.2.7 数据寄存器
由ADCx_CR2寄存器的ALIGN位设置数据对齐方式,可选择:右对齐或者左对齐
1.2.8 中断
中断事件 | 事件标志 | 使能控制位 |
规则通道转换结束 | EOC | EOCIE |
注入通道转换结束 | JEOC | JEOCIE |
设置了模拟看门狗状态位 | AWD | AWDIE |
溢出(F1没有) | OVR | OVRIE |
DMA请求(只适用于规则组)
规则组每个通道转换结束后,除了可以产生中断外,还可以产生DMA请求,我们利用DMA及时把转换好的数据传输到指定的内存里,防止数据被覆盖。
1.3 ADC 标准库
1.3.1 常用函数
// 初始化 ADC 模块(如 ADC1)
void ADC_Init(ADC_TypeDef* ADCx, ADC_InitTypeDef* ADC_InitStruct);
// 初始化 ADC 公共配置(适用于多个 ADC 模块的通用设置)
void ADC_CommonInit(ADC_CommonInitTypeDef* ADC_CommonInitStruct);
// 使能或禁用指定 ADC(如 ADC1)
void ADC_Cmd(ADC_TypeDef* ADCx, FunctionalState NewState);
// 配置 ADC 的通道、序号和采样时间
void ADC_RegularChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel, uint8_t Rank, uint8_t ADC_SampleTime);
// 启动 ADC 的软件触发转换
void ADC_SoftwareStartConv(ADC_TypeDef* ADCx);
// 读取当前 ADC 的转换结果
uint16_t ADC_GetConversionValue(ADC_TypeDef* ADCx);
// 获取 ADC 状态标志位(如判断是否转换完成)
FlagStatus ADC_GetFlagStatus(ADC_TypeDef* ADCx, uint8_t ADC_FLAG);
1.3.2 结构体说明
ADC_CommonInitTypeDef
— 公共配置结构体
成员名称 | 含义 |
---|---|
ADC_Mode | 设置 ADC 的工作模式(独立、双重、三重) |
ADC_Prescaler | 设置 ADC 时钟分频(影响采样速度) |
ADC_DMAAccessMode | 设置 DMA 模式(启用或禁用 DMA 共享) |
ADC_TwoSamplingDelay | 设置多采样之间的延迟时间 |
ADC_InitTypeDef
— 单个 ADC 的配置结构体
成员名称 | 含义 |
---|---|
ADC_Resolution | 设置 ADC 分辨率(12b、10b、8b、6b) |
ADC_ScanConvMode | 设置是否开启扫描模式(多通道) |
ADC_ContinuousConvMode | 是否连续转换(true 连续采样) |
ADC_ExternalTrigConvEdge | 外部触发转换的边沿(上升、下降、无) |
ADC_ExternalTrigConv | 外部触发源选择(如定时器触发) |
ADC_DataAlign | 数据对齐方式(左对齐或右对齐) |
ADC_NbrOfConversion | 本次转换的通道数量(1 表示只采一个通道) |
第二章 ADC实验
1.4 实验1:ADC 单通道 检测电位器电压
PB1 -> ADC1_IN9
💡 本实验使用 STM32F4 的 ADC1,采样引脚为
PB1
(对应 ADC1_IN9),通过串口打印电压值。
✅ GPIO 配置函数
void GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
// 开启 GPIOB 时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
// 配置 PB1 为模拟输入模式
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AN; // 模拟模式
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL; // 无上下拉
GPIO_Init(GPIOB, &GPIO_InitStruct);
}
✅ ADC 配置函数
void ADC_Config(void)
{
ADC_InitTypeDef ADC_InitStruct;
ADC_CommonInitTypeDef ADC_CommonInitStruct;
// 开启 ADC1 时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
// 配置 ADC 公共部分
ADC_CommonInitStruct.ADC_Mode = ADC_Mode_Independent; // 独立模式
ADC_CommonInitStruct.ADC_Prescaler = ADC_Prescaler_Div4; // 预分频系数为4
ADC_CommonInitStruct.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled; // 不使用 DMA
ADC_CommonInitStruct.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles; // 两次采样之间延迟
ADC_CommonInit(&ADC_CommonInitStruct);
// 配置 ADC1 工作参数
ADC_InitStruct.ADC_Resolution = ADC_Resolution_12b; // 12位精度
ADC_InitStruct.ADC_ScanConvMode = DISABLE; // 单通道采样
ADC_InitStruct.ADC_ContinuousConvMode = DISABLE; // 单次转换
ADC_InitStruct.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None; // 软件触发
ADC_InitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1; // 必填,实际无效
ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right; // 结果右对齐
ADC_InitStruct.ADC_NbrOfConversion = 1; // 只转换1个通道
ADC_Init(ADC1, &ADC_InitStruct);
// 配置 ADC1 的规则通道 9(PB1 对应 ADC_Channel_9)
ADC_RegularChannelConfig(ADC1, ADC_Channel_9, 1, ADC_SampleTime_144Cycles);
// 启用 ADC1
ADC_Cmd(ADC1, ENABLE);
}
✅ 主函数
int main(void)
{
uint16_t adc_value = 0; // ADC原始数值
float voltage = 0.0f; // 转换后的电压值(单位:伏)
GPIO_Config(); // 配置模拟输入引脚
ADC_Config(); // 配置 ADC 模块
while (1)
{
ADC_SoftwareStartConv(ADC1); // 启动 ADC 转换
// 等待转换完成
while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);
// 读取转换结果
adc_value = ADC_GetConversionValue(ADC1);
// 计算电压值(假设参考电压为3.3V,12位精度)
voltage = (float)adc_value * 3.3f / 4095.0f;
// 串口输出采样结果
printf("ADC Value: %d, Voltage: %.3f V\r\n", adc_value, voltage);
Delay_ms(500); // 延时 500ms
}
}
- 感谢你赐予我前进的力量