Skip to content

ADC ​

1. ADC数模转换器 ​

那对于GPIO来说,它只能读取引脚的高低电平,要么是高电平,要么是低电平,只有两个值,而使用了ADC之后,我们就可以对这个高电平和低电平之间的任意电压进行量化,最终用一个变量来表示,读取这个变量,就可以知道引脚的具体电压到底是多少了。所以ADC其实就是一个电压表,把引脚的电压值测出来,放在一个变量里,这就是ADC的作用。

  • ADC(Analog-Digital Converter)模拟-数字转换器
  • ADC可以将引脚上连续变化的模拟电压转换为内存中存储的数字变量,建立模拟电路到数字电路的桥梁
  • 12位逐次逼近型ADC,1us转换时间

       逐次逼近型这是这个ADC的工作模式。然后12位和1us的转换时间,这里就涉及到ADC的两个关键参数了,第一个是分辨率,一般用多少位来表示,12位AD值,它的表示范围就是0-2^12-1,就是量化结果的范围是0~4095。位数越高,量化结果就越精细,对应分辨率就越高;

第二个是转换时间,就是转换频率,AD转换是需要花一小段时间的,这里1us就表示从AD转换开始到产生结果,需要花1us的时间,对应AD转换的频率就是1MHz,这个就是STM32 ADC的最快转换频率。如果你需要转换一个频率非常高的信号,那就要考虑一下这个转换频率是不是够用,如果你的信号频率比较低,那这个最大1MHz的转换频率也完全够用了。

  • 输入电压范围:0~ 3.3V,转换结果范围:0~4095
  • 18个输入通道,可测量16个外部和2个内部信号源

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

  • 规则组和注入组两个转换单元

规则组和注入组两个转换单元,这个就是STM32 ADC的增强功能了。普通的AD转换流程是,启动一次转换、读一次值,然后再启动、再读值,这样的流程。但是STM32的ADC就比较高级,可以列一个组,一次性启动一个组,连续转换多个值。并且有两个组,一个是用于常规使用的规则组,一个是用于突发事件的注入组。

  • 模拟看门狗自动监测输入电压范围

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

  • STM32F103C8T6 ADC资源:ADC1、ADC2,10个外部输入通道

**10个外部输入通道,也就是它最多只能测量10个外部引脚的模拟信号。**之前这里说的16个外部信号源,意思是这个系列最多有16个外部信号源,但是我们这个芯片引脚比较少,有很多引脚没有引出来,所以就只有10个外部信号源,如果你想要更多的外部通道,可以选择引脚更多的型号,具体有多少个通道,那还需要再参考—下数据手册了。

小结 ​

ADC可以将模拟信号转换为数字信号,是模拟电路到数字电路的桥梁。那反过来,有模拟到数字的桥梁,那肯定就有数字到模拟的桥梁。这就是DAC,数字模拟转换器,使用DAC就可以将数字变量转化为模拟电压。

不过在上一节,我们还学到了一个数字到模拟的桥梁,PWM。上一节我们使用PWM来控制LED的亮度、电机的速度,这就是DAC的功能,同时PWM只有完全导通和完全断开两种状态,在这两种状态上都没有功率损耗。

所以在直流电机调速这种大功率的应用场景,使用PWM来等效模拟量,是比DAC更好的选择,并且PWM电路更加简单,更加常用。所以可以看出PWM还是挤占了DAC的很多应用空间。

目前DAC的应用主要是在波形生成这些领域,比如信号发生器、音频解码芯片等,这些领域PWM还是不好替代的。

内部结构 ​

这个图是ADC0809的内部结构图,它是一个独立的8位逐次逼近型ADC芯片。在以前单片机性能不太好的时候,是通过外挂一个ADC芯片才能进行AD转换,这个ADC0809就是一款比较经典的ADC芯片。随着单片机的性能和集成度都有很大的提升,很多单片机内部就已经集成了ADC外设。

  • 输入选择部分

首先左边这里INO~IN7,是8路输入通道,通过通道选择开关,选中一路,输入到所标点进行转换。下面这里是地址锁存和译码,就是你想选中哪个通道,就把通道号放在这三个脚(ADD…)上,然后给一个锁存信号(ALU),上面这里对应的通路开关就可以自动拨好了。这部分就相当于一个可以通过模拟信号的数据选择器。

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

就是这个输入通道选择的部分,这个ADC0809只有8个输入通道,我们STM32内部的ADC是有18个输入通道的,所以对应输入电路,就是一个18路输入的多路开关

  • 核心结构

那然后输入信号选好了,到这里(所标红点)来,怎么才能知道这个电压对应的编码数据是多少呢?这就需要我们用逐次逼近的方法来比较了

首先这是一个电压比较器,它可以判断两个输入信号电压的大小关系,输出一个高低电平指示谁大谁小。它的两个输入端,一个是待测的电压,另一个是这里DAC的电压输出端,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次,这就是逐次逼近的过程。

STM32(逐次逼近型)ADC电路图详解 ​

在这里,左边是ADC的输入通道,包括16个GPIO口,INO~IN15和两个内部的通道(一个是内部温度传感器,另—个是VREFINT(V Referencelnternal)内部参考电压)。

总共是18个输入通道,然后到达这里,这是一个模拟多路开关,可以指定我们想要选择的通道。右边是多路开关的输出,进入到模数转换器,(这里模数转换器就是执行的我们刚才讲过的逐次比较的过程),转换结果会直接放在这个数据寄存器里,我们读取寄存器就能知道ADC转换的结果了。

然后在模拟多路开关输出,对于普通的ADC,多路开关一般都是只选中一个的,就是选中某一个通道、开始转换、等待转换完成进取出结果,这是普通的流程。

但是STM32这里就比较高级了,它可以同时选中多个,而且在转换的时候还分成了两个组:规则通道组和注入通道组。其中规则组可以一次性最多选中16个通道,注入组最多可以选中4个通道。

举个例子,这就像是你去餐厅点菜,普通的ADC是,你指定一个菜,老板给你做,然后做好了送给你;这里就是,你指定一个菜单,这个菜单最多可以填16个菜,然后你直接递个菜单给老板,老板就按照菜单的顺序依次做好,一次性给你端上菜,这样的话就可以大大提高效率。当然,你的菜单也可以只写一个菜,这样这个菜单就简化成了普通的模式了。

那对于这个菜单呢,也有两种,一种是规则组菜单,可以同时上16个菜,但是它有个尴尬的地方。就是这个规则组只有一个数据寄存器,就是这个桌子比较小,最多只能放一个菜,你如果上16个菜,那不好意思,前15个菜都会被挤掉些,你只能得到第16个菜。

所以对于规则组转换来说,如果使用这个菜单的话,最好配合DMA来实现。DMA是一个数据转运小帮手,它可以在每上一个菜之后,把这个菜挪到其他地方去,防止被覆盖。这个DMA我们下一节就会讲,现在先大概了解一下,那现在我们就知道了,这个规则组虽然可以同时转换16个通道,但是数据寄存器只能存一个结果,如果不想之前的结果被覆盖,那在转换完成之后,就要尽快把结果拿走。

接着我们看一下注入组,这个组就比较高级了,它相当于是餐厅的VIP座位,在这个座位上,一次性最多可以点4个菜,并且这里数据寄存器有4个,是可以同时上4个菜的。对于注入组而言,就不用担心数据覆盖的问题了,这就是规则组和注入组的介绍。

一般情况下,我们使用规则组就完全足够了,如果要使用规则组的菜单,那就再配合DMA转运数据,这样就不用担心数据覆盖的问题了。所以接下来就只讲规则组的操作,注入组涉及的不多,大家可以看手册自行了解。

那我们接着继续看这个模数转换器外围的一些线路

首先,左下角这里是触发转换的部分,也就是这里的START信号,开始转换。那对于STM32的ADC,触发ADC开始转换的信号有两种,一种是软件触发,就是你在程序中手动调用一条代码,就可以启动转换了;另一种是硬件触发,就是这里的这些触发源。

上面这些是注入组的触发源,下面这些是规则组的触发源,这些触发源主要是来自于定时器,有定时器的各个通道,还有TRGO定时器主模式的输出,这个之前讲定时器的时候也介绍过。定时器可以通向ADC、 DAC这些外设,用于触发转换。

那因为ADC经常需要过一个固定时间段转换一次。比如每隔1ms转换一次,正常的思路就是,用定时器,每隔1ms申请一次中断,在中断里手动开始一次转换,这样也是可以的。但是频繁进中断对我们的程序是有一定影响的,比如你有很多中断都需要频繁进入,那肯定会影响主程序的执行,并且不同中断之间,由于优先级的不同,也会导致某些中断不能及时得到响应。

