STM32-AD单通道&AD多通道(十九)

AD单通道&AD多通道

这个电路图还是比较简单的。在这里我们接了一个电位器,这个电位器有三个引脚,分别插在这三排孔里。电位器的内部结构是这样的啊,左边和右边的两个引脚接的是电阻的两个固定端,中间这个引脚接的是滑动抽头。电位器外边这里有个十字形状的槽可以拧啊,往左拧抽头就往左靠,往右拧抽头就往右靠。

所以外围电路这里,我们把左边的固定端接在负极,右边的固定端接在正极,中间就可以输出从负极到正极可调的电压了。然后右边这里我们把可调的电压输出接在 PA0 口。在这里根据引脚定义表, PA0 到 PB7 这 10 个引脚是 ADC 的 10 个通道啊。所以这 10 个引脚你可以任意选,接在哪个都行。但是其他的这些引脚不是 ADC 的通道,就不能接模拟电压了。

那初始化的步骤呢?我们还是看一下这里我总结的这个结构框图啊。我们把这个结构打通,在原理上能够让这个外设运转起来,那该怎么配置,自己心里就应该有数了。

具体的步骤就是,第一步,开启 RCC 时钟,包括 ADC 和 GPI 的时钟。另外这里 ADC CLK 的分频器啊,也需要配置一下。

然后从左到右,第二步,配置 GPI, 把需要用的 GPI 配置成模拟输入的模式。

第三步,配置这里的多路开关,把左边的通道进入到右边的规则组列表里。这个过程就是我们之前说的点菜啊,把各个通道的菜列在菜单里。

接着第四步就是配置 ADC 转换器了。在库函数里是用结构解来配置的,可以配置这一大块电路的参数,包括 ADC 是单次转换还是连续转换,扫描还是非扫描,有几个通道。触发源是什么,数据对齐是左对齐还是右对齐,这一大堆参数啊,用一个结构体配置就可以了。

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

那接下来就是开关控制,调用一下 ADC Cmd函数开启 ADC, 这样 ADC 就配置完成了,就能正常工作了。当然在开启 ADC 之后,根据手册里的建议啊,我们还可以对 ADC 进行一下校准,这样可以减小误差。

那在 ADC 工作的时候,如果想要软件触发转换,那会有函数可以触发啊。如果想读取转换结果,那也会有函数可以读取结果,这个等会介绍库函数的时候就可以看到了。

ADC 相关的库函数啊。首先我们看一下 ADCCLK 的配置函数,这个是在 RCC 里面的啊,我们找一下,打开这个 RCC 点 H 文件,拖到最后。在这里啊,可以看到这个 RCCADCCLKConfig 这个函数是用来配置 ADCCLK 分频器的

它可以对 APB2 的 72 兆赫兹时钟选择 2 4 6 8 分频,输入到 ADCCLK,这就是这个函数的作用,是在 RCC 库函数里面的啊,别忘配置的。

然后我们找一下 ADC 的函数,打开 ADC 点 h 文件,拖到最后。看一下,首先前面几个, Deinit 恢复缺省配置, Init 初始化, Struct Init 结构体初始化,这些都是老朋友啊,不用多讲。

然后 ADC cmd 这个是用于给 ADC 上电的

也就是 PPT 这里的开关控制啊。然后 DMA cmd ,这个是用于开启 DMA 输出信号的,如果使用 DMA 转运数据,那就得调用这个函数,这个我们下节讲 DMA 的时候再用啊。

接着是 ITConfig 中断输出控制,也就是 PPT 的这点啊,用于控制某个中断能不能通往 NVIC。

接下来这里有四个函数,分别是复位校准,获取复位校准状态,开始校准,获取开始校准状态。这就是用于控制校准的函数,我们在 ADC 初始化完成之后依次调用就行了。

然后继续啊, ADC 软件开始转换控制,这个就是用于软件触发的函数了,调用一下就能软件触发转换了,也就是这里的触发控制。我们这个代码目前使用软件触发啊

下面是 ADC 获取软件开始转换状态。从名字上看这个函数好像是判断转换是不是正在进行的,那我们是不是可以调用这个函数来判断转换是否已经结束呢?答案是不行的啊。这个可以分析下源码,我们跳到源码,可以看出这个函数就是用来获取 CR2 的 SWSTART 这一位。然后再对照手册的寄存器描述找一下,可以看到啊,这一位的作用是开始转换规则通道,由软件设置该位已启动转换,转换开始后硬件马上清除此位。

