ADC模数转换器
那我们先来看一下本节课程序的现象啊。本节课共有两个程序,第一个程序是 AD 单通道,第二个是 AD 多通道。先看一下第一个 AD 单通道啊,下载,看一下面包板。在面包板的这里啊,我接了一个电位器,就是滑动变阻器啊。用这个电位器产生一个 0~3.3 伏连续变化的模拟电压信号,然后接到 STM32 的 PA0 口上。之后用 STM32 内部的 ADC 读取电压数据,显示在屏幕上。这里屏幕第一行显示的是 AD 转换后的原始数据,第二行是经过处理后实际的电压值哈。那我们拧一下这个电位器试一下。往左拧 AD 值减小,电压值也减小啊。 AD 值最小是 0,对应的电压就是 0 伏。往右拧 AD 值变大,对应电压值也变大啊。
STM32 的 ADC 是 12 位的,所以 AD 结果最大值是 4095 哈,也就是 2 的 12 次方减一,对应的电压是 3.3 伏。这就是第一个程序的现象。
那对于 GPIO 来说,它只能读取引脚的高低电平哈,要么是高电平,要么是低电平,只有两个值。而使用了 ADC 之后呢,我们就可以对这个高电电平和低电平之间的任意电压进行调啊,最终用一个变量来表示,读取这个变量就可以知道引脚的具体电压到底是多少了。所以 ADC 其实就是一个电压表啊,把引脚的电压值测出来,放在这个变量里,这就是 ADC 的作用。

好那我们继续看一下第二个代码,第二个代码是 AD 多通道,看一下。在这里我又接了三个传感器模块啊,分别是光敏电阻、热敏电阻和反射红外模块。把它们的 AO 啊模拟电压输出端分别接在了 A1 A2 A3 引脚,加上刚才的电位器啊,总共四个输出通道。然后测出来的四个 AD 数据啊分别显示在屏幕上,我们来试一下,第一个电位器啊,看第一行的 AD0。往左拧减小,往右拧增大,和上一个程序现象一样哈。然后光敏电阻看第二行的 AD1,遮挡一下光敏电阻,光线减小, AD 值增大,离开光线增大, AD 值减小。然后热敏电阻看第三行的 AD2 啊,用手热一下这个热敏电阻啊,温度升高, AD 值减小,离开温度降低, AD 值增大。最后是反射红外传感器啊,手靠近有反光, AD 值减小,离开没有反光, AD 值增大。这就是第二个程序的现象。


ADC 可以将引脚上连续变化的模拟电压转化为内存中存储的数字变量,建立模拟电路到数字电路的桥梁。那刚才通过程序现象我们也看到了哈, STM32 主要是数字电路,数字电路只有高低电平,没有几伏电压的概念哈。所以如果想读取电压值,就需要借助 ADC 模数转换器来实现了。 ADC 读取引脚上的模拟电压,转化为一个数据,存放在寄存器里,我们再把这个数据读取到变量里来,就可以进行显示啊、判断啊、记录啊等等操作了。 ADC 可以将模拟信号转换为数字信号,是模拟电路到数字电路的桥梁啊。 ADC 是模拟到数字的桥梁
那反过来,有模拟到数字的桥梁,那肯定就有数字到模拟的桥梁对吧?这就是 DAC 数字模拟转换器。使用 DAC 就可以将数字变量转化为模拟电压。不过在上一节呢我们还学到了一个数字到模拟的桥梁,是啥呢? PWM。 上节我们使用 PWM 来控制 LED 的亮度啊,电机的速度,这就是 DAC 的功能哈。同时 PWM 只有完全导通和完全断开两种状态,在这两种状态上都没有功率损耗哈,所以在直流电机调速这种大功率的应用场景,使用 PWM 来等量是比 DAC 更好的选择哈,并且 PWM 电路更加简单,更加常用。所以可以看出 PWM 还是挤占了 DAC 的很多应用空间哈,目前 DAC 的应用主要是在波形生成这些领域,比如信号发生器,音频解码芯片等,这些领域 PWM 还是不好替代的哈。好,这就是 ADC 和 DAC 的简单介绍了。我们本节学习的是 ADC, DAC 大家就自行了解了。我们这个型号的 STM32 也没有 DAC 的外设哈。
那继续看下一条, STM32 的 ADC 是 12 位逐次逼近型的 ADC, 1 微秒的转换时间。逐次逼近型这是这个 ADC 的工作模式啊,等会再细说。然后 12 位和 1 微秒的转换时间,这里就是涉及到 ADC 的两个关键参数了。第一个是分辨率,一般用多少位来表示啊?12 位 AD 值,它的表示范围就是 0~2 的 12 次方减一,就是量化结果的范围是 0~4095。位数越高啊,量化结果就越精细,对应分辨率就越高。第二个是转换时间,就是转换频率啊。 AD 转换是需要花一小段时间的,这里一微秒就表示从 AD 转换开始到产生结果需要花一微秒的时间,对应 AD 转换的频率就是一兆赫兹。这个就是 STM32 ADC 的最快转换频率啊。如果你需要转换一个频率非常高的信号,那就要考虑一下这个转换频率是不是够用啊。如果你的信号频率比较低,那这个最大一兆赫兹的转换频率也完全够用了啊。
然后看下一条,输入电压范围 0~3.3 伏,转换结果范围 0~4095。这个 ADC 的输入电压啊,一般要求都是要在芯片供电的负极和正极之间变化的啊,最低电压就是负极 0 伏。最高电压是正极 3.3 伏,经过 ADC 转换之后,最小值就是 0,最大值是 4095,0 伏对应 0,3.3 伏对应 4095,中间都是一一对应的线性关系啊,这个计算起来就非常简单了是吧?直接乘除一个系数就行了。
然后继续看,这个 ADC 有 18 个输入通道,可测量 16 个外部和两个内部信号源,外部信号源就是 16 个 GPIO 口啊,在引脚上直接接模拟信号就行了,不需要任何额外的电路。引脚就直接能测电压,这个还是非常方便的。两个内部信号源是内部温度传感器和内部参考电压,温度传感也可以测量 CPU 的温度哈,比如你电脑可以显示一个 CPU 温度,就可以用 ADC 读取这个温度传感器来测量。内部参考电压是一个 1.2 伏左右的基准电压,这个基准电压是不随外部供电电压变化而变化的。所以如果你芯片的供电不是标准的 3.3 伏哈,那测量外部引脚的电压可能就不对,这时就可以读取这个基准电压进行校准,这样就能得到正确的电压值了。
然后继续,规则组和注入组两个转换单元,这个就是 STM32 ADC 的增强功能了哈。普通的 AD 转换流程是启动一次转换读一些值,然后再启动再读值,这样的流程。但是 STM32 的 ADC 就比较高级哈,可以列一个组,一次性启动一个组,连续转换多个值。并且有两个组哈,一个是用于常规使用的规则组,一个是用于突发事件的注入组。这个等会我们再细看哈。
那继续看,模拟看门狗自动监测输入电压范围。这个 ADC 啊,一般可以用于测量光线强度,温度这些值,并且经常会有需求啊,就是如果光线高于某个阈值,低于某个阈值,或者温度高于某个阈值低于某个阈值时,执行一些操作。这个高于某个阈值低于某个阈值的判断,就可以用模拟看门狗来自动执行。模拟看门狗可以监测指定的某些通道,当 AD 值高于它设定的上阈值或者低于下阈值时,它就会申请中断,你就可以在中断函数里执行相应的操作。这样你就不用不断的手动读值啊,再用 if 进行判断了。这个可以了解一下。
然后最后一条, STM32F103C8T6 啊,就是我们用的这个型号。它的 ADC 资源有 ADC1、 ADC2 共有两个 ADC 外设,10 个外部输入通道,也就是它最多只能测量 10 个外部引脚的模拟信号。我们之前这里说的 16 个外部信号源啊,这是这个系列最多有 16 个外部信号源,但是我们这个芯片引脚比较少,有很多引脚没有引出来啊,所以就只有 10 个外部信号源。如果你想要更多的外部通道,可以选择引脚更多的型号。具体有多少通道呢,那还需要再参考一下数据手册。
接下来我们就来了解一下这个逐次逼近型 ADC 到底是怎么电压的。

逐次逼近型 ADC 的内部结构啊,了解这个结构对你学习 STM32 的 ADC 有很大帮助啊。因为 STM32 的 ADC 原理和这个是一样的,但是 STM32 只画了一个框啊表示 ADC, 并没有描述内部结构,所以我们先介绍一下这个结构哈,这样再理解 STM32 的 ADC 就会简单一些了。
我们来看一下,这个图是 ADC0809 的内部结构图哈,它是一个独立的 8 位逐次逼近型 ADC 芯片。在以前的时候啊,单片机的性能还不是很强,所以需要外挂一个 ADC 芯片才能进行 AD 转换。这个 ADC 零八零九就是一款比较经典的 ADC 芯片啊。那现在单片机的性能和集成度都有很大的提升,很多单片机内部就已经集成了 ADC 外设,这样就不用外挂芯片了,引脚可以直接测电压,使用还是非常方便的啊。
我们来看下这个结构,首先左边这里, IN0 到 IN7 是八路输入通道,通过通道选择开关,选中一路输入到这个点进行转换。
下面这里是地址锁存和译码,就是你想选中哪一路哈,就把通道号放在这三个脚上,然后给一个锁存信号,上面这里对应通路开关就可以自动拨好了。这部分就相当于一个可以通过模拟信号的数据选择器啊。因为 ADC 转换是一个很快的过程,你给一个开始信号,过个几微秒就转换完成了。所以说如果你想转换多路信号,那不必设计多个 AD 转换器啊,只需要一个 AD 转换器,然后加一个路选择开关,想转换哪一路就先拨一下开关,选中对应通道,然后再开始进行转换就行了。这就是这个输入通道选择的部分。这个 ADC0809 只有 8 个输入通道,我们 STM32 内部的 ADC 是有 18 个输入通道的,所以对应这里就是一个 18 路输入的多路开关。

那然后输入信号选好了,到这里来,怎么才能知道这个电压对应的编码数据是多少呢?这就需要我们用逐次逼近的方法来一一比较了。首先这是一个电压比较器,它可以判断两个输入信号电压的大小关系。输出一个高低电平,指示谁大谁小。它的两个输入端,一个是待测的电压,另一个是这里 DAC 的电压输出端。 DAC 是数模转换器啊,我们之前说过了,给它一个数据,它就可以输出数据对应的电压。 DAC 内部是使用加权电阻网络来实现的转换啊,具体可以看我 51 单片机教程里的 D / A 那一节,这里我就不再细说了啊。那现在我们有了一个外部通道输入的未知编码的电压,和一个 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 这一数据正好是二进制每一位的位权。这个判断过程就相当于是对二进制从高位到低位依次判断是一还是零的过程。这就是逐次逼近型名字的来源。那对于 8 位的 ADC,从高位到低位依次判断 8 次就能找到未知电压的编码了。对于 12 位的 ADC,就需要依次判断 12 次,这就是逐次逼近的过程。

那然后 AD 转换结束后, DAC 的输入数据就是未知电压的编码,通过这里进行输出, 8 位就有 8 根线啊,12 位就有 12 根线。最后上面这里, EOC 是 End of Convert, 转换结束信号。 Start 是开始转换,给一个输入脉冲开始转换啊。 Gloc 是 ADC 始终因为 ADC 内部是一步一步进行判断的,所以需要时钟来推动这个过程。下面, VREF 正和 VREF 负是 DAC 的参考电压。比如你给一个数据 255 啊,是对应 5 伏还是 3.3 伏呢?就由这个参考电压决定。这个 DAC 的参考电压也决定了 ADC 的输入范围,所以它也是 ADC 参考电压。最后左边是整个芯片电路的供电啊, VCC 和 GND。 通常参考电压的正极和 VCC 是一样的,会接在一起。参考电压的负极和 GND 也是一样的,也接在一起啊。所以一般情况下, ADC 输入电压的范围就和 ADC 的供电是一样的。好,到这里,相信你对逐次逼近型 ADC 就已经了解差不多了。

接下来我们就来看看 STM32 的逐次逼近型 ADC, 看看 STM32 的 ADC 和这个相比有什么更高级的变化。那我们看一下 STM32 的这个 ADC 框图。

在这里左边是 ADC 的输入通道包括 16 个 GPIO 口 IN0 到 IN15 和两个内部的通道一个是内部温度传感器另一个是 VREFINT 内部参考电压总共是 18 个输入通道

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

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

