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


目的:让单片机输出一段可调频率的脉冲,用于驱动步进电机。
方案: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位传输。

  • 38
    点赞
  • 127
    收藏
    觉得还不错? 一键收藏
  • 22
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 22
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值