上个章节介绍了ADC的基本框架和用法,本章节会较为全面地讲解如何灵活地使用ADC功能。
通常来说,外设执行的各种命令都是由CPU发出的,比如我们需要进行AD转换,就通过某个寄存器标志位启动ADC,随后用判断函数去检测执行情况,亦或是开启中断功能,转换完成之后会提示CPU,再由CPU判断接下来该做什么。
这当然没问题,不过有些时候,我们需要实现一些循环重复的功能,比如在做傅里叶分析的时候,需要按照固定间隔采集一定数量的数据,比如采样率1000Hz,共采集1024个数据。为了实现这个目标,当然可以使用软件定时实现,也可以使用定时器外设,在中断服务函数中直接进行AD转换来定时获取数据,但数据很多,若是还要进行快速傅里叶变换,CPU会在定时器中断中花费很多时间来采集数据,留给计算的时间就少了很多。有一种方案可以完美解决这类问题,不仅可以保证数据采集的精确性,还几乎不需要CPU参与,那就是使用定时器触发ADC转换,而AD转换完成之后会由DMA进行数据搬运,若CPU需要使用这批数据,直接访问数据进行读取即可 。具体的逻辑关系如下图所示。
这种组合涉及到DMA、ADC、定时器的使用,文章将详细介绍上图的联动过程。
- 按照顺序,首先是定时器触发AD转换。
前文也提到过,一般来讲可以用软件开关去启动AD转换,此处并没有用到这一功能,而是使用定时器触发,这是一种硬件触发方式,且触发源不属于ADC外设本身,故而也是一种外部触发方式。在ADC外设的配置项中,可以设置AD转换的开启是否由外部信号触发,只要ADC能正常使用,再配置好外部触发源,我们就能用外部硬件信号触发AD采集了。
注意!在实际使用中,外部触发源不一定非得是定时器,它也可以是某个片外模块提供的事件电平(完成事件会改变指定IO的电平,就和中断一样),最简单的就是按键触发的IO中断。
- 接下来是第二个部分,DMA功能的配置。
DMA,全名叫直接存储器访问,这个外设可以像CPU一样访问单片机内部的各个存储区(包括RAM和外设寄存器),但它只能做一件事情,就是数据搬运。通俗来说,数据存放在0-9共10个格子里面,DMA可以把这10个数据搬运到其他地方(并不会改变原本的值,效果类似赋值语句),而其他地方可以是同一个地方(将10个数据按顺序搬运10次,最终一层层覆盖到同一个地址),也可以是不同地方(将10个数据搬运10次并最终存放在10个不同地址的存储空间)。
总体上来讲DMA只做搬运这一个功能,除了搬运转换完成的AD数据,他还能用于各种数据接口(比如UART、SPI等),或是其他需要大量赋值的场合。
注意!****DMA并不能完全代替CPU进行数据转移,因为DMA*不像* CPU那样拥有全部存储区域的访问权限。
- 最后是定时器的配置,这一部分没什么变化,就是选择一个可以用来触发AD采集的定时器,按需求配置即可。不过需要注意的是,记得把溢出中断打开,这个是AD转换的启动开关 。
介绍完了每一步需要做的事情,下面说说代码应该怎么写。我会贴出主要代码,对于一些变量我会做说明但不做代码展示(变量什么的自己随便定义一个就行了呀!)
这段就是正常配置一个定时器,本案例中选择的是通用定时器2,根据手册,它可以触发AD采集。
除最后一行,其余代码与上一章节的初始化过程没太大差别,只需把DMA功能开启即可。这里补充一下其他配置项的说明:
- 时钟频率和采样时钟周期,如上一章说到的,采样时间当然是越长越好,其中的时钟频率就是ADC外设从内部时钟电路获得的时钟信号,频率越高,ADC速度越快,这个时钟速度一定程度上决定了一个ADC的最大采样速率(1秒内完成的转换次数)。
- 右对齐设置。AD转换的结果是存放在一个寄存器里面的,它本质上是个二进制数,右对齐就是将转换结果的第0位放到最右边,高位不为1的补0,若仍不清楚,可自行百度。
- 转换结果累加。为满足一些需求,ADC结果可能需要进行数据处理来减小误差,求加权平均值是一种很常用的办法,该功能开启之后,每次转换的结果都会直接累加,方便开发者取出后直接求平均值(所以实际上不要这个功能也可以)。
- 我们使用的是定时器触发采样,所以只需要使用单次采集模式就行了,ADC会在每次转换完成之后停止并等待下一次采集的启动信号(定时器溢出中断)。
总的来说,一定记住要把外部触发打开并绑定定时器2,随后只要定时器2的标志位被置位一次,ADC就会启动一次。
相对来说比较麻烦的是DMA的配置,要配置一个DMA,首先需要确定数据搬运的起点和终点 ,我们搬运的起点只有一个,就是ADC转换结果寄存器,搬运的终点是一个1024大小的数组,其中起点被称为源地址,终点被称为目标地址,且这两个地址都可以设置成自增模式(完成一次搬运之后,地址自动+1)。
关于DMA的初始化,DMA通常会有不止一个通道,每个通道都能独立进行数据搬运,初始化函数需要开启DMA的时钟总线,打开要用的DMA通道的中断。
关于DMA的启动和配置:
传输模式 :DMA帮助CPU进行数据搬运,这难免会出现DMA和CPU传输冲突的情况,非锁定传输会在DMA搬运数据的间隔内预留时间间隙,必要时候CPU会抢占对外设的访问权限,届时CPU可进行数据传输,DMA只能等待CPU放弃对该外设的使用权限后才能继续自己的数据传输。相对应的,还有锁定传输,具体的功能在用户手册DMA章节有详细介绍。
传输位宽 :根据需要搬运的数据长度进行选择即可。
触发方式和触发源 :由于我们需要的是在AD转换完成之后立刻进行数据搬运,所以DMA的触发方式选择为硬件触发,触发源应该设置为ADC转化为完成,这会让DMA在每次ADC转换完成之后都进行一次数据搬运,且不论ADC处于什么模式。
源地址和目标地址 :源地址设置为ADC数据寄存器,具体的地址有两种办法来获取,一种是用图中的办法,对寄存器取地址,另一种办法是直接查用户手册,找到ADC数据寄存器的基地址和偏移量计算得到地址(这种办法最终填进去的是一个hex数)。目标地址也是直接取地址数组即可。
地址自增 :我们不希望源地址有变动,故源地址设置成不自增;目标地址是一个数组,所以每搬运一次数据都要让目标地址自增一个元素,这样下一次搬运的时候,数据就会被正确写入下一个格子。
传输次数 :需求是采集1024个数据,那传输次数自然是1024次。
全部配置完后完成DMA的功能初始化,随后只需要启动ADC、定时器、DMA功能即可,图中代码做了一个开关启动,方便随时关闭。关闭的时候清零定时器计数器是为了在下次启动的时候让定时采集依然准确。
准备工作基本完成,接下来还剩一些小细节需要处理,我们打开了DMA和定时器的中断功能,他们会在完成搬运之后进入中断服务函数,至少得写个清除标志位的语句,除此之外还要加一些小功能。
定时器的中断服务函数只需要清除标志位即可,DMA的中断函数除了清除标志位,还需要将采样功能关掉(至少得把定时器关了)。如果使用的不是定时器触发,而是ADC的连续采样模式,需要在这里手动关闭ADC,不然即使DMA完成了搬运,ADC还会继续进行AD转换,由于DMA搬运已经完成了,自增的地址也不会自己变回去,之后就会出现“意想不到的”DMA错误搬运,但本案例中使用的是单次采集模式,ADC只在启动之后采集一次,所以不必担心ADC会连续采样导致程序异常的问题。
现在快速编写一个按键检测函数并调用刚刚写好的启动代码,其中的key_down变量会在按键中断服务函数中被置位。
再在main函数中循环调用这个按键检测函数,按键按下抬起后就会自动采集ADC数据,这里我用vofa+上位机显示一下采集的数据。
我这里采集的是单片机上定时器1输出的50Hz方波,占空比为50%,周期为20ms,效果还不错。
审核编辑 黄宇
-
ADC
+关注
关注
0文章
100浏览量
16845 -
CW32
+关注
关注
1文章
154浏览量
472
发布评论请先 登录
相关推荐
【有那么点详细的CW32学习笔记】通用定时器——输出比较
![【<b class='flag-5'>有</b><b class='flag-5'>那么点</b><b class='flag-5'>详细</b>的<b class='flag-5'>CW32</b><b class='flag-5'>学习</b><b class='flag-5'>笔记</b>】通用定时器——输出比较](https://file1.elecfans.com/web2/M00/B9/59/wKgZomWM6KeAOkNXAACbzsh_phs727.png)
【有那么点详细的CW32学习笔记】模数转换器
![【<b class='flag-5'>有</b><b class='flag-5'>那么点</b><b class='flag-5'>详细</b>的<b class='flag-5'>CW32</b><b class='flag-5'>学习</b><b class='flag-5'>笔记</b>】模数转换器](https://file1.elecfans.com/web2/M00/C1/D0/wKgZomXe2xGACIsPAAORHVtF7mQ820.png)
【应用笔记】CW32 自举程序中使用的 ISP 协议
应用笔记(二)| 武汉芯源CW32自举程序中使用的ISP协议
![应用<b class='flag-5'>笔记</b>(二)| 武汉芯源<b class='flag-5'>CW32</b>自举程序中使用的ISP协议](https://file.elecfans.com/web2/M00/52/78/pYYBAGLL2paAVDK6AAAektoL06g510.png)
cw32和gd32的区别
应用笔记-CW32 自举程序中使用的 ISP 协议
【有那么点详细的CW32学习笔记】通用异步收发器—发送篇
![【<b class='flag-5'>有</b><b class='flag-5'>那么点</b><b class='flag-5'>详细</b>的<b class='flag-5'>CW32</b><b class='flag-5'>学习</b><b class='flag-5'>笔记</b>】通用异步收发器—发送篇](https://file1.elecfans.com/web2/M00/B0/AC/wKgZomVXMlOAcHnhAAB6IPl3PBc244.jpg)
【有那么点详细的CW32学习笔记】IIC接口-主机发送
![【<b class='flag-5'>有</b><b class='flag-5'>那么点</b><b class='flag-5'>详细</b>的<b class='flag-5'>CW32</b><b class='flag-5'>学习</b><b class='flag-5'>笔记</b>】IIC接口-主机发送](https://file1.elecfans.com/web2/M00/BB/FE/wKgZomWeiQWAKEeBAACrG_jJKww287.jpg)
【有那么点详细的CW32学习笔记】IIC接口-OLED驱动
![【<b class='flag-5'>有</b><b class='flag-5'>那么点</b><b class='flag-5'>详细</b>的<b class='flag-5'>CW32</b><b class='flag-5'>学习</b><b class='flag-5'>笔记</b>】IIC接口-OLED驱动](https://file1.elecfans.com/web2/M00/BC/E9/wKgaomWeiuOAVdmyAAd8-3dPvG4290.jpg)
评论