所以这里这个函数就是给 SWSTART 位置一,以开始转换的。

这个函数是返回 SWSTART 的状态。由于 SWSTART 位在转换开始后立刻就清 0 了,所以这个函数的返回值跟转换是否结束毫无关系啊。

那如如何才能知道转换是否结束呢?我们需要用到下面的函数, Get Flag Status, 获取标志位状态。然后参数给 EOC 的标志位,判断 EOC 标志位是不是置一了。如果转换结束, EOC 标志位置一,然后调用这函数。标志位,这样才是正确的判断转换是否结束的方法。所以简单来说,这个函数其实没啥用啊,我们一般不用,不要被它误导了。

然后下面这两个函数是用来配置间断模式的。第一个函数是每隔几个通道间断一次,第二个函数是是不是启用间断模式。需要间断模式的话可以了解一下。

接着下面这个函数, ADC 规则组通道配置,这个函数比较重要啊,它的作用就是这里给序列的每个位置填写指定的通道,就是填写点菜菜单的过程。第一个参数是 ADCx, 第二个 ADC channel 就是理想指定的通道,第三个 rank 就是序列几的位置。然后第四个 sample time 就是指定通道的采样时间,这是这个函数。

接着下面, ADC 外部触发转换控制,就是是否允许外部触发转换。

然后是 ADC 获取转换值,这个函数也比较重要啊,就是获取 AD 转换的数据寄存器,读取转换结果就要使用这个函数。

之后, ADC 获取双模式转换值,这个是双 ADC 模式读取转换结果的函数啊,我们暂时不用。

好到这以上这些函数就是对 ADC 的一些基本功能和规则组的配置。然后接下来这里有一大片函数,这些函数里面都带了一个 injected, 就是注入组的意思啊。所以这一大批函数都是对 ADC 注入组进行配置的。那注入组我们暂时不多讲啊,大家感兴趣的话可以自己看看。

然后继续下面的,这三个函数就是对模拟看门狗进行配置的。第一个是是否启动模拟看门狗,第二个是配置高低阈值,第三个是配置看门的通道。

之后 ADC 温度传感器内部参考电压控制,这个是用来开启内部的两个通道的。如果你要用这两个通道,那得调用一下这个函数开启一下,要不然是读不到正确的结果的,这个注意一下。

那最后四个,获取标志位状态,清除标志位,获取中断状态,清除中断挂起位,这些函数也是常用函数了啊,不用多说。

好,函数看完,我们来开始写代码:

AD单通道

回到 AD 点 C 文件,在这里开始初始化。第一步,那当然是开启时钟了。 RCC AHB2 外设时钟控制, RCC AHB2 外设ADC1 enable 开启 ADC1 的时钟, ADC 都是 AHB2 上的设备啊,所以这里用 AHB2 开启时钟的函数。然后我们还要使用 PA0 口,所以复制一下,这里改成 GPIOB, 再开启一下 GPIOB 的时钟,这样时钟就开启好了。

接着不要忘了哈,还有一个 ADCCLK, 看一下参数啊。这里一目了然,有四个参数,分别是二四六八分频。配置好之后, ADC 的 CLK 等于 PCLK2÷2 4 6 8,这个 PCLK2 就是 AHB2 时钟的意思,那我们选择这个六分频。放到这里。分屏之后, ADC C L K 等于 72 兆赫兹除以 6 等于 12 兆赫兹,这样 ADC C L K 就配置好了。

接着我们进行下一步,配置 GPIO。我们打开这个 LED 点 C,复制一下初始化代码,放到这里看一下。

首先 GPIO 模式这个要改一下,我们转到定义。这里要选择 AIN 模拟输入这个模式,这个我们之前讲 GPIO 的时候也说过哈。在 AIN 模式下 GPIO 口是无效的,断开 GPIO, 防止你 GPIO 口的输入输出对我模拟电压造成干扰。所以 AIN 模式就是 ADC 的专属模式,那这里复制 GPIO 木的 AIN 放到这里。

