STM32CubeMX和HAL库踩坑记——DMA+TIM输出比较模式

  • Post author:
  • Post category:其他


目的:让单片机输出一段可调频率的脉冲,用于驱动步进电机。

方案:1、使用定时器溢出中断,定时中断一次,在中断通过判断来翻转IO口。

优点:实现比较简单,对硬件要求不高。

缺点:不适合高速脉冲输出,而且脉冲分辨率也很低。

2、使用PWM模式,通过改变ARR的值来改变脉冲周期,从而控制IO口反转。

优点:可以输出高速的脉冲。

缺点:一个定时器只能输出一路脉冲,脉冲数量不可设置。

3、使用定时器的输出比较模式,设置输出比较匹配时翻转IO口,并开启输出比较中断,

在中断中装载下一次比较值。

优点:可以输出高速脉冲,并且脉冲数量控制。

缺点:进入中断频繁,增加CPU负担。

4、使用定时器的输出比较模式,设置输出比较匹配时翻转IO口,不开启输出比较中断,

开启DMA模式。

优点:可以输出高速脉冲,并且脉冲数量控制。

缺点:需要预装载脉冲频率的值,占用空间多。

结论:由于本次需要多个步进电机同时工作,并且步进电机需要加减速启动,电机转动的圈数不多。决定使用方案4。

*我搞这个模式搞了三天,今天终于把这坑踩平了,把这点经验分享给大家,避免重复踩坑。

*网上百度找了好久,稀稀疏疏就一两篇文章有用到这个模式的,而且还不是HAL库的,这个模式对于高速脉冲的输出真的是好用。

好了,直接上配置和代码,以及需要避坑的地方。



1、CubeMX配置



1.1 基本设置



1、先开始进行时钟配置,如上图,一般选用外部晶振

在这里插入图片描述

2、配置下载方式,Serial Wire就是SWD下载

在这里插入图片描述

3、配置定时器,我现在只需要通道2的比较输出模式,如图配置,其他模式看简介。



1.2时钟配置

在这里插入图片描述

我是用的是STM32F103的芯片,系统时钟最高72MHZ,所以如图配置。



1.3 具体配置

1、用到定时器肯定要配置定时器,这里的配置是:72分频,向上计数,计数最大值65536,通道2的输出比较模式要反转IO口。

在这里插入图片描述

2、DMA配置如图所示,这里不用循环模式,用半字传输,因为定时器是十六位的,CR2寄存器也是十六位的,存储器的地址需要递增,而外设地址不需要地址。

在这里插入图片描述

3、最后配置NVIC,这个版本的CubeMX是强制配置DMA中断的,因为用DMA传输数据,所以定时器输出比较中断不需要配置。

在这里插入图片描述



2 代码



2.1使能

首先需要一个数组,然后往数组里面放数据,这个就是输出比较模式里CR2设置的值。第一个数为200,

第二个为400,第三个为800,第四个为1400,看出规律没有。

在这里插入图片描述

在这里插入图片描述

**第一个坑来了

程序运行完这些就使能定时器和DMA吧,刚开始我是这样写的

在这里插入图片描述

其实是可以的,后来点进去第一个函数HAL_TIM_OC_Start_DMA看看,发现里面有这么一段。

在这里插入图片描述

就知道第二个函数在第一个函数里面已经调用了,重复调用浪费时间。



2.2调试

既然开启了自动装载,那就仿真看看CR2的值是不是真的在改变吧。(当时我们公司没有示波器和逻辑分析仪这些东西)

**第二个坑报道

然后发现,一开始全速运行,CR2的值就马上变成这样子。。。

在这里插入图片描述

我在想,应该能看到变化才对,我想,是不是频率太快了,然后就把数组增大,使用6400个数据,大致算了下也要几秒钟的时间,后来仿真一看还是直接变成最后那个值。

后来各种搞,各种不行。今天上班来问隔壁公司借个示波器测测,一看,波形很对。上波形。

在这里插入图片描述

在这里插入图片描述

第二张波形图可以看到,后面还有无数个波形,而且波形比较大,这个是因为定时器一直没有关闭,所以这个脉冲周期就是定时器溢出周期的两倍(不知道为什么是两倍的同学看看比较输出模式的IO口翻转功能)。



2.3中断回调函数

第三个坑,神坑

因为DMA中断已经开启了,在是stm32f1xx_it.c函数里面有这些中断函数,找到这个定时器CH2的DMA中断函数。

在这里插入图片描述

然后我小心翼翼地点了进去,然后找回调函数

在这里插入图片描述

当时我惊呆了,hdma->XferCpltCallback(hdma);这个是啥玩意,点进去看居然是这样的。

在这里插入图片描述

这个写的是DMA传输完成中断回调函数,但是这玩意咋用,学了C语言这么多年,没见过能在结构体里面定义函数的。。。

不知道就百度吧,百度出一筐,看得云里雾里,最后一个热心的群友解答了我的问题,说这是一个指针,不是函数

实体,我个人就认为这只是用来指向某个具体函数的(大佬来解答一下,这个东西真的是第一次见到)。

看完还是不会用,然后试一下直接使用,下载,仿真,DMA进入了中断,但是没有进入这个回调函数,放弃(我是说再百度试试,毕竟搞不出来老板会扣我工资的)。

又百度到说DMA中断调用的其实是和普通中断调用一样的函数,怎么理解,例如不用DMA而用中断,触发中断调用的是A函数,那么用DMA,DMA完成中断也是调用A函数,哇,ST设计的HAL真厉害(这对于小白很好用)。然后我马上在

void HAL_TIM_OC_DelayElapsedCallback(TIM_HandleTypeDef *htim);这个函数失能定时器和DMA。发觉不好使,调试也进不去。

搞了一个多小时,想了想可以一步一步地仿真,看看程序到底进入了那个回调函数,发现TMD进入的是这个函数。如图。

在这里插入图片描述

好吧,那就用这个函数失能定时器和DMA,注意一下,这里的__weak是弱函数,也就是说你可以重新定义一个同名的函数,而不需要把他删除,程序就会执行你定义的函数。如图。

在这里插入图片描述

最终完成了,波形图。

在这里插入图片描述

最后说明一下,这个方式可以一个定时器输出4个频率可变的波形,而且数量可以控制,也不需要单片机频繁进入中断。但是会很耗内存,在波形不会因为其他因素而改变的情况下可以放在单片机的ROM,或者可以试试使用8位传输。



版权声明:本文为hyh80227原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。