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

时钟节拍类似于人体心脏的跳动,人体依赖心脏的跳动将血液输入身体各个部位,支撑生命活动。时钟节拍的是操作系统的时基,操作系统依赖于时钟节拍推动 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 时钟。每个时钟节拍到来时,时基任务就会执行,节拍任务的重点是更新节拍任务列表。在节拍列表中,存放的均是与时间事件(如延时或超时)相关的任务。如果任务到期,则需要更新响应的任务状态。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,233评论 6 495
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,357评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,831评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,313评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,417评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,470评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,482评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,265评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,708评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,997评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,176评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,827评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,503评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,150评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,391评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,034评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,063评论 2 352