然后 GPIO pin 改成 pin 0,下面初始化 GPIOA, 这样 PA0 口就被初始化成了模拟输入的引脚了。

接下来我们进行下一步,选择规则组的输入通道,这里看一下,我们需要用到这个 ADC_ Regular_ Channel_ Config 函数,复制一下。放到这里。参数第一个 ADCX 给 ADC1,剩下的转到定义看一下。第二个参数是指定通道,这个参数可以是下面的一个啊。然后下面就是通道 0 到通道 17,我们选择通道 0 啊。放到这里。接着继续。

第三个参数, rank, 解释是规则组序列器里的次序。这个参数必须在 1~16 之间。对应的就是规则组这里的 16 个序列。那我们目前只有 PA0 一个通道,使用的是非扫描的模式,所以这里指定的通道就放在第一个序列一的位置。之后继续啊。

还有一个参数,指定通道的采样时间。

下面就是采样时间的参数啊,这个就根据你的需求来了。需要更快的转换就选择小的参数,需要更稳定的转换就选择大的参数。如果对速度和稳定性都没啥要求,那随便选就可以了。这里我们这个项目没啥要求啊,所以就随便选个五五五吧,这时的采样时间就是五十五点五个 ADC CLK 的周期。那之后把参数放到这里。这样输入通道就选择好了。

现在我们的配置是,在规则组菜单列表的第一个位置写入通道 0 这个通道。在 PPT 的这个图里表示的话,就是在这个序列一的位置写入通道 0。

如果你还想在序列二的位置写入其他的通道,那就复制一下这个代码,把这个序列数改成 2,然后指定你想要的通道,比如通道 3 通道 8 通道 10 等等。如果还想继续填充菜单,那就再复制,修改序列和通道,这样就可以了。另外每个通道也可以设置不同的采样时间啊,这个在最后的参数修改了就是了。

那这就是填充菜单列表的方法。我们目前只需要一个通道 0,后面的先删掉啊。

那接下来我们进行下一步,用结构体初始化 ADC。 这里我们需要用到 ADC1_ Init 函数。第一个参数给 ADC1,第二个参数是结构体,那我们转到定义。

复制一下这个结构体类型名,在这里粘贴,起个名字啊, ADC_ Init_ Stucture。把结构体成员都引出来,这里把结构体稍微换个次序啊,这样好看一些,换个次序对功能没有任何影响啊。最后把结构体地址放在初始化函数里,这样就好了。

然后依次看下参数,第一个 ADC mode 是 ADC 的工作模式,我们看下介绍。这个参数是配置 ADC 是工作在独立模式还是双 ADC 模式。

搜索一下参数取值范围啊,有这么多模式可以选择,其中第一个 independent 是独立模式,就是 ADC1 和 ADC2 各转换各的,剩下的就全是双 ADC 的模式,这些比较复杂啊,我们先不用,所以这里就选择第一个独立模式。放在这里。

接着是数据对齐,看一下,这里介绍是指定 ADC 数据是左对齐还是右对齐。搜索一下参数列表啊,这里第一个是右对齐,第二个是左对齐。

这两个对齐方式我们也讲过啊,所以这里就选择右对齐,复制。放在这里,这是数据对齐。接着下一个,外部触发转换选择,就是触发控制的触发源啊,看一下介绍。这里说的是定义用于启动规则组转换的外部触发源。参数取值列表搜索一下。在这里有这么多参数啊

它们对应的就是 PPT 这里的,这个结构光图的这里啊,外部触发源选择。

这里这些参数都是一一对应的,大家可以看一下。然后这里有个外部触发,那就是不使用外部触发,也就是使用内部软件触发的意思。那我们本节代码使用软件触发,所以就选择这个参数,复制。然后放在这里。这就是触发源,使用软件触发。

接着下面三个参数啊,第一个连续转换模式,这个可以选择是连续转换还是单次转换。第二个扫描转换模式,这个可以选择是扫描模式还是非扫描模式。第三个通道数目,这个是指定在扫描模式下,总共会用到几个通道。那对应的就是我们 PPT 的这四种模式了。单次转换非扫描,连续转换非扫描,单次转换扫描,连续转换扫描。通道数目的参数就是这里啊,扫描模式总共需要扫描几个通道。

那回到代码,这三个参数怎么配置,应该就有思路了。我们来看一下参数介绍,第一个,跳转一下。这里写的是指定转换是连续模式还是单次模式,这个参数可以是 Enable 或 Disable, Enable 就是连续模式, Disable 就是单次模式。

然后另一个参数在这里,介绍是指定转换是扫描模式多通道还是非扫描模式单通道,这个参数也是 Enable 或 Disable, Enable 就是扫描模式, Disable 就是非扫描模式。

然后最后一个参数在这里啊,介绍是指定规则组转换列表里通道的数目,这个参数必须在 1~16 之间,这些应该不难理解吧?

那我们回到这里,我们这个代码目前使用的是单次转换、非扫描的模式,所以这两个参数都给 Disable。 这样转换模式就配置好了。然后通道数目目前是一个通道,所以给一。其实这个参数仅在扫描模式下才需要用啊。如果是非扫描的模式,那整个列表就只有第一个序列有效。所以在非扫描的模式下,这个参数其实是没有用的,你无论写多少数目,最终都只有序列一的位置有效。好到这里我们 ADC 的整体结构就配置完成了。

之后中断和模拟看门狗,你如果需要的话,可以在这里继续配置,那我们就暂时不用了哈,所以这里就可以开启 ADC 的电源了,看一下函数。复制这个 ADC cmd 函数,放到这里。参数第一个 ADC1,第二个 enable, 开启 ADC 的电源。这样 ADC 就准备就绪了。

那在开启电源之后,根据手册的建议,我们还需要对 ADC 进行校准。对于校准,我们之前已经介绍过校准的函数了,对吧?来找一下,这里有四个函数,对应校准的四个步骤。

第一步调用第一个函数,复位校准。第二步调用第二函数,等待复位校准完成。第三步调用第三个函数开始校准。第四步调用第四个函数等待校准完成。这样就行了。那我们复制第一个函数。放在这里,参数给 ADC1,这样就可以复位校准了。接下来继续,复制第二个函数,放到这里,参数 ADC1。这个函数是返回复位校准的状态,所以如果要等待复位完成的话,还需要加一个 while 循环。如果没调准完成,就在这个 while 空循环里一直等待。那获取的标志位和是否调准完成是怎样的对应关系呢?

这个得参考一下寄存器说明啊,我们调转的函数定返回值说明是 ADC 复位校准寄存器的状态, SET 或 RESET。 在下面可以看出来啊,它获取的就是 CR2 寄存器里的 RSTCAL 标志位。那之后就需要参考一下手册的寄存器描述啊,在这个 CR2 寄存器里找一下。这里就是这个标志位的说明啊,他说该位由软件设置,并由硬件清除。在校准寄存器被触发后,该位将被清除。所以该位的用法就是,以软件置该位为一,那硬件就会开始复位校准。当复位校准完成后,该位就会有硬件自动清零。

那回到代码的这个,我们这一行代码开始复位校准,看一下啊,就是把这一位置一。

然后获取复位校准状态,就是读取这一位。所以在读取这一位的时候,如果它是一,那就需要一直空循环等待。如果它变为零了,那就说明复位校准完成,可以跳出等待。所以这里 while 的条件就是获取标志位是不是等于等于 SET。如果等于 SET, while 条件为真,就会一直空循环,一旦标志位被硬件清零了,这个空循环就会自动跳出来,这样就实现了等待复位校准完成的效果。当然这个等于等于 SET 也是可以省略的哈,因为返回值 SET 直接作为校准格是不是等于等于 SET 作为条件,效果是一样的。

好,接下来继续啊,我们看一下函数,复制第三个,开始校准,放到这里,参数给 ADC1,这样就能启动校准了。之后内部电路就会自动进行校准,过程不需要我们管了。

最后我们还需要等待校准完成,看一下函数,复制第四个函数,获取校准状态。然后放到这里,参数还是 ADC1 啊。同样我们也用 while 把它套起来,循环条件是,校准标志位是不是等于等于 SET。这样就可以等待校准是否完成了。到这里, ADC 的初始化就已经完成了。

我们配置了时钟, GPIO 口,选择好了输入通道,初始化好了 ADC,我们目前的模式是单次转换,非扫描的模式。最后给 ADC 上电,进行校准,这样 ADC 就处于准备就绪的状态。

