深入了解STM32 ADC数模转换器的原理与应用

STM32 ADC数模转换器

ADC简介

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

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

  • STM32主要是数字电路,数字电路只有高低电平,没有几V电压的概念,所以如果想读取电压值,就需要借助ADC模数转换器来实现了,ADC读取引脚上的模拟电压,转换为一个数据,存在寄存器里,我们再把这个数据读到变量里来,就可以进行显示、判断、记录等等操作了,ADC可以将模拟信号转换为数字信号,是模拟电路到数字电路的桥梁。
  • DAC,数字模拟转换器,使用DAC就可以将数字量转化为模拟电压;PWM也可以作为数字电路到模拟电路的桥梁,同时PWM只有完全导通和完全断开两种状态,在这两种状态上都没有功率损耗,所以在直流电机这种大功率的应用场景,使用PWM来等效模拟量是比DAC更好的选择,并且PWM电路更加简单,更加常用。目前DAC的应用主要是在波形发生这些领域,比如信号发生器、音频解码芯片等。
  • 12位逐次逼近型ADC,1us转换时间

  • ADC的两个关键参数
  • 分辨率,一般用多少位来表示,12位AD值,它的表示范围就是0~212-1,就是量化结果的范围是0~4095,位数越高,量化结果就越精细,对应分辨率就越高。
  • 转换时间(转换频率),AD转换需要花费一小段时间,这里1us就表示从AD转换开始,需要花1us的时间,对应AD转换的频率就是1MHz,这个就是STM32ADC的最快转换频率,如果你需要转换一个频率非常高的信号,那就要考虑一下这个转换频率是不是够用,如果你的信号频率比较低,那最大1MHz的转换频率也完全够用了。
  • 输入电压范围:0~3.3V,转换结果范围:0~4095

  • ADC的输入电压,一般要求都是要在芯片供电的负极和正极之间变化的,最低电压就是负极0V,最高电压是正极3.3V,经过ADC转换滞后,最小值就是0,最大值是4095,0V对应0,3.3V对应4095,中间都是一一对应的线性关系。
  • 18个输入通道,可测量16个外部和2个内部信号源

  • 外部信号源就是16个GPIO口,在引脚上直接接模拟信号即可,不需要任何额外的电路,引脚就能直接测电压。
  • 两个内部信号源是内部温度传感器和内部参考电压
  • 温度传感器可以测量CPU的温度,比如电脑可以显示一个CPU的温度,就可以用ADC读取这个温度传感器来测量。
  • 内部参考电压是一个1.2V左右的基准电压,这个基准电压是不随外部供电电压变化而变化的,所以如果芯片供电不是标准的3.3V,那测量外部引脚的电压可能就不对,这时就可以读取这个基准电压进行校准,就能得到正确的电压值了。
  • 规则组和注入组两个转换单元

  • 普通的AD转换流程是,启动一次转换,读一次值,然后再启动,再读值,这样的流程。
  • 但是STM32的ADC可以列一个组,一次性启动一个组,连续转换多个值,并且有两个组
  • 一个是用于常规使用的规则组
  • 一个是用于突发事件的注入组,
  • 模拟看门狗自动监测输入电压范围

  • 一般可以用于测量光线强度、温度这些值,并且经常会有个需求,就是如果光线高于某个阈值、低于某个阈值,或者温度高于某个阈值、低于某个阈值时,执行一些操作,这个高于某个阈值、低于某个阈值的判断,就可以用模拟看门狗来自动执行,模拟看门狗可以检测指定的某些通道,当AD值高于它设定的上阈值或者低于下阈值时,它就会申请中断,你就可以在中断函数里执行相应的操作,这样就不用不断地手动读值,在进行if判断操作了。
  • STM32F103C8T6 ADC资源:ADC1、ADC2,10个外部输入通道

  • 最多只能测量10个外部引脚的模拟信号。之前说的16个信号源,这是这个系列最多有16个外部信号。但是STM32F103C8T6芯片引脚比较少,有很多引脚没有引出来,所以就只有10个外部信号源。如果需要更多的外部通道,可以选择引脚更多的型号。
  • 逐次逼近型ADC

  • STM32的ADC与ADC0809的工作原理相同,均为逐次逼近型ADC,所以为了更好地理解STM32ADC的工作原理,我们不妨先来看一下逐次逼近型ADC的工作原理。
  • ADC0809是一个独立的8位逐次逼近型ADC芯片,在以前的时候,单片机的性能还不是很强,所以需要外挂一个ADC芯片才能进行AD转换,现在单片机的性能和集成度都有很大的提升,很多单片机内部就已经集成了ADC外设,这样就可以不用外挂芯片了,引脚可以直接测电压,使用很方便。
  • 首先左边是IN0~IN7,一共是8路输入通道,并通过通道选择开关,选择一路,输入到电压比较器进行转换。

  • 下面是地址锁存和译码,就是你想选中哪个通道,就把通道号放在这三个脚上(三个bit位对应0~7),然后给一个锁存信号,上面这里对应的通路开关就可以自动拨好了,这部分就相当于一个可以通过模拟信号的数据选择器。

  • 因为ADC转换是一个很快的过程,你给个开始信号,过几个us就转换完成了,所以说如果你想转换多路信号,那不必涉及多个AD转换器,只需要一个AD转换器,然后加一个多路选择开关,像转换哪一路,就先拨一下开关,选中对应通道,然后再开始转换就行了。

  • STM32内部的ADC有18个输入通道,对应这里,就是一个18路输入的多路开关。
  • 输入信号选择完成后,如何知道这个电压对应的编码数据是多少呢,这就需要用逐次逼近的方法来一一比较了。电压比较器,可以判断两个输入信号电压的大小关系,输出一个高低电平指示谁大谁小,它的两个输入端,一个是待测的电压,另一个是DAC电压输出端,DAC是数模转换器,给它一个数据就可以输出数据对应的电压,DAC内部是使用加权电阻网络来实现的转换。

  • 现在我们有了一个外部通道输入的未知编码的电压和一个DAC输出的已知编码的电压,它们同时输入到电压比较器,进行大小判断。如果DAC>外部通道的输入电压,我们就调小DAC输出的电压,如果DAC<外部通道的输入电压,我们就调大DAC的输出电压,直到DAC输出的电压和外部通道输入的电压近似相等,这样DAC输入的数据就是外部电压的编码数据了。

  • 这个电压调节的过程就是这个逐次逼近SAR来完成的,为了最快找到未知电压的编码,通常我们会使用二分搜索算法。

  • 比如这里是8位的ADC,那编码就是从0~255,第一次比较的时候,我们就给DAC输入255的一般,进行比较,那就是128,然后看看谁大谁小,如果DAC电压大了,那第二次比较的时候,再就给128的一半,64,如果还大,第三次比较的时候就给32,如果这次DAC电压小了,那第四次就给32到64的中间的值,然后继续,这样依次进行下去,就能最快地找到未知电压的编码。
  • 这个过程,如果你用二进制来表示的话,你会发现128、64、32这些数据,正好是二进制每一位的位权,这个判断过程就相当于是对二进制从高到低位依次判断是1还是0的过程。那对于8位的ADC,从高位到低位依次判断8次就能找到未知电压的编码了,对于12位的ADC,就需要判断12次,这就是逐次逼近的过程,AD转换结束后,DAC的输入数据,就是未知电压的编码,输出到8位三态锁存缓冲器,8位就有8根线,12位就有12根线。
  • EOC,End Of Convert,转换结束信号

  • START是开始转换,给一个输入脉冲,开始转换

  • CLOCK是ADC时钟,因为ADC内部是一步一步进行判断的,所以需要时钟来推动这个过程。

  • VREF+和VREF-是DAC的参考电压,比如你给一个数据255,是对应5V还是3.3V,就由这个参考电压决定,这个DAC的参考电压也决定了ADC的输入范围,所以它也是ADC的参考电压。

  • VCC和GND是整个芯片电路的供电,通常参考电压的正极和VCC是一样的,会接在一起,参考电压的负极和GND是一样的,也接在一起,所以一般情况下,ADC输入电压的范围就和ADC的供电是一样的。

  • ADC框图

  • ADC的输入通道(ADCx_IN0~15、温度传感器、VREFINT)包括16个GPIO口,IN0~IN15,和两个内部的通道,一个是内部温度传感器,一个是VREFINT(V Reference Internal) 内部参考电压。总共是18个输入通道,然后到达模拟多路开关,可以指定我们想要选择的通道,右边是多路开关的输出,进入到模数转换器,这里的模数转换器执行的就是逐次比较的过程,转换结果会直接放到数据寄存器里,读取寄存器就可以知道ADC转换的结果了。
  • 对于普通的ADC,多路开关一般都是只选中一个,就是选中某个通道、开始转换、等待转换完成、取出结果。但是STM32的ADC可以同时选中多个通道,而且在转换的时候,还分成了两个组,规则通道组和注入通道组。
  • 规则组可以一次性最多选中16个通道
  • 注入组可以一次性最多选中4个通道
  • 举个例子

  • 就像去餐厅点菜,普通的ADC是指定一个菜,老板给你做,然后做好了送给你
  • STM32的ADC就是指定一个菜单,这个菜单最多可以填16个菜,然后直接递个菜单给老板,老板就按照菜单的顺序依次做好,一次性给你端上来,这样的话就可以大大提高效率,当然你的菜单也可以只写一个菜,这样这个菜单就简化成了普通模式了。那对于这个菜单呢,也有两种
  • 一种是规则组菜单,可以同时上16个菜,但是规则组只有一个数据寄存器,就是这个桌子比较小,最多只能放一个菜,如果上16个菜,那前15个菜都会被挤掉,你只能得到第16个菜,多以对于规则组转换来说,如果使用这个菜单的话,最好配合DMA来实现,DMA可以实现数据转运,它可以在每上一个菜之后,把这个菜挪到其它地方去,防止被覆盖。
  • 规则组虽然可以同时转换16个通道,但是数据寄存器只能存一个结果,如果不想之前的结果被覆盖,那在转换完成之后,就要尽快把结果拿走。
  • 注入组,相当于是餐厅的VIP座位,在这个座位上,一次性最多可以点4个菜,并且这里数据寄存器有4个,是可以同时上4个菜的,对于注入组而言,就不用担心数据覆盖的问题了。
  • 触发转换(START 开始转换),对于STM32的ADC,触发ADC开始转换的信号有两种。
  • 软件触发,就是在程序中手动调用一条代码,就可以启动转换了
  • 硬件触发,就是这里的这些触发源
  • 这些触发源主要来自于定时器,有定时器的各个通道,还有TRGO定时器主模式的输出,定时器可以通向ADC、DAC这些外设,用于触发转换,因为ADC经常需要过一个固定时间转换一次,比如每隔1ms转换一次,正常的思路就是用定时器,每隔1ms申请一次中断,在中断里手动开启一次转换,但是频繁进中断对程序会造成一定的影响,比如你有很多中断都需要频繁进入,那肯定会影响主程序的执行,并且不同中断之间,由于优先级的不同,也会导致某些中断不能及时得到响应,如果触发ADC的中断不能及时响应,那ADC的转换频率肯定会产生影响,所以对于这种需要频繁进中断,并且在中断里只完成简单工作的情况,一般都会有硬件的支持,比如这里就可以给TIM3定个1ms的时间,并且把TIM3的更新时间选择为TRGO输出,然后在ADC这里,选择开始触发信号为TIM3_TRGO,这样TIM3的更新时间就能通过硬件自动触发ADC转换了,这个过程不需要进中断,节省了中断资源,当然这里还可以选择外部中断引脚来触发转换。
  • 注入组触发源
  • 规则组触发源
  • TIM1_CH1
  • TIM1_CH2
  • TIM1_CH3
  • TIM2_CH2
  • TIM3_TRGO
  • TIM4_CH4
  • VREF+、VREF-、VDDA和VSSA
  • VREF+、VREF-是ADC的参考电压,决定了ADC输入电压的范围,
  • VDDA和VSSA是ADC的供电引脚
  • 一般情况下VREF+要接VDDA,VREF-要接VSSA,在STM32F103C8T6上没有VREF-和VREF+的引脚,其在内部就已经和VDDA和VSSA接在一起了,VDDA和VSSA是内部模拟部分的电源,比如ADC、RC振荡器、锁相环等,在这里VDDA接3.3V,VSSA接GND,所以ADC的输入电压范围就是0~3.3V。
  • ADCCLK是ADC的时钟(CLOCK),适用于驱动内部逐次比较的时钟,来自ADC预分频器,这个ADC预分频器来源于RCC时钟树,APB2 72MHz然后通过ADC预分频器进行分频,得到ADCCLK,但ADCCLK最大是14MHz,所以这个预分频器就有点尴尬,可以选择2,4,6,8分频,但选择2分频和4分频都超出了14MHz,所以对于ADC预分频器,只能选择6分频,结果是12MHz和8分频,结果是9MHz,这两个值。
  • DMA请求,用于触发DMA进行数据转运。
  • 两个数据寄存器,注入通道数据寄存器,规则通道数据寄存器,用于存放转换结果。
  • 模拟看门狗,可以存一个阈值高限和阈值低限,如果启动了模拟看门狗,并制定了看门的通道,那这个看门狗就会关注它看门的通道,一旦超过这个阈值范围,就会申请一个模拟看门狗的中断,最后通向NVIC。、
  • 对于规则组和注入组而言,它们转换完成之后,也会有一个EOC转换完成的信号,在这里,EOC是规则组的完成信号,JEOC是注入组的完成信号,这两个信号会在状态寄存器置一个标志位,读取这个标志位,就可以知道是不是转换结束了,同时这两个标志位也可以去到NVIC,申请中断,如果开启了NVIC对应的通道,它们就会触发中断。
  • ADC基本结构

  • 输入通道,包括16个GPIO口,外加两个内部的通道。
  • 进入AD转换器,AD转换器里有两个组,转换的结果可以存放在AD数据寄存器中
  • 规则组,最多可以选择16个通道,只有1个数据寄存器
  • 注入组,最多可以选择4个通道,有4个数据寄存器
  • 触发控制,提供了开始转换这个START信号,触发控制可以选择软件触发和硬件触发,硬件触发主要是来自于定时器,当然也可以选择外部中断的引脚
  • 来自RCC的ADC时钟CLOCK,ADC逐次比较的过程就是由这个时钟推动的。
  • 可以布置一个模拟看门狗用于监测转换结果的范围,如果超出设定的阈值,就通过中断输出控制,向NVIC申请中断。
  • 规则组和注入组转换完成后会有个EOC信号,它会置一个标志位,当然也可以通向NVIC。
  • 开关控制,ADC_Cmd(),使能ADC。
  • 输入通道

  • STM32F103C8T6的ADC12_IN0对应的是PA0引脚,IN1对应PA1引脚,然后IN2~9,依次对应的是PA2到PB1,总共只有10个通道,ADC12_IN0的意思是,ADC1和ADC2的IN0都在PA0上,ADC1和ADC2的引脚全都是相同的。
  • 双ADC模式,ADC1和ADC2一起工作,可以配合组成同步模式、交叉模式等模式,比如交叉模式,ADC1和ADC2交叉地对一个通道进行采样,据可以进一步提高采样率。
  • 通道16对应ADC1的温度传感器,通道17对应ADC1的内部参考电压,只有ADC1由通道16和17,ADC2和ADC3是没有的
  • 通道 ADC1 ADC2 ADC3
    通道0 PA0 PA0 PA0
    通道1 PA1 PA1 PA1
    通道2 PA2 PA2 PA2
    通道3 PA3 PA3 PA3
    通道4 PA4 PA4 PF6
    通道5 PA5 PA5 PF7
    通道6 PA6 PA6 PF8
    通道7 PA7 PA7 PF9
    通道8 PB0 PB0 PF10
    通道9 PB1 PB1
    通道10 PC0 PC0 PC0
    通道11 PC1 PC1 PC1
    通道12 PC2 PC2 PC2
    通道13 PC3 PC3 PC3
    通道14 PC4 PC4
    通道15 PC5 PC5
    通道16 温度传感器
    通道17 内部参考电压

    转换模式

    在ADC初始化的结构体中,有两个参数,一个是选择单次转换还是连续转换,另一个是选择扫描模式还是非扫描模式的。

    这两个参数组合起来就有这4种转换方式。

  • 单次转换,非扫描模式
  • 列表就表示规则组里的菜单,有16个空位,分别是序列1到序列16,可以在这里“点菜”,就是写入你要转换的通道,在非扫描的模式下,这个菜单就只有第一个序列1的位置有效,这时,菜单同时选中一组的方式就退化为简单地选中一个的方式了。
  • 在这里我们可以在序列1的位置指定我们想转换的通道,比如通道2,写到序列1的位置,然后我们就可以触发转换,ADC就会对这个通道2进行模式转换,过一小段时间后,转换完成,转换结果放在数据寄存器里,同时给EOC标志位置1,整个转换过程就结束了。
  • 判断这个EOC标志位,如果转换完了,就可以在数据寄存器里读取结果了,如果我们想再启动一次转换,那就需要再触发一次,转换结束,置EOC标志位,读结果。
  • 如果想换一个通道转换,那在转换之前,把第一个位置的通道2改成其他通道,然后再启动转换,这样就行了。
  • 连续转换,非扫描模式
  • 还是非扫描模式,所以菜单列表中就只用第一个。
  • 与单次转换不同的是,连续转换在一次转换结束后不会停止,而是立刻开始下一轮的转换,然后一直持续下去,这样就只需要最开始触发一次,之后就可以一直转换了。
  • 这个模式的好处就是,开始转换后不需要等待一段时间,因为它一直都在转换,所以就不需要手动开始转换了,也不用判断是否结束,想要读AD值的时候,直接从数据寄存器取就是了。
  • 单次转换,扫描模式
  • 单次转换,所以每触发一次,转换结束后就会停下来,下次转换就得再触发才能开始。
  • 扫描模式,这就要用到这个菜单列表了,你可以在菜单里点菜,比如第一个序列1是通道2,序列2是通道5等等,这里每个位置是通道几可以任意指定,并且也是可以重复的。
  • 初始化结构体里还有个参数,通道数目,因为这16个位置你可以不用完,只用前几个,那你就需要再给一个通道数目的参数,告知有几个通道,比如下面指定通道数目为7,那它就只看前7个位置,然后每次触发之后,它就会一次对前7个位置进行AD转换,转换结果都放在数据寄存器里,为了防止数据被覆盖,就需要用DMA及时将数据挪走,那7个通道转换完成之后,产生EOC信号,转换结束,然后再触发下一次,就又开始新一轮的转换。
  • 连续转换,扫描模式
  • 一次转换完成后,立刻开始下一次的转换。
  • 在扫描模式的情况下,还可以有一种模式,叫间断模式,它的作用是,在扫描的过程中,每隔几个转换,就暂停一次,需要再次触发,才能继续。
  • 触发控制

    数据对齐

  • ADC是12位的,它的转换结果就是一个12位的数据,但是数据寄存器是16位的,所以就存在一个数据对齐的问题。

  • 一般使用数据右对齐,这样读取这个16位的数据寄存器,直接就是转换结果。
  • 如果选择左对齐,直接读的话,得到的数据会比实际的大,因为数据左对齐实际上就是把数据左移了4位,二进制有个特点,就是数据左移一次,就等效于把这个数据乘2,这里左移了4次,就相当于把结果乘16了,所以直接读的话,会比实际值大16倍。
  • 数据右对齐:12位的数据向右靠,高位多出来的几位就补0

  • 一般选右对齐,如果需要裁剪一些分辨率,可以先把12位都取出来,再作处理。
  • 数据左对齐:12位的数据想左靠,低位多出来的几位就补0
  • 左对齐的用途就是,如果不想要这么高的分辨率,0~4095数太大了,就只需要做个简单的判断,不需要这么大的分辨率,那你就可以选择左对齐,然后再把这个数据的高8位取出来,这样就舍弃了后面4位的精度,这个12位的ADC就退化成了8位的ADC。
  • 转换时间

    转换时间这个参数,一般不太敏感,因为一般AD转换都很快,如果不需要非常高速的转换频率,那转换时间就可以忽略了,AD转换是需要一小段时间的。

  • AD转换的步骤:采样,保持,量化,编码

  • AD转换,需要一小段时间,如果在这一小段时间里,输入的电压还在不断变化,就无法定位输入电压到底在哪了,所以在量化编码之前,我们需要设置一个采样开关,先打开采样开关,收集一下外部电压,比如可以用一个小容量的电容存储一下这个电压,存储好了之后,断开采样开关,在进行后面的AD转换,这样在量化编码的期间,电压始终保持不变,这样才能精确地定位未知电压的位置,这就是采样保持电路。那采样保持的过程,需要闭合采样开关,过一段时间再断开,这里就会产生一个采样时间。

  • 量化编码,就是ADC逐次比较的过程,这个过程需要花费一段时间,一般位数越多,花的时间越长。

  • STM32 ADC的总转换时间为:

  • TCONV = 采样时间 + 12.5个ADC周期
  • 采样时间是采样保持花费的时间,可以在程序中进行配置,采样时间越大,越能避免一些毛刺信号的干扰,不过转换时间也会相应延长。
  • 12.5个ADC周期是量化编码花费的时间,因为是12位的ADC,所以要花费12个周期,这里多了0.5个周期,可能是做一些其他东西花的时间。ADC周期就是从RCC分频过来的ADCCLK,这个ADCCLK最大是14MHz。
  • 例如:当ADCCLK=14MHz,采样时间为1.5个ADC周期

  • TCONV = 1.5 + 12.5 = 14个ADC周期 = 1μs
  • 校准

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

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

  • 启动校准前, ADC必须处于关电状态超过至少两个ADC时钟周期

  • 这个校准过程是固定的,我们只需要在ADC初始化的最后,加几条代码即可。

  • 5 + 12.5 = 14个ADC周期 = 1μs

    校准

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

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

  • 启动校准前, ADC必须处于关电状态超过至少两个ADC时钟周期

  • 这个校准过程是固定的,我们只需要在ADC初始化的最后,加几条代码即可。

  • 物联沃分享整理
    物联沃-IOTWORD物联网 » 深入了解STM32 ADC数模转换器的原理与应用

    发表评论