RTOS学习(4)--启动过程分析

比较常见的启动流程有两种,一种是在main函数中完成硬件和RTOS系统的初始化,并且创建所需的任务,最后只需要开启调度器即可。还有一种是在main函数中将硬件和RTOS初始化,然后创建一个启动任务,在启动任务中完成其余任务的创建。

很明显UCOS属于后者,下面我们来分析一下UCOS是如何启动的。

主函数

/**
 * @brief  The application entry point.
 * @retval int
 */
int main(void)
{

  OS_ERR err;

  OSInit(&err); /* Init uC/OS-III.                                      */

  OSTaskCreate((OS_TCB *)&AppTaskStartTCB, /* Create the start task                                */
               (CPU_CHAR *)"App Task Start",
               (OS_TASK_PTR)AppTaskStart,
               (void *)0,
               (OS_PRIO)APP_TASK_START_PRIO,
               (CPU_STK *)&AppTaskStartStk[0],
               (CPU_STK_SIZE)APP_TASK_START_STK_SIZE / 10,
               (CPU_STK_SIZE)APP_TASK_START_STK_SIZE,
               (OS_MSG_QTY)5u,
               (OS_TICK)0u,
               (void *)0,
               (OS_OPT)(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR),
               (OS_ERR *)&err);

  OSStart(&err);
}

简单说一下,可以看到首先创建一个变量,类型是OS_ERR,如果找到定义的话,可以发现,OS_ERR是一个枚举值,里面包括各种错误的类型可以方便检查错误。

紧接着就是OSIinit()函数,用于初始化uC/OS-III系统,这里面初始化了空闲任务和时钟节拍任务,同时给一些变量赋初值。下面是使用OSTaskCreate函数

创建启动任务,然后调用OSStart启动调度器。

void AppTaskStart(void *p_arg)
{
  CPU_INT32U cpu_clk_freq;
  CPU_INT32U cnts;
  OS_ERR err;

  (void)p_arg;

  CPU_Init();
  BSP_Init(); /* Initialize BSP functions                             */

  cpu_clk_freq = BSP_CPU_ClkFreq();                    /* Determine SysTick reference freq.                    */
  cnts = cpu_clk_freq / (CPU_INT32U)OSCfg_TickRate_Hz; /* Determine nbr SysTick increments                     */
  OS_CPU_SysTickInit(cnts);                            /* Init uC/OS periodic time src (SysTick).              */

  Mem_Init(); /* Initialize Memory Management Module                  */

#if OS_CFG_STAT_TASK_EN > 0u
  OSStatTaskCPUUsageInit(&err); /* Compute CPU capacity with no task running            */
#endif

#ifdef CPU_CFG_INT_DIS_MEAS_EN
  CPU_IntDisMeasMaxCurReset();
#endif

  while (DEF_TRUE)
  { /* Task body, always written as an infinite loop.       */
    HAL_GPIO_TogglePin(GPIOF, GPIO_PIN_9);
    OSTimeDly(200, OS_OPT_TIME_DLY, &err);
  }
}

CPU_Init()是对CPU的初始化,里面包含一些时间戳的初始化、最大关中断时间的测量初始化、CPU名字的初始化。

BSP_Init()是板级初始化,对硬件外设的初始化

紧接着下面三段是对滴答定时器的初始化,通常是每1ms中断一次,为操作系统提供时基。可以修改

截至到上面这些,操作系统启动就没有问题了。

OSInit函数

