STM32 ADC模数转换

目录

ADC简介

  • ADC(Analog-Digital Converter)模拟-数字转换器

  • ADC可以将引脚上连续变化的模拟电压转换为内存中存储的数字变量,建立模拟电路到数字电路的桥梁

  • 12位逐次逼近型ADC,1us转换时间。12位是分辨率,1us是转换频率,就是1MHz。

  • 输入电压范围:0 ~ 3.3V,转换结果范围:0~4095

  • 18个输入通道,可测量16个外部和2个内部信号源。2个内部的是温度传感器和内部参考电压。温度传感器可以测量CPU温度。内部参考电压是一个1.2V左右的基准电压。这个基准电压是不随外部供电电压变化而变化的。

  • 规则组和注入组两个转换单元

  • 模拟看门狗自动监测输入电压范围

  • STM32F103C8T6 ADC资源:ADC1、ADC2,10个外部输入通道,没有DAC转换

存在ADC当然也存在DAC,数字电路到模拟电路的桥梁。使用DAC可以将数字信号转化为模拟信号,其实之前学习的PWM就是数字到模拟的桥梁,这种方式也完成了数字信号输出模拟信号。因为PWM只有完全导通和完全断开两种状态,这两种状态都没有功率损耗,所以在直流电机调速这种大功率的应用场景使用PWM来等效模拟量比DAC本身更好用。并且PWM更加简单常用。DAC的应用主要是在波形生成等领域,比如信号发生器,音频解码芯片。

STM32F103C8T6只有两个ADC资源和10个外部引脚的模拟信号,上面说的16个外部信号源是这个系列最多有16个外部信号源。但是C8T6引脚较少,所以其他信号源没有。

逐次逼近型ADC

上图是ADC0809芯片的原理图,这个芯片是逐次逼近型的ADC转换和C8T6是一样的原理。

ADC0809是一个独立的8位逐次逼近型ADC,单片机内部没有集成ADC时需要外挂ADC芯片,ADC0809就是这么一款经典的ADC芯片。现在很多单片机内部已经集成了ADC外设,就不需要外挂芯片,可以直接测量电压。

IN7~IN0:8路模拟输入。

ADDA、ADDB、ADDC、ALE:地址锁存,选择当前的模拟输入引脚。相当于38译码器。

CLOCK:ADC时钟线,ADC需要时钟来推动这个过程。

START:开始AD转换。

EOC:转换结束标志位。

内部DAC:加权电阻网络,用于产生和输入模拟信号进行比较的模拟信号。

OE:输出使能,控制三态门输出。

D7~D0:输出的8位数字信号。

VREF(+)、VREF(-):参考电压。

内部存在一个电压比较器,可以判断两个输入信号电压的大小关系。左侧接入的通道是待测电压,下面DAC是电压输出端,输出到比较器。DAC的电压我们知道,通过这个电压和待测电压的比较,然后再调节DAC,逐次逼近待测电压。这样DAC的电压就是待测电压了。DAC电压调节的过程就是图中SAR来完成的。通常使用二分法来完成。最终电压通过锁存器输出,输出的值就是待测的值。图中输出的是8位,就有8根线,C8T6是12位就有12根线。

上面EOC是End Of Convert,转化结束信号。START是开始转换,给一个输入脉冲,开始转换。

CLOCK是ADC时钟线,,因为ADC内部是一步一步进行判断的,所以需要时钟来推动这个过程。VREF(+)和VREF(-)是DAC的参考电压。255对应是5V还是3.3V就由这个参考电压决定。这个电压也决定了ADC的输入范围,所以也是ADC的参考电压。

VCC和GND是整个芯片的供电,通常参考电压的正极和VCC是一样的,参考电压的负极和GND是一样的。

STM32的ADC

上图是ADC的结构图。

最左边是ADC的输入通道,包括16个GPIO口,IN0 ~ IN15,稍右边还有两个内部的通道,一个内部温度传感器,另一个是VREFINT(V Reference Internal),内部参考电压。总共是18个输入通道。

然后通过模拟多路开关指定我们想要选择的通道,然后输出到模数转换器。这里就没有给内部细节执行流程了,大致和上面说的转换流程是一样的。转换结果直接放到上面的数据寄存器中。读取寄存器就可以知道ADC转换的结果了。

