每节课都是一个项目 手把手用STM32打造联网气象站-5-STM32基础三件套-采用TIM定时器,实现无阻塞LED任意占空比和次数闪烁

本节主要目的是:

1. 通过项目学编程:通过实现一个小小的项目功能,梳理编码思路;

2. 积累代码库:实现的这个小功能,后续可以不断使用并且在多处移植;

1.项目需求:

实现一个LED控制函数,可以控制LED点亮x毫秒,熄灭y毫秒,并重复z次;为了避免阻塞,不能采用任何delay。

2.实现方法1: 采用全局变量来实现

由于不能使用delay函数,我们只能按照上一节提到的方法,将LED分为两种状态:熄灭状态和点亮状态。

在熄灭状态时,设定倒计时时间y,在定时器中自减;自减为0后,点亮LED,并设定倒计时时间x,同样再定时器自减。如此反复z次,即可实现led按照指定占空比和指定次数来闪烁。

2.1 初始化全局变量on_time和off_time

这里必须用全局变量,因为这里的on_time和off_time会分别在led.c it.c和main.c中调用。

  • 凡是必须在多个文件中使用的变量,必须采用全局变量。
  • 全局变量建议在.c文件中定义,在.h文件中声明。这样需要用到这个全局变量的.c文件,直接通过包含头文件即可使用对应的全局变量。

下面介绍下具体步骤:

 

 第一步:我们首先在led.c中,定义两个全局变量on_time和off_time;注意,这里类型是uint16_t,也就是两个字节长度的变量。另外还需要一个静态全局变量。

  • 静态全局变量是指这个全局变量只能在文件内部使用,在文件外部无法使用。
  • 不需要外部使用的全局变量,尽量采用静态方式,避免在其他地方被误修改,便于维护;

第二步:写一个函数,配置上面设置的静态全局变量 。

同样为了便于维护,对于静态全局变量,我们一般需要用set或者get函数,设置对应值,或者读取对应数值。

这里led_cnt是静态全局变量,我们必须用set_led_config方式修改。而led_on_time和led_off_time为全局变量,我们可以直接赋初值方式进行修改。

所有的数字都采用宏定义方式,这样便于后续统一修改on_time和off_time

 

需要被其他文件调用的全局变量,放在头文件中进行声明,这样通过查看头文件,就会了解有哪些全局变量需要查看和修改。

 写完这个函数后,不要忘记在头文件中,更新对应的

  • 宏定义;
  • 外部变量;
  • 外部函数;

第三步:在中断处理函数中,对刚才的on_time和off_time进行自减操作;

按照前面的配置,我们每隔1ms进入一次中断,也就是on_time和off_time每隔1ms,会自动减1,一直减到0为止。

 

第四步:在main.c中,增加set_led_config函数的调用,增加led_check函数的调用。

这样,就实现了开机时,设置led闪烁3次,每次闪烁开100ms,熄灭900ms的效果。

 

我们下载下去,发现启动后,LED确实亮了3次,但是还有一个问题:第三次LED点亮后,居然没有熄灭。

注意:我们的教程中,常常会记录这些BUG解决过程,因为在真实的开发过程中,调试和解决BUG往往是开发的必备技能,甚至说是核心技能。

 代码中,在检查led_cnt的地方出了问题: 

1.当led_cnt=1时,这时执行到了led_cnt--的地方,led_cnt会变为0;

2.下一次检查的时候,发现不符合条件(led_cnt>0),就会直接退出函数,而没有能够再次进入函数,执行led_off()语句。

如何解决这个问题呢?

很简单:在if(led_cnt>0)判断结束后,增加else if(led_cnt==0),这样就可以在最后,把led熄灭了。

如上面代码,增加一条判断调节,即可解决问题。

 

代码执行结果如上图,每次启动时候,就会LED点亮三次,点亮时间为100ms,熄灭时间为900ms;

 如果现在需求变了,需要点亮200ms,熄灭800ms,怎么办呢?

由于我们坚持采用一切数字都用宏定义的方式,所以直接修改前面定义的宏,就可以完成这个功能变更。

纸上得来终觉浅,绝知此事要躬行!

马上下载测试代码,看看效果。

https://download.youkuaiyun.com/download/book_drabit/85926549

3.实现方法改进: 采用结构体变量实现

