UCOS----时钟节拍源码分析

2018-08-21  本文已影响147人  守拙圆

时钟节拍类似于人体心脏的跳动,人体依赖心脏的跳动将血液输入身体各个部位,支撑生命活动。时钟节拍的是操作系统的时基,操作系统依赖于时钟节拍推动 CPU 去执行指令。

1 时钟节拍原理

时钟节拍是系统以固定的频率产生中断(时基中断),并在中断处理与时间相关的事件,推动所有任务向前运行。时钟节拍需要依赖于硬件定时器,STM32 通常使用 systick 时钟作为 MCU 的内核定时器。

2 系统时钟初始化

初始化流程
void  BSP_Tick_Init (void)
{
    CPU_INT32U  cpu_clk_freq;
    CPU_INT32U  cnts;
    
    //获取CPU内核时钟频率(SysTick 工作时钟)
    cpu_clk_freq = BSP_CPU_ClkFreq();  

    //根据用户设置的时钟节拍频率计算 SysTick 定时器的计数值
#if (OS_VERSION >= 30000u)
     /* Determine nbr SysTick increments                     */
    cnts = (cpu_clk_freq / (CPU_INT32U)OSCfg_TickRate_Hz);      
#else
      /* Determine nbr SysTick increments.                    */
    cnts = (cpu_clk_freq / (CPU_INT32U)OS_TICKS_PER_SEC);       
#endif

    //调用 SysTick 初始化函数,设定定时器的计数值,并启动定时器
    /* Init uC/OS periodic time src (SysTick).   */
    OS_CPU_SysTickInit(cnts); 
}

3 系统时钟中断管理

根据系统时钟的初始化,在系统计数达到后,产生时钟中断,并调用中断处理函数 OS_CPU_SysTickHandler

/*
*********************************************************************************************************
*                                          SYS TICK HANDLER
*
* Description: Handle the system tick (SysTick) interrupt, which is used to generate the uC/OS-II tick
*              interrupt.
*
* Arguments  : None.
*
* Note(s)    : 1) This function MUST be placed on entry 15 of the Cortex-M3 vector table.
*********************************************************************************************************
*/

void  OS_CPU_SysTickHandler (void)
{
    CPU_SR_ALLOC(); //分配保存中断状态的局部变量,后面关中断的时候可以保存中断状态
     //ISR表示 interrupt service routine
    //CPU_CRITICAL_ENTER 和 CPU_CRITICAL_EXIT 之间形成临界区,避免期间程序运行时受到干扰
    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             */
}

OS_CPU_SysTickHandler 函数中调用了 UCOS 的时间片处理函数 OSTimeTick,对系统的时间片进行处理。

/*
************************************************************************************************************************
*                                                 PROCESS SYSTEM TICK
*
* Description: This function is used to signal to uC/OS-III the occurrence of a 'system tick' (also known as a
*              'clock tick').  This function should be called by the tick ISR.
*
* Arguments  : none
*
* Returns    : none
************************************************************************************************************************
*/

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_TickTask, OS_TickTask 接收到该信号量就会继续执行
    //中断发送延迟可以减少中断时间,将中断事件转化为任务级,可以提高操作系统的实时性
    
    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
   //如果禁止了中断发送延迟,直接发送信号量给时钟节拍任务 OS_TickTask
   (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

    //如果使能了软件定时器,软件定时器自减
    //如果软件定时器减至 0,重载软件定时器计数器
    //发送信号量给软件定时器任务
#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
}

4 时基任务

OSTimeTick 函数中给时基任务、定时器任务都发送了信号量。这里先介绍时基任务。时基任务是在 OS 初始化函数 void OSInit (OS_ERR *p_err) 中创建。

/*
************************************************************************************************************************
*                                                 INITIALIZE TICK TASK
*
* Description: This function is called by OSInit() to create the tick task.
*
* Arguments  : p_err   is a pointer to a variable that will hold the value of an error code:
*
*                          OS_ERR_TICK_STK_INVALID   if the pointer to the tick task stack is a NULL pointer
*                          OS_ERR_TICK_STK_SIZE      indicates that the specified stack size
*                          OS_ERR_PRIO_INVALID       if the priority you specified in the configuration is invalid
*                                                      (There could be only one task at the Idle Task priority)
*                                                      (Maybe the priority you specified is higher than OS_CFG_PRIO_MAX-1
*                          OS_ERR_??                 other error code returned by OSTaskCreate()
*
* Returns    : none
*
* Note(s)    : This function is INTERNAL to uC/OS-III and your application should not call it.
************************************************************************************************************************
*/

