从零开始学STM32:掌握ADC模数转换原理与应用技巧
原文地址: https://88box.top 生成时间: 2026-05-20 01:04:06
从零开始学嵌入式之STM32——34.ADC-模数转换 - hey99 知识搜索引擎
精选文章
从零开始学嵌入式之STM32——34.ADC-模数转换
ADC(analog digital convert)模拟-数字转换。在嵌入式领域,ADC模块是现实世界与数字世界的桥梁,在现实世界中,信号是连续变化的模拟量,ADC模块通过采集不断变化的模拟电压信号,将其转换为具有特定含义的二进制编码,实现信号的输入。
更新于 2026-05-19 16:27
目录
一、ADC模数转换简介
二、AD转换工作原理
(1)采样阶段
(2)量化阶段
三、ADC主要参数
(1)参考电压
(2)通道数
(3)采样率
(4)分辨率
(5)转换时间
四、使用STM32中的ADC
(1)功能框图
(2)输入通道
1.规则通道组
2.注入通道组
五、STM32 ADC 寄存器方式配置与使用
5.1 ADC 时钟配置
5.2 ADC 转换方式配置
5.2.1 扫描模式(ADC_CR1->SCAN)
5.2.2 连续/单次转换(ADC_CR2->CONT)
5.3 数据对齐模式选择
5.4 转换时间配置
5.4.1 总转换时间计算
5.4.2 采样时间配置寄存器
5.5 ADC 编码转换为电压数据
5.6 ADC 核心寄存器详解与配置
5.6.1 控制寄存器2(ADC_CR2)关键位
5.6.2 规则序列寄存器(ADC_SQR1~3)
5.6.3 采样时间寄存器(ADC_SMPR1)
5.7 寄存器方式 ADC 完整配置流程(总结)
六、寄存器方式使用ADC读取芯片内部温度与参考电压的实现
(1)整体思路
(2)实现代码
七、HAL库实现
(1)基础配置(按实际情况)
(2)ADC与DMA配置
(3)调用HAL库实现业务
一、ADC模数转换简介
ADC(analog digital convert)模拟-数字转换。
在嵌入式领域,ADC模块是现实世界与数字世界的桥梁,在现实世界中,信号是连续变化的模拟量,ADC模块通过采集不断变化的模拟电压信号,将其转换为具有特定含义的二进制编码,实现信号的输入。
二、AD转换工作原理
AD转换指的就是模数转换。转换的阶段可以分为
采样阶段
和
量化阶段
。采样实现
时间离散
,量化实现
幅值离散
,再经编码完成模数转换。
(1)采样阶段
对于模拟量,不可能每时每刻都采集到它的值,在实际应用中,采用的策略是:每一段时间采集一次,这样得到一组离散的数据量。
tips:
若采样频率满足采样定理,便可由采样后的离散数据无失真还原原始模拟波形。
(2)量化阶段
将采样得到的
连续电压幅值
,按照 ADC 的分辨率划分成若干个
等级刻度
,把无限连续的电压值
近似归整
到最接近的数字刻度上。再对每个量化后的等级进行
二进制编码
,最终输出单片机能识别的数字量。
量化的方法:
逐次逼近型:最常见
,通过逼近法模拟信号的大小,性能均衡。
sigma-delta:
通过比较两个信号的累积值和一个参考值,产生一个高精度的输出,精度高,但是转换时间长
flash:
通过一组比较器和编码器,以高速并行的方式进行转换,速度快,但是分辨率低,精度一般。
(3)逐次逼近型工作原理简介
在STM32中的ADC模使用的就是逐次逼近型ADC,其工作原理类似砝码天平称重的原理。下图是芯片中的ADC电路图:
假如8位DA转换器的参考电压是256V(方便计算,这样二进制数每增加1,就代表了电压增加1)
待测电压33V
在测量时,会按照如下步骤测量:
规则:
每位单独置位电压:D7=128V、D6=64V、D5=32V、D4=16V、D3=8V、D2=4V、D1=2V、D0=1V
逻辑控制电路先将 8 位寄存器
D7 位置位
,DA 转换器输出电压
128V
;
比较器与待测电压比较,待测电压低于 128V,比较器输出
低电平
;
8 位寄存器
D7 清零
,
D6 位置位
,DA 转换器输出
64V
;
比较器与待测电压比较,待测电压低于 64V,比较器输出
低电平
;
8 位寄存器
D6 清零
,
D5 位置位
,DA 转换器输出
32V
;
比较器与待测电压比较,待测电压高于 32V,比较器输出
高电平
;
8 位寄存器
D5 保持为 1
,
D4 位置位
,DA 转换器输出
48V
;
比较器与待测电压比较,待测电压低于 48V,比较器输出
低电平
;
8 位寄存器
D5 保持、D4 清零
,
D3 位置位
,DA 转换器输出
40V
;
比较器与待测电压比较,待测电压低于 40V,比较器输出
低电平
;
8 位寄存器
D5 保持、D4 清零、D3 清零
,
D2 位置位
,DA 转换器输出
36V
;
比较器与待测电压比较,待测电压低于 36V,比较器输出
低电平
;
8 位寄存器
D5 保持、D4/D3/D2 清零
,
D1 位置位
,DA 转换器输出
32V
;
比较器与待测电压比较,待测电压高于 32V,比较器输出
高电平
;
8 位寄存器
D5、D1 保持
,
D0 位置位
,DA 转换器输出
33V
;
比较器与待测电压比较,等于待测电压,比较器输出
高电平
;
总结:
以 8 位逐次逼近型 ADC、参考电压 256V 为例,逻辑控制电路从
最高位 D7 到最低位 D0
依次逐位试探:先将最高位置 1,由 DAC 输出对应权值电压,与待测电压送入比较器对比;若待测电压
低于
DAC 输出电压,该位
清零
;若
高于
,该位
保留置 1
;逐位试探完毕后,8 位寄存器中保存的二进制数值,即为待测电压对应的数字量。
比较器仅能输出高低电平,只能判断电压大小,无法识别电压相等;因此逐次逼近 ADC 必须从最高位到最低位逐位全部比较完成,不能中途提前结束转换。
三、ADC主要参数
(1)参考电压
作为 ADC 模数转换的电压基准,决定了
可测量模拟电压的最大幅度范围
,所有模拟输入电压都以参考电压为量程上限进行量化换算。
(2)通道数
ADC 具备多路独立模拟输入接口,
通道数越多,可同时或分时采集更多路不同来源的模拟信号
,如温度、电压、光照等多路模拟量。
(3)采样率
指 ADC
每秒能够完成的采样次数
。采样率越高,ADC 捕捉高频变化模拟信号的能力越强,不易发生信号失真。
(4)分辨率
表示 ADC 能把满量程模拟电压划分成多少个
离散量化等级
,通常以位数表示。
例如:12 位 ADC,总级数为
2^12 = 4096
个离散级别;位数越高,划分越精细,电压识别精度越高。
(5)转换时间
指 ADC 完成
一次模数转换所需要的时间
,由 ADC 工作时钟频率、分辨率以及转换架构共同决定;分辨率越高,通常单次转换时间越长。
四、使用STM32中的ADC
在STM32中,集成了ADC功能,不同的系列里的资源有所不同,部分型号参考下图:
不论如何,所有系列至少都有一个ADC1,并且其功能基本上是通用的,我们就再ADC1上展开讨论。
(1)功能框图
(2)输入通道
ADC的输入通道用来输入模拟电压的通道,ADC1模块有18个通道:
16个外部通道(ADCx_IN ~ ADCx_IN15),对应16个IO口;
内部温度传感器(测量芯片温度):通道16;
内部电压,(测量芯片供电电压):通道17。
所有的通道都接到了模拟多路开关,可以通过编程对通道进行控制,实现输入信号的选择和切换。
ADC同一时刻只能转换一路信号,在需要采集多路信号时,可以将多路信号进行排列,按照一定的顺序依次进行检测、转换、输出。在芯片中,为ADC设置了规则通道组和插入通道组两种类型的队列。
规则通道组:其实更应该翻译为常规组。
插入通道组:更应该翻译为特殊组,可以插队。
1.规则通道组
可理解为
常规 / 普通通道组
,是最常用的转换队列。
最多可配置 16 个通道;
转换完成后,数据存入
共用的规则数据寄存器 DR
;
由于多通道共用一个寄存器,
新数据会直接覆盖旧数据
;
为避免数据丢失,必须
及时读取数据
,或
开启 DMA 自动搬运
。
2.注入通道组
可理解为
优先 / 可插队通道组
,用于紧急、实时性要求高的信号。
最多支持 4 个通道;
每个通道拥有
独立的数据寄存器
,不会出现数据覆盖;
可随时打断规则通道的转换,执行优先级更高的任务;
转换完成后
无 DMA 请求
,通常由 CPU 直接读取。
注意:
在ZET6系列芯片中,ADC2没有DMA通道,ADC1和ADC3有DMA通道
五、STM32 ADC 寄存器方式配置与使用
5.1 ADC 时钟配置
STM32的ADC模块的时钟频率不能超过14MHz,因此RCC外设中专门为ADC配置了预分频器(通过RCC->ADCPRE寄存器),用于选择输入时钟的分频系数,具体配置如下:
RCC->ADCPRE 寄存器配置(2位控制位):
00:PCLK2 2分频
01:PCLK2 4分频
10:PCLK2 6分频
11:PCLK2 8分频
5.2 ADC 转换方式配置
ADC的转换方式主要通过ADC_CR1和ADC_CR2两个寄存器的对应位配置,分为扫描模式和连续/单次转换模式,具体如下:
5.2.1 扫描模式(ADC_CR1->SCAN)
开启(置1):多通道转换模式,按顺序对通道组中的所有通道依次扫描、逐个转换;
关闭(置0):单通道转换模式,仅转换通道组中第一个通道。
5.2.2 连续/单次转换(ADC_CR2->CONT)
开启(置1):连续转换模式,转换一轮完成后,无需等待外部触发,自动开始下一轮转换,持续进行;
关闭(置0):单次转换模式,转换一轮完成后停止,需等待下次触发(软件或外部触发)才能启动新的转换。
5.3 数据对齐模式选择
ADC的转换结果存储在16位的DR寄存器中,而ADC的分辨率为12位,因此存在数据对齐问题,通过ADC_CR2->ALIGN寄存器配置,默认采用右对齐模式,具体说明如下:
右对齐(默认):转换结果的低12位有效,高4位补零,是实际使用中最常用的对齐方式;
左对齐:转换结果的高12位有效,低4位补零。
注意:注入组转换得到的结果包含符号位,若为正数,高位补0;若为负数,高位补1。
5.4 转换时间配置
ADC转换分为采样和模数转换两个阶段:首先使用若干个ADC时钟周期对输入电压进行采样(采样周期可通过寄存器配置),随后经过固定的12.5个ADC时钟周期完成模数转换。
5.4.1 总转换时间计算
总转换时间 = 采样时间 + 12.5个ADC周期
示例:当ADC时钟为14MHz(最大允许频率),假设采样时间配置为1.5个ADC周期,则总转换时间 = 1.5 + 12.5 = 14个ADC周期,对应时间为1μs(14MHz时钟周期为1/14μs,14个周期即1μs)。
5.4.2 采样时间配置寄存器
采样时间通过ADC_SMPR1寄存器进行配置,可根据实际需求选择不同的采样周期,以平衡转换速度和采样精度。
5.5 ADC 编码转换为电压数据
模拟电压经过ADC转换后,会得到一个12位的数字值,直接打印该数字值可读性较差,需将其转换为对应的模拟电压值。
实际设计中,ADC的输入电压范围通常设定为0~3.3V(与STM32的IO口供电电压匹配),由于ADC为12位分辨率,其满量程(所有位为1)对应的数字值为2¹² - 1 = 4095,数字值0对应模拟电压0V。
设转换后的数字值为x,对应的模拟电压为Y,两者满足以下等式(线性关系):
Y = (x / 4095) × 3.3V
通过该等式可将数字值换算为直观的模拟电压。
5.6 ADC 核心寄存器详解与配置
ADC的所有配置均通过对应寄存器实现,以下为核心寄存器的功能及配置要点,结合前文配置需求,明确各寄存器的作用及使用方法:
5.6.1 控制寄存器2(ADC_CR2)关键位
CONT:连续转换控制位,对应5.2.2节,控制ADC为连续或单次转换模式;
ALIGN:数据对齐方式控制位,对应5.3节,控制转换结果为右对齐或左对齐,默认右对齐;
EXTTRIG:外部触发选择位,用于开启或关闭外部触发转换功能;
EXTSEL:触发源选择位,当开启外部触发(EXTTRIG置1)时,用于选择触发ADC转换的外部信号源;
SWSTART:软件触发控制位,置1时启动一次ADC转换(适用于软件触发模式);
CAL:校准控制位,置1时启动ADC校准,校准完成后由硬件自动清零(校准需在ADC转换前执行,确保转换精度);
ADON:ADC模块启动位,第一次置1启动ADC模块,再次置1触发一次ADC转换(单次转换模式下)。
5.6.2 规则序列寄存器(ADC_SQR1~3)
规则组通道的配置通过ADC_SQR1、ADC_SQR2、ADC_SQR3三个寄存器实现,具体要点如下:
ADC_SQR1->L:规则组通道长度控制位,4位宽度,取值范围0~15,对应规则组包含1~16个通道;
通道编号:STM32 ADC共18个通道,每个通道需用5位控制位表示,三个SQR寄存器共可提供80位(16×5)控制位,刚好覆盖1~16个规则通道的配置(ADC_SQR1_SQ相关位为5位宽度,用于配置具体通道号)。
5.6.3 采样时间寄存器(ADC_SMPR1)
用于配置各通道的采样时间,通过对应位的配置,可选择不同的采样周期(如1.5、7.5、13.5等ADC周期),结合ADC时钟频率,决定采样精度和转换速度(采样周期越长,采样精度越高,转换速度越慢)。
5.7 寄存器方式 ADC 完整配置流程(总结)
结合上述所有配置要点,寄存器方式配置ADC的完整流程如下:
配置 ADC 时钟
:通过
RCC->CFGR
的
ADCPRE
位设置预分频,确保不超过 14MHz;
配置转换模式
:通过
ADC_CR1->SCAN
设置扫描 / 单通道,
ADC_CR2->CONT
设置连续 / 单次;
配置数据对齐
:通过
ADC_CR2->ALIGN
设置右对齐(默认)或左对齐;
配置采样时间
:通过
ADC_SMPR1/2
设置各通道采样周期;
配置规则组通道
:通过
ADC_SQR1~3
设置序列长度与通道号;
ADC 上电
:置位
ADC_CR2->ADON
,给 ADC 上电(
必须先上电
);
ADC 校准
:置位
ADC_CR2->CAL
启动校准,等待
CAL
自动清零;
启动转换
:软件触发置位
SWSTART
,或等待外部触发,开始转换;
读取数据
:转换完成后读取
DR
寄存器,换算成电压。
六、寄存器方式使用ADC读取芯片内部温度与参考电压的实现
(1)整体思路
通过ADC的规则组队列采集多路信号时,为了防止DR中的数据被覆盖。需要打开ADC的DMA功能,每转换完成一路信号,通过DMA传送到内存中,确保数据的准确。
初始化函数:
ADC初始化:
1.时钟配置
2.ADC配置
2.1.工作模式:配置扫描模式
2.2.启用连续转换模式(单曲循环)
2.3.数据对齐,右对齐(默认)
2.4.通道配置
2.4.1.设置通道的采样时间SMPR寄存器 001代表7.5个周期
2.4.2.规则组通道序列配置-规则组通道数量配置--L
2.4.3.将通道号保存到通道序列中--SQR3(倒着排列的)
2.5.触发方式选择--使用软件触发AD转换
DMA初始化:
1.时钟配置
2.数据宽度配置
3.传输方向配置
4.地址自增配置
开始转换函数
1.上电唤醒
2.执行校准,等待校准完成
3.开启DMA(DMA必须在上电后开启,否则不生效)
3.1.设置DMA,源地址、目的地址、数据长度等
4.启动转换
5.等待转换完成
计算函数
1.读取数据后进行计算并输出
(2)实现代码
文件名adc.c
include "adc.h"
void ADC_Init(void)
{
// 1.打开时钟
RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;
// 2.ADC输入时钟频率设置,主时钟频率6分频-12MHz-10
RCC->CFGR |= RCC_CFGR_ADCPRE_1;
RCC->CFGR &= ~RCC_CFGR_ADCPRE_0;
// 3.ADC工作模式配置
// 3.1.扫描模式开
ADC1->CR1 |= ADC_CR1_SCAN;
// 3.2.连续转换模式开
ADC1->CR2 |= ADC_CR2_CONT;
// 3.3.外部触发配置
ADC1->CR2 |= ADC_CR2_EXTTRIG;
ADC1->CR2 |= ADC_CR2_EXTSEL;
// 3.4.数据对齐设置,右对齐(默认)
ADC1->CR2 &= ~ADC_CR2_ALIGN;
// 3.5.使用芯片内部传感器,开启对应控制位
ADC1->CR2 |= ADC_CR2_TSVREFE;
// 4.转换列表配置,列表长度2
ADC1->SQR1 &= ~ADC_SQR1_L;
ADC1->SQR1 |= ADC_SQR1_L_0;
ADC1->SQR3 &= ~ADC_SQR3_SQ1;
ADC1->SQR3 &= ~ADC_SQR3_SQ2;
ADC1->SQR3 |= 16 << 0;
ADC1->SQR3 |= 17 << 5;
// 5.采样频率设置,
/*
内置温度传感器的采样频率按照手册要求,不能低于17.1us,因此通道16的周期需要设置为239.5;-SMP - 111
通道17为检测芯片工作电压,采样频率无需过高,也可以采用239.5
*/
ADC1->SMPR1 |= ADC_SMPR1_SMP16;
ADC1->SMPR1 |= ADC_SMPR1_SMP17;
// 6.ADC模块上电
ADC1->CR2 |= ADC_CR2_ADON;
// 7.开启DMA(注意,该步骤需要在ADC模块上电后才可操作,否则无效)
ADC1->CR2 |= ADC_CR2_DMA;
}
void ADC_Start_ON_DMA(uint16_t* data, uint16_t size)
{
// 1.开启ADC校准,并等待校准完成
ADC1->CR2 |= ADC_CR2_CAL;
while(ADC1->CR2 & ADC_CR2_CAL);
// 2.设置DMA传输信息
DMA_Truansit((uint32_t)&(ADC1->DR), (uint32_t)data, size);
// 3.开启ADC转换
ADC1->CR2 |= ADC_CR2_SWSTART;
}
文件名:dma.c
include "dma.h"
void DMA_Init(void)
{
// 1.打开时钟
RCC->AHBENR |= RCC_AHBENR_DMA1EN;
// 2.DMA工作模式配置
// 2.1.数据宽度设置, 16位数据宽度-01
DMA1_Channel1->CCR &= ~DMA_CCR1_MSIZE_1;
DMA1_Channel1->CCR |= DMA_CCR1_MSIZE_0;
DMA1_Channel1->CCR &= ~DMA_CCR1_PSIZE_1;
DMA1_Channel1->CCR |= DMA_CCR1_PSIZE_0;
// 2.2.数据传输方向设置,从外设读-DIR- 0
DMA1_Channel1->CCR &= ~DMA_CCR1_DIR;
// 2.3.地址自增设置,外设地址不自增,内存地址自增
DMA1_Channel1->CCR &= ~DMA_CCR1_PINC;
DMA1_Channel1->CCR |= DMA_CCR1_MINC;
// 2.3.循环模式配置,打开循环模式
DMA1_Channel1->CCR |= DMA_CCR1_CIRC;
// // 3.开启DMA通道
// DMA1_Channel1->CCR |= DMA_CCR1_EN;
}
void DMA_Truansit(uint32_t src, uint32_t dst, uint16_t dataLen)
{
// 1.暂停DMA通道
DMA1_Channel1->CCR &= ~DMA_CCR1_EN;
// 2.设置源地址
DMA1_Channel1->CPAR = src;
// 3.设置目的地址
DMA1_Channel1->CMAR = dst;
// 4.设置数据长度
DMA1_Channel1->CNDTR = dataLen;
// 5.开启DMA通道
DMA1_Channel1->CCR |= DMA_CCR1_EN;
}
文件名main.c
include "usart.h"
include "dma.h"
include "adc.h"
include "delay.h"
int main(void)
{
// data[0]存放通道16读取的温度原始值
// data[1]存放通道17读取的电压原始值
uint16_t data[2] = {0};
float temperature = 0.0f;
float voltage = 0.0f;
Usart1_Init();
DMA_Init();
ADC_Init();
ADC_Start_ON_DMA(data, 2);
printf("test\n");
while(1)
{
temperature = (1.43f - (float)data[0] * 3.3 / 4095.0f) / 0.0043f + 25.0f;
voltage = (float)data[1] * 3.3 / 4095.0f;
printf("Temperature = %.2f\nVoltage = %.2f\n", temperature, voltage);
Delay_nms(1000);
}
}
七、HAL库实现
(1)基础配置(按实际情况)
(2)ADC与DMA配置
(3)调用HAL库实现业务
int main(void)
{
/ USER CODE BEGIN 1 /
uint16_t adc_data[2] = {0};
float temperature = 0.0f;
float voltage = 0.0f;
/ USER CODE END 1 /
/ MCU Configuration--------------------------------------------------------/
/ Reset of all peripherals, Initializes the Flash interface and the Systick. /
HAL_Init();
/ USER CODE BEGIN Init /
/ USER CODE END Init /
/ Configure the system clock /
SystemClock_Config();
/ USER CODE BEGIN SysInit /
/ USER CODE END SysInit /
/ Initialize all configured peripherals /
MX_GPIO_Init();
MX_DMA_Init();
MX_ADC1_Init();
MX_USART1_UART_Init();
/ USER CODE BEGIN 2 /
// 数据校准
HAL_ADCEx_Calibration_Start(&hadc1);
// adc开始转换
HAL_ADC_Start_DMA(&hadc1, (uint32_t*)adc_data, 2);
//HAL_ADCEx_MultiModeStart_DMA(&hadc1, (uint32_t*)adc_data, 2);
/ USER CODE END 2 /
/ Infinite loop /
/ USER CODE BEGIN WHILE /
while (1)
{
temperature = (1.43f - (float)adc_data[0] * 3.3f / 4095.0f) / 0.0043f + 25.0f;
voltage = (float)adc_data[1] * 3.3f / 4095.0f;
printf("temperature = %.2f\nvoltage = %.2f\n", temperature, voltage);
HAL_Delay(1000);
/ USER CODE END WHILE /
/ USER CODE BEGIN 3 /
}
/ USER CODE END 3 /
}
查看原文
🏷 标签: STM32, ADC, 嵌入式系统, 模数转换, 寄存器配置