文章目录
一、ADC基础知识
1.1 ADC 简介
ADC 模数转换器,Analog-to-Digital Converter的缩写,通常是指将连续变化的模拟信号转换为离散的数字信号的电子元件。
1.2 为什么需要 ADC
微控制器MCP是离散的数字器件,而实际的物理信号时连续模拟的,如温度、湿度、压力、速度等等。
通过传感器将带测量的模拟信号转换成电信号,而这个电信号也是连续的模拟量。
所以为了便于MCU处理数据,我们通过模拟-数字转换器ADC将传感器的连续的模拟电信号转换成离散的数字电信号。
数据采集系统如下图所示,ADC为核心。
数据采集系统由模拟信号采集,A/D转换、数字信号处理三大部分组成。
1.3 ADC的历史由来
-
二元权重取水法
如18世纪的二元权重取水法,就运用了模拟量转数字量的思想。
用8倍,4倍,2倍,1倍不同粗细的管子在在池子边上凿开许多眼。取水的时候把水量转化成数据,然后堵上一些管子,留下一些管子,放指定长的时间,就得到了一定分数的水。这也是一个模拟量变成数字量的过程。显然,给水的精度就是最细的那根管子放单位时间的水量。比这个更小的水量就忽略不记了,即把它当作误差了。 -
天平称重
联系现实,我们用天平称量重量也用到这种思想。
我们在做某些化学实验需要称量一定重量的物品,我们会使用到天平秤。称量时我们使用到砝码去称量所需物品的重量。我们需要多少克的物品,我们就放相应重量的砝码。(这里假设我们只用一种重量的砝码)
例如:只使用5g的砝码,称量40g的NaCl,这时我们就需要8个5g的砝码。
由上例,如果我们需要42g的NaCl,用5g的砝码就测量不出来。显然用5g砝码称量5的倍数的重量的物品才精确。
这时我们引出精度的概念,我们称量物品的精度就是最小重量的砝码确定的。如果我们使用1g的砝码,我们就能够准确称量1的倍数的重量的物品。 -
1-bit ADC
1bit ADC就是运用一个比较器来判断电压大小。
例:电压范围是0-3.3V,参考电压Uref=1.68V。输入电压Uin>Uref,就得到逻辑1,输入电压Uin<Uref,就得到逻辑0。 -
2-bit ADC
我们把输入电压给到3个电压比较器的比较端,3个电压比较器分别使用3个不同的参考电压Uref作为阈值。于是,我们电压就在这4个区间之中的一个区间中。我们把这4个区间识别成00、01、10、11。
提问:如果电压在Rank2,那么Uout1=?Uout2=?Uout3=?
Uout1=1,Uout2=1,Uout3=0
输出了 3个bit 的数据,而我们需要 2bit 的数据,这时我们就还需一个编码电路把这 3bit 的输出 转化为对应的 2bit 的输出才形成我们的2bit ADC。
可见,一个完整的2bit ADC就由3个比较器,1个编码电路组成的。
-
结构原理最简单的ADC(n-bit ADC):FLASH ADC
由这种利用电压比较器的思想做出来的ADC就是FLASH ADC。
这种比较器优点是很快速。
缺点是电路复杂。精度越高,电路越复杂。
比如说我们要一个12bit精度,我们就需要2^12-1=4095的比较器放在一起工作。这样占用空间就大,连接复杂。
由上可见,FLASH ADC想法很简单,但是却实现困难。我们就转换思路,想想有没有其他方式实现ADC。
- Counting ADC
如图:将600KHz的CLOCK100分频,作为脉冲取样频率(6kHz)。仅当脉宽PULSE WIDTH MODULATOR为逻辑1的时候,与门输出1。后面的计数器COUNTER就在这个时间宽度内数一数有多少个逻辑1,即有多少个窄高脉冲。这个计数值就量化了这个脉冲信号的宽度。脉冲信号的宽度与输入脉冲的幅度相关。于是就实现了对模拟输入电压的数字量化。
逐次渐进型(SA)ADC
类比猜数字游戏,给一个数字7让你猜。你猜是8,提示高了。你猜是6,提示猜低了。你猜7,对了。
逐次渐进型ADC就是类似于猜数字的思想。
猜一个数,我们先用DAC将数字还原成一个模拟电压值,与输入电压值比较。比较器告诉我们是大了,还是小了。然后我们在产生一个数,比较器再告诉我们是大了还是小了。猜的次数越多,就越逼近真实值。
逐次渐进型ADC就是一次次的去猜模拟电压,不断逼近真实的模拟电压。
例如下图:假设X = 45,去猜数字X。
下图是4bits的逐次渐进型ADC的一次猜数字过程。
逐次渐进型ADC用时间换取了空间,通过多次进行比较(时间) 去换取 多个比较器并行起来去比较(空间)。
在输入端加个电容,可以用来加强输入电压的稳定性。
1.4 A/D转换器的一些技术指标
- 分辨率(Resolution,可以理解为猜数的次数n,n越大越精确)
分辨率Resolution = 2n。
分辨率是指数字量变化一个最小量时模拟信号的变化量,定义为满刻度与2^n的比值。通常以数字信号的位数来表示。显然A/D转换器的位数越多,分辨最小模拟电压的值就越小。
如一个最大输入电压为5V的8位A/D转换器,所能分辨的最小输入电压变化量为5V / 28 = 19. 53mV。而同样输入电压大小的10位A/D转换器,分辨的最小输入电压变化量为5V / 210= 4. 88mV。 - 转换时间
转换时间是指完成一次转换所用的时间。即从发出转换控制信号开始,直到输出端得到稳定的数字输出为止所用的时间,通常为微秒级。
一般约定,转换时间大于1ms的为低速,1ms~ 1μs的为中速,小于1μs的为高速,小于1 ns的为超高速。 - 转换速率(Conversion Rate)
转换时间的倒数称为转换速率。即每秒转换的次数。
例如ADC0809的转换时间为100μs,则转换速率为每秒1万次。 - 相对精度。
相对精度是指A/D转换器实际输出数字量与理论输出数字量之间的最大差值。通常用最低有效位LSB的倍数来表示。
1.5 ADC转换基本原理
AD转换中通常要完成采样保持和量化编码两个内容。(采样保持的时间取决于量化编码的算法)
-
采样保持
图解:f(t)作为输入,采样器在一个高脉冲到来时进行采样(即闭合开关)。 -
量化编码(模/数转换的核心)
1.量化:将取样电压表示为最小数量单位(∆)的整数倍
2.编码:将量化的结果用代码表示出来(二进制,十进制)
3.量化误差:当采样电压不能被∆整除时,将引入量化误差
常见的量化编码技术有:计数式转换、双积分式转换、逐次逼近式转换、并联式转换等。
图解:输入0-1V的信号,用3位二进制数表示。常见思路,量化得到 ∆=1/8;编码就是对每一段区间的电压用二进制代码标识。量化误差是∆。
二、STM32的ADC
2.1 引入例子ADC0809(粗略了解)
IN7-IN0:8路模拟量输入线。
D7-D0:8位数字量输出线。
ADDC- ADDA:3位地址线,用来选通8路模拟通道中的一路。
ALE:地址锁存允许,在ALE的上升沿,ADDC、ADDB、ADDA三位地址信号被锁存到地址锁存器。
START:启动转换的信号,正脉冲有效。地址锁存后,在该引脚加一正脉冲,该脉冲的上升沿使所有内部寄存器清零,从下降沿开始进行A/D转换。如果在转换过程中,接到新的启动信号,则原来的转换进程被中止。
CLOCK:时钟信号,输入线。时钟越高,转换的速率越快。
EOC:转换结束信号,高电平有效,输出。当该信号为低电平时,表明ADC 0809已准备开始转换,或正在转换进行之中,不能提供一个有效的稳定的数据。当EOC变为高电平时,表示转换己经结束,A/D转换器可提供有效数据。EOC可作为被查询的状态信号,亦可用于申请中断。ADC 0809转换一次共需64个时钟CLOCK周期。
OE:输出允许。当OE为高电平时打开输出三态缓冲器,使转换后的数据进入数据总线。
VREF(+) 、VREF(-): 基准电压输入。-般应用情况下,VEF(+)接+5V, 而VEF(:)与GND相连。
2.2 stm32f103 ADC基本特性
●STM32F103单片机一般有3个ADC,每个ADC是 12位分辨率 的逐次逼近型的ADC。
●每个ADC共有16个外部通道(单片机的ADC输入引脚),ADC1有两个内部通道:内部温度通道、参考电压 VREFINT 通道。
●ADC供电要求: 2.4V 到 3.6V
●ADC输入范围: VREF- ≤ VIN ≤ VREF+
●自校准
●单次和连续转换模式
●从通道0到通道n的 自动扫描模式
●采样间隔可以按通道分别编程
●ADC转换时间:
STM32F 103xx增强型产品:
APB2时钟为56MHz,4分频时为1μs
APB2时钟为72MHz,6分频时为1.17μs
●规则通道转换期间有DMA请求产生。
●转换结束、注入转换结束和发生模拟看门狗事件时产生中断
●规则转换 和 注入转换 均有外部触发选项
●双重 模式(带2个或以上:ADC的器件)
2.3 stm32f103 ADC框图
-
电压输入范围。
ADC电源:VDDA接3.3V,VSSA接GND
ADC参考电压:2.4V ≤ VREF+ ≤ VDDA ; VREF- = VSSA
注意:VDDA不能高于3.3V。输入电压范围ADCx_IN应是0 ~ 3.3V。 -
输入通道。
ADCx_IN0 ~ ADCx_IN15 这16个外部通道(即输入口)可以采集模拟电压信号。
看框图标号2的旁边还有两个内部通道:一个是内部温度通道,另一个是内部参考电压 VREFINT 通道。(只有ADC1有)
-
转换顺序。
16个输入通道可以分为两组:注入通道,规则通道。
规则通道(最多16个):平民通道,规规矩矩。
注入通道(最多4个):VIP通道,可以插队。规则通道在执行转换时,注入通道有需要转换时,可以抢断规则通道的转换,优先进行转换。
常规转换扫描模式:
我们定义一个规则组(即定义了通道扫描的顺序),根据规则组顺序扫描。
注入转换扫描模式(VIP模式):
在常规转换扫描的过程中,注入组来了个触发信号,就打断常规组的转换扫描然后进行注入组的转换扫描,注入组完成转换扫描之后,产生中断信号,回到常规转换扫描。 -
ADC触发源:软件触发,事件触发。
软件触发:ADC_CR2:ADON、SWSTART、JSWSTART
外部事件触发:内部定时器/外部IO
选择: ADC_ CR2 :EXTSEL[2:0]和JEXTSEL[2:0]
激活: ADC_ CR2 :EXTTRIG和JEXTTRIG -
时钟源ADCCLK 。
ADCCLK,最大为14MHz。
ADCCLK 由APB2提供,经分频2/4/6/8 得到ADCCLK。
通常来说,APB2为72MHz,由于ADCCLK 最大为14M,我们可以6分频得到比较高的ADCCLK=72/6=12MHz。
-
根据组的不同,将数据存储在不同的16位数据寄存器当中。
规则组就对应放到规则组寄存器,注入组对对应放到注入组寄存器。
因为ADC分辨率时12位,只是用到数据寄存器的12位,所以会有左对齐,右对齐的问题。
规则数据寄存器只有一个,多通道采集的时候处理不当可能会出现数据覆盖问题,故规则多通道采集时多会使用DMA。 -
中断。
如果开启中断,且对应的中断标志位置位可以触发中断。
转换结束标志EOC(End Of Convert),注入转换结束标志JEOC,模拟看门狗事件标志AWD。
总结
2.4 ADC工作模式
- 单次转换模式
单次转换模式里,ADC只执行一次转换。
此模式可通过设置ADC_CR2寄存器上的ADON位(只适用于规则通道) 或 通过外部触发启动(适用于规则通道或注入通道)启动,这时CONT位为0。
- 连续转换模式
在连续转换模式中,当前面ADC转换一结束马上就启动另一次转换。
此模式可通过设置ADC_CR2寄存器上的ADON位 或 通过外部触发启动,此时CONT位是1。
- 中断模式
略
单次/连续转换与单/多通道扫描结合
应用:
- 单通道单次转换
■ 对一个通道进行一次转换,然后ADC停止
■ 应用举例:启动系统功能之前测量电压 - 多通道单次转换
■ 对多个通道进行逐个的独立转换,然后ADC停止
■ 可对多达16个通道的转换配置不同采样时间和顺序
无需中途停止ADC来配置下一个通道的采样时间
节省CPU负载
■ 应用举例:启动系统之前测量多个参数
电压、温度、压力等
机器人操作臂的坐标
物体所受扭力和形变的方向以及受力 - 单通道连续转换
■ 对一个规则通道进行连续转换
■ 通常可用于后台,无需CPU干涉的连续转换;
配合DMA的循环模式,进一-步减少CPU负载
■ 应用举例:监测电池电压,测量并调节炉温等 - 多通道连续转换
■ 对多个通道进行连续的独立转换
■ 可对多达16个通道的转换配置不同采样时间和顺序
■ 应用举例:多电池充电器中监测多个电压和温度的信息
多通道示波器-扫描通道,获取数据通过DMA送到SRAM里面,通过液晶显示屏显示数据。
2.5 相关寄存器
- 通道配置寄存器
ADC规则序列寄存器1(ADC_SQR1)
ADC规则序列寄存器2(ADC_SQR2)
ADC规则序列表存器3(ADC_SQR3)
ADC注入序列表存器(ADC_JSQR)
L[3:0]:定义转换的个数,取值000~1111,16个不同的数据代表不同的转换个数。
SQx[4:0]:定义通道的编号。规则组有16个,注入通道有4个。
同上。 - 控制寄存器1
SACN:开启或关闭扫描
JEOCIE:允许产生注入通道转换结束中断
EOCIE:允许产生规则通道转换结束中断
DUALMOD:双模式选择,使用1个ADC(ADC1) 或 使用2个ADC(ADC1、ADC2)
AWDCH:看门狗通道通道选择位 - 控制寄存器2
ADCON:开启/关闭AD转换器
CONT:设置是否连续转换
CAL、RSTCAL:AD校准
ALIGN:设置数据格式对齐方式;左对齐置1,右对齐置0
DMA:开始/关闭DMA
SWSTART、JSWSTART:软件触发
EXTTRIG、JEXTTRIG:外部事件触发
软件触发位:
ADON、SWSTART、JSWSTART
外部事件触发位:
选择: ADC_CR2 :EXTSEL[2:0]和JEXTSEL[2:0]
激活: ADC_CR2 :EXTTRIG和JEXTTRIG - ADC采样事件寄存器
这两个寄存器用于设置通道0~17的采样时间,每个通道占用3个位。
采样时间短可能会有噪声影响。 - ADC数据寄存器
规则序列中的AD转化结果都将被存在这个寄存器里面,而注入通道的转换结果被保存在ADC_JDRx里面。
读取数据时,注意该寄存器的数据对齐方式 - 左对齐/右对齐。
(对齐方式可以通过ADC_ CR2的ALIGN位设置左对齐还是右对齐) - ADC状态寄存器
2.6 ADC转换时间计算
转化时间于时钟频率息息相关。对于每个要转换的通道,采样时间建议尽量长一点,以获得较高的准确度,但是这样会降低ADC的转换速率。
ADC的转换时间可以由下式计算:
Tconv =采样时间+12.5个周期。
其中:
Tconv为总转换时间;
采样时间可以配置每个通道的SMP位选择;
12.5个周期是固定值,不可改变。(逐次逼近式的原则导致)
理论最短的转换时间(ADCCLK=14MHz):
Tconv = 1.5+12.5 = 14周期 = 14* 1/14=1us
常用最短的转换时间(ADCCLK=12MHz):
Tconv = 1.5+12.5 = 14周期 = 14* 1/12us=1.17us
三、STM32 ADC编程
准备:
新建文件adc.h,adc.c,添加库文件stm32f10x_adc、stm32f10x_dma,添加adc驱动源文件adc.c
ADC初始化结构体
typedef struct
{
uint32_t ADC_Mode;//ADC模式:配置ADC_CR1寄存器的位[19:16] : DUALMODE[3:0]位
FunctionalState ADC_ScanConvMode; //是否使用扫描模式。ADC_CR1位8: SCAN位
FunctionalState ADC_ContinuousConvMode; //单次转换OR连续转换: ADC_CR2的位1: CONT
uint32_t ADC_ExternalTrigConv; //触发方式: ADC_CR2的位[19:17]: EXTSEL[2:0]
uint32_t ADC_DataAlign; //对齐方式: 左对齐还是右对齐: ADC_CR2的位11: ALIGN
uint8_t ADC_NbrOfChannel;//规则通道序列长度: ADC_SQR1的位[23:20]: L[3:0]
}ADC_InitTypeDef;
3.1 测量3.3V引脚电压
烧写代码后,通过杜邦线分别连接PA1~ 3.3V, PA1 ~ GND ,然后通过串口观察ADC值。切忌不要作死接5V。
编程思路:
①开启时钟(PA\ADC1),初始化端口(设置PA1为模拟输入)。
APB2PeriphClockCmd();
GPIO_Init();
②复位ADC1,设置ADC1预分频系数。
ADC_ Deinit(ADC1)
RCC_ ADCCLKConfig(RCC PCLK2 Div6);
③初始化ADC1(设置ADC1的工作模式以及规则序列的相关信息)。
void ADC_Init(ADC_TypeDef*ADCx, ADC_IntTypeDef ADC InitStruct)
④使能ADC并校准(类比万用表)。
ADC_Cmd(ADC1, ENABLE);
ADC_ResetCalibration(ADC1); //使能复位校准
while(ADC_GetResetCalibrationStatus(ADC1)); //等待复位校准结束
ADC_StartCalibration(ADC1); //开启AD校准
while(ADC_GetCalibrationStatus(ADC1)); //等待校准结束
⑤配置规则通道参数:
ADC_RegularChannelConfig();
⑥开启软件转换:
ADC_SoftwareStartConvCmd(ADC1),
⑦等待转换完成,读取ADC值。
ADCGetConversionValue(ADC1);
代码:
adc.h
#ifndef ADC_H
#define ADC_H
#include "stm32f10x.h"
void ADC_GPIO_Init(void);
void ADC_Configuration(void);
u16 get_ADC(u8 channel);
#endif
adc.c
#include "adc.h"
void ADC_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE );//使能GPIOA通时钟
//PA1 作为模拟通道输入引脚
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入引脚
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
void ADC_Configuration(void)
{
ADC_InitTypeDef ADC_InitStructure;
ADC_GPIO_Init();
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE ); //使能ADC1通道时钟
RCC_ADCCLKConfig(RCC_PCLK2_Div6); //设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M
ADC_DeInit(ADC1); //复位ADC1,将外设 ADC1 的全部寄存器重设为缺省值
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC工作模式:ADC1和ADC2工作在独立模式
ADC_InitStructure.ADC_ScanConvMode = DISABLE; //模数转换工作在单通道模式
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //模数转换工作在单次转换模式
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //转换由软件而不是外部触发启动
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //ADC数据右对齐
ADC_InitStructure.ADC_NbrOfChannel = 1; //顺序进行规则转换的ADC通道的数目
ADC_Init(ADC1, &ADC_InitStructure); //根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器
ADC_Cmd(ADC1, ENABLE); //开启ADC1
ADC_ResetCalibration(ADC1); //使能复位校准
while(ADC_GetResetCalibrationStatus(ADC1)); //等待复位校准结束
ADC_StartCalibration(ADC1); //开启AD校准
while(ADC_GetCalibrationStatus(ADC1)); //等待校准结束
}
u16 get_ADC(u8 channel)
{
//设置指定ADC的规则组通道,一个序列,采样时间
ADC_RegularChannelConfig(ADC1, channel, 1, ADC_SampleTime_239Cycles5); //ADC1,ADC通道,采样时间为239.5周期
ADC_SoftwareStartConvCmd(ADC1, ENABLE); //使能指定的ADC1的软件转换启动功能
while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));//等待转换结束
return ADC_GetConversionValue(ADC1); //返回最近一次ADC1规则组的转换结果
}
main.c
#include "stm32f10x.h"
#include "bsp_systick.h"
#include "usart.h"
#include "adc.h"
int main(void)
{
usart1_Init(9600);
ADC_Configuration();
u16 adc_Value = 0 ;
float temp;
printf("start!\n");
while(1)
{
adc_Value = get_ADC(ADC_Channel_1);//指定ADC_Channel_1,获取ADC的值
printf("adc_Value:%d\n",adc_Value);//通过串口打印获取ADC的值
temp=(float)adc_Value*(3.3/4096);//转换ADC的值,得到具体的电压
printf("temp:%f\n",temp);//通过串口打印获取电压的值
SysTick_Delay_ms(500);
}
}
现象: