当前位置: 首页 > 科技观察

关于软件定时器的一些讨论

时间:2023-03-13 18:31:40 科技观察

简介这里先介绍一下软件定时器和硬件定时器的区别,对应的定时器中断处理函数会被执行。硬件定时器一般还有其他功能,如PWM输出、输入捕捉等。但缺点是硬件定时器数量少!!软件定时器:软件定时器允许设置一个时间段。当设定的时间到达时,将执行指定的功能。定时器调用的函数称为定时器的回调函数。两次执行回调函数之间的时间间隔称为定时器的计时周期。简而言之,回调函数将在定时器的计时周期到时执行。FreeRTOS中有一个专门的软件定时器功能,我们可以简单的在MCU中实现“软件定时器”如下:voidtimer_1000ms(void){printf("timer_1000ms\r\n");}intmain(void){statictimer_tick=0;timer_tick=systick_ms;while(){if((systick_ms-timer_tick)>1000){timer_tick=systick_ms;timer_1000ms();}}}这是一个简单的软件定时器,没错,这是一个特别精简版的软件定时器。当然它也有缺点,比如systick_ms每1ms加1,所以软件定时器的精度是以ms为单位的,如果while(1)中阻塞了其他代码,那么软件定时器也会被阻塞。这个简单的软件定时器毕竟“简单”。可以自己打包丰富,也可以参考现有的开源方案:MultiTimer,一个可无限扩展的软件定时器。MultiTimer是一个软件定时器扩展模块,可以无限扩展你需要的定时器任务,替代传统的标志位判断方式,更优雅方便的管理程序的时间触发定时。开源地址:https://github.com/0x1abin/MultiTimer。MultiTimerMultiTimer的设计比较简单,只有2个文件,而且只有4个函数,一共82行代码,稍微下点功夫就能看懂。迁移步骤配置系统时间参考接口,安装定时器驱动uint64_tPlatformTicksGetFunc(void){/*平台实现*/}MultiTimerInstall(PlatformTicksGetFunc);实例化一个定时器对象MultiTimertimer1;设置计时时间,超时回调处理函数,用户上下指针,启动定时器intMultiTimerStart(&timer1,uint64_ttiming,MultiTimerCallback_tcallback,void*userData);调用定时器后台处理函数intmain(intargc,char*argv[]){...while(1){...MultiTimerYield();具体怎么移植我就不做手把手的教程了。STM32F207中移植的项目开源地址:开源地址:https://github.com/strongercjd/STM32F207VCT6/tree/master/23-Timer-MultiTimer。我们来分析一下MultiTimer。移植第一步,配置系统时间参考接口,安装定时器驱动。uint64_tPlatformTicksGetFunc(void){/*平台实现*/}MultiTimerInstall(PlatformTicksGetFunc);查看MultiTimerInstall函数原型。typedefuint64_t(*PlatformTicksFunction_t)(void);staticPlatformTicksFunction_tplatformTicksFunction=NULL;intMultiTimerInstall(PlatformTicksFunction_tticksFunc){platformTicksFunction=ticksFunc;return0;}这其实就是函数指针实现的回调函数,其实就是MultiTimer的一个计数器。这个开源项目除了回调函数,还是单链表的好例子。学习数据结构比较繁琐。这个开源项目是单链表的一个很好的应用。不太了解的同学可以学习一下。我们看一下删除部分代码链表:/*从列表中删除*/break;}}插入链表:for(nextTimer=&timerList;;nextTimer=&(*nextTimer)->next){if(!*nextTimer){timer->next=NULL;*nextTimer=定时器;休息;}if(timer->deadline<(*nextTimer)->deadline){timer->next=*nextTimer;*nextTimer=定时器;休息;}}遍历链表:MultiTimer*entry=timerList;for(;entry;entry=entry->next){/*排序后的列表,只处理前面的部分。*/if(platformTicksFunction()deadline){return(int)(entry->deadline-platformTicksFunction());}/*从列表中删除过期的计时器*/timerList=entry->next;/*调用回调*/if(entry->callback){entry->callback(entry,entry->userData);}}当然MultiTimer也有缺点,比如一个定时器是1000ms,另一个定时器是500ms。调度时会发生冲突,没有定时器调度抢占,会随着其他代码阻塞而阻塞。这种类似的问题就不详细说了,大家在使用的时候多测试一下就好了。任务调度看了上面的操作,如果我们不把它叫做软件定时器,称之为“任务”,是不是类似于FreeRTOS的任务,感觉比较高端,连这篇文章的标题都可以改成《一篇文章教你实现操作系统》,开怀大笑,不做头条党。有些项目实时性要求高,需要任务抢占,所以需要FreeRTOS等操作系统,但其资源占用比例过大,不利于项目开发。一般的小项目,RTOS的太多功能是用不到的。使用上面的思路,可以设置每个任务在不同的时间间隔周期性调用。如果有实时性要求高的事件,可以通过中断来处理。当然也可以一开始就用粗略的方法:if((systick_ms-timer_tick)>1000){timer_tick=systick_ms;timer_1000ms();}这个功能可以实现,但是没有模块化,不利于代码维护。我们可以借鉴MultiTimer的思想来封装软件接口。而且,如果你项目中的任务数是固定的,你可以去掉MultiTimer中的链表,直接使用全局变量。如果你有多余的时间模仿FreeRTOS实现一些信号量、队列等,这就是你自己的OS(没有抢占)。(当然,这是重新发明轮子,但是对于一些公司来说,有最适合自己业务的精简代码框架是很有必要的)。本文转载自微信公众号“知乎编程”,可通过以下二维码关注。转载本文请联系知编程公众号。