STM32
直播中

云中云

8年用户 936经验值
私信 关注
[问答]

STM32的ADC模数转换器有哪几种工作模式及应用呢

ADC模数转换器是什么?为什么需要ADC?
STM32的ADC模数转换器有哪几种工作模式及应用呢?

回帖(2)

廖阿朋

2021-11-15 09:31:21
  一、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
  
举报

魏荣梅

2021-11-15 09:31:37
三、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。
  编程思路:
  ①开启时钟(PAADC1),初始化端口(设置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:%dn”,adc_Value);//通过串口打印获取ADC的值
  temp=(float)adc_Value*(3.3/4096);//转换ADC的值,得到具体的电压
  printf(“temp:%fn”,temp);//通过串口打印获取电压的值
  SysTick_Delay_ms(500);
  }
  }
  现象:
  
举报

更多回帖

发帖
×
20
完善资料,
赚取积分