跳转至

从零开始学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, 嵌入式系统, 模数转换, 寄存器配置