第06章 ADC模数转换器
6.1 ADC模数转换器
STM32的ADC是12位的,所以AD结果最大值是4095,也就是2^12-1。对于GPIO来说,它只能读取引脚的高低电平,要么是高电平,要么是低电平,只有两个值。而使用了ADC之后,我们就可以对这个高电平和低电平之间的任意电压进行量化,最终用一个变量来表示,读取这个变量,就可以知道引脚的具体电压是多少了。
6.1.1 ADC简介
ADC(Analog-Digital Converter)模拟-数字转换器;
ADC可以将引脚上连续变化的模拟电压转换为内存中存储的数字变量,建立模拟电路到数字电路的桥梁;
反过来DAC就是就数模转换器。
12位逐次逼近型ADC,1us转换时间;
从AD转换开始到产生结果,需要花1us的时间,对应AD转换的频率就是1MHz
输入电压范围:0~3.3V,转换结果范围:0~4095;
ADC的输入电压,一般都是要求在芯片供电的负极和正极之间变化的,最低电压就是负极0V,最高电压是正极3.3V,经过ADC转换之后,最小值就是0,最大值是4095,0V对应0,3.3V对应4095,中间都是一一对应的线性关系。
18个输入通道,可测量16个外部和2个内部信号源;
外部信号源就是16个GPIO口,在引脚上直接模拟信号就行了,不需要任何额外的电路,引脚就能直接测电压;2个内部信号源是内部温度传感器和内部参考电压,温度传感器可以测量CPU的温度,比如电脑可以显示一个CPU温度,就可以用ADC读取这个温度传感器来测量。内部参考电压是一个1.2V左右的基准电压,这个基准电压是不随外部供电电压变化而变化的,所以如果芯片的供电不是标准的3.3V,那测量外部引脚的电压可能就不对。这时就可以读取这个基准电压进行校准,这样就能得到正确的电压值了。
规则组和注入组两个转换单元;
这个就是STM32ADC的增强功能了,普通的AD转换流程是,启动一次转换,读一次值,然后再启动,再读值这样的流程,但是STM32的ADC就比较高级,可以列一个组,一次性启动一个组,连续转换多个值;并且有两个组,一个是用于常规使用的规则组,一个是用于突发事件的注入组。
模拟看门狗自动监测输入电压范围;
这个ADC一般可以用于测量光线强度、温度这些值,并且经常会有个需求,就是如果光线高于某个阈值、低于某个阈值;或者温度高于某个阈值、低于某个阈值时,执行一些操作。这个高于某个阈值、低于某个阈值的判断,,就可以用模拟看门狗来自动执行。模拟看门狗可以监测指定的某些通道,当AD值高于它设定的上阈值或者低于下阈值时,它就会申请中断,就可以在中断函数里执行相应的操作,这样就不用不断地手动读值,再用if进行判断了。
STM32F103C8T6 ADC资源:ADC1、ADC2,10个外部输入通道。
也就是最多只能测量10个外设引脚的模拟信号。上面所说的16个外部信号源,这是这个系列最多有16个外部信号源。但是我们这个系列引脚比较少,有很多引脚没有引出来,,所以只有10个外部信号源。如果想要更多的外部通道,可以选择引脚更多的型号,具体有多少个通道,就要参考数据手册了。
6.1.2 逐次逼近型ADC
这个图是ADC0809的内部结构图, 它是一个独立的8位逐次逼近型ADC芯片,在以前的时候,单片机的性能还不是很强,所以需要外挂一个ADC芯片才能进行AD转换,这个ADC0809就是一款比较经典的ADC芯片,现在单片机的性能和集成度都有很大的提升。很多单片机内部就已经继承了ADC外设,这样就不用外挂芯片了,引脚可以直接测电压。
这个结构左边是IN0~IN7,是8路输入通道,通过通道选择开关,选择一路,输出后进行转换。下面是地址锁存器和译码,就是想选择哪一路,就把通道号放在这三个引脚上,然后给一个锁存信号,上面对应的通路开关就可以自动拨好了。上面部分就相当于一个可以通过模拟信号的数据选择器。因为ADC转换是一个很快的过程,给一个开始信号,过一个几us就转换完成了,所以说如果想转换多路信号,不必设计多个AD转换器,只需要一个AD转换器,然后加一个多路选择开关,想转换哪一路,先拨一下开关,选中对应通道,然后再开始转换就行了。这就是输入通道选则的部分。这个ADC0809只有8个输入通道,STM32内部有18个输入通道,所以对应这里就是一个18路输入的多路开关。输入通道选好后,怎么知道对应的数据是多少呢,就需要用逐次逼近的方法来一一比较了。首先是一个电压比较器,它可以判断两个输入信号电压的大小关系,输出一个高电平只是谁大谁小,它的两个输入端,一个是待测的电压,另一个是DAC的电压输出端,DAC是数模转换器,给它一个数据,它就可以输出数据对应的电压,DAC内部是使用加权电阻网络来实现的转换。有了一个外部通道输入的,未知编码的电压,和一个DAC输出的,已知编码的电压,它两1同时输入到电压比较器,进行大小判断,如果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次,这就是逐次逼近的过程。然后,ADC转换结束后,DAC的输入数据,就是未知电压的编码,往右走进行输出,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的供电是一样的。
6.1.3 STM32ADC框图
左边是ADC的输入通道,包括16个GPIO口,IN0~IN15 ,和两个内部二点通道,一个是内部温度传感器,另一个是VREFINT(V Reference Internal),内部参考电压,总共是18个输入通道,然后到达模拟多路开关,可以指定我们想要选则的通道,右边是多路开关的输出,进入到模数转换器。这里模数转换器就是执行刚刚上面讲的逐次比较的过程。转换结果会直接放在数据寄存器里,我们读取寄存器就能直到ADC转换的结果了。对于普通的ADC,多路开关一般是只选中1个的,就是选中某个通道、开始转换、等待转换完成、取出结果、这是普通的流程。但是STM32上就比较高级了,它可以同时选中多个,而且在转换的时候,还分成了两个组,规则通道组和注入通道组;其中规则组可以一次性最多选择中16个通道,注入组最多可以选中4个通道。这有什么作用呢?举个例子:就像是去餐厅点菜、普通的ADC是,指定一个菜,老板做完后送给自己,这里就是,指定一个菜单,这个菜单最多可以填16个菜,老板按照菜单的顺序依次做好,一次性端上来,这样就大大提高效率,当然,菜单也可以只写一个菜,就简化成普通的模式了。对于这个菜单也有两种,1种是规则组,可以同时上16个菜,但是有个尴尬的地方,就是规则组只有一个数据寄存器,就是这个桌子比较小,只能放一个菜,如果上16个菜,前15个菜就会被挤掉。所以对于规则组来说,如果使用这个菜单的话,最好配合DMA来实现。DMA是一个数据转运小帮手,它可以在每上一个菜之后,把这个菜挪到其它地方去,防止被覆盖。规则组虽然可以同时转换16个通道,但是数据寄存器只能存一个结果,如果不想之前的结果被覆盖,那就转换完成之后,要尽快把结果拿走。注入组,这个组就比较高级,相当于是餐厅的VIP座位,在这个座位上,一次性最多可以点4个菜,并且数据寄存器有4个,是可以同时上4个菜的,对于注入组而言,就不用担心数据覆盖的问题了。这就是规则组和注入组的介绍。一般情况下,使用规则组就完全足够了,如果要使用规则组的菜单,那就再配合DMA转运数据。
模数转换器外围电路:左下角是触发转换的部分,也就是ADC0809的START。对于STM32的ADC,触发ADC开始转换的信号有两种,一种是软件触发,就是再程序中手动调用一条代码,就可以启动转换了;另一种是硬件触发,就是左下角的触发源。上面是注入组的触发源,下面是规则组的触发源,这些触发源主要是来自定时器,有定时器的各个通道,还有TRGO定时器主模式的输出。之前讲定时器的时候也介绍过,定时器可以通过ADC、DAC这些外设,用于触发转换。那因为ADC经常要过一个固定时间段转换一次,比如每隔1ms转换一次,正常的思路就是,用定时器,每隔1ms申请1次中断,在中断里手动开始一次转换,这样也是可以的。但是频繁进中断对程序是有一定影响的,比如有很多中断都需要频繁进入,肯定会影响主程序的执行,并且不同中断之间,由于优先级的不同,也会导致某些中断不能及时得到响应,如果触发ADC的中断不能及时响应,那我们ADC的转换频率就肯定会产生影响了,所以对于这种需要频繁进中断,并且在中断里只完成了简单工作的情况,一般都会有硬件的支持。比如这里就可以给TIM3定一个1ms的时间,并且把TIM3的更新事件选择位TRGO输出,然后在ADC这里,选择开始触发信号为TIM3的TRGO,这样TIM3的更新事件就能通过硬件自动触发ADC转换了,整个过程不需要进中断,节省了中断资源。这就是这里定时器触发的作用。当然这里还可以选择外部中断引脚来触发转换,都可以在程序中配置,这就是触发转换的部分。
左上角是VREF+、VREF-、VDDA和VSSA,上面两个是ADC的参考电压,决定了ADC输入电压的范围,下面两个是ADC的供电引脚,一般情况下,VREF+要接VDDA、VREF-要接VSSA,在这款芯片上,没有VREF+和VREF-的引脚,它在内部就已经和VDDA和VSSA接在一起了。VDDA和VSSA在引脚定义里就可以看到。VDDA和VSSA是内部模拟部分的电源,比如ADC、RC振荡器、锁相环等等,在这里VDDA接3.3V、VSSA接GND、所以ADC的输入电压范围就是0~3.3V。
右边ADCCLK是ADC的时钟,也就是ADC0809上的CLOCK,是用于驱动内部逐次比较的时钟。这个是来自ADC预分频器,这个ADC预分频器是来源于RCC的。如下图(RCC时钟树框图)所示:APB272MHz通过预分频器进行分频,得到ADCCLK,ADCCLK最大是14MHz,所以这个预分频器就有点尴尬,它可以选择2、4、6、8分频,如果选择2分频,72M/2=36M,超出允许范围了;4分频之后是18M,也超了;所以对于ADC预分频器,只能选择6分频,结果是12M和8分频、结果是9M,这两个值。
DMA请求是用于触发DMA进行数据转运的。
两个数据寄存器是用于存放转换结果的,上面还有一个模拟看门狗,里面可以存一个阈值高限和阈值低限,如果启动了模拟看门狗,并指定了看门的通道,那这个看门狗就会关注它看门的通道、一旦超过这个阈值范围了,它就会乱叫,就会在上面申请一个模拟看门狗的中断,最后通向NVIC。然后对于规则组和注入组而言,它们转换完成后,也会有一个EOC转换完成的信号,在这里EOC是规则组的完成信号,JEOC是注入组完成的信号,这两个信号会在状态寄存器里置一个标志位,读取这个标志位,就能知道是不是转换结束了,同时这两个标志位也可以去到NVIC,申请中断,如果开启了NVIC对应的通道,它们就会触发中断。
6.1.4 ADC基本结构图
左边是输入通道,16个GPIO口、外加两个内部的通道;然后进入AD转换器、AD转换器里有两个组、一个是规则组、一个是注入组。规则组最多可以选中16个通道、注入组最多可以选择4个通道,然后转换的结果可以存放在AD数据寄存器里,其中规则组只有一个数据寄存器,注入组有4个,下面有触发控制、提供了开始转换这个START信号,触发控制可以选择软件触发和硬件触发,硬件触发主要来自于定时器,当然也可以选择外部中断的引脚。右边是来自RCC的ADC时钟CLOCK,ADC逐次比较的过程就是由这个时钟推动的。然后上面可以布置一个模拟看门狗用于监测转换结果的范围,如果超出设定的阈值,就通过中断输出控制,向NVIC申请中断,另外、规则组和注入组转换完成后会有个EOC信号,它会置一个标志位,当然也可以通向NVIC。最后右下角还有一个开关控制,在库函数种,就是ADC_Cmd函数,用于给ADC上电的。