整整捣鼓了3
天的TIM2+ADC+DMA
实验,以下是我对该实验的一些总结,希望能对刚入门STM32
的朋友起到一些帮助。同时,也感谢论坛大神对我提出的疑问进行解答。第一次发分享帖,如有错误,请指正!
首先来说说ADC。ADC有那么几种工作模式,扫描模式/非扫描模式,单次转换模式/连续转换模式。这些模式都在什么情况下使用呢?扫描模式,很容易理解,只有开启了多条通道我们才需要扫描,倘若我们只有一条通道,那就非扫描模式好了。至于是单次转换模式还是连续转换模式,那要以你的实际情况而定了。值得一提的是,单次转换模式下,启动一次ADC转换,就只能输出一次转换的值,要想不断转换,就得不断地启动ADC(要等待上一次转换结束)。如果开启了连续转换,那就方便多了,只需在程序初始化的时候,启动一次ADC转换,就能在ADC_DR不断地读出变化的值了。说到启动,该如何启动呢?STM32提供了好几种触发ADC启动的外部事件,具体大家可以查手册看看。比较常用的就是定时器触发和软件触发了。如果是软件触发,那就向ADC_CR2的SWSTART写1。要注意的是,在单次转换模式下,可以通过不断地向ADC_CR2的SWSTART写1来达到连续转换的目的。再来看定时器触发。在一些帖子上看到说,要使用定时器触发ADC,必须将定时器配置为比较输出PWM模式,且只有在TIMx_CHx输出上升沿才触发。为什么要这么做我并没有深究,我只知道按照这么配置我达到目的了。总之,就是要动用定时器的pwm功能,至于是pwm1模式还是pwm2模式这无关紧要,因为一个周期结束总会产生一次上升沿,通过修改pwm的周期,就可以间接控制ADC转换的周期了。需要补充说明的是,pwm波并不需要输出到引脚,所以可以免去引脚的配置。还有,使用定时器触发,应该使用的是单次转换模式,即由定时器触发的周期单次转换(若是多通道,再开启扫描模式就好)。通道的采样时间就不说了,多通道下,每一个通道可设置不同的采样时间。再说说转换完成中断吧。要使用转换完成中断,首先得在ADC_CR1将EOCIE置1,使能转换完成中断。然后配置MY_NVIC_Init(),最后写中断服务函数ADC1_2_IRQHandler()。一般中断服务函数要完成的任务就是读取ADC_DR啦,读取ADC_DR后,中断标志位EOC会自动被清0,就不需要我们管了。说完ADC,就到DMA了。为什么需要DMA?楼主本也想跳过DMA的使用,毕竟我也想省下这一部分学习。但后来发现,要扫描多路ADC,就必须使用DMA。因为ADC_DR寄存器只有一个,所有通道的转换值都会放到ADC_DR上,一个通道转换完成后的值会自动覆盖上一个通道转换完成的值,而只有ADC将所有通道都转换完了(通道的转换是按顺序一个接一个来的),才会置位转换完成标志,所以我们在转换完成中断函数里读到的ADC_DR的值永远是最后一个通道的,这与我们想要得到多通道的值的初衷不符。DMA的具体使用方法我不赘述了,只谈谈我认为比较关键的地方。ADC_DR传来的值是12位的,所以我们将外设数据宽度和存储器数据宽度都设置成16位的,还有就是作为存储器的数组,也必须设置成16位的。
DMA也是有中断功能的,通常接收完成中断函数就是用来显示ADC传过来的值的。在此,我要补充说明一下,DMA的普通模式/循环模式。普通模式,就是软件使能一次,DMA就搬运一次。而循环模式,就是DMA连续不断地搬运数据,而不管ADC有没有在工作。为什么这么说?因为我前面用了TIM2来触发ADC转换,每0.5S转换一次,如果是ADC转换完成一次,DMA就搬运一次,那么DMA接收完成中断函数也是每0.5S才调用一次。然而,事实是,DMA接收完成中断函数在以极高的频率被调用,这个通过我的串口调试助手不断更新的数据可以得知。那么,如果我想ADC转换完成一次,才搬运一次数据该怎么做呢?我的做法是,设置一个标志位,在ADC转换完成中断函数末尾将其置1,在DMA接收完成中断函数开头判断其是否为1,末尾再将其置0。最终,可以在串口调试助手上看到每隔0.5S输出一次的数据。
TIM2 ADC DMA.zip
(1.23 MB, 下载次数: 748)
2016-9-1 07:40 上传
点击文件名下载附件
工程代码
大哥,你好!我仔细看了你的解释和代码了,受益匪浅!谢谢了。不过我有个疑问:若ADC要多路扫描,例如有5个通道,那么我要开启扫描模式。设置DMA为普通模式的话,用ADC的中断服务程序,那么ADC转换的前4路,DMA就收不到了;设置DMA为循环模式的话,DMA又会不管ADC工不工作而一直工作,那又怎么样保证DMA传输的是这ADC的5个路的转换值,而不是多次转换ADC第1路的值后,又多次转换ADC第2路的值,又多次转换ADC第3路的值,以此类推呢。
一周热门 更多>