STM32外设学习-ADC模数转换器(代码部分)四个模块,光敏,热敏,电位,反射式红外。

上次的博客我们记录了STM32外设ADC数模转换器的理论学习,这次我们来进行代码部分的学习。

一.电位器程序编写

1.硬件接线图

电位器我们一端接在3.3V一端接在GND,让中间控制端我们接入PA0。根据引脚定义表,PA0-PB1这十个引脚都是ADC的通道。这十个引脚都可以接入,那个都行。

2.创建程序

复制一下我们OLED显示的代码。

然后添加我们AD模块的.c和.h文件。

快速写好我们.h文件。

初始化我们ADC的函数。

按照我们的结构进行初始化模块。

3.函数了解

(1)ADCCLK预分频

这个是我们用来配置ADCCLK预分频器的。可以对APB2的72MHz时钟进行2,4,6,8分频,输入到ADCCLK中。

(2)恢复缺省配置

恢复缺省配置:指将某个系统、设备或软件的设置恢复到默认的初始状态。

(3)初始化ADC函数

初始化ADC配置。

(4)结构体初始化

结构体初始化。

(5)使能ADC

用于使能ADC,使我们的ADC上电。

(6)开启DMA信号

开启DMA的输出信号。如果使用DMA转运数据,那么我们会用到这个函数。

(7)中断输出控制

中断输出控制。

图中的这里。

(8)校准函数

第一个是复位校准,第二个是获取复位校准状态,第三个是开始校准,第四个是获取开始校准状态。

这就是用来控制ADC校准的函数,我们在初始化ADC结束之后,就可以调用这些函数,用于校准

(9)软件触发函数

ADC软件开始转换控制,这个就是用于软件触发的函数。

也就是这里的触发控制。

(10)ADC获取软件开始状态

判断转换是否在进行,我们可以用来判断结束吗,显然是不行,可以看一下源码

这个函数是用来控制CR2的SWSTART这一位的函数。

对照手册可以看到这一位的作用是,开始转换规则通道,有软件设置该位以启动转换,转换开始后,立马清除此位

(10)返回状态

这个函数是用来返回SWSTART的状态。

由于SWSTAR位在转换开始后立刻就清零了。所以这个函数的返回值跟转换是否结束,没有关系。

(11)获取标志位

那么我们如何知道转换是否结束了

就需要我们这个函数,获取标志位状态。然后参数给EOC的标志位,判断EOC的标志位是否为1.

如果转换结束EOC就置1了,然后调用这个函数判断标志位,这样子才是判断转换是否结束的方法。

(12)间断模式配置

这两个函数是用来配置间断的。

第一个函数是每隔几个通道间断1次,第二个函数是,是否启用间断模式。

(13)ADC规则组通道配置

这个函数比较重要,第二个参数是你想要指定的通道,第三个参数是序列几的位置,第四个参数是指定通道的采样时间。

是用来给这些序列每个位置填写指定的通道。

(14)ADC外部触发控制

这个函数,是控制,是否允许外部触发转换。

(15)ADC获取转换值

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

(16)ADC获取双模式转换值

双模式读取转换结果的值,我们暂时不用

(17)注入组

这一大堆函数都带了一个injected,就是注入组的意思,所以这一大批函数都是对ADC注入组进行配置的

(18)模拟看门狗

这三个是我们模拟看门狗的函数。

第一个函数是是否启动看门狗,第二个函数是配置高低阈值,第三个函数是配置看门的通道。

(19)ADC温度传感器

用来开启内部的两个通道。

(20)获取标志位状态

可以获取我们的标志位

(21)清除标志位

清除标志位状态。

(22)获取中断状态

是用来获取我们中断控制的函数。

(23)清除中断挂起位

清除中断标志位。

4.代码的编写

(1)时钟以及GPIO

开启ADC1的时钟,因为ADC都是APB2上面的设备。

因为我们用到了PA0口,所以我们还需要开启GPIOA端口的时钟。

然后是ADCCLK预分频的设置