void  OSInit (OS_ERR  *p_err)
{
    CPU_STK      *p_stk;
    CPU_STK_SIZE  size;



#ifdef OS_SAFETY_CRITICAL
    if (p_err == (OS_ERR *)0) {
        OS_SAFETY_CRITICAL_EXCEPTION();
        return;
    }
#endif

    OSInitHook();                                           /* Call port specific initialization code                 */

    OSIntNestingCtr                 = (OS_NESTING_CTR)0;    /* Clear the interrupt nesting counter                    */

    OSRunning                       =  OS_STATE_OS_STOPPED; /* Indicate that multitasking not started                 */

    OSSchedLockNestingCtr           = (OS_NESTING_CTR)0;    /* Clear the scheduling lock counter                      */

    OSTCBCurPtr                     = (OS_TCB *)0;          /* Initialize OS_TCB pointers to a known state            */
    OSTCBHighRdyPtr                 = (OS_TCB *)0;

    OSPrioCur                       = (OS_PRIO)0;           /* Initialize priority variables to a known state         */
    OSPrioHighRdy                   = (OS_PRIO)0;
    OSPrioSaved                     = (OS_PRIO)0;

#if OS_CFG_SCHED_LOCK_TIME_MEAS_EN > 0u
    OSSchedLockTimeBegin            = (CPU_TS)0;
    OSSchedLockTimeMax              = (CPU_TS)0;
    OSSchedLockTimeMaxCur           = (CPU_TS)0;
#endif

#ifdef OS_SAFETY_CRITICAL_IEC61508
    OSSafetyCriticalStartFlag       =  DEF_FALSE;
#endif

#if OS_CFG_SCHED_ROUND_ROBIN_EN > 0u
    OSSchedRoundRobinEn             = DEF_FALSE;
    OSSchedRoundRobinDfltTimeQuanta = OSCfg_TickRate_Hz / 10u;
#endif

    if (OSCfg_ISRStkSize > (CPU_STK_SIZE)0) {
        p_stk = OSCfg_ISRStkBasePtr;                        /* Clear exception stack for stack checking.              */
        if (p_stk != (CPU_STK *)0) {
            size  = OSCfg_ISRStkSize;
            while (size > (CPU_STK_SIZE)0) {
                size--;
               *p_stk = (CPU_STK)0;
                p_stk++;
            }
        }
    }

#if OS_CFG_APP_HOOKS_EN > 0u
    OS_AppTaskCreateHookPtr = (OS_APP_HOOK_TCB )0;          /* Clear application hook pointers                        */
    OS_AppTaskDelHookPtr    = (OS_APP_HOOK_TCB )0;
    OS_AppTaskReturnHookPtr = (OS_APP_HOOK_TCB )0;

    OS_AppIdleTaskHookPtr   = (OS_APP_HOOK_VOID)0;
    OS_AppStatTaskHookPtr   = (OS_APP_HOOK_VOID)0;
    OS_AppTaskSwHookPtr     = (OS_APP_HOOK_VOID)0;
    OS_AppTimeTickHookPtr   = (OS_APP_HOOK_VOID)0;
#endif

#if OS_CFG_TASK_REG_TBL_SIZE > 0u
    OSTaskRegNextAvailID    = (OS_REG_ID)0;
#endif

    OS_PrioInit();                                          /* Initialize the priority bitmap table                   */

    OS_RdyListInit();                                       /* Initialize the Ready List                              */

    
#if OS_CFG_FLAG_EN > 0u                                     /* Initialize the Event Flag module                       */
    OS_FlagInit(p_err);
    if (*p_err != OS_ERR_NONE) {
        return;
    }
#endif


#if OS_CFG_MEM_EN > 0u                                      /* Initialize the Memory Manager module                   */
    OS_MemInit(p_err);
    if (*p_err != OS_ERR_NONE) {
        return;
    }
#endif


#if (OS_MSG_EN) > 0u                                        /* Initialize the free list of OS_MSGs                    */
    OS_MsgPoolInit(p_err);
    if (*p_err != OS_ERR_NONE) {
        return;
    }
#endif


#if OS_CFG_MUTEX_EN > 0u                                    /* Initialize the Mutex Manager module                    */
    OS_MutexInit(p_err);
    if (*p_err != OS_ERR_NONE) {
        return;
    }
#endif


#if OS_CFG_Q_EN > 0u
    OS_QInit(p_err);                                        /* Initialize the Message Queue Manager module            */
    if (*p_err != OS_ERR_NONE) {
        return;
    }
#endif


#if OS_CFG_SEM_EN > 0u                                      /* Initialize the Semaphore Manager module                */
    OS_SemInit(p_err);
    if (*p_err != OS_ERR_NONE) {
        return;
    }
#endif


#if defined(OS_CFG_TLS_TBL_SIZE) && (OS_CFG_TLS_TBL_SIZE > 0u)
    OS_TLS_Init(p_err);                                     /* Initialize Task Local Storage, before creating tasks   */
    if (*p_err != OS_ERR_NONE) {
        return;
    }
#endif


    OS_TaskInit(p_err);                                     /* Initialize the task manager                            */
    if (*p_err != OS_ERR_NONE) {
        return;
    }


#if OS_CFG_ISR_POST_DEFERRED_EN > 0u
    OS_IntQTaskInit(p_err);                                 /* Initialize the Interrupt Queue Handler Task            */
    if (*p_err != OS_ERR_NONE) {
        return;
    }
#endif

    
    OS_IdleTaskInit(p_err);                                 /* Initialize the Idle Task                               */
    if (*p_err != OS_ERR_NONE) {
        return;
    }


    OS_TickTaskInit(p_err);                                 /* Initialize the Tick Task                               */
    if (*p_err != OS_ERR_NONE) {
        return;
    }


#if OS_CFG_STAT_TASK_EN > 0u                                /* Initialize the Statistic Task                          */
    OS_StatTaskInit(p_err);
    if (*p_err != OS_ERR_NONE) {
        return;
    }
#endif


#if OS_CFG_TMR_EN > 0u                                      /* Initialize the Timer Manager module                    */
    OS_TmrInit(p_err);
    if (*p_err != OS_ERR_NONE) {
        return;
    }
#endif


#if OS_CFG_DBG_EN > 0u
    OS_Dbg_Init();
#endif


    OSCfg_Init();
}