那我们想启动转换,获取结果就可以在下面再写一个函数,函数的返回值是, uint 16-7,无符号 16 位整形,名字起个啊,叫 AD Get Value。 参数暂时不需要。然后在这个函数里面,我们执行的流程就是 PPT 的这里,按照这个流程来进行就行了。

首先软件触发转换,然后等待转换完成,也就是等待 EOC 标志位置一啊,最后读取 ADC 数据寄存器就完事了。回到代码,看一下函数,触发转换我们用这个软件触发转换函数复制。放到这里,第一个参数给 ADC1,第二个新的状态给 Enable, 这样就可以触发, ADC 就已经开始进行转换了。

那转换需要一段时间,所以我们还需要等待一下。看一下函数啊,我们需要用到这个获取标志位状态的函数,复制。放到这里,第一个参数给 ADC1

第二个参数看一下,这里有五个参数,第一个, AWG, 模拟看门狗标志位,第二个, EOC, 规则组转换完成标志位,第三个, JEOC, 注入组转换完成标志位,第四个, JSTRT, 注入组开始转换标志位,第五个, STRT, 规则组开始转换标志位。那我们需要判断规则组是不是转换完成了,所以就使用第二个,规则组转换完成标志位这个参数。然后放在这里第二个参数的位置。

同样我们也需要套一个 while 空循环来实现一个等待的过程。那返回的标志位 SET 与 RESET 和转换是否完成的对应关系是怎样的呢?我们还是参考一下手册的寄存器描述啊。在这个状态寄存器里,有这个 EOC 转换结束标志位,我们获取的就是这个 EOC 标志位啊。

介绍是,该位由硬件在规则或注入通道组转换结束时设置。哦这个 EOC 是规则组或注入组完成时都会置一。呀,之前说的这个 EOC 只是规则组的完成标志位,是有点问题的哈,这里排查一个错误,注意一下。所以说这个寄存器描述也是很重要的啊,有很多细节的问题还是需要看寄存器描述才能清楚。那我们接着看,这一位由软件清除,或由读取 ADC DR 时清除。 ADC DR 是数据寄存器啊,一般 EOC 标志位置一,我们就会来读取数据,所以它就多设计了一个功能,就是这一位可以在读取数据寄存器之后自动清除,就不需要你再手动清除了,可以省一条代码。

这就是这个标志位,当它为零时,表示转换未完成,为一表示转换完成。所以在这里当 EOC 标志位等于等于 RESET 时,转换未完成, while 条件为真,执行空循环。转换完成后, EOC 由硬件自动置一,那 while 循环就自动跳出了。这样就是等待转换完成的代码。

那具体会等待多长时间呢?我们刚才配置的时候指定了,这个通道的采样周期是 55.5,转换周期是固定的 12.5,加在一起就是 68 个周期。

前面我们配置的 ADCCLK 是 72 兆赫兹的六分频,就是 12 兆赫兹。12 兆赫兹进行 68 个周期转换才能完成,最终的时间可以算一下,就是 1/12 兆再乘 68,结果大概是 5.6 微秒哈。

这个我们上一小节讲转换时间的时候也讲过啊,如果想算的话就这样算。所以这个 while 循环大概会等待 5.6 微秒,这就是等待的时间。

那等待完成之后,我们就可以取结果了。看一下函数啊。取结果就用这个 ADC 获取转换值,复制,放到这里。参数给 ADC1,它的返回值就是 ADC1 转换结果。这里我们可以直接把返回值 return 过去。获取转换值的函数也可以看下内部代码,它就是直接读取 ADC 的 DR 数据寄存器。那这里因为读取 DR 寄存器会自动清除 EOC 标志位,所以这之后我们就不需要再手动清除标志位了。

这样启动等待读取的过程就写好了。它和我们 PPT 的这个流程是对应的啊,调用一次这个函数,就相当于执行了一次这个流程。大家可以对照着来理解啊。好,那这个 AD 单通道的代码就写好了,我们来测试一下啊。首先把这两个函数的第一行放在头文件里声明一下。