普通的ADC转换只能转换一路ADC,这里可以转换多路。分为两组,规则通道组和注入通道组,规则通道组一次性最多选中16个通道,注入通道组最多选中4个通道。

注入通道【使用不多】:最多一次性选4路通道,配合4个16位寄存器,就可以一次性转换4路模拟数据。

规则通道【常用】:最多一次性选16路通道,但只有1个16位寄存器,存在新来的数据覆盖上一个数据的问题,此时要么尽快将数据取走,要是使用DMA帮助转运数据,进而可以实现一次性转换16路模拟数据。当然,一次就选一个通道,就是普通的ADC功能。

左下角是触发转化的部分,就是上面说的START信号。对于STM32,触发方式有两种。

软件触发:在程序中手动调一句代码。

硬件触发:上图所示的就是硬件触发源,分为注入组的触发源和规则组的触发源。主要来自于定时器TIMx,定时器TRGO主模式的输出,也可以外部中断引脚EXTI。

正常思路是:定时器每隔1ms产生一次中断 --> 中断函数中开启触发转换信号 --> ADC完成一次转换。缺点是需要频繁进入中断,消耗软件资源。但是得益于上图的硬件电路设计,stm32可以直接使用定时器主模式触发ADC转换,硬件全自动无需申请中断,可以极大地减轻CPU负担。

左上部分有VREF(+)和VREF(-)是ADC的参考电压,VDDA和VSSA是ADC的供电引脚,一般VREF(+)要接VDDA,VREF(-)要接VSSA

VDDA和VSSA在引脚定义中也可以看到

VDDA和VSSA是内部模拟部分的电源,如ADC,RC振荡器,锁相环等。这里的VDDA接了3.3V,VSSA接GND。

接着看框图,转换器右边的ADCCLK是ADC的时钟。

来自ADC的预分频器,这个ADC的预分频器则来自于"RCC时钟树"。具体可以查看时钟树的电路,APB2时钟输出72MHz然后通过ADC预分频器进行预分频得到ADCCLK,由于ADCCLK最大14MHz,所以只能选择6分频/8分频

接着看框图,ADCCLK上面是DMA请求,这个是用于触发DMA进行数据转运的。

模拟看门狗 :一旦高于上阈值或低于下阈值,就会申请模拟看门狗的中断,最终进入NVIC。
EOC :规则或注入通道转换完成信号。
JEOC :注入通道转换完成信号。

这两个信号会在状态寄存器里置一个标志位,通过读取这个标志位就能知道是不是转换结束。同时这两个标志位也可以去到NVIC申请中断。

输入通道

STM32有16个外部通道,这些通道对应的GPIO口如下

只有ADC1存在内部的两个ADC,其他两个不存在。c8t6不存在ADC3。

通过引脚定义表也可以知道对应哪些引脚,但是写的是ADC12,这表示ADC1和ADC2复用的一个引脚,为什么还要ADC2呢?

对于c8t6这个型号来说,ADC1和ADC2共用引脚,不仅可以单独使用,可以组成更加复杂的双ADC模式。双ADC模式通过配合可以组成同步模式、交叉模式(ADC1和ADC2交叉对同一个通道进行采样,以提高采样率)等。

规则组的转换模式

stm32的ADC最多同时支持16个通道,那么ADC每次扫描1个通道还是多个通道,便是选择非扫描模式/扫描模式 ;而对于单个通道的ADC转换来说,触发一次ADC是只转换一次,还是自动的进行连续转换,便是选择 单次转换/连续转换 。上面这两种选择进行组合,便产生了规则组的4种转换模式:

单次转换、非扫描模式

触发一次仅转换一次;仅序列1有效,但可以任意指定需要转换的通道。此时ADC选择一组的方式退化成只能选择一个。读取数据时,需要等待EOC标志位置1,然后从数据寄存器读取结果。如要再进行转换,就需要再次触发转换。

连续转换、非扫描模式

相比于上一个模式,仅需要一次触发,ADC就会在一次转换完成后立刻进入下一次转换,实现不断地自动进行转换。此时就不需要读EOC看转换是否完成,直接想读数据的时候就读。

单次转换、扫描模式

相比于第一种模式,可以一次性转换多个通道,不过还是触发一次、所有通道只转换一次。此时需要指定多少个序列,每个序列用的哪个通道。因为数据寄存器只有一个,所以此时需要DMA配合转运数据。n个通道转化完成后产生EOC信号就标志转换结束。

