所谓时钟节拍,就是CPU以固定的频率产生中断,可以看做是系统的心跳。内核利用这个时钟节拍来管理各个任务的一些时间管理比如延时,定时,超时检测,时间轮片调度等。时钟节拍的频率一般10Hz--1000Hz。频率太高内核负担加重,频率太低实时性就差。这个频率在μC/OS-III源码OS_CFG_APP.c中可以自定义,在STM32平台的具体实现如下:
1.配置滴答定时器

void OS_CPU_SysTickInit (CPU_INT32U cnts) { CPU_INT32U prio; CPU_REG_NVIC_ST_RELOAD = cnts - 1u; /* Set SysTick handler prio. */ prio = CPU_REG_NVIC_SHPRI3; prio &= DEF_BIT_FIELD(24, 0); prio |= DEF_BIT_MASK(OS_CPU_CFG_SYSTICK_PRIO, 24); CPU_REG_NVIC_SHPRI3 = prio; /* Enable timer. */ CPU_REG_NVIC_ST_CTRL |= CPU_REG_NVIC_ST_CTRL_CLKSOURCE | CPU_REG_NVIC_ST_CTRL_ENABLE; /* Enable timer interrupt. */ CPU_REG_NVIC_ST_CTRL |= CPU_REG_NVIC_ST_CTRL_TICKINT; }
2.定时器中断

void OS_CPU_SysTickHandler (void) { CPU_SR_ALLOC(); CPU_CRITICAL_ENTER(); OSIntNestingCtr++; /* Tell uC/OS-III that we are starting an ISR */ CPU_CRITICAL_EXIT(); OSTimeTick(); /* Call uC/OS-III's OSTimeTick() */ OSIntExit(); /* Tell uC/OS-III that we are leaving the ISR */ }
这就是一个滴答定时器的中断函数,函数内主要做了中断嵌套级变量OSIntNestingCtr加1的操作,和调用了OSTimeTick()这个函数