如果触发ADC的中断不能及时响应,那我们ADC的转换频率就肯定会产生影响了。所以对于这种需要频繁进中断,并且在中断里只完成了简单工作的情况,一般都会有硬件的支持。

然后接着看,左上角这里是VREF+、VREF-、VDDA和VSSA。上面两个是ADC的参考电压,决定了ADC输入电压的范围;下面两个是ADC的供电引脚。

一般情况下,VREF+要接VDDA,VREF-要接VSSA,在我们这个芯片上,没有VREF+和VREF-的引脚,它在内部就已经和VDDA和VSSA接在一起了。VDDA和VSSA是内部模拟部分的电源,比如ADC、RC振荡器、锁相环等。在这里VDDA接3.3V, VSSA接GND,所以ADC的输入电压范围就是0~3.3V。

在电子和电路设计中,VREF+、VREF-、VDDA 和 VSSA 是常见的电源和参考电压信号。它们的含义如下:

VREF+:正参考电压,通常用于提供一个稳定的正电压基准,供模拟电路或ADC(模数转换器)使用。
VREF-:负参考电压,通常用于提供一个稳定的负电压基准,可能在一些需要双极性电源的电路中使用。
VDDA:模拟电源电压,通常为模拟电路提供电源,确保模拟电路的正常工作。
VSSA:模拟地,通常是模拟电路的接地参考点,与VDDA配合使用。

然后继续看,右边这里是ADCCLK是ADC的时钟,也就是这里的CLOCK,是用于驱动内部逐次比较的时钟。这个ADCCLK是来自ADC预分频器,而ADC预分频器是来源于RCC的。

APB2时钟72MHZ,然后通过ADC预分频器进行分频,得到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对应的通道,它们就会触发中断。

2. ADC基本结构 ​

左边是输入通道,16个GPIO口,外加两个内部的通道,然后进入AD转换器。AD转换器里有两个组,一个是规则组,一个是注入组,规则组最多可以选中16个通道,注入组最多可以选择4个通道。然后转换的结果可以存放在AD数据寄存器里,其中规则组只有1个数据寄存器,注入组有4个。

然后下面这里有触发控制,提供了开始转换这个START信号,触发控制可以选择软件触发和硬件触发。硬件触发主要是来自于定时器,当然也可以选择外部中断的引脚,右边这里是来自于RCC的ADC时钟CLOCK,ADC逐次比较的过程就是由这个时钟推动的。

然后上面,可以布置一个模拟看门狗用于监测转换结果的范围,如果超出设定的阈值,就通过中断输出控制,向NVIC申请中断,另外,规则组和注入组转换完成后会有个EOC信号,它会置一个标志位,当然也可以通向NVIC。最后右下角这里还有个开关控制,在库函数中,就是ADC_Cmd函数,用于给ADC上电的,那这些,就是STM32 ADC的内部结构了。

ADC1和ADC2的引脚全都是相同的,既然都相同,那要ADC2还有啥用呢。这个就要再说一个ADC的高级功能了,就是双ADC模式,这个模式比较复杂。这里只简单介绍一下,不需要掌握。双ADC模式就是ADC1和ADC2一起工作,它俩可以配合组成同步模式、交叉模式等等模式。比如交叉模式,ADC1和ADC2交叉地对一个通道进行采样,这样就可以进一步提高采样率。

2.1 规则组的4种转换模式 ​

接下来,我们再来了解一下规则组的4种转换模式,分别是单次转换,非扫描模式和连续转换,扫描模式。

那在我们ADC初始化的结构体里,会有两个参数,一个是选择单次转换还是连续转换的,另一个是选择扫描模式还是非扫描模式的,这两个参数组合起来,就有这4种转换方式。我们来逐一看一下。

单次转换 非扫描模式 ​

第一种,单次转换,非扫描模式,这里我画了一个列表,这个表就是规则组里的菜单,有16个空位,分别是序列1到序列16,你可以在这里“点菜”,就是写入你要转换的通道,在非扫描的模式下,这个菜单就只有第一个序列1的位置有效,这时,菜单同时选中一组的方式就退化为简单地选中一个的方式了。

