当前位置: 首页 > article >正文

06-ADC

ADC简介  Analog-Digital Converter 模拟-数字转换器

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

12位逐次逼近型ADC,1us转换时间;输入电压范围:0-3.3V,转换结果范围0-4095;18个输入通道,可测量16个外部和2个内部信号源;规则组和注入组两个转换单元;模拟看门狗自动检测输入电压范围。

        数字到模拟的桥梁:PWM或DAC。目前DAC的主要应用是在波形生成这些领域,比如信号发生器、音频解码芯片等。

ADC参数:分辨率、转换速度。输入电压范围。

        16个外部信号就是GPIO口,可在引脚上直接测模拟信号即可。2个内部信号时内部温度传感器和内部参考电压,温度传感器可以测量CPU的温度,内部参考电压是一个1.2V左右的基准电压,基准电压是不随外部供电电压变化而变化的。

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

        ADC可以用模拟看门狗来自动执行,模拟看门狗可以检测指定的某些通道,当AD值高于它设定的上阈值或者低于下阈值时,就会申请中断可以在中断函数里执行相应的操作。就不用不断地手动读值,再进行判断了。

逐次逼近型ADC0809

        逐次逼近型ADC0809的内部结构,了解这个结构对学习STM32的ADC帮助很大。在以前芯片集成功能不强的时候,需要外挂一个ADC芯片进行AD转换。

        左边IN0-IN7,是8路输入通道,通过通道选择开关,选中一路,输入到一点进行转换。地址锁存和译码是一个多路选择开关的作用。输入信号进入到比较器后,通过逐次逼近的方法来获取电压对应的编码数据。

        DAC内部使用加权电阻网络来实现的转换。如果DAC输出的电压比较大,就调小DAC数据,如果比较小,就增大DAC数据,直到DAC输出的电压和外部通道输入的电压近似相等。这样DAC输入的数据就是外部电压的编码数据。

        电压调节的过程是逐次逼近SAR来完成的,为了最快找到未知电压的编码,通常我们会使用二分法进行寻找,会发现128,64,32这些数据,正好是二进制每一位的位权,判断过程就是二进制从高位到低位依次判断是1还是0的过程。这就是逐次逼近型名字的由来。

EOC End Of Convert 转换结束信号;

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

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

VREF+和VREF-是DAC的参考电压,参考电压决定ADC的输入范围。通常低要求时,参考电压的正极和VCC接到一起、参考电压的负极和GND接在一起。

STM32的ADC

一般手册里,每个外设的最前面都有一个整体的结构图,这个结构图是非常重要的,需要多花时间看看。

        左边IN0-IN15是ADC的16个GPIO外部输入通道;VREFINT (V Reference Internal),内部参考电压;温度传感器。总共18个通道,到一个模拟多路开关,指定我们想要选择的通道。进入模数转换器,执行逐次比较,将转换结果放在数据寄存器里,用户读取寄存器获取ADC转换数值。

        普通的ADC,多路开关一般只选中一个的。但是此ADC可以同时选中多个,而且在转换时还分成了两个组,规则通道组和注入通道组,其中规则组一次性最多选中16个通道,注入组最多可以选中4个通道。
举个例子:就像是去餐厅点菜,普通的ADC是指定一个菜,老板给你做,然后做好了送给你,这里就是指定一个菜单,这个菜单最多可以填16个菜,直接把菜单寄给老板,老板就按照菜单的顺序依次做好,一次性给你端上菜,这样的话可以大大提高效率,这样这个菜单就简化成普通的模式了。那对于这个菜单也有两种,一种规则组菜单,可以同时上16个菜,有个尴尬的地方,就是这个规则组只有一个数据寄存器,就是这个桌子比较小,最多只能放一个菜,如果要上16个菜,那不好意思,前15个菜都会被挤掉,只能得到第16个菜,所以对于规则组转换来说,如果使用这个菜单的话,最好配合DMA来实现,DMA是一个数据转运小帮手,它可以在每上一个菜之后,把这个菜挪到其他地方去,防止被覆盖。

规则组虽然可以同时转换16个通道,但是数据寄存器只能存一个结果,如果不想之前的结果被覆盖,那在转换之后,就要尽快把结果拿走。
注入组就比较高级了,相当于餐厅的VIP座,在这个座位上,一次性最多可以点4个菜,并且这里数据寄存器有4个,是可以同时上4个菜的,对于注入组而言,就不用担心数据覆盖的问题了,一般情况下,使用规则组就完全足够了。如果要使用规则组的菜单,那就再配合DMA转运数据,就不用担心数据覆盖的问题。

注入组的操作:涉及的不多,可以看手册自行了解。
规则组的操作:先看模数转换器外围的一些线路,首先左下角是触发转换的部分,也就是ADC0809的START信号,开始转换。
对于STM32的ADC,触发ADC开始转换的信号有两种,一种是软件触发,就是在程序中手动调用一条代码,就可以启动转换了;另一种是硬件触发,就是这里的这些触发源。上面是注入组的触发源,下面是规则组的触发源。这些触发源主要是来自于定时器,,有定时器的各个通道,还有TRGO定时器主模式的输出,定时器可以通向ADC、DAC这些外设,用于触发转换,因为ADC经常需要过一个固定时间段转换一次,比如每隔1ms转换一次。正常的思路就是用定时器,每隔1ms申请一次中断,在中断里手动开始一次转换,这样也是可以的。但是频繁进中断对我们的程序是由一定影响的。
比如有很多中断都需要频繁进入,那肯定会影响主程序的执行,并且不同中断之间,由于优先级的不同,也会导致某些中断不能及时得到响应。那我们ADC的转换频率就肯定会产生影响了,所以对于这种需要频繁进中断,并且在中断里只完成了简单工作的情况,一般都会有硬件的支持。
比如这里给TIM3定1ms的时间,并且把TIM3的更新事件选择为TRGO输出,然后在ADC这里,选择开始触发信号为TIM3的TRGO,这样TIM3的更新事件就能通过硬件自动触发ADC转换了,整个过程不需要进中断,节省了中断资源。这就是这里定时器的触发作用。
当然这里还可以选择外部中断引脚来触发转换,都可以在程序中配置。

ADCCLK是ADC的时钟,相当于0809的CLOCK,用于驱动内部逐次比较的时钟。ADC预分频器来源于RCC。
DMA的请求:是用于触发DMA进行数据转运的,DMA章节再讲。


两个数据寄存器,是用于存放转换结果的。
        模拟看门狗,里面可以存一个阈值高限和阈值低限,如果启动了模拟看门狗,并指定了看门的通道,那这个看门狗就会关注它看门的通道,一旦超过这个阈值范围了,它就会乱叫,就会在上面,申请一个模拟看门狗的中断,最后通向NVIC。

        然后对于规则组和注入组而言,它们转换完成之后,也会有一个EOC规则组转换完成的信号,JEOC是注入组完成的信号,这两个信号会在状态寄存器里置一个标志位,我们读取这个标志位,就能知道是不是转换结束了,同时这两个标志位也可以去NVIC,申请中断,如果开启了NVIC对应的通道,它们就会触发中断,。

ADC基本结构图

左边是输出通道,16个GPIO口,外加两个内部的通道,然后进入AD转换器(有两个组,一个规则组,一个注入组),规则组最多可以选中16个通道,注入组最多可以选择4个通道,然后转换的结果可以存放在AD数据寄存器里,其中规则组只有1个数据寄存器,注入组有4个,触发控制提供了开始转换这个START信号,触发控制可以选择软件触发和硬件触发。硬件触发主要是来自于定时器,当然也可以选择外部中断的引脚,右边这里是来自于RCC的ADC时钟CLOCK,ADC的逐次比较的过程就是由这个时钟推动的。然后上面可以布置一个模拟看门狗用于监测转换结果的范围。

最后右下角还有一个开关控制,在库函数中,就是ADC_Cmd函数,用于给ADC上电的,这些就是STM32 ADC的内部结构了。

接下来是一些细节问题:

ADC12_IN0的意思是这个引脚是ADC1和ADC2的IN0都是在PA0上的。
ADC的一个高级功能:双ADC模式,这个模式比较复杂,暂时简单了解,不需要掌握。
双ADC模式就是ADC1和ADC2一起工作,它俩可以配合组成同步模式、交叉模式等等模式,比如交叉模式:ADC1和ADC2交叉地对一个通道进行采样,就可以进一步提高采样率。当然ADC1和ADC2可以分开使用。可以分别对不同的引脚进行采样。

规则组的4种转换模式:

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

单次转换,每触发一次,转换结束后,就会停下来,下次转换得再触发才能开始。

连续转换:一次转换完成后,立刻开始下一次的转换。

非扫描模式:所以菜单列表就只用第一个。

扫描模式:一次可以转换多个序列,但需要使用DMA进行数据转运。

单次转换-非扫描模式:

        最简单的一种模式。序列是规则组里的菜单,有16个空位,分别是序列1到序列16。可以写入要转换的通道,在非扫描的模式下,只有序列1的位置有效。在序列1的位置指定我们想转换的通道,比如通道2,写到这个位置,然后以触发转换,ADC就会对这个通道2进行模数转换。转换结果放在数据寄存器里,同时给EOC标志位置1。我们判断这个EOC标志位,如果转换完了,如果想再启动一次转换,那就需要再触发一次,转换结束,置EOC标志位,读结果。
        如果想换一个通道转换,那在转换之前,把第一个位置的通道2改成其他通道,然后再启动转换,这样就行了。


连续转换-非扫描模式

       与单次转换不同的是,它在一次转换结束后不会停止,而是立刻开始下一轮的转换。好处:开始转换之后不需要等待一段时间的,因为它一直在转换,所以就不需要手动,也不用判断是否结束,想要读AD值的时候,直接从数据寄存器取就可以了。

单次转换-扫描模式

        
        扫描模式就用到这个菜单列表了,可以在这个菜单里点菜,比如第一个菜是通道2,第二个菜是通道5,等等,这里每个位置是通道几可以任意指定,并且也是可以重复的。然后初始化结构体有个参数,就是通道数目,因为这16个通道可以不用完,只用前几个,那就需要再给他一个通道数目的参数。告诉它,我有几个通道,比如这里指定通道数目为7,那它就只看前7个位置,然后每次触发之后,它就一次对这前7个位置进行AD转换,转换结构都放在数据寄存器里,这里为了防止数据被覆盖,就需要用DMA及时将数据挪走,那7个通道转换完成之后,产生EOC信号,转换结束,然后再触发下一次,就开始新一轮的转换。

连续转换-扫描模式

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

触发控制

        这个表是规则组的触发源:有来自引脚或定时器的信号,具体是引脚还是定时器,需要用AFIO重映射来确定。软件控制位也就是我们之前说的软件触发。这些触发信号怎么选择,可以通过设置右边的寄存器来完成,当然使用库函数的话,直接给一个参数就行了。这就是触发控制。

数据对齐

        ADC是12位的,它的转换结果是一个12位的数据,但寄存器是16位的,所以就存在一个数据对齐的问题。12位的数据向右靠,高位多出来几位补0;12位的数据向左靠,低位多出来的几位补0。

        一般使用右对齐:在读取16位寄存器直接就是转换结果。

        左对齐用途:如果你不需要高分辨率,觉得0-4095太大了,就做个简单的判断,可以选择左对齐,把数据的高8位取出来,舍弃后4位的精度。当然也可以把12位都取出来。

转换时间

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

STM32ADC的总采样时间:Tconv=采样时间+12.5个ADC周期

例如:当ADCCLK=14MHz,采样时间位1.5个ADC周期,Tconv=1.5+12.5=14个ADC周期=1us

一般不敏感,因为一般AD转换都很快,如果不需要非常高速的转换频率,那转换时间就可以忽略了,AD转换需要一小段时间,就像厨子做菜一样,也需要等待一会才能上菜的。
采样保持可以放在一起,量化编码可以放在一起,总共是这两大步。量化编码就是之前讲的ADC逐次比较的过程。这个需要花一段时间,一般位数越多,花的时间就越长。
采样保持是因为 AD转换后面的量化编码,是需要一小段时间的,如果在这一小段时间里,输入电压还在不断变化,那就没法定位输入电压到底在哪里了,所以在量化编码之前,需要设置一个采样开关,比如可以用一个小容量的电容存储一下这个电压,存储好了之后,断开采样开关,在进行后面的AD转换。这样在量化编码的过程中,电压始终保持不变,这就是采样保持电路。
那采样保持的过程,需要闭合采样开关,过一段时间再断开,这里就会产生一个采样时间,那回到这里这里,我们就得到了第二条。
采样时间就是采样保持花费的时间,这个可以在程序中进行配置,采样时间越大,越能避免一些毛刺信号的干扰,不过转换时间也会相应延长,12.5个ADC周期是量化编码花费的时间,因为是12位的ADC,所以需要花费12个周期,这里多了半个周期,可能是做其他一些东西画的时间。ADC周期就是从RCC分频过来的ADCCLK,这个ADCCLK最大是14MHz,所以下面有个例子,这里就是最快的转换时间。