void OSTimeTick (void) { OS_ERR err; #if OS_CFG_ISR_POST_DEFERRED_EN > 0u CPU_TS ts; #endif OSTimeTickHook(); /* Call user definable hook */ #if OS_CFG_ISR_POST_DEFERRED_EN > 0u ts = OS_TS_GET(); /* Get timestamp */ OS_IntQPost((OS_OBJ_TYPE) OS_OBJ_TYPE_TICK, /* Post to ISR queue */ (void *)&OSRdyList[OSPrioCur], (void *) 0, (OS_MSG_SIZE) 0u, (OS_FLAGS ) 0u, (OS_OPT ) 0u, (CPU_TS ) ts, (OS_ERR *)&err); #else (void)OSTaskSemPost((OS_TCB *)&OSTickTaskTCB, /* Signal tick task */ (OS_OPT ) OS_OPT_POST_NONE, (OS_ERR *)&err); #if OS_CFG_SCHED_ROUND_ROBIN_EN > 0u OS_SchedRoundRobin(&OSRdyList[OSPrioCur]); #endif #if OS_CFG_TMR_EN > 0u OSTmrUpdateCtr--; if (OSTmrUpdateCtr == (OS_CTR)0u) { OSTmrUpdateCtr = OSTmrUpdateCnt; OSTaskSemPost((OS_TCB *)&OSTmrTaskTCB, /* Signal timer task */ (OS_OPT ) OS_OPT_POST_NONE, (OS_ERR *)&err); } #endif #endif }
注意 其中的信号发布函数指向了任务控制块OSTickTaskTCB,每次到达滴答定时器中断时都会发布一个信号量给这个任务控制块,从而使这个任务就可以执行下去,任务详情见代码

void OS_TickTask (void *p_arg) { OS_ERR err; CPU_TS ts; p_arg = p_arg; /* Prevent compiler warning */ while (DEF_ON) { (void)OSTaskSemPend((OS_TICK )0, (OS_OPT )OS_OPT_PEND_BLOCKING, (CPU_TS *)&ts, (OS_ERR *)&err); /* Wait for signal from tick interrupt */ if (err == OS_ERR_NONE) { if (OSRunning == OS_STATE_OS_RUNNING) { OS_TickListUpdate(); /* Update all tasks waiting for time */ } } } }
任务中请求信号,如果请求到就执行OS_TickListUpdate(),这个函数主要是用到了哈希算法(求余)来对加入的时间列表的任务检查看是否到时,对于延时到时且符合就绪条件的任务加入就绪任务列表里下次任务调度时就会按照该有的优先级进行任务调度。不满足就绪条件的按情况修改任务状态。

void OS_TickListUpdate (void) { CPU_BOOLEAN done; OS_TICK_SPOKE *p_spoke; OS_TCB *p_tcb; OS_TCB *p_tcb_next; OS_TICK_SPOKE_IX spoke; CPU_TS ts_start; CPU_TS ts_end; CPU_SR_ALLOC(); OS_CRITICAL_ENTER(); ts_start = OS_TS_GET(); OSTickCtr++; /* Keep track of the number of ticks */ spoke = (OS_TICK_SPOKE_IX)(OSTickCtr % OSCfg_TickWheelSize); p_spoke = &OSCfg_TickWheel[spoke]; p_tcb = p_spoke->FirstPtr; done = DEF_FALSE; while (done == DEF_FALSE) { if (p_tcb != (OS_TCB *)0) { p_tcb_next = p_tcb->TickNextPtr; /* Point to next TCB to update */ switch (p_tcb->TaskState) { case OS_TASK_STATE_RDY: case OS_TASK_STATE_PEND: case OS_TASK_STATE_SUSPENDED: case OS_TASK_STATE_PEND_SUSPENDED: break; case OS_TASK_STATE_DLY: p_tcb->TickRemain = p_tcb->TickCtrMatch /* Compute time remaining of current TCB */ - OSTickCtr; if (OSTickCtr == p_tcb->TickCtrMatch) { /* Process each TCB that expires */ p_tcb->TaskState = OS_TASK_STATE_RDY; OS_TaskRdy(p_tcb); /* Make task ready to run */ } else { done = DEF_TRUE; /* Don't find a match, we're done! */ } break; case OS_TASK_STATE_PEND_TIMEOUT: p_tcb->TickRemain = p_tcb->TickCtrMatch /* Compute time remaining of current TCB */ - OSTickCtr; if (OSTickCtr == p_tcb->TickCtrMatch) { /* Process each TCB that expires */ #if (OS_MSG_EN > 0u) p_tcb->MsgPtr = (void *)0; p_tcb->MsgSize = (OS_MSG_SIZE)0u; #endif p_tcb->TS = OS_TS_GET(); OS_PendListRemove(p_tcb); /* Remove from wait list */ OS_TaskRdy(p_tcb); p_tcb->TaskState = OS_TASK_STATE_RDY; p_tcb->PendStatus = OS_STATUS_PEND_TIMEOUT; /* Indicate pend timed out */ p_tcb->PendOn = OS_TASK_PEND_ON_NOTHING; /* Indicate no longer pending */ } else { done = DEF_TRUE; /* Don't find a match, we're done! */ } break; case OS_TASK_STATE_DLY_SUSPENDED: p_tcb->TickRemain = p_tcb->TickCtrMatch /* Compute time remaining of current TCB */ - OSTickCtr; if (OSTickCtr == p_tcb->TickCtrMatch) { /* Process each TCB that expires */ p_tcb->TaskState = OS_TASK_STATE_SUSPENDED; OS_TickListRemove(p_tcb); /* Remove from current wheel spoke */ } else { done = DEF_TRUE; /* Don't find a match, we're done! */ } break; case OS_TASK_STATE_PEND_TIMEOUT_SUSPENDED: p_tcb->TickRemain = p_tcb->TickCtrMatch /* Compute time remaining of current TCB */ - OSTickCtr; if (OSTickCtr == p_tcb->TickCtrMatch) { /* Process each TCB that expires */ #if (OS_MSG_EN > 0u) p_tcb->MsgPtr = (void *)0; p_tcb->MsgSize = (OS_MSG_SIZE)0u; #endif p_tcb->TS = OS_TS_GET(); OS_PendListRemove(p_tcb); /* Remove from wait list */ OS_TickListRemove(p_tcb); /* Remove from current wheel spoke */ p_tcb->TaskState = OS_TASK_STATE_SUSPENDED; p_tcb->PendStatus = OS_STATUS_PEND_TIMEOUT; /* Indicate pend timed out */ p_tcb->PendOn = OS_TASK_PEND_ON_NOTHING; /* Indicate no longer pending */ } else { done = DEF_TRUE; /* Don't find a match, we're done! */ } break; default: break; } p_tcb = p_tcb_next; } else { done = DEF_TRUE; } } ts_end = OS_TS_GET() - ts_start; /* Measure execution time of tick task */ if (OSTickTaskTimeMax < ts_end) { OSTickTaskTimeMax = ts_end; } OS_CRITICAL_EXIT(); }
整个时钟节拍相关的大致流程如下: