时间是一个非常重要的概念,我们和朋友出去游玩需要约定一个时间,做事情也需要花费一段时间,总之,我们的生活离不开时间。操作系统也一样,也需要一个时间来规范其任务的执行。
我们生活中,时间的最小单位是秒,硬件电路中,产生的最小的时间是晶振的频率,在操作系统中,其最小的时间单位叫做时钟节拍(Time Tick),其是有硬件定时器产生的一个周期为毫秒(ms)级的时间,即当定时器达到指定的时间后,产生一个中断(OSTickISR()),时钟节拍即两个中断之间间隔的时间。
当进入时钟节拍中断函数(OSTickISR())后,其首先保存CPU寄存器的值,(其值保存在哪呢?执行ISR和执行任务的时候,保存的地方应该不同啊,执行任务的时候,应该保存在任务堆栈中,但执行ISR的时候,保存在哪呢?)对终端嵌套层数(OSIntNesting)加1,然后判断其是否没有中断嵌套的情况,如果没有,则将堆栈指针(SP)赋值给当前任务的TCB成员OSTCBStkPtr。接着调用时钟节拍函数(OSTimeTick()),再接着清除中断,最后退出中断(OSIntExit()),在推出中断函数中,会引发一次任务调度。
在时钟节拍函数(OSTimeTick())中,主要的任务有两个,一是给记录时间的变量OSTime+1,二是遍历所有的任务控制块链表,给任务块中用来保存任务延时时限的OSTCBDly-1(即告诉任务,时间已经过去一个节拍了),若非挂起任务的时限已经到了,则使其进入任务就绪状态(注意:是非挂起的任务,挂起的任务(OSTaskSuspend()),必须调用恢复任务函数(OSTaskResume())来恢复其状态)。
有的时候,我们希望在每个时钟节拍都做一些工作,比如现实生活中,我让手机信号灯每秒钟闪一下。我们可以调用时钟节拍服务函数的钩子函数(OSTimeTickHook())完成这项任务。钩子函数有很多种,比如在任务切换的时候,要做的工作(OSTaskSwHook())。总共有10个这样的钩子函数。当然,如果不是很必要,最好必要使用钩子函数,因为执行也需要时间,会造成定时不准确。(在执行钩子函数的时候,硬件定时器不一样也是在工作的吗?当它达到定时时间的时候,一样会产生中断,这样会造成定时不准确吗?)
在每个任务(除了空闲任务)中,都必须使用延时函数(OSTimeDly())来使当前任务的运行暂停一段时间并执行一次任务调度,使系统去执行其他优先级高的就绪任务,否则该任务会一直占用CPU的使用权,造成任务的独占CPU的现象。
OSTimeDly()的作用就是将延时的时限存入任务控制开TCB的成员OSTCBDly中,并取消当前任务的就绪状态,接着进行一次任务调度,使系统去执行任务优先级最高的就绪任务。OSTimeDly(INT16U ticks)是以时钟节拍数来定时的一个函数,系统还定义了另一个延时函数INT8U OSTimeDlyHMSM(INT8U hours, INT8U minutes, int8u seconds, INT8U milli),即规定时、分、秒、毫秒。
有使任务延时的函数,自然也需要使任务取消延时的函数,OSTimeDiy(),无论调用的是上述中的那种延时函数,都是通过调用OSTimeDlyResume()取消该任务的延时从而进入就绪状态。该函数的原型是 INT8U OSTimeDlyResume(INT8U prio);
最后,还可以调用OSTimeGet()和OSTimeSet()来获取或设置系统时间,即发生的时钟节拍数(OSTime的值)。其函数原型分别问 INT32U OSTimeGet(void)和void OSTimeSet(INT32U ticks)。