没有问题,然后回到主函数,先 include 一下 ad 点 h 的头文件。然后 main 函数里先调用 ad init 初始化 ADC。之后在主循环里直接调用 ad get value。这个函数里启动等待读取,一气呵成啊。返回值直接就是转换结果。那我们在上面定义一个变量, int 16_7 ad value。 然后把返回值放在 AD 值里存着。之后调用 OLED 显示一下。先在上面啊, OLED 修 string,一行一列显示 AD 值。然后在下面, OLED 修 number 一行九列,显示 AD 值,长度为 4 啊,这样就行了。编译看一下,没问题啊。

下载,看一下面包板,可以看到 OLED 上已经有数据了啊,拧一下电位器看一下。往左拧数据减小,最小值是 0,往右拧数据增大,最大是 4095。

可以看出啊,目前的代码功能是没问题的。在这里, AD 值的末尾会有些抖动啊,这是正常的波动。如果你想对这个值进判断啊,再执行一些操作,比如光线的 AD 值小于某一阈值就开灯,大于某一阈值就关灯。那可能会存在这样的情况啊,比如光线逐渐变暗, AD 值逐渐变小。但是由于波动, AD 值会在判断阈值附近来回跳变,这会导致输出产生抖动,来回开灯关灯开灯关灯是吧?那

如何避免这种情况呢?这个可以使用迟滞比较的方法来完成。设置两个阈值,低于下阈值时开灯,高于上阈值时才关灯,这就可以避免输出抖动的问题了。这个跟我们 GPIO 那一节讲的斯密特触发器是一个原理啊。另外,如果你觉得数据跳变太厉害,还可以采用滤波的方法,让 AD 值平滑一些。比如均值滤波啊,就是读取 10 个或 20 个值,取平均值作为滤波的 AD 值。或者还可以裁剪分辨率,把数据的尾数去掉,这样也可以减少数据波动。这都是可行的方法啊,大家实际遇这方面问题的话可以考虑一下。

好,我们接着来看这个代码。如果想显示一下实际的电压值怎么办呢?这只需要对这个数据进行一个线性变换就行了。那我们再在上面定义一个变量,表示电压。 float voltage, 然后修 string 复制一下。现在二行一列显示个 voltage 冒号零点零零伏。之后下面 voltage 等于 ad value 除以 4095 乘以三点三,这样就能把 0~4095 的范围变换到 0~3.3 了。

另外这里因为 ad value 是整数啊,在除 4095 之后会舍弃掉小数部分,这样会导致计算错误。所以我们先把 ad value 类型强转为 float,这样再除才不会出问题。还有啊,这里实际上 AD 值等于 4096 时才对应 3.3 伏啊,会有一个数的偏差。所以 AD 值最大的 4095 实际上对应应该是 3.3 伏小一丢丢,没有办法达到满量程 3.3 伏。这个是受限于 ADC 的结构啊,具体就不再细说了。总之就是你认为 4095 对应 3.3 伏可以,认为 4096 对应 3.3 伏也可以,只有一点点偏差啊,也看不出来差别。

那回到这里,电压的数据有了,接下来用 OLED 显示一下吧。我提供的 OLED 驱动还没有显示浮点数的函数啊,这个之后讲 OLED 的时候再给大家安排上。还有显示汉字啊,显示图像的功能也都会安排的。目前这里我们如果想显示浮点数,可以用显示整数的函数来操作啊。

OLED 修 number, 在二行九列显示 voltage, 长度为一。 voltage 的范围是 0~3.3,如果直接修 number 的话,小数就会舍弃掉,这就是显示整数的部分。然后再来一个 OLED 修 number。 在二行十一列显示 voltage 乘以一百。先把数扩大一百倍,比如原来是一点二三,现在就是一百二十三。然后再对一百取余,一百二十三对一百取余就是二十三,这样就把一点二三的小数部分取出来了,显示在十一列。

另外由于浮点数是不能取余的,所以 voltage 乘一百之后要括起来,然后进行强制类型转换,变成整数,比如用 int 16_7 再对一百取余,这样才行。然后显示的长度为二。这样就可以显示浮点数了。那最后加一个 delay 100 毫秒啊,让它刷新的慢一些。

然后再试一下,编译,下载,看一下,这样电压值就已经出来了。拧动电位器, AD 值在变化,电压值也对应变化。这就是第一个程序的现象。