我们转到函数定义,就可以一目了然。分别是2,4,6,8分频,对72MHz。这里的PCLK2就是APB2时钟的意思。因为我们ADC最大时钟是12Mhz,所以不能选择,2,4分频。

这里我们给6分频。

复制LED.c的代码,初始化GPIO。

但是GPIO的模式修改改为AIN。模拟输入这个模式。在AIN模式下,GPIO输入是无效的。断开GPIO防止GPIO对我们模拟电压造成干扰。所以AIN模式就是ADC的专属模式。

修改模式。

选择我们PIN0口。

(2)规则组通道设置

第二个参数是指定通道,可以是下面的一个值。

我们选择通道0

第三个参数rank,解释规则组序列器里面的顺序,这个参数在1-16之间

对应的我们这里的序列。

目前我们只有PA0一个通道,使用的是非扫描的模式。所以指定的通道就放在第一个序列1的位置。

第四个参数是指定通道采样时间。

他的值可以是这些参数。需要更快的转换就选择较小的参数,需要更稳定的转换就选择较大的参数。

这里没什么要求,我们就选一个555吧。

这样子通道就配置好了,我们到这一步的解释是,在规则组菜单列表的第一个位置,写入通道0这个通道。

就是在这个位置写入通道0.

如果想要写入通道2,我们直接复制代码,把序列改为通道2就行。

(3)初始化ADC

老两套,定义结构体,起个新名字。

然后把结构体成员一一引出,我们在进行配置。

ADC的工作模式。

转到定义,看到有这么多模式。第一个是独立模式ADC1和ADC2个转个的。剩下的全是双ADC模式我们暂时不涉及。

选择我们的独立模式。

数据对齐。左对齐,还是右对齐。

右对齐和左对齐。

选择右对齐,上个博客说过。

外部触发转换选择,就是触发控制的触发源

转到定义

对应我们这里。这些参数都是一一对应的。

这个是不适用外部触发。也就是内部软件触发的意思。

我们的代码时不使用外部触发,也就是软件触发,也就是填写这个参数。

连续转换模式设置。

指定选择是连续还是单次enable是连续,disable是单次

扫描转换模式选择,可以选择扫描模式还是非扫描模式

扫描模式(多通道),非扫描模式(低通道),参数enable和disable。enable是扫描模式

通道数目指定扫描模式下会用到几个通道

数值可以是1-16之间。

对应的是我们四种模式,单次转换,非扫描。连续转换,非扫描。单次转换,扫描。连续转换,扫描。

我们这个代码使用的是单次转换,非扫描模式,所以两个参数都给disable。

然后就是开启ADC电源。

到这里我们的ADC就初始化完成了。

(4)校准

根据手册的建议,我们在开启adc电源之后需要进行校准。

这四个就是我们ADC校准函数。

首先复位校准。

这个函数是返回复位校准的状态,需要加个函数,如果没有校准完成就一直校准。

那么获取的标志位和是否校准完成是怎样的关系呢。我们可以看一下寄存器说明。

返回值说明是ADC复位校准寄存器的状态,SET或RESET。

在下面可以看到,获取的就是CR2_的RSTCAL位。

在手册中也可以看到。使用方法是,软件给了置位1,那么就开始校准,校准完成后自动清零。

来到我们开始复位校准的代码,就可以看到,开始复位校准就是把这一位置1。

读取这一位状态,如果是1就一直空循环等待,如果是0就跳出来,复位校准就完成了

所以说我们判断他是不是一直等于SET,等于就一直循环。

开始校准。

这样就能启动校准了。

等待校准完成,我们还需要获取校准状态看看是否校准完成

同样也是判断我们的校准标志位是不是等于1。

(5)获取结果

按照我们这里,首先软件触发转换,然后等待转换完成,也就是EOC的标志位置1,最后读取ADC数据寄存器就可以了。

我们用这个软件触发转换的函数。

写在我们子函数中。这样子ADC就可以开始转换了

我们还需要用到这个,获取标志位状态的函数。

然后转到定义。

第一个:AWD模拟看门狗标志位

第二个:EOC规则组转换完成标志位

第三个:JEOC注入组转换完成标志位

第四个:JSTR注入组开始转换标志位。