但是这里就比较高级了它可以同时选中多个而且在转换的时候还分成了两个组啊规则通道组和注入通道组,其中规则组可以一次性最多选 16 个通道注入组最多可以选中 4 个通道,这有什么用呢举个例子啊这就像是你去餐厅点菜普通的 ADC 是你指定一个菜老板给你做然后做好了送给你这里就是你指定一个菜单这个菜单最多可以填 16 个菜然后你直接递个菜单给老板老板就按照菜单的顺序依次做好一次性给你端上来这样的话就可以大大提高效率啊,当然你的菜单也可以只写一个菜啊这样这个菜单就简化成了普通的模式了

那对于这个菜单呢也有两种一种是规则组菜单可以同时上 16 个菜但是它有个尴尬的地方啊就是在这里这个规则组只有一个数据寄存器就是这个桌子比较小啊最多只能放一个菜如果上 16 个菜那不好意思啊前 15 个菜都会被挤掉你只能得到第 16 个菜所以对于规则组转换来说如果使用这个菜单的话最好配合 DMA 来实现 DMA 是一个数据转运小帮手啊它可以可以在每上一个菜之后把这个菜挪到其他地方去防止被覆盖这个 DMA 我们下一节就会讲啊现在先大概了解一下

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

注入组涉及的不多大家可以看手册自行了解啊
那我们接着继续看这个魔术转换器外围的一些线路首先左下角这里是触发转换的部分,也就是这里的 START 信号开始转换啊


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

当然这里还可以选择外部中断引脚来触发转换,都可以在程序中配置啊。这就是触发转换的部分。

接着看左上角这里是 VREF 正 VREF 负 VDD 和 VSSA 上面两个是 ADC 的参考电压决定了 ADC 输入电压的范围下面两个是 ADC 的供电引脚一般情况下 VREF 正要接 VDD VREF 负要接 VSSA 在我们这个芯片上没有 VREF 正和 VREF 负的引脚它在内部就已经和 VDD 和 VSSA 接在一起了

VDDA 和 VSSA 在这个引脚定义你也可以看到哈我们之前讲过了 VDD 和 VSS 是内部模拟部分的电源,比如 ADC ,RC 振荡器,锁相环等在这里 VDD 接 3.3 伏 VSS 接 GND 所以 ADC 的输入电压范围就是 0-3.3 伏

然后继续看右边这里是 ADCCLK 是 ADC 的时钟,也就是这里的 clock


是用于驱动内部逐次比较的时钟,这个是来自 ADC 预分频器,这个 ADC 预分频器是来源于 RCC 的我们找一下在这里哈 APB2 时钟 72 兆赫兹然后通过 ADC 预分频器进行分频得到 ADCCLK ADCCLK 最大是 14 兆赫兹,所以这个预分频器就有点尴尬哈它可以选择 2 4 6 8 分频如果选择二分频 72 兆除以 2 等于 36 兆超出允许范围了, 4 分频之后是 18 兆也超了所以对于 ADC 预分频器只能选择 6 分频结果是 12 兆,和 8 分频结果是 9 兆这两个值这个在程序里要注意一下啊

然后回到这里这是 ADC 的时钟继续看上面这里是 DMA 请求这个就是用于触发 DMA 进行数据转运的我们下节再讲哈然后是两个数据寄存器用于存放转换结果的上面这里还有模拟看门狗它里面可以存一个阈值高限和阈值低限如果启动了模拟看门狗并且指定了看门的通道那这个看门狗就会关注它看门的通道一旦超过这个阈值范围了它就会乱叫啊就会在上面申请一个模拟看门狗的中断最后通向 NVIC

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

好有关 ADC 的这个框图啊我们就介绍完了那接下来就来看一下我这里总结的一个 ADC 基本结构图再来回顾一下哈

