美文网首页
UCOS----时钟节拍源码分析

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

作者: 守拙圆 | 来源:发表于2018-08-21 11: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 时钟。每个时钟节拍到来时,时基任务就会执行,节拍任务的重点是更新节拍任务列表。在节拍列表中,存放的均是与时间事件(如延时或超时)相关的任务。如果任务到期,则需要更新响应的任务状态。

    相关文章

      网友评论

          本文标题:UCOS----时钟节拍源码分析

          本文链接:https://www.haomeiwen.com/subject/dayzbftx.html