UCOSII源码分析六——定时器(Tmr.c)
作者:互联网
定时器(Tmr.c)
1. 软件定时器
定时器的实现是需要硬件的支持,硬件进行计数递减,当递减到0时触发中断,即时钟节拍。系统开始运行时,OS_TMR_EN 为1 的话,使用定时器功能,在OSInit()中调用OSTmr_Init()函数,对该函数对时间轮进行初始化,并将定时器连接成链表,并在函数内部创建信号量,该信号量即用来进行定时器触发,在系统的时钟节拍到来时执行函数OSTimeTick (void),在函数内部执行全局变量OSTime++; 同时调用OSTimeTickHook();在该函数中实现信号量的发送,但发送的时钟是用户自定义时间的,由宏OS_TICKS_PER_SEC / OS_TMR_CFG_TICKS_PER_SEC决定。控制块中还有一变量ptcb->OSTCBDly,该变量用来延时计时的,在每个Tick到来时进行控制块遍历检查,若值为0,说明时间到了,使任务就绪。
2.定时器实现原理方法
uCOS-ii中将定时器进行分组,在Tick到来进行检查时,只需要对延时的时间对应的分组进行检查,时间到了将定时器从分组中删除,若为周期模式则在将定时期插入即可,分组缩短了代码处理的时间,提高效率。
内核代码中定时器相关的变量比较重要的是:
OSTmrTbl[OS_TMR_CFG_MAX]; /* Table containing pool of timers */
OSTmrFreeList; /* Pointer to free list of timers */
OSTmrWheelTbl[OS_TMR_CFG_WHEEL_SIZE]
OSTmrTbl [OS_TMR_CFG_MAX]:
用来存储所有创建的定时器的任务控制块RAM空间。
OSTmrFreeList:
空闲控制块的链表头指针。OSTmrNext、 OSTmrPrev双向链表指向空闲控制块中的的空闲定时器。建立定时器时,从这个链表中搜索空闲控制块。
OSTmrWheelTbl[OS_TMR_CFG_WHEEL_SIZE]:
该数组每一个元素为已开启的定时器的一个分组,元素中记录了指向该分组的第一个定时器控制块的指针,以及控制块的个数。运行态的定时器控制块中,OSTmrNext、 OSTmrPrev两个指针同样也组织了所在分组中定时器控制块的双向链表,
定时器分组:
宏OS_TMR_CFG_WHEEL_SIZE的大小决定了定时器轮的大小,即数组OSTmrWheelTbl的大小,uCOS-ii根据ptmr->OSTmrMatch % OS_TMR_CFG_WHEEL_SIZE值进行分组,不同的值分配在不同的定时器分组中,最后由双向链表连接, 余数取值范围是 0 ~OS_TMR_CFG_WHEEL_SIZE -1 ,正好对应于不同分组 OSTmrWheelTbl[0] ~ OSTmrWheelTbl[OS_TMR_CFG_WHEEL_SIZE - 1]。
例如默认OS_TMR_CFG_WHEEL_SIZE的值为8,则余数可取0~7,将设置的时间对8取余,进而将设置的时间分组。在系统时钟Tick中,发送定时器信号量,在定时器任务中执行定时器任务函数OSTmr_Task (void *p_arg),在函数中信号量等待,并将变量OSTmrTime++; 最后通过
spoke = (INT16U)(OSTmrTime % OS_TMR_CFG_WHEEL_SIZE)
检查,只有余数相同的才是到时的分组,取出现在的到时间分组后,对分组中的每个定时器控制块进行检查,将OSTmrTime 与 控制块的值ptmr->OSTmrMatch进行比较,若相等,则通过
(*pfnct)((void *)ptmr, ptmr->OSTmrCallbackArg)
执行定时器的回调函数,再将定时器从时间轮中解绑,若不相等,继续轮询该分组内的所有定时器,直到双链表的结尾。而其他分组的轮询则随着变量OSTmrTime的增加进行。
而ptmr->OSTmrMatch的值在创建定时器的时候进行初始化,在函数
OSTmrStart (OS_TMR *ptmr,
INT8U *perr)
中调用OSTmr_Link(ptmr, OS_TMR_LINK_DLY);进行状态改变,打开计时OS_TMR_STATE_RUNNING后,由ptmr->OSTmrMatch = ptmr->OSTmrPeriod + OSTmrTime 赋值,根据设置的周期加上此时的OSTmrTime值作为匹配的定时时间。
标签:TMR,定时器,Tmr,CFG,UCOSII,源码,分组,ptmr,OS 来源: https://blog.csdn.net/qq_15555275/article/details/121478443