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);
}
相关推荐
Python小老六28 分钟前
从“0”开始入门PCB之(5)完结篇!--快速入门原理图DRC,PCB的符号与布局,2D与3D效果,PCB的图层和布线,PCB板框
单片机·嵌入式硬件·3d
花落已飘2 小时前
STM32Cubemx配置E22-xxxT22D lora模块实现定点传输
stm32·单片机·嵌入式硬件
我爱蛋蛋后3 小时前
Linux驱动开发之ADC驱动与基础应用编程
linux·c语言·驱动开发·adc
BUG_MeDe3 小时前
[51 单片机] --串口编程
单片机·嵌入式硬件
华清远见成都中心6 小时前
学单片机能从事什么工作?
单片机·嵌入式硬件
cykaw259013 小时前
如果STM32板子上晶振不是8MHz而是其他(如12MHz)怎么办?
stm32·单片机·嵌入式·电子
三年呀15 小时前
19.4-STM32接收数据-状态显示在屏幕
stm32·单片机·嵌入式硬件·智能小车
晓风伴月18 小时前
单片机应用:定时闪烁的LED小灯的实现
单片机·嵌入式硬件
平凡灵感码头18 小时前
STM32 两个单片机之间的通信
stm32·单片机·mongodb
晓风伴月19 小时前
单片机入门(一)
单片机·嵌入式硬件