连续转换、扫描模式

不仅可以一次性转换多个通道,还可以实现触发一次、自动不间断转换。

上面所说的序列其实就是DMA模式下,ADC的采集顺序。

间断模式

每几次转换就暂停一下,需要再次触发才可以继续......

数据对齐

因为ADC是12位的,而寄存器宽度为16位,所有便有了数据对齐方式的选择。

右对齐【常用】:读出的值就是实际值。

左对齐:有时候不需要太大的分辨率,便将12位ADC的转换数据左对齐,然后只取高8位。

转换时间

低速采样可以忽略转换频率,高速采样必须考虑转换时间 的损耗。AD转换的步骤主要为:采样,保持,量化,编码。"采样"时间越长,越可以消除一些毛刺信号的干扰;而"量化、编码"是逐次比较的过程,消耗的时间则比"采样、保持"更长。"采样、保持"主要是因为"量化、编码"需要一段时间,所以"采样、保持"需要维持自己一段时间电压,不让这个电压不停的变化。

12.5个ADC周期因为是12位的ADC,这个周期就是ADCCLK。多出的0.5个周期在做其他事情。

校准

ADC有一个内置自校准模式。校准可大幅减小因内部电容器组的变化而造成的准精度误差。校准期间,在每个电容器上都会计算出一个误差修正码(数字值),这个码用于消除在随后的转换中每个电容器上产生的误差

建议在每次上电后执行一次校准

启动校准前, ADC必须处于关电状态超过至少两个ADC时钟周期
校准过程的代码是固定的,只需要在ADC初始化之后加几句代码即可。

代码

第一步,开启RCC时钟,包括ADC和GPIO的时钟。ADCCLK的配置

第二部,配置GPIO,将需要用到的GPIO配置为模拟输入模式

第三步,配置多路开关,将左边的通道接入到右边的规则组列表中

第四步,配置ADC转换器,在库函数在用结构体就可以配置好,可以配置AD转换器和AD数据寄存器。ADC的单次还是连续转换,扫描还是非扫描,有几个通道,触发源,数据对齐方式等。

第五步,如果需要配置模拟看门狗,有几个函数用来配置阈值和监测通道的。

第六步,开关控制没开启ADC。开启之后可以进行一下ADC校准。

库函数:

c 复制代码
void RCC_ADCCLKConfig(uint32_t RCC_PCLK2);

用来配置ADCCLK分频器的,可以对APB2的72MHz时钟选择2,4,6,8分频输入到ADCCLK。

c 复制代码
void ADC_DeInit(ADC_TypeDef* ADCx);
void ADC_Init(ADC_TypeDef* ADCx, ADC_InitTypeDef* ADC_InitStruct);
void ADC_StructInit(ADC_InitTypeDef* ADC_InitStruct);

ADC_DeInit恢复缺省配置,ADC_Init初始化,ADC_StructInit结构体初始化。

c 复制代码
void ADC_Cmd(ADC_TypeDef* ADCx, FunctionalState NewState);

用来给ADC上电,就是ADC的开关控制。

c 复制代码
void ADC_DMACmd(ADC_TypeDef* ADCx, FunctionalState NewState);

开启DMA输出信号,如果使用DMA转运数据就需要调用这个函数。

c 复制代码
void ADC_ITConfig(ADC_TypeDef* ADCx, uint16_t ADC_IT, FunctionalState NewState);

中断输出控制

c 复制代码
void ADC_ResetCalibration(ADC_TypeDef* ADCx);
FlagStatus ADC_GetResetCalibrationStatus(ADC_TypeDef* ADCx);
void ADC_StartCalibration(ADC_TypeDef* ADCx);
FlagStatus ADC_GetCalibrationStatus(ADC_TypeDef* ADCx);

分别是复位校准,获取复位校准状态,开始校准,获取开始校准状态

c 复制代码
void ADC_SoftwareStartConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState);

ADC软件开始转换控制,这个是用于软件触发的函数

c 复制代码
FlagStatus ADC_GetSoftwareStartConvStatus(ADC_TypeDef* ADCx);

ADC获取软件开始转换状态,功能是给SWSTART位置1,以开始转换。返回SWSTART的状态,但是手册上说,在置1后立刻由硬件置0。这个函数大多数情况下没用,而是用下面的。