第五个:STRT规则组开始转换标志位。

我们需要判断规则组是否转换完成,需要用到第二个函数。

同样我们也需要加入一个空循环,进行等待。

我们打开手册来到状态寄存器中。有一个EOC转换结束标志位,我们获取的就是这个标志位。

我们可以看到这个EOC在规则组或者注入组完成了都会置1。

这一位由软件清除,或者读取ADC_DR时清除。ADC_DR是数据寄存器一般EOC标志位置1我们就会来读取数据。所以他多设计了一个功能,让他在读取数据寄存器之后自动清除。就不需要手动清除了。

这里表示当EOC标志位==RESET时候转换未完成while条件位真,转换未完成,一直等待,转换完成后EOC由硬件直接自动置1,while循环就跳出来了

那么具体会等待多长时间呢,我们刚才配置时候制定了转换周期是固定的12.5,然后采样周期是55.5,加起来是68个周期,前面我们配置的是ADCCLK的6分频也就是12MHz。12MHz进行68个周期才能转换完成,就是1/12MHz*68=5.6uS,大概是5.6us就可以转换完成

等待完成后我们就可以获取结果了,获取结果是这个函数

他的返回值就是ADC转换完成的结果,我们可以直接返回他。

这个函数内部的代码就是直接获取ADC的DR数据寄存器的值,然后返回。

在这里因为读取DR寄存器会自动清除EOC标志位,所以在函数最后我们不需要手动清除标志位了。这样子我们的函数就完成了,我们每调用一次这个函数就会读取数据寄存器的值,然后数据寄存器清零。

放在头文件中声明。

添加头文件,然后编译。

直接调用这个函数,这个函数里:启动,等待,读取,一气呵成。返回值就是转换结果

我们再定义一个变量把返回值存起来。

定义一个OLED显示

显示我们的转换值

main.c中所有的文件。我们进行编译下载。

(6)程序现象

0错误,0警告。

可以看到往左凝数字变小,最小是0。

可以看到,我们AD值末尾会有一些抖动,如果想对这个值进行判断,然后再执行一些操作的话比如说光线的AD值小于,某一阈值就开灯,大于某一阈值就关灯,可能会存在这样子的情况,比如光线逐渐变暗,AD逐渐减少,但是由于波动,AD值会在判断阈值附近来回跳动。就会导致输出来会抖动,造成一直开灯,关灯的影响,如何避免这种情况呢

可以使用滞回比较的方法设置两个阈值,低于下阈值时候开灯,高于上阈值时候再关灯,这就可以避免输出抖动的问题了。

这里只能显示数值,不能显示电压,我们还需要修改一些,因为这个值和电压是线性变化的关系,

我们定义一个电压变量。

然后OLED显示。

这样就能把0-4095范围变换至0-3.3V了。因为ADVALUE是整数,计算时候会把小数省去,所以我们需要把ADVALUE强制转化为浮点型小数。

这里我们再调用OLED显示函数。因为我们Voltage的范围是0-3.3V直接显示的话小数会被省略。这里我们让他显示整数

然后我们可以把VOLTAGE扩大100倍再取余数,比如以前是1.23扩大100被就是123,然后对100取余数就是23,这样子小数部分就有了。因为浮点数不支持取余数,所以voltage要括起来,放大100倍后在进行强制类型转换。然后显示长度为2。

最后我们延迟100ms让他的刷新慢一些。

编译--下载。o错误,0警告。

拧动电位器,发现电压值在变化,数也在变化是一一对应的。

(7)连续转换非扫描方式。

这里我们的程序使用的是单词转换非扫描,我们也可以变为连续转换非扫描方式。这个模式的喊出就是不需要不断地触发,也不需要等待转换完成。

我们只需要把这个参数转化为ENABE那么就是连续转换模式了。

然后看一下流程图,连续转换只需要最开始触发一次就可以了。

我们就可以把软件触发的函数,放在初始化之后,执行一次就可以了。这时候内部的ADC就会一次接着一次,连续不断地对我们指定的通道0,进行转换,转换结果会存放在数据寄存器里。此时数据寄存器就会不断的刷新最新的转换结果了,所以再AD_GetValur这里,就不需要进行判断标志位了。