图再来回顾一下哈,左边是输入通道,16 个 GPIO 口外加两个内部的通道,然后进入 AD 转换器,AD 转换器里有两个组,一个是规则组,一个是注入组,规则组最多可以选中 16 个通道,注入组最多可以选择 4 个通道,然后转换的结果可以存放在 AD 数据寄存器里,其中规则组只有一个数据寄存器,注入组有 4 个。然后下面这里有触发控制,提供了开始转换这个 START 信号,触发控制可以选择软件触发和硬件触发,硬件触发主要是来自于定时器啊,当然也可以选择外部中断的引脚,右边这里是来自于 RCC 的 ADC 时钟 clock,ADC 逐次比较的过程就是由这个时钟推动的,然后上面可以布置一个模拟看门狗,用于监测转换结果的范围,如果超出设定的阈值就通过中断输出控制向 NVIC 申请中断,另外规则组和注入组转换完成后会有个 EOC 信号,它会置一个标志位啊,当然也可以通向 NVIC。最后右下角这里还有个开关控制,在库函数入啊,就是 ADC CMD 函数,用于给 ADC 上电的,那这些就是 STM32 ADC 的内部结构图了

接下来我们再了解一些细节的问题啊,首先看一下输入通道
刚才我们说了这里有 16 个外部通道,那这 16 通道对应的都是哪些 GPIO 口呢,我们就可以看一下这个表,这些就是 ADC 通道和引脚复用的关系

这个对应关系也可以通过引脚定义表看出来啊,我们看一下,在这里可以看到 ADC12 映 0 对应的是 PA0 引脚,映 1 对应 PA1 引脚,然后映 2、映 3、映 4、映 5、映 6、映 7、映 8、映 9,依次对应的是 PA2 到 PB1,这里只有映 0 到映 9 啊,总共只有 10 个通道,然后其他地方就没有了,所以这个芯片就只能有 10 个外部输入通道

然后 ADC12 IN 0 的意思是 ADC1 和 ADC2 的IN 0 都是在 PA0 上的,然后下面全都是 ADC12 啊,这说明 ADC1 和 ADC2 的引脚全都是相同的,既然都相同,那要 ADC2 还有啥用呢?这个就要再说一个 ADC 的高级功能了啊,就是双 ADC 模式,这个模式比较复杂啊,这里只简单介绍一下,不需要掌握啊,双 ADC 模式就是 ADC1 和 ADC2 一起工作,它俩可以配合组成同步模式啊,交叉模式式等等模式,比如交叉模式,ADC1 和 ADC2 交叉的对一个通道进行采样,这样就可以进一步提高采样率,就像你打拳一样啊,左手打一拳,右手打一拳,左手打一拳,右手打一拳,快速交叉的打拳,那打击的频率肯定就比一个拳头打得快对吧,当然还有其他的模式啊,这里就不再细说了,这就是 ADC1 和 ADC2 配合使用的双 ADC 模式,当然 ADC1 和 ADC2 也是可以分开使用的哈,可以分别对不同的引脚进行采样,这样也是可以的

那回到这里,这个表就是 ADC 的通道和引脚对应表和引脚定义那个是一样的啊,这里通道有 017,总共是 18 个通道,通道 16 对应 ADC1 的温度传感器,通道 17 对应 ADC1 的内部参考电压,这里只有 ADC1 有通道 16 和 17 啊,ADC2 和 ADC3 是没有的。然后上面是 GPIO 的引脚,ADC1 和 ADC2 的引脚是完全相同的,ADC3 中间会有些变化,不过我们这个芯片也没有 ADC3 啊,不用管了,这里引脚的顺序就是是 PA0 到 PA7,然后 PB0 到 PB1,再然后 PC0 到 PC5,由于我们这个芯片没有 PC0 到 PC5,所以这些通道就没有了,

这就是通道和引脚的关系,了解一下。接下来我们再了解一下规则组的四种转换模式
接下来我们再了解一下规则组的四种转换模式,分别是单次转换非扫描模式 、连续转换非扫描模式、单次转换扫描模式 和连续转换扫描模式。那在我们 ADC 初始化的结构体里,会有两个参数啊,一个是选择单次转换还是连续转换的,另一个是选择扫描模式还是非扫描模式的。这两个参数组合起来就有这四种转换方式,我们来逐一看一下
第一种,单次转换非扫描模式。这个是最简单的哈,这里我画了一个列表,这个表就是规则组里的菜单,有 16 个空位,分别是序列 1~序列 16。你可以在这里点菜啊,就是写入你要转换的通道。在非扫描的模式下,这个菜单就只有第一个序列一的位置有效,这时菜单同时选中一组的方式就退化为简单的选中一个的方式了。在这里我们可以在序列一的位置指定我们想转换的通道,比如通道 2 写到这个位置。然后我们就可以触发转换了, ADC 就会对这个通道 2 进行模数转换。过一小段时间后啊,转换完成,转换结果放在数据寄存器里,同时, OC 标志位置一,整个转换过程就结束了。我们判断这个 EOC 标志位,如果转换完了,那我们就可以在数据寄存器里读取结果了。如果我们想再启动一次转换,那就需要再触发一次转换。结束置 EOC 标志位无结果。如果想换一个通道转换啊,那在转换之前把第一个位置的通道 2 改成其他通道,然后再启动转换,这样就行了。这就是单次转换非扫描的模式,没有用到这个菜单列表啊,也是比较简单的一种模式。