c 复制代码
FlagStatus ADC_GetFlagStatus(ADC_TypeDef* ADCx, uint8_t ADC_FLAG);

获取标志位状态,参数给EOC的标志位,判断EOC标志位是不是置1,如果置1,那么转换结束。

c 复制代码
void ADC_DiscModeChannelCountConfig(ADC_TypeDef* ADCx, uint8_t Number);
void ADC_DiscModeCmd(ADC_TypeDef* ADCx, FunctionalState NewState);

用来配置间断模式。

c 复制代码
void ADC_RegularChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel, uint8_t Rank, uint8_t ADC_SampleTime);

ADC规则组通道配置,给序列的每个位置填写指定的通道

c 复制代码
void ADC_ExternalTrigConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState);

ADC外部触发转换控制,就是是否允许外部触发转换。

c 复制代码
uint16_t ADC_GetConversionValue(ADC_TypeDef* ADCx);

ADC获取转换值,获取AD转化的数据寄存器,读取转换结果就要调用这个函数。

c 复制代码
uint32_t ADC_GetDualModeConversionValue(void);

ADC获取双模式转换值,这是双ADC模式读取转换结果的函数。

c 复制代码
void ADC_AnalogWatchdogCmd(ADC_TypeDef* ADCx, uint32_t ADC_AnalogWatchdog);
void ADC_AnalogWatchdogThresholdsConfig(ADC_TypeDef* ADCx, uint16_t HighThreshold, uint16_t LowThreshold);
void ADC_AnalogWatchdogSingleChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel);

上面三个函数都是对模拟看门口进行配置的。是否启动模拟看门狗,配置高低阈值,配置看门的通道。

c 复制代码
void ADC_TempSensorVrefintCmd(FunctionalState NewState);

ADC温度传感器内部参考电压控制,用来开启内部的两个通道,如果要使用这两个通道,就要调用这个函数。

软件触发单次转换非扫描模式

c 复制代码
#include "Config.h"
void AD_Init()
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	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);
	
	//哪个ADC,用通道几,使用第几个序列,采样时间
	ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_13Cycles5);//ADC规则组通道配置,给序列的每个位置填写指定的通道
	
	ADC_InitTypeDef ADC_InitStructure;
	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;//独立模式还是双ADC模式
	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//数据对齐方式
	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//外部触发转换选择,即触发控制的触发源,这里选择软件触发
	ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;//连续转换模式,选择连续转换还是单次转换
	ADC_InitStructure.ADC_ScanConvMode = DISABLE;//扫描转换模式,选择扫描模式还是非扫描模式
	ADC_InitStructure.ADC_NbrOfChannel = 1;//通道数目,在扫描模式下用到几个通道
	ADC_Init(ADC1, &ADC_InitStructure);
	
	ADC_Cmd(ADC1, ENABLE);
	
	ADC_ResetCalibration(ADC1);//复位校准
	while(ADC_GetResetCalibrationStatus(ADC1) == SET);//等待复位校准完成
	ADC_StartCalibration(ADC1);//开始校准
	while(ADC_GetCalibrationStatus(ADC1) == SET);//等待校准完成
}


uint16_t AD_GetValue()
{
	ADC_SoftwareStartConvCmd(ADC1, ENABLE);
	while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);
	return ADC_GetConversionValue(ADC1);
}
相关推荐
信奥洪老师1 小时前
2025年12 电子学会 机器人三级等级考试真题
单片机·嵌入式硬件·机器人
程序员zgh1 小时前
MCU 锁步(Lockstep)
单片机·嵌入式硬件
恶魔泡泡糖2 小时前
最小系统组成部分
c语言·单片机
czhaii2 小时前
USB拓展库及使用示例
单片机·嵌入式硬件·硬件工程
iCxhust2 小时前
8088单板机C语言汇编混合编程实验方法与步骤
c语言·汇编·单片机·嵌入式硬件·微机原理
逆小舟3 小时前
【RTOS】任务间通信IPC
单片机·嵌入式硬件
电化学仪器白超3 小时前
《可编程固定阻值电子负载的制作与自动化标定技术》
python·单片机·嵌入式硬件·自动化
三佛科技-134163842123 小时前
LP3799FAES-B 反激式电源控制器芯片 典型应用电路
单片机·嵌入式硬件·物联网·智能家居·pcb工艺
风行男孩4 小时前
stm32基础学习——按键的使用
stm32·嵌入式硬件·学习