很多初始化面都有这一段话,应该是调试作用,个人猜测。

这里是对钩子函数的初始化,还有一些变量赋初值。

OSIntNestingCtr代表中断嵌套数

OSRunning代表OS的运行状态

OSSchedLockNestingCtr表示调度器上锁计数

OSTCBCurPtr指向当前运行的任务控制块

OSTCBHighRdyPtr指向优先级最高的就绪任务的任务控制块

OSPrioCur当前运行任务的优先级

OSPrioHighRdy表示就绪任务的最高优先级

OSPrioSaved用于保存任务的优先级

这一段话用于清除µC/OS-III’sinterrupt stack。

这一段话用于清除钩子函数指针,默认关闭所有动作的钩子函数。

OS_CFG_TASK_REG_TBL_SIZE用于定义任务寄存器的数组的大小,UCOS分配给任务一个寄存器,用于存储数据,根本上是一个无符号32位整型数组。

OSTaskRegNextAvailID用于存储下一个可用的任务寄存器号,每获取一次,加一。

OS_PrioInit()初始化优先级列表,这里要注意,每一位代表一个优先级,64个优先级数组大小只需要为2。

OS_RdyListInit()初始化就绪列表

其余都是初始化各部分功能,相似程度非常高,大多都是将数量归零。但是好包括空闲任务和时钟节拍任务,这两个比较特殊,是系统自带的两个任务。

空闲任务创建

/*$PAGE*/
/*
************************************************************************************************************************
*                                               INITIALIZE THE IDLE TASK
*
* Description: This function initializes the idle task
*
* Arguments  : p_err    is a pointer to a variable that will contain an error code returned by this function.
*
* Returns    : none
*
* Note(s)    : 1) This function is INTERNAL to uC/OS-III and your application MUST NOT call it.
************************************************************************************************************************
*/

void  OS_IdleTaskInit (OS_ERR  *p_err)
{
#ifdef OS_SAFETY_CRITICAL
    if (p_err == (OS_ERR *)0) {
        OS_SAFETY_CRITICAL_EXCEPTION();
        return;
    }
#endif

    OSIdleTaskCtr = (OS_IDLE_CTR)0;
/* ---------------- CREATE THE IDLE TASK ---------------- */
    OSTaskCreate((OS_TCB     *)&OSIdleTaskTCB,
                 (CPU_CHAR   *)((void *)"uC/OS-III Idle Task"),
                 (OS_TASK_PTR)OS_IdleTask,
                 (void       *)0,
                 (OS_PRIO     )(OS_CFG_PRIO_MAX - 1u),
                 (CPU_STK    *)OSCfg_IdleTaskStkBasePtr,
                 (CPU_STK_SIZE)OSCfg_IdleTaskStkLimit,
                 (CPU_STK_SIZE)OSCfg_IdleTaskStkSize,
                 (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);
}

初始化空闲任务,空闲任务的作用比较大,CPU在某一时刻没有任务去执行时,便会执行空闲任务,实际上就是去累加一个变量,所以说任务优先级最低。首先对累加变量进行清零,接着创建任务。

时钟节拍任务

void  OS_TickTaskInit (OS_ERR  *p_err)
{
#ifdef OS_SAFETY_CRITICAL
    if (p_err == (OS_ERR *)0) {
        OS_SAFETY_CRITICAL_EXCEPTION();
        return;
    }
#endif

    OSTickCtr                    = (OS_TICK)0u;                         /* Clear the tick counter                            */

    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;
    }

    if (OSCfg_TickTaskPrio >= (OS_CFG_PRIO_MAX - 1u)) {                 /* Only one task at the 'Idle Task' priority         */
       *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);
}

时钟节拍任务相对来说比较复杂,

OSTickCtr表示累加变量,初始化时清零。

这两句话和延时有关,实际上UCOS上面的每个延时任务都会添加到这个列表里面,超时的也有一个列表。在每个时钟周期,都会维护这两个列表。

TCB_Ptr是一个指针,指向列表。

这一段用于记录列表中任务的数量,DEBUG时才会用到。

这段话很简单。就是创建任务。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

苦瓜人生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值