如果你需要把 AD 值换算成电压的话,就可以这样做啊,乘除一个系数,变换一下就行。一般的话,如果就只是进行阈值判断,数据记录的话,也可以不进行变换啊,直接使用原始的 AD 数据,这样也是可以的。好,第一个程序到这里就差不多了。

看一下 PPT 啊,这里我们使用的是第一种转换方式,单次转换非扫描。那在这里我们还可以使用第二种转方式,连续转换非扫描。

这个模式的好处就是不需要不断的触发,也不需要等待转换完成的。这种模式也可以在这个工程上给大家演示一下哈,只要稍作修改就行。我们回到 AD 点 C 文件。

我们回到 AD 点 C 文件,要切换为连续转换,那这个连续转换模式这个参数就要改成 enable。 现在就是连续转换的模式。

然后看一下流程图,连续转换仅需要在最开始触发一次就行了。所以这里软件触发转换的函数就可以挪到初始化的最后,在初始化完成之后触发一次就行了。这时内部的 ADC 就会一次接着一次,连续不断的对我们指定的通道 0 进行转换,转换结果放在数据寄存器里,此时数据寄存器会不断的刷新最新的转换结果,所以在 AD_ GetValue 这里就不需要判断标志位了,直接 return 数据寄存器的值就行了。

这样程序就是单通道连续转换非上秒的模式。我们看一下现象啊。下载,程序现象和刚才是一样的,也能实现单通道的 AD 转换。这就是连续转换非扫描模式。

那回到代码,这里就先改回来哈,需要哪种模式你可以根据实际需求来选。那第一个代码到这里就结束了

AD多通道

我们来看一下下一个代码。先看一下现象图啊,打开 7-2 AD 多通道的图片,看一下。在这里我们使用了四个 AD 通道,第一个通道还是电位器啊,接在 PA0 口。之后上面又接了三个传感器模块,分别是光明传感器、热敏传感器、反射式红外传感器。它们的 VCC 和 GND 都分别接在面包板的正负极啊。然后这个 AO O就是模拟量的输出引脚,三个模块的 AO 分别接在 PA1、 PA2 和 PA3 口,加上电位器的 PA0,总共是四个输入通道啊。

同样这些 GPIO 口也是可以在 PA0 到 PB1 之间任意选择的,我这里就选的前四个

在这个工程里,我们修改一下这个代码哈,来实现 AD 多通道采集的功能。那看一下 PPT,如何实现多通道采集呢?

多通道我们首先想到的应该是后面这两种扫描模式是吧?利用这个列表把四个通道都填进去,然后触发转换,这样就能实现多通道了。这样确实是一种不错的方法,但是还是那个数据覆盖的问题。如果想要用扫描模式实现多通道,最好要配合 DMA 来实现。我们下节课讲完 DMA 之后,再来试一下这个模式啊。

那你可能会问啊,我们一个通道转换完成之后,手动把数据转运出来不就行了?为啥非要用 DMA 来转运呢?这个方案看似简单啊,但是实际操作起来会有一些问题。

第一个问题就是,在扫描模式下,你启动列表之后,它里面每一个单独通道转换完成之后,不会产生任何的标志位,也不会触发中断啊。你不知道某一个通道是不是转换完了,它只有在整个列表都转换完成之后,才会产生一次 EOC 标志位,才能触发中断。而这时前面的数据就已经覆盖丢失了。

第二个问题就是, AD 转换是非常快的,刚才我们也计算过,转换一个通道大概只有几微秒。也就是说,如果你不能在几微秒的时间内把数据转运走,那数据就会丢失。这对我们程序手动转运数据要求就比较高了。所以在扫描模式手动转移数据是比较困难的哈。不过比较困难也不是说手动转移不可行啊,我们可以使用间断模式,在扫描的时候,每转换一个通道就暂停一次,等我们手动把数据转移走之后,再继续出发,继续下一次转换。这样可以实现手动转移数据的功能哈。但是由于单个通道转换完成之后没有标志位,所以启动转换完成之后,只能通过滴泪延时的方式,延时足够长的时间才能保证转换完成。这种方式既不能让我们省心啊,也不能提高效率,所以我们暂时不推荐使用。