在这里我们可以在序列1的位置指定我们想转换的通道,比如通道2,写到这个位置。然后,我们就可以触发转换,ADC就会对这个通道2进行模数转换,过一小段时间后,转换完成,转换结果放在数据寄存器里,同时给EOC标志位置1,整个转换过程就结束了。

我们判断这个EOC标志位,如果转换完了, 那我们就可以在数据寄存器里读取结果了。如果我们想再启动一次转换,那就需要再触发一次,转换结束,置EOC标志位,读结果。如果想换一个通道转换,那在转换之前,把第一个位置的通道2改成其他通道,然后再启动转换,这样就行了。这就是单次转换,非扫描的转换模式。没有用到这个菜单列表,也是比较简单的一种模式。

连续转换 非扫描模式 ​

接下来我们看一下连续转换,非扫描模式。首先,它还是非扫描模式,所以菜单列表就只用第一个,然后它与上一种单次转换不同的是,它在一次转换结束后不会停止,而是立刻开始下一轮的转换,然后一直持续下去。

这样就只需要最开始触发一次,之后就可以一直转换了。这个模式的好处就是,开始转换之后不需要等待一段时间的,因为它直都在转换,所以你就不需要手动开始转换了,也不用判断是否结束的,想要读AD值的时候,直接从数据寄存器取就是了。这就是连续转换,非扫描的模式。

单次转换 扫描模式 ​

然后继续看,单次转换,扫描模式。这个模式也是单次转换,所以每触发一次,转换结束后,就会停下来,下次转换就得再触发才能开始。然后它是扫描模式,这就会用到这个菜单列表了,你可以在这个菜单里点菜,比如第一个菜是通道2,第二个菜是通道5,等等等等,这里每个位置是通道几可以任意指定,并且也是可以重复的,然后初始化结构体里还会有个参数,就是通道数目。

因为这16个位置你可以不用完,只用前几个,那你就需要再给一个通道数目的参数,告诉它,我有几个通道。比如这里指定通道数目为7,那它就只看前7个位置,然后每次触发之后,它就依次对这前7个位置进行AD转换,转换结果都放在数据寄存器里,这里为了防止数据被覆盖,就需要用DMA及时将数据挪走。那7个通道转换完成之后,产生EOC信号,转换结束,然后再触发下一次,就又开始新一轮的转换,这就是单次转换,扫描模式的工作流程。

连续转换 扫描模式 ​

那最后再看一下连续转换,扫描模式。它就是在上一个模式的基础上,变了一点,就是一次转换完成后,立刻开始下一次的转换。和上面这里非扫描模式的单次和连续是一个套路,这就是连续转换,扫描模式。

当然在扫描模式的情况下,还可以有一种模式,叫间断模式。它的作用是,在扫描的过程中,每隔几个转换,就暂停一次,需要再次触发,才能继续。这个模式没有列出来,要不然模式太多了。大家了解一下就可以了,暂时不需要掌握,好,这些就是STM32 ADC的4种转换模式。

2.2 额外知识点 ​

2.2.1 触发控制 ​

这个表就是规则组的触发源,也就是ADC总框图中的ADC。在这个表里,有来自定时器的信号;还有这个来自引脚或定时器的信号,这个具体是引脚还是定时器,需要用AFIO重映射来确定;最后是软件控制位,也就是我们之前说的软件触发。

这些触发信号怎么选择,可以通过设置右边这个寄存器来完成,当然使用库函数的话,直接给一个参数就行了,这就是触发控制。

2.2.2 数据对齐 ​

我们这个ADC是12位的,它的转换结果就是一个12位的数据,但是这个数据寄存器是16位的,所以就存在一个数据对齐的问题。这里第一种是数据右对齐,就是12位的数据向右靠,高位多出来的几位就补0;第二种是数据左对齐,是12位的数据向左靠,低位多出来的几位补0。在这里我们一般使用的都是第一种右对齐,这样读取这个16位寄存器,直接就是转换结果。

如果选择左对齐,直接读的话,得到的数据会比实际的大,因为数据左对齐实际上就是把数据左移了4次。二进制有个特点,就是数据左移一次,就等效于把这个数据乘2,那这里左移4次,就相当于把结果乘16了,所以直接读的话会比实际值大16倍。