如果采样周期再长些,它就达不到1us了,另外也可以把ADCCLK的时钟设置超过14MHz,这样就是在超频了,那转换时间可以比1us还短,不过这样稳定性就没办法保证了

听起来很复杂,但是这个校准我们暂时不需要理解,因为校准过程是固定的,我们只需要在ADC初始化最后,加几条代码就行了,至于怎么计算,怎么校准的,我们不需要管,所以了解一下就行了

ADC的外围电路
第一个是电位器产生一个可调的电压,电阻阻值不能给的太小,
第二个是传感器输出电压的电路,一般来说,像光敏电阻,热敏电阻,红外接收管,麦克风等等,都可以等效为一个可变电阻,所以这里就可以通过和一个固定电阻串联分压,这个福鼎电阻一般可以选择和传感器阻值相近的电阻。这样可以得到一个位于中间电压区域比较好的输出。
第三个是电压转换电路,使用电阻进行分压,但是如果是高电压采集最好使用一些专用的采集芯片,比如隔离放大器等等,做好高低电压的隔离,保证电路的安全

手册介绍:
见参考手册。

代码步骤:
1.开启RCC时钟,包括ADC和GPIO的时钟,另外ADCCLK的分频器,也需要配置一下
2.配置GPIO,把需要用到的GPIO配置成模拟输入的模式
3.配置多路开关,把左边的通道接入到右边的规则组列表里,这个过程就是我们之前说的点菜,把各个通道的菜,列在菜单里
4.配置ADC转换器,在库函数里,是用结构体来配置的,可以配置这一大块电路的参数。包括ADC是单次转换还是连续转换、扫描还是非扫描、有几个通道、触发源是什么、数据对齐是左对齐还是右对齐,这一大批参数,用一个结构体配置就可以了,如果需要模拟看门狗,那会有几个函数用来配置阈值和监测通道的,那就在中断输出控制里ITConfig函数开启对应的中断输出,然后再在NVIC里,配置一下优先级,这样就能触发中断了。不过这一块模拟看门狗和中断,本节暂时不用。
开关控制,调用一下ADC_Cmd函数,开启ADC,这样ADC就配置完成了,就能正常工作了,当然在开启ADC之后,根据手册里的建议,还可以对ADC进行一下校准,这样可以减小误差,那在ADC工作的时候,如果想要软件触发转换,那会有函数可以触发,如果想读取转换结果,那也会有函数可以读取结果
配置程序的大致思路。

void RCC_ADCCLKConfig(uint32_t RCC_PCLK2);
配置ADCCLK分频器的,它可以对应APB2的72MHz时钟选择2、4、6、8分频,输入到ADCCLK

DeInit恢复缺省设置、Init初始化、StructInit结构体初始化
ADC_Cmd是用于给ADC上电的,
DMACmd是用于开启DMA输出信号的
ITConfig中断输出控制,用于控制某个中断,能不能通往NVIC
ADC_ResetCalibration 复位校准
ADC_GetResetCalibrationStatus 获取复位状态
ADC_StartCalibration 开始校准
ADC_GetCalibrationStatus 获取开始校准状态
在ADC初始化完成之后,依次调用就行了。
然后继续,ADC软件开始控制转换,就是这个用于软件触发的函数了 ADC_SoftwareStartConvCmd
ADC获取软件开始转换状态 ADC_GetSoftwareStartConvStatus
从名字上来看,这个函数好像是判断是不是正在进行的,那我们可不可以调用这个函数,来判断转换是否已经结束呢,这个可以分析一下源码,源码分析可得这个函数的返回值跟转换是否结束,毫无关系

ADC_GetFlagStatus 获取标志位状态,知道转换是否结束,然后参数给EOC的标志位,判断EOC标志位是不是置1了,如果转换结束,EOC标志位置1,然后调用这个函数,判断这个标志位,这样才是正确的判断转换是否结束的方法。
所以简单俩说, ADC_GetSoftwareStartConvStatus这个函数其实没啥用,我们一般不用,不要被他误导了

下面两个函数是用来配置间断模式的
ADC_DiscModeChannelCountConfig 是每隔几个通道间断一次,
ADC_DiscModeCmd 是不是启用间断模式,需要间断模式的话可以理解一下,

void ADC_RegularChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel, uint8_t Rank, uint8_t ADC_SampleTime); ADC_规则组通道配置,这个函数比较重要,它的作用是给序列的每个位置填写指定的通道,就是填写点菜菜单的过程,第一个参数ADCx,第二个ADC_Channel,就是想要指定的通道,第三个Rank,就是序列几的位置,第四个SampleTime指定通道的采样时间

ADC_ExternalTrigConvCmd 外部触发控制转换,就是是否允许外部触发转换,
ADC_GetConversionValue 是ADC获取转换值,这个函数也比较重要,获取AD转换的数据寄存器,读取转换结果就要使用这个函数
ADC_GetDualModeConversionValue ADC获取双模式转换值,这个是双ADC模式读取转换结果的函数

以上函数就是对ADC的一些基本功能和规则组的配置,

接下来的一大批函数里面都带了一个Injected,就是注入组的意思,所以一大批函数都是对ADC注入组进行配置的,注入组暂时不多讲,

这三个函数就是对模拟看门狗进行配置的,第二个是配置高低阈值、第三个是配置看门的通道、之后ADC温度传感器、内部参考电压控制、如果要用到这两个通道,那得调用这个函数,开启一下,要不然是读不到正确的结果,这个要注意一下。

最后四个,获取标志位状态,清除标志位,获取终端状态,清除终端挂起位。常用函数,不多说。

EOC看手册得知,是规则或注入。这一点需要看参考手册进行笔记的修改。

如果想要用扫描模式实现多通道,最好要配合DMA来实现,

可能会问:一个通道转换完成之后,手动把数据转运出来,不就行了,为啥非要用DMA来转运呢,这个方案看似简单,实际操作起来会有一些问题,第一个就是在扫描模式下,你启动列表之后,它里面每一个单独的通道转换完成之后,不会产生任何的标志位,也不会触发中断,你不知道某一个通道是不是转换完了,它只有在整个列表都转换完成之后,才会产生一次EOC标志位,才能触发中断,而这时,前面的数据就已经覆盖丢失了。
第二个问题:AD转换是非常快的,通过计算转换一个通道大概只有几us,也就是说,如果你不能在几us的时间内把数据转运走,那数据就会丢失,这对于我们程序手动转运数据,要求就比较高,所以在扫描模式下,手动转运数据是比较困难的。
不过比较困难也不是说手动转运不可行,在扫描的时候,每转换一个通道就暂停一次,等我们手动把数据转运走之后,再继续触发,继续下一次转换,这样可以实现手动转运数据的功能,但是由于单个通道转换完成之后,没有标志位,所以启动转换之后,只能通过Delay延时的方法,才能保证转换完成,这种方式既不能让我们省心,也不能提高效率。所以暂不推荐使用

实现多通道一个简单的方法:可以使用单次转换、非扫描的模式。只需要在每次触发转换之前,手动更改一下列表第一个位置的通道就行了,比如第一次转换,先写入通道0,之后触发、等待、读值;第二次转换,再先把通道0改成通道1,之后触发、等待、读值。第三次转换,再先改成通道2,等等,在转换前,先指定一下通道,再启动转换。就可以轻松地实现多通道转换的功能了。


http://www.kler.cn/a/613234.html

相关文章:

  • Linux内核调试 - Hung_task机制分析下
  • LLM 优化技术(2)——paged_attention 原理
  • 几种常见的.NET单元测试模拟框架介绍
  • leetcode day32 763+56
  • 【软件测试】:软件测试实战
  • I.MX6ULL 开发板上挂载NTFS格式 U 盘
  • vue将页面导出成word
  • Python_电商erp自动拆分组合编码
  • 规范Unity工程目录和脚本结构能有效提升开发效率、降低维护成本
  • Maven中为什么有些依赖不用引入版本号
  • 【ManiSkill】环境success条件和reward函数学习笔记
  • YOLOv8 中的损失函数解析
  • 构建可扩展、可靠的网络抓取、监控和自动化应用程序的终极指南
  • 【天梯赛】L2-004 这是二叉搜索树吗(经典问题C++)
  • Go语言中regexp模块详细功能介绍与示例
  • 什么是架构,以及当前市面主流架构类型有哪些?
  • X.509证书与证书请求生成原理及其应用(C/C++代码实现)
  • STM32基础教程——旋转编码器测速
  • Mysql的单表查询和多表查询
  • 记录一次TDSQL事务太大拆过binlog阈值报错