我们把它注释掉。

这样程序就是单通道,连续转换,非扫描的模式了

编译一下,0错误,0警告。我们下载到单片机中。

发现程序的现象和刚刚是一样的。

二.四个模块代码

1.硬件接线图

这里我们使用了四个通道。他们的VCC和GND都分别接在电源的正负极。然后AO及时模拟量输出引脚。三个模块的AO分别接在PA1,PA2,PA3这三个端口。加上电位器的PA0,总共是4个通道。

复制我们单通道的代码,改歌名字。如果用扫描模式来设置多通道的问题,我们最好是配合DMA来进行数据搬运,所以我们先使用非扫描模式配置。

2.编程思路

那么可能会有疑问

我们一个通道转化完成之后 手动把数据转运出来不就行了吗?为什么一定要用DMA来转运数据

这样子是行不通的。

1.第一个问题

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

2.第二个问题

AD转换时非常快的。转换一个通道大概只有几uS也就是说入果你在几uS的时间内把数据转移走。那么数据就会丢失。这对我们程序手动转运数据要求就比较高了。所以在扫描模式下手动转运数据是很困难的。我们可以使用间断模式,每转换一个通道就间断,等我们手动把数据转运走了之后再继续触发,进行下一次的转换,这样可以实现手动转运数据的功能。但是由于单个通道转换完成之后没有中断标志位,只能通过Delay延时的模式,延时足够长的时间,才能保证转换的完成。这种方式效率太低,不推荐使用。

3.单次转换,非扫描模式

我们只需要再每次转换之前,手动更改一下列表第一个位置的通道就行了。

比如第一次转换,先写入通道0之后触发,等待,读值。第二次转换,再把通道0改为通道1,之后触发,等待,读值。第三次转换,再改为通道2,等等。

我们可以把填充通道的代码剪切,然后粘贴在触发转换之前。

然后转到定义,把他的参数复制一下

然后把通道选择改为这个。这样子我们在调用AD_GetValue时候只需要指定一个转换的通道,返回值就是我们指定通道的返回结果了。我们现在要指定的通道是0,1,2,3。

所以GPIO初始化也不要忘记。

更新头文件说明

编译一下0错误0警告。

定义变量。

然后OLED显示。

然后选择参数

可与i选这些参数。

我们选择通道0。

我们选择我们所有的通道。

然后OLED显示。

现在是一次启动四个转换,并且在转换之前制定了转换通道。每次转换完成,把结果分别存放在4个数据里,然后用OLED进行显示。

这就是使用单次转换非扫描模式,实现AD多通道的方法。

编译一下,0错误,0警告。

然后就i可以看到我们的结果了。可以自己设置一下,光敏看AD1,热敏看AD2,对射看AD3。

到这里我们ADC编码器代码就完成了关于更高级的模式,我们稍后就会更新。

相关推荐
bbxyliyang2 小时前
基于430单片机多用途定时提醒器设计
单片机·嵌入式硬件·51单片机
nenchoumi31192 小时前
ROS2 Humble 笔记(八)动作 action
笔记·机器人·ros2
范纹杉想快点毕业2 小时前
STM32百问百答:从硬件到软件全面解析
单片机·嵌入式硬件
笨鸟笃行2 小时前
百日挑战——单词篇(第十六天)
学习
三品吉他手会点灯3 小时前
STM32F103学习笔记-16-RCC(第3节)-使用HSE配置系统时钟并使用MCO输出监控系统时钟
c语言·笔记·stm32·单片机·嵌入式硬件·学习
摇滚侠3 小时前
Vue 项目实战《尚医通》,医院详情菜单与子路由,笔记17
前端·vue.js·笔记
CarmenHu3 小时前
IBM RAG挑战赛冠军方案学习笔记
笔记·学习
赶飞机偏偏下雨3 小时前
【MySQL笔记】索引 (非常重要)
笔记
朱嘉鼎3 小时前
GPIO中断编程
单片机·嵌入式硬件