接下来我们看一下连续转换非扫描的模式,首先它还是非扫描模式,所以菜单列表就只用第一个。然后它与上一种单次转换不同的是,它在一次转换结束后不会停止,而是立刻开始下一轮的转换,然后一直持续下去。这样就只需要最开始触发一次,之后就可以一直转换了。这个模式的好处就是开始转换之后不需要等待一段时间的,因为它一直都在转换,所以你就不需要手动开始转换了,也不用判断是否结束的,想要读 AD 值的时候,直接从数据寄存器取就是了。这就是连续转换非扫描的模式。

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

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

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

好,这些就是 STM32 ADC 的四种转换模式,我们在写程序的时候会用到啊,到时候再看看程序现象有什么不同。那接下来继续。我们再来看几个小知识点,第一个是触发控制。


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

接着下一个知识点是数据对齐。

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

那要这个左对齐有啥用呢?这个用途就是,如果你不想要这么高的分辨率,你觉得 0~4095 数太大了,我就做个简单的判断,不需要这么高分辨率。那你就可以选择左对齐,然后再把这个数据的高 8 位取出来,这样就舍弃掉了后面 4 位的精度,这个 12 位的 ADC 就退化成了 8 位的 ADC 了。这就是左对齐的作用。
不过我们一般用的话,选右对齐就行了哈。如果需要裁剪一些分辨率,大不了就先把 12 位都取出来再做处理,这也是可以的哈,就是多算那一步而已。这就是数据左对齐和右对齐。

接着看一下下一个知识点,转换时间。这个大概讲一下哈,不过转换时间这个参数我们一般不太敏感,因为一般 AD 转换都很快啊,如果不需要非常高速的转换频率,那转换时间就可以忽略了。我们来看一下哈,之前我们说了, AD 转换是需要一小段时间的,就像厨子做菜一样,也是需要等一小会才能上菜的。那 AD 转换的时候有哪些步骤需要花时间呢?

这里第一条, AD 转换的步骤有四步,分别是采样保持量化编码,其中采样保持可以放在一起哈。量化编码可以放在一起,总共是这两大步。量化编码好理解哈,就是我们之前讲过的 ADC 逐次比较的过程,这个是需要花一段时间的,一般位数越多花的时间就越长。那采样保持是干啥的呢?我们看一下前面,这个我们前面这里并没有设计哈,为什么需要采样保持呢?
这是因为我们的 AD 转换,就是后面的量化编码哈,是需要一小段时间的,如果在这一小段时间里输入的电压还在不断变化,那就没法定位输入电压到底是在哪是吧?所以在量化编码之前,我们需要设置一个采样开关。先打开采样开关收集一下外部的电压,比如可以用一个小容量的电容存储一下这个电压,存储好了之后断开采样开关,再进行后面的 AD 转换。这样在量化编码的期间,电压始终保持不变,这样才能精确的定位未知电压的位置,这就是采样保持电路。那采样保持的过程啊,需要闭合采样开关,过一段时间再断开,这里就会产生一个采样时间。