void  OS_TickTaskInit (OS_ERR  *p_err)
{
#ifdef OS_SAFETY_CRITICAL
    if (p_err == (OS_ERR *)0) {
        OS_SAFETY_CRITICAL_EXCEPTION();
        return;
    }
#endif
    /* Clear the tick counter */
    //清除系统计数值

    OSTickCtr                    = (OS_TICK)0u;                       
    OSTickListDly.TCB_Ptr        = (OS_TCB   *)0;
    OSTickListTimeout.TCB_Ptr    = (OS_TCB   *)0;

#if OS_CFG_DBG_EN > 0u
    OSTickListDly.NbrEntries     = (OS_OBJ_QTY)0;
    OSTickListDly.NbrUpdated     = (OS_OBJ_QTY)0;

    OSTickListTimeout.NbrEntries = (OS_OBJ_QTY)0;
    OSTickListTimeout.NbrUpdated = (OS_OBJ_QTY)0;
#endif

     /* ---------------- CREATE THE TICK TASK ----------- */                                                                   
    if (OSCfg_TickTaskStkBasePtr == (CPU_STK *)0) {
       *p_err = OS_ERR_TICK_STK_INVALID;
        return;
    }

    if (OSCfg_TickTaskStkSize < OSCfg_StkSizeMin) {
       *p_err = OS_ERR_TICK_STK_SIZE_INVALID;
        return;
    }

      /* Only one task at the 'Idle Task' priority         */
    if (OSCfg_TickTaskPrio >= (OS_CFG_PRIO_MAX - 1u)) {               
       *p_err = OS_ERR_TICK_PRIO_INVALID;
        return;
    }

    OSTaskCreate((OS_TCB     *)&OSTickTaskTCB,
                 (CPU_CHAR   *)((void *)"uC/OS-III Tick Task"),
                 (OS_TASK_PTR )OS_TickTask,
                 (void       *)0,
                 (OS_PRIO     )OSCfg_TickTaskPrio,
                 (CPU_STK    *)OSCfg_TickTaskStkBasePtr,
                 (CPU_STK_SIZE)OSCfg_TickTaskStkLimit,
                 (CPU_STK_SIZE)OSCfg_TickTaskStkSize,
                 (OS_MSG_QTY  )0u,
                 (OS_TICK     )0u,
                 (void       *)0,
                 (OS_OPT      )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR | OS_OPT_TASK_NO_TLS),
                 (OS_ERR     *)p_err);
}
/*
************************************************************************************************************************
*                                                      TICK TASK
*
* Description: This task is internal to uC/OS-III and is triggered by the tick interrupt.
*
* Arguments  : p_arg     is an argument passed to the task when the task is created (unused).
*
* Returns    : none
*
* Note(s)    : This function is INTERNAL to uC/OS-III and your application should not call it.
************************************************************************************************************************
*/

void  OS_TickTask (void  *p_arg)
{
    OS_ERR  err;
    CPU_TS  ts_delta;
    CPU_TS  ts_delta_dly;
    CPU_TS  ts_delta_timeout;
    CPU_SR_ALLOC();

    
    /* Prevent compiler warning */
    /* Wait for signal from tick interrupt  */
    (void)&p_arg;                                               
    while (DEF_ON) {
        //等待信号量
        (void)OSTaskSemPend((OS_TICK  )0,
                            (OS_OPT   )OS_OPT_PEND_BLOCKING,
                            (CPU_TS  *)0,
                            (OS_ERR  *)&err);                   
        if (err == OS_ERR_NONE) {
            /* Keep track of the number of ticks     */
            OS_CRITICAL_ENTER();
            OSTickCtr++;                                        
#if (defined(TRACE_CFG_EN) && (TRACE_CFG_EN > 0u))
            /* Record the event.   */
            TRACE_OS_TICK_INCREMENT(OSTickCtr);                 
#endif
            OS_CRITICAL_EXIT();
            //遍历更新时延任务列表
            ts_delta_dly     = OS_TickListUpdateDly();
            //遍历更新超时任务列表
            ts_delta_timeout = OS_TickListUpdateTimeout();
            /* Compute total execution time of list updates         */
            ts_delta         = ts_delta_dly + ts_delta_timeout; 
            if (OSTickTaskTimeMax < ts_delta) {
                OSTickTaskTimeMax = ts_delta;
            }
        }
    }
}

5 总结

本章阐述了时钟节拍的工作原理,看似微小却是整个 uC/OS 系统的命脉。时钟节拍的运行依赖于 CPU 的定时器, STM32 专门为此量身定制了 SysTick 时钟。每个时钟节拍到来时,时基任务就会执行,节拍任务的重点是更新节拍任务列表。在节拍列表中,存放的均是与时间事件(如延时或超时)相关的任务。如果任务到期,则需要更新响应的任务状态。

上一篇下一篇

猜你喜欢

热点阅读