那要这个左对齐有啥用呢?这个用途就是,如果你不想要这么高的分辨率,你觉得0~4095数太大了,我就做个简单的判断,不需要这么高分辨率,那你就可以选择左对齐,然后再把这个数据的高8位取出来,这样就舍弃掉了后面4位的精度,这个12位的ADC就退化成了8位的ADC了,这就是左对齐的作用。

不过我们一般用的话,选右对齐就行了,如果需要裁剪一些分辨率,大不了就先把12位都取出来,再做处理。

2.2.3 转换时间 ​

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

我们来看一下,之前我们说了,AD转换是需要一小段时间的,就像厨子做菜一样,也是需要等一会儿才能上菜的,那AD转换的时候都有哪些步骤需要花时间呢?AD转换的步骤,有4步,分别是采样,保持,量化,编码,其中采样保持可以放在一起,量化编码可以放在一起,总共是这两大步。量化编码好理解,就是我们之前讲过的,ADC逐次比较的过程,这个是要花一段时间的,一般位数越多,花的时间就越长。

那采样保持是干啥的呢?这个我们前面这里并没有涉及,为什么需要采样保持呢?这是因为,我们的AD转换,就是后面的量化编码,是需要一小段时间的,如果在这一小段时间里,输入的电压还在不断变化,那就没法定位输入电压到底在哪了,所以在量化编码之前,我们需要设置一个采样开关。

先打开采样开关,收集一下外部的电压,比如可以用一个小容量的电容存储一下这个电压,存储好了之后,断开采样开关,再进行后面的AD转换。这样在量化编码的期间,电压始终保持不变,这样才能精确地定位未知电压的位置,这就是采样保持电路。

那采样保持的过程,需要闭合采样开关,过一段时间再断开,这里就会产生一个采样时间。那回到这里,我们就得到了第二条,STM32 ADC的总转换时间为TCONV=采样时间+12.5个ADC周期,采样时间是采样保持花费的时间,这个可以在程序中进行配置,采样时间越大,越能避兔一些毛刺信号的干扰,不过转换时间也会相应延长。12.5个ADC周期是量化编码花费的时间,因为是12位的ADC,所以需要花费12个周期,这里多了半个周期,可能是做其他一些东西花的时间。ADC周期就是从RCC分频过来的ADCCLK,这个ADCCLK最大是14MHz。

所以下面有个例子,这里就是最快的转换时间,当ADCCLK=14MHz,采样时间为1.5个ADC周期,TCONV = 1.5 +12.5 = 14个ADC周期,在14MHz ADCCLK的情况下就 = 1us,这就是转化时间最快1us时间的来源。如果你采样周期再长些,它就达不到1us了;另外你也可以把ADCCLK的时钟设置超过14MHz,这样的话ADC就是在超频了,那转换时间可以比1us还短,不过这样稳定性就没法保证了。

2.2.4 校准 ​

  • ADC有一个内置自校准模式。校准可大幅减小因内部电容器组的变化而造成的准精度误差。校准期间,在每个电容器上都会计算出一个误差修正码(数字值),这个码用于消除在随后的转换中每个电容器上产生的误差
  • 建议在每次上电后执行一次校准
  • 启动校准前, ADC必须处于关电状态超过至少两个ADC时钟周期

第一个是一个电位器产生一个可调的电压,这里电位器的两个因定端,一端接3.3V,另一端接GND,这样中间的滑动端就可以辅出一个0~3.3V可调的电压输出了,我们这里可以接ADC的输入通道,比如PA0口,当滑动端往上滑时,电压增大,往下滑时,电压减小。

另外注意一下这个电阻的阻值不要给太小,因为这个电阻两流也是直接跨接在电源正负极的,如果阻值太小,那这个电阻就会比较费电,再小就有可能发热冒烟了,一般至少要接K欧级的电阻。比如这里接的是10K的电阻,这是电位器产生可调电压的电器。

然后中间是传感器输出电压的电路,一般来说,像光敏电阻、热敏电阻、红外接收管、麦克风等等,都可以等效为一个可变电阻(N1),那电阻值没法直接测量,所以这里就可以通过和一个固定电阻串联分压,来得到一个反应电阻值电压的电路。

那这里,传感器阻值变小时,下拉作用变强,输出电压就下降:传感器阻值变大时,下拉作用变弱,输出端受上拉电阻的作用,电压就会升高。这个固定电阻一般可以选择和传感器阻值相近的电阻,这样可以得到一个位于中间电压区域比较好的输出。