那回到这里,我们就得到了第二条, STM32 ADC 的总转换时间为 T convert 等于采样时间加 12.5 个 ADC 周期。采样时间是采样保持花费的时间,这个可以在程序中进行配置哈。采样时间越大,越能避免一些毛刺。信号的干扰,不过转换时间也会相应延长啊。十二点五个 ADC 周期是量化编码花费的时间,因为是十二位的 ADC, 所以需要花费十二个周期,这里多了半个周期啊,可能是做一些其他东西花的时间。
ADC 周期就是从 RCC 分频过来的 ADCCLK, 这个 ADCCLK 最大是十四兆赫兹,所以下面有个例子啊,这里就是最快的转换时间,当 ADCCLK 等于十四兆赫兹,采样时间为一点五个 ADC 周期, T convert 等于一点五加十二点 5 等于 14 个 ADC 周期,在 14 兆赫兹 ADCCLK 的情况下就等于 1 微秒,这就是最快 1 微秒时间的来源。如果你采样周期再长些,它就达不到 1 微秒了。另外你也可以把 ADCCLK 的时钟设置超过 14 兆赫兹,这样的话 ADC 就是在超频了,那转换时间可以比 1 微秒还短,不过这样稳定性就没法保证了哈。

好,有关转换时间的内容就讲这么多,接下来看一下下一个知识点,校准。

看下这几段话啊, ADC 有一个内置自校准模式,校准可大幅减少因内部电容器组的变化而造成的精度误差。校准期间,在每个电容器上都会计算出一个误差修正码,这个码用于消除在随后的转换中每个电容器上产生的误差。建议在每次上电后执行一次校准。启动校准前, ADC 必须处于关电状态超过至少两个 ADC 时钟周期。这个看上去挺复杂的是吧?但是我们不需要理解,这个校准过程是固定的,我们只需要在 ADC 初始化的最后加几条代码就行了。至于怎么计算,怎么校准的啊,我们不需要管。所以这个了解一下就行。
最后看一下硬件电路啊。对于 ADC 的外围电路,我们应该怎么设计呢?这里我给出了三个电路图

第一个是电位器产生一个可调的电压,这里电位器的两个固定端啊,一端接 3.3 伏,另一端接接地。这样中间的滑动端就可以输出一个 0~3.3 伏可调的电压输出了。我们这里可以接 ADC 的输入通道,比如 PA0 口。当滑动端往上滑时,电压增大,往下滑时电压减小。另外注意一下这个电阻的阻值啊,不要给太小,因为这个电阻两端也是直接跨接在电源正负极的。如果阻值太小,那这个电阻就会比较费电啊,再小就有可能发热冒烟了。一般至少要接千欧级的电阻啊,比如这里接的是 10K 的电阻。这是电位器产生可调电压的电路。

然后中间是传感器输出电压的电路。一般来说像光敏电阻热敏电阻红外接收管麦克风等等啊,都可以等效为一个可变电阻。那电阻阻值没法直接测量啊,所以这里就可以通过和一个固定电阻串联分压,来得到一个反应电阻值电压的电路。那这里传感器阻值变小时,下拉作用变小,输出端电压就下降。传感器阻值变大时,下拉作用变弱,输出端受上拉电阻的作用,电压就会升高。这个固定电阻一般可以选选择和传感器阻值相近的电阻哈,这样可以得到一个位于中间电压区域比较好的输出。但这里传感器和固定电阻的位置也可以换过来,这样的话输出电压的极性就反过来了。这就是这个分压方法来输出传感器阻值的电路。

来看一下我们之前传感器模块的介绍哈。在这里传感器也是使用分压的方法来进行输出的,我们这一节 AD 转换需要用到 AO 这个硬件,所以这一块和这一块是没用的。 AO 直接从这里输出,我们在这里接线就行了。

好回到这里,我们看一下最后这个电路,这是一个简单的电压转换电路啊,比如你想测一个 0~5 伏的微应电压,但是 ADC 只能接收 0~3.3 伏的电压,那就可以搭建一个这样的简易转换电路。在这里还是使用电阻定分压啊,上面阻值 17K, 下面阻值 33K, 加一起是 50K 啊。所以根据分压公式,中间的电压就是 VIN 除以 50K 乘以 33K, 就得到它的电压范围就是 0~3.3 伏,就可以进入 ADC 转换了。这就是这个简单的电压转换电路。

如果你想采集 5 伏 、 10 伏这些电压的话,可以使用这个电路哈。但是如果你电压再高一些,就不建议使用这个电路了,那可能会比较危险哈。高电压采集最好使用一些专用的采集芯片,比如隔离放大器等等。做好高低电压的隔离,保证电路的安全哈。好