那这些方法都不行,我们本节是不是就不能实现多通道了呢?答案是能实现,而且非常简单。怎么实现呢?我们可以使用上面的这个单次转换非扫描的模式来实现多通道。只需要在每次触发转换之前,手动更改一下列表第一个位置的通道就行了。比如第一次转换,先写入通道 0,之后触发,等待通知。第二次转换,再把通道 0 改成通道 1,之后触发,等待通知。第三次转换,再先改成通道 2,等等等等。这样在转换前先指定一下通道。再启动转换就可以轻松的实现多通道转换的功能了。

那回到代码,这里也只需做一些简单的修改就行了哈。我们可以把这个填充通道的代码剪切,然后在触发转换之前粘贴,放在这里。

然后我们想指定的通道可以提取成 AD_ GetValue 的参数,这里可以先转到第一啊,把它的这个参数复制一下。然后回来,原封不动的向上一级传递。这里通道 0 改成参数指定的通道,这样就行了。这样我们在调用 AD Get Value 进行转换时,只需要指定一个转换的通道,返回值就是我们指定通道的结果了。

我们现在要指定的通道是通道 0123,所以上面这里的 GPI 初始化也不要忘了,或上 GPIK 一,复制一下。再或上 P2 P3,把这几个通道的引脚初始化一下,然后剩下的就不需要更改了。

最后头文件这个函数的声明啊也更新一下

这样就完成了。好,接下来我们试一下看看。回到 main 点 C, 先把这些先删掉啊,编一下看看。没问题啊,先定义几个变量,无 INT16 杠 7, AD0, AD1, AD2, AD3。然后显示字符串,一行一列显示 AD0 冒号,复制一下。二列显示 AD1,三列 AD2,四列 AD3。

之后在主循环里, AD0 等于 ADGetValue, 给个参数指定通道。参数是什么呢?跳转定义看一下,然后再跳转这个函数。这些就是可选的参数

我们选择通道 0 放在这里,这样返回值就是通道 0 的转换数据了。之后同样的方法,复制粘贴几个, AD1 等于通道一, AD2 等于通道二, AD3 等于通道三,这样就行了。最后 OID 显示一下, OID show number。 一行五列显示 AD0,长度为 4,复制一下。二行五列显示 AD1,三行五列显示 AD2,四行五列显示 AD3。最后 delay 100 毫秒,这样就完事了。

现在是依次启动四次转换,并且在转换之前指定了转换的通道,每次转换完成之后,把结果分别存在四个数据里,最后显示一下。这就是使用单次转换非扫描的模式实现 AD 多通道的方法,也是一个比较简单直观的方好吧,试一下看看。

下载,看一下现象。首先是电位器,看 AD0 的数据啊,没问题。然后是光敏,看 AD1,也没问题啊。热敏看 AD2,热敏这个值变化的比较小哈,也没问题的。最后对射传感器。看 AD3 也是没问题的啊。

光敏

热敏

对射传感器:

相关推荐
南岸的水2 小时前
BMS国标充电解析
单片机·嵌入式硬件·mcu
清风6666662 小时前
基于单片机的可调数控电源设计
单片机·嵌入式硬件·mongodb·毕业设计·课程设计·期末大作业
sramdram2 小时前
低功耗国产蓝牙芯片,蓝牙MCU方案
单片机·嵌入式硬件·mcu·蓝牙mcu·蓝牙方案
yuan199972 小时前
CMS8S5880 电子锁程序(51单片机)
单片机·嵌入式硬件·51单片机
振南的单片机世界3 小时前
启动流程:上电先取栈顶和复位向量,再进main
arm开发·stm32·嵌入式硬件
fanged3 小时前
设备树学习2--一个设备
嵌入式硬件
数智工坊3 小时前
【ROS 2 全栈入门指南三】:Action、参数与Launch文件全链路指南
android·stm32·嵌入式硬件·学习·机器人
Inhand陈工4 小时前
映翰通IG502实战:通过RS232采集交通信号灯数据,实现自动短信告警
网络·嵌入式硬件·物联网·网络安全·边缘计算·信息与通信·信号处理
不脱发的程序猿4 小时前
我把360里真正用得到的功能拆出来,做个轻量工具
stm32·单片机·嵌入式硬件