当然这里传感器和固定电阻的位置也可以换过来,这样的话,输出电压的极性就反过来了,这就是这个分压方法来输出传感器阻值的电路。

我们看一下最后这个电路,这是一个简单的电压转换电路,比如你想测一个0-5V的VIN电压,但是ADC只能接收0-3.3V的电压,那就可以搭建—个这样的简易转换电路。

在这里还是使用电阻进行分压,上面阻值17K,下向阻值33K,加一起是50K,所以根据分压公式,中间的电压就是VIN/50K×33K,最后得到的电压范国就是0~3.3V,就可以进入ADC转换了。

2.3 代码实战 ​

程序现象:在面包板的中间,也就是芯片左边接了一个电位器,就是滑动变阻器。用这个电位器产生一个0~3.3V连续变化的模拟电压信号。然后接到STM32的PA0口上,之后用STM32内部的ADC读取电压数据,显示在屏幕上。

这里屏幕第一行显示的是AD转换后的原始数据,第二行是经过处理后实际的电压值。电位器往左拧,AD值减小,电压值也减小,AD值最小是0,对应的电压就是0V;反之同理STM32的ADC是12位的,所以AD结果最大值是4095,也就是2^12-1,对应的电压是3.3V。

  • 第一步:开启RCC时钟,包括ADC和GPIO的时钟,另外这里ADCCLK的分频器,也需要配置一下
c
/*开启时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);	//开启ADC1的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	//开启GPIOA的时钟
  • 第二步,配置GPIO。把需要用的GPIO配置成模拟输入的模式
c
/*设置ADC时钟*/
RCC_ADCCLKConfig(RCC_PCLK2_Div6);		//选择时钟6分频,ADCCLK = 72MHz / 6 = 12MHz

/*GPIO初始化*/
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);					//将PA0引脚初始化为模拟输入
  • 第三步,配置这里的多路开关。把左边的通道接入到右边的规则组列表里。这个过程就是我们之前说的点菜,把各个通道的菜,列在菜单里
c
/*规则组通道配置*/
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);		//规则组序列1的位置,配置为通道0
  • 第四步,就是配置ADC转换器了。在库函数里,是用结构体来配置的,可以配置这一大块电路的参数。包括ADC是单次转换还是连续转换、扫描还是非扫描、有几个通道,触发源是什么,数据对齐是左对齐还是右对齐。
c
/*ADC初始化*/
ADC_InitTypeDef ADC_InitStructure;					//定义结构体变量
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;	//模式,选择独立模式,即单独使用ADC1
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;	//数据对齐,选择右对齐
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;	//外部触发,使用软件触发,不需要外部触发
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;	//连续转换,失能,每转换一次规则组序列后停止
ADC_InitStructure.ADC_ScanConvMode = DISABLE;	//扫描模式,失能,只转换规则组的序列1这一个位置
ADC_InitStructure.ADC_NbrOfChannel = 1;			//通道数,为1,仅在扫描模式下,才需要指定大于1的数,在非扫描模式下,只能是1
ADC_Init(ADC1, &ADC_InitStructure);				//将结构体变量交给ADC_Init,配置ADC1

如果你需要模拟看门狗,那会有几个函数用来配置阈值和监测通道的。如果你想开启中断,那就在中断输出控制里用ITConfig函数开启对应的中断输出,然后再在NVIC里,配置一下优先级,这样就能触发中断了。不过这一块,模拟看门狗和中断,我们本节暂时不用,如果你需要的话,可以自己配置试一下。

c
/*ADC使能*/
ADC_Cmd(ADC1, ENABLE);									//使能ADC1,ADC开始运行

/*ADC校准*/
ADC_ResetCalibration(ADC1);						//固定流程,内部有电路会自动执行校准
while (ADC_GetResetCalibrationStatus(ADC1) == SET);
ADC_StartCalibration(ADC1);
while (ADC_GetCalibrationStatus(ADC1) == SET);
c
/**
  * 函    数:获取AD转换的值
  * 参    数:无
  * 返 回 值:AD转换的值,范围:0~4095
  */
uint16_t AD_GetValue(void)
{
	ADC_SoftwareStartConvCmd(ADC1, ENABLE);					//软件触发AD转换一次
	while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);	//等待EOC标志位,即等待AD转换结束
	return ADC_GetConversionValue(ADC1);			//读数据寄存器,得到AD转换的结果
}