在GPIO初始化的第二节,我们讲到,可以跟着ST学编程,沿用ST常用的方式,进行初始化设置。具体可以看下面链接:

每节课都是一个项目 手把手用STM32打造联网气象站-3-STM32基础三件套-掌握GPIO初始化_可志嵌入式的博客-优快云博客

ST常用的初始化方式,我们再复习一下:

1. 创建结构体类型,把对应参数放入结构体类型中;

2. 用这个类型,创建结构体变量;

3. 初始化这个变量,完成参数设置。

这一套方法,在GPIO初始化,TIM初始化,NVIC初始化中,都有反复使用。

这套方法的好处是:通过定义结构体,将GPIO相关参数汇聚在一起,方便根据结构体类型,进行修改,提高代码的内聚性,避免代码松散。

接下来,我们就用类似方法,把LED初始化为结构体,并进行相应参数设置。

先复习一下ST教我们的写法,然后照葫芦画瓢写代码

 

 

 第一步:在头文件中,定义Led_TypeDef;包含了全部LED的全部三个控制参数;

第二步:在.c文件中,用前面定义的结构体类型,定义led_ctl这个参数; c文件中完成定义后,需要在.h文件中进行声明,方便其他文件调用;

第三步:it.c中,对结构体变量进行自减; 

第四步:更新led.c中函数,采用结构体变量来进行计数; 

完成上面步骤之后,采用结构体类型定义的led_ctl就完成了。在其他代码中,如果需要重复使用这个led_ctl,只需要简单包含对应头文件即可。

 纸上得来终觉浅,绝知此事要躬行!

马上下载测试代码,看看效果。

https://download.youkuaiyun.com/download/book_drabit/85928189

 4.采用一个定时器,进行分钟定时

需求描述:在前面一期项目基础上,需要增加功能。

  1. 每分钟LED进行闪烁,闪烁占空比为:亮50ms,熄灭200ms;闪烁次数为开机之后度过的分钟数。比如5分钟就会闪烁5次,10分钟就会闪烁10次;
  2. 为了避免闪烁次数过多,超出10分钟后,改为按照小时闪烁,闪烁次数为开机后的小时数;
  3. 超出10小时候,就不再闪烁了;

根据上面需求,完成代码开发。

4.1需求分析

根据上面需求,我们首先需要实现按照分钟计数,然后还需要实现按照小时计数。其中计数的分钟值和小时值作为参数,传递到set_led_config中。

在主循环中,每到1分钟,我们设定点亮led,完成led闪烁;

当分钟数超出10之后,改为每小时点亮led,完成led闪烁;

完成上面需求分析后,我们还需要掌握一个重要的方法:求余数法。在C语言的宇宙中,常常会通过余数为零,来找到整数倍。例如:一小时为60分钟,我们将分钟数除以60,当余数为0时,就恰好为一小时时间。

4.2分步实现代码

需求梳理完毕,接着分步开始整代码。

 

第一步:在tim.h中,增加SysTimer这个结构体类型定义;同时,定义多个时间标志位;

  第二步:在it.c中,SysCounter自增,当增加到60000毫秒时,设置1分钟标志位;达到60分钟时,设置1小时标志位;

 

第三步:在main.c中,检查SysTickFlags时间标志,根据分钟标志,实现分钟显示;

 

第四步:在main.c中,根据分钟显示,计算小时,并且将小时参数放入led_set_config中,实现小时显示。

根据上面步骤,我们就完美实现了项目需求。

4.3 重用代码

上面代码中,我们有大量可以重复使用的内容,在这里归纳一下:

1.用SysCounter和SysFlag来设置不同计时间隔时间;

2. 用%n==0方式,来取得不同倍数关系;

3. 用&SysTickFlag方式,获取整点标志位,获取之后,需要用SytemTimer.SysFlag&=(~)方式,将标志位清零;

这里需要补充一下C中的知识点:

C中,给x数值的第nbit设置为1,常用方式是:x|=(0x01)<<n;

同样,如果需要把x数值的第nbit设置为0,常用方式是x&=~(0x01)<<n;

上面这两种操作是需要熟练掌握的。



https://download.youkuaiyun.com/download/book_drabit/85932726

纸上得来终觉浅,绝知此事要躬行!

马上下载测试代码,看看效果。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值