zoukankan      html  css  js  c++  java
  • 手把手,嘴对嘴,讲解UCOSII嵌入式操作系统的任务(一)

      做过软件的同学都知道,任何一个程序都必须要有初始化的过程,在初始化过程中,我们会对外围硬件以及CPU的奔跑环境进行初步的设置,以便接下来的使用和调度。

      以前在写单片机逻辑程序之时,系统初始化过程大概分为两种:

      ①外围硬件的初始化(比如MCU寄存器,时钟,看门狗,串口,IO口,SPI等等)

      ②代码内参数的初始化(比如堆栈,变量,结构体等等)

      UCOSII操作系统想要跑起来,当然也需要一系列的初始化,比如中断模式、延时模块、外围硬件等等,但本文不讲硬件相关,只对操作系统本身的初始化进行一些讲解。

      首先请看一段熟悉的代码:

     1 #include "sys.h"
     2 #include "delay.h"
     3 #include "led.h"
     4 #include "includes.h"
     5 
     6 
     7 /////////////////////////UCOSII任务设置///////////////////////////////////
     8 //START 任务
     9 //设置任务优先级
    10 #define START_TASK_PRIO                 10 //开始任务的优先级设置为最低
    11 //设置任务堆栈大小
    12 #define START_STK_SIZE                  64
    13 //任务堆栈
    14 OS_STK START_TASK_STK[START_STK_SIZE];
    15 //任务函数
    16 void start_task(void *pdata);
    17 
    18 //LED0任务
    19 //设置任务优先级
    20 #define LED0_TASK_PRIO                  7
    21 //设置任务堆栈大小
    22 #define LED0_STK_SIZE                   64
    23 //任务堆栈
    24 OS_STK LED0_TASK_STK[LED0_STK_SIZE];
    25 //任务函数
    26 void led0_task(void *pdata);
    27 
    28 
    29 //LED1任务
    30 //设置任务优先级
    31 #define LED1_TASK_PRIO                  6
    32 //设置任务堆栈大小
    33 #define LED1_STK_SIZE                   64
    34 //任务堆栈
    35 OS_STK LED1_TASK_STK[LED1_STK_SIZE];
    36 //任务函数
    37 void led1_task(void *pdata);
    38 
    39 int main(void)
    40 {
    41     NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组为组2:2位抢占优先级,2位响应优先级
    42     delay_init();       //延时函数初始化
    43     LED_Init();         //初始化与LED连接的硬件接口
    44     OSInit();
    45     OSTaskCreate(start_task,(void *)0,(OS_STK *)&START_TASK_STK[START_STK_SIZE-1],START_TASK_PRIO );//创建起始任务
    46     OSStart();
    47 }
    48 
    49 //开始任务
    50 void start_task(void *pdata)
    51 {
    52     OS_CPU_SR cpu_sr=0;
    53     pdata = pdata;
    54     OS_ENTER_CRITICAL();            //进入临界区(无法被中断打断)
    55     OSTaskCreate(led0_task,(void *)0,(OS_STK*)&LED0_TASK_STK[LED0_STK_SIZE-1],LED0_TASK_PRIO);
    56     OSTaskCreate(led1_task,(void *)0,(OS_STK*)&LED1_TASK_STK[LED1_STK_SIZE-1],LED1_TASK_PRIO);
    57     OSTaskSuspend(START_TASK_PRIO); //挂起起始任务.
    58     OS_EXIT_CRITICAL();             //退出临界区(可以被中断打断)
    59 }
    60 
    61 //LED0任务
    62 void led0_task(void *pdata)
    63 {
    64     while(1)
    65     {
    66         LED0=0;
    67         delay_ms(80);
    68         LED0=1;
    69         delay_ms(920);
    70     };
    71 }
    72 
    73 //LED1任务
    74 void led1_task(void *pdata)
    75 {
    76     while(1)
    77     {
    78         LED1=0;
    79         delay_ms(300);
    80         LED1=1;
    81         delay_ms(300);
    82     };
    83 }

      以上的代码大家都不陌生,这几乎便是UCOSII系统初始化的标准格式,首先是定义任务的基本信息,优先级,堆栈大小,堆栈空间,任务函数等等。

      然后由main函数开始执行具体的初始化过程,分别是中断模式设定,延时功能设定,以及外围硬件设定,等这些东西都设定完成以后,便进入了操作系统的设定,最后起始任务执行完毕,程序会跳进应用任务中执行。

      而main函数中的OSInit()这个函数便是我们本次讲解的重点。

      UCOSII操作系统在初始化的过程中,到底做了一些什么?或者说函数OSInit()中到底有些什么处理?

      废话不多说,直接进入这个函数的定义:

     1 void  OSInit (void)
     2 {
     3     OSInitHookBegin();                                           /* Call port specific initialization code   */
     4 
     5     OS_InitMisc();                                               /* Initialize miscellaneous variables       */
     6 
     7     OS_InitRdyList();                                            /* Initialize the Ready List                */
     8 
     9     OS_InitTCBList();                                            /* Initialize the free list of OS_TCBs      */
    10 
    11     OS_InitEventList();                                          /* Initialize the free list of OS_EVENTs    */
    12 
    13 #if (OS_FLAG_EN > 0u) && (OS_MAX_FLAGS > 0u)
    14     OS_FlagInit();                                               /* Initialize the event flag structures     */
    15 #endif
    16 
    17 #if (OS_MEM_EN > 0u) && (OS_MAX_MEM_PART > 0u)
    18     OS_MemInit();                                                /* Initialize the memory manager            */
    19 #endif
    20 
    21 #if (OS_Q_EN > 0u) && (OS_MAX_QS > 0u)
    22     OS_QInit();                                                  /* Initialize the message queue structures  */
    23 #endif
    24 
    25     OS_InitTaskIdle();                                           /* Create the Idle Task                     */
    26 #if OS_TASK_STAT_EN > 0u
    27     OS_InitTaskStat();                                           /* Create the Statistic Task                */
    28 #endif
    29 
    30 #if OS_TMR_EN > 0u
    31     OSTmr_Init();                                                /* Initialize the Timer Manager             */
    32 #endif
    33 
    34     OSInitHookEnd();                                             /* Call port specific init. code            */
    35 
    36 #if OS_DEBUG_EN > 0u
    37     OSDebugInit();
    38 #endif
    39 }

      以上便是系统初始化函数中的处理,看得出来,它只是一个接口,一个容器,真正的处理还在它内部调用的那些函数里,接下来我们开始一句一句的理解。(简单的函数用黑色,复杂的函数用红色)

        一:函数OSInitHookBegin(); 

      其定义如下:

    1 #if OS_CPU_HOOKS_EN > 0 && OS_VERSION > 203
    2 void  OSInitHookBegin (void)
    3 {
    4 #if OS_TMR_EN > 0
    5     OSTmrCtr = 0;
    6 #endif
    7 }
    8 #endif

         这个函数俗称钩子函数,它里面本身不带任何处理,是专门留给用户扩展的,当用户需要在初始化里执行某些处理的时候,可以在这里把自己的代码添加进去。

         比如我想在初始化时点亮一个LED小灯或者让喇叭叫起来,那么就可以在里面写个点灯的代码(注意这个函数不能被意外打断)。

         我们可以看到在它的头上有两个宏开关,只有都满足才会执行,第一个开关OS_CPU_HOOKS_EN是使能位,如果我们不需要在这里执行任何处理,可以直接把OS_CPU_HOOKS_EN宏定位为0(不建议定义为0,因为编译会出问题,需要修改的地方不少)。

    1 #define OS_CPU_HOOKS_EN              0u                /* uC/OS-II hooks are found in the processor port files         */

      第二个宏开关是系统版本,只有系统在203版本以上才能用这个功能。

    1 #define  OS_VERSION                 291u                /* Version of uC/OS-II (Vx.yy mult. by 100)    */

    我现在的版本是291,所以当然没问题啦!

      二:函数OS_InitMisc()  

      其定义如下:

     1 static  void  OS_InitMisc (void)
     2 {
     3 #if OS_TIME_GET_SET_EN > 0u
     4     OSTime                    = 0uL;                       /* Clear the 32-bit system clock            */
     5 #endif
     6 
     7     OSIntNesting              = 0u;                        /* Clear the interrupt nesting counter      */
     8     OSLockNesting             = 0u;                        /* Clear the scheduling lock counter        */
     9 
    10     OSTaskCtr                 = 0u;                        /* Clear the number of tasks                */
    11 
    12     OSRunning                 = OS_FALSE;                  /* Indicate that multitasking not started   */
    13 
    14     OSCtxSwCtr                = 0u;                        /* Clear the context switch counter         */
    15     OSIdleCtr                 = 0uL;                       /* Clear the 32-bit idle counter            */
    16 
    17 #if OS_TASK_STAT_EN > 0u
    18     OSIdleCtrRun              = 0uL;
    19     OSIdleCtrMax              = 0uL;
    20     OSStatRdy                 = OS_FALSE;                  /* Statistic task is not ready              */
    21 #endif
    22 
    23 #ifdef OS_SAFETY_CRITICAL_IEC61508
    24     OSSafetyCriticalStartFlag = OS_FALSE;                  /* Still allow creation of objects          */
    25 #endif
    26 }

      这个函数的作用,是对系统中的某些全局变量进行初始化:

      OSTime是系统中滴答时钟的存储变量,如果想要使用这个变量的话,那么宏开关OS_TIME_GET_SET_EN必须要设置为1。

      ※这个变量还是挺有用的,比如当我需要判断系统时间是否经过了50ms,那么就可以调用OSTimeGet()函数读一下这个变量,然后过一会儿再读一下,只要两次读数相差达到50,那就是经过了50ms,这样就不用专门再开一个定时器了。

      OSIntNesting是中断嵌套计数变量,进中断时加1,出中断时减1,当变量为0的时候表示没有进入过中断,当等于1的时候表示进入了1重中断,当等于2的时候表示进入了2重中断……以此类推。

      OSLockNesting是调度器上锁次数变量,当调用函数OSSchedLock()便会对这个变量进行加1处理,当调用函数OSSchedUnlock()便会对它减1处理,只有在变量OSLockNesting等于0时,系统才会进行任务调度(当执行某些不能被别的任务打断的处理时,可以用这个功能)。

      OSTaskCtr是记录系统中一共有多少个任务,比如我上面的那段代码自己创建了3个任务,那等任务建立完毕以后,这个变量至少是大于3了,由于UCOSII系统还保留了一些系统任务(空闲任务,统计任务等),所以这个变量肯定比3大。

           OSRunning是记录操作系统当前的状态,操作系统跑起来是TRUE,没跑起来是FALSE,现在还在初始化,肯定是FALSE。

      OSCtxSwCtr是记录任务切换的次数,当从优先级0的任务切换到优先级1的任务之时,它就会加1,在切换回来,它又会加1,等加到极限以后,重新变成0。

      OSIdleCtr是记录空闲任务执行的次数,每执行一次空闲任务,它就加1,这个变量可以配合统计任务来计算CPU的使用率。

    1 #if OS_TASK_STAT_EN > 0u
    2     OSIdleCtrRun              = 0uL;
    3     OSIdleCtrMax              = 0uL;
    4     OSStatRdy                 = OS_FALSE;                  /* Statistic task is not ready              */
    5 #endif

      这三句代码有宏开关,和统计任务相关,专门用来计算CPU的使用率,如果不需要这个数据,直接把宏开关设0便可。

    1 #ifdef OS_SAFETY_CRITICAL_IEC61508
    2     OSSafetyCriticalStartFlag = OS_FALSE;                  /* Still allow creation of objects          */
    3 #endif

      这段代码我没有找到是用来做什么的,如果有哪位同学了解,还希望不吝赐教。

      三:函数OS_InitRdyList()

      其定义如下:

     1 static  void  OS_InitRdyList (void)
     2 {
     3     INT8U  i;
     4 
     5 
     6     OSRdyGrp      = 0u;                                    /* Clear the ready list                     */
     7     for (i = 0u; i < OS_RDY_TBL_SIZE; i++) {
     8         OSRdyTbl[i] = 0u;
     9     }
    10 
    11     OSPrioCur     = 0u;
    12     OSPrioHighRdy = 0u;
    13 
    14     OSTCBHighRdy  = (OS_TCB *)0;
    15     OSTCBCur      = (OS_TCB *)0;
    16 }

      该函数的作用是用来初始化任务、以及任务优先级相关的变量。

      OSRdyGrp 是记录当前所有任务组的就绪状态,是任务调度算法的重要变量。

      OSRdyTbl[]是记录当前所有任务的就绪状态,是任务调度算法的重要变量。

      OSPrioCur是记录当前正在执行的任务。

      OSPrioHighRdy是记录当前就绪任务中,优先级最高的那个任务的优先级。

      OSTCBHighRdy这是一个结构体,里面记录优先级最高的那个任务的信息,初始化时没有任务执行,其值为0。

      OSTCBCur也是一个结构体,里面记录当前正在执行的那个任务的信息,初始化时没有任务执行,其值为0。

      

      四:函数OS_InitTCBList()

      定义如下:

     1 static  void  OS_InitTCBList (void)
     2 {
     3     INT8U    ix;
     4     INT8U    ix_next;
     5     OS_TCB  *ptcb1;
     6     OS_TCB  *ptcb2;
     7 
     8 
     9     OS_MemClr((INT8U *)&OSTCBTbl[0],     sizeof(OSTCBTbl));      /* Clear all the TCBs                 */
    10     OS_MemClr((INT8U *)&OSTCBPrioTbl[0], sizeof(OSTCBPrioTbl));  /* Clear the priority table           */
    11     for (ix = 0u; ix < (OS_MAX_TASKS + OS_N_SYS_TASKS - 1u); ix++) {    /* Init. list of free TCBs     */
    12         ix_next =  ix + 1u;
    13         ptcb1   = &OSTCBTbl[ix];
    14         ptcb2   = &OSTCBTbl[ix_next];
    15         ptcb1->OSTCBNext = ptcb2;
    16 #if OS_TASK_NAME_EN > 0u
    17         ptcb1->OSTCBTaskName = (INT8U *)(void *)"?";             /* Unknown name                       */
    18 #endif
    19     }
    20     ptcb1                   = &OSTCBTbl[ix];
    21     ptcb1->OSTCBNext        = (OS_TCB *)0;                       /* Last OS_TCB                        */
    22 #if OS_TASK_NAME_EN > 0u
    23     ptcb1->OSTCBTaskName    = (INT8U *)(void *)"?";              /* Unknown name                       */
    24 #endif
    25     OSTCBList               = (OS_TCB *)0;                       /* TCB lists initializations          */
    26     OSTCBFreeList           = &OSTCBTbl[0];
    27 }

    在讲解函数之前,首先看一下任务信息结构体的定义:

     1 typedef struct os_tcb {
     2     OS_STK          *OSTCBStkPtr;           /* Pointer to current top of stack                         */
     3 
     4 #if OS_TASK_CREATE_EXT_EN > 0u
     5     void            *OSTCBExtPtr;           /* Pointer to user definable data for TCB extension        */
     6     OS_STK          *OSTCBStkBottom;        /* Pointer to bottom of stack                              */
     7     INT32U           OSTCBStkSize;          /* Size of task stack (in number of stack elements)        */
     8     INT16U           OSTCBOpt;              /* Task options as passed by OSTaskCreateExt()             */
     9     INT16U           OSTCBId;               /* Task ID (0..65535)                                      */
    10 #endif
    11 
    12     struct os_tcb   *OSTCBNext;             /* Pointer to next     TCB in the TCB list                 */
    13     struct os_tcb   *OSTCBPrev;             /* Pointer to previous TCB in the TCB list                 */
    14 
    15 #if (OS_EVENT_EN)
    16     OS_EVENT        *OSTCBEventPtr;         /* Pointer to          event control block                 */
    17 #endif
    18 
    19 #if (OS_EVENT_EN) && (OS_EVENT_MULTI_EN > 0u)
    20     OS_EVENT       **OSTCBEventMultiPtr;    /* Pointer to multiple event control blocks                */
    21 #endif
    22 
    23 #if ((OS_Q_EN > 0u) && (OS_MAX_QS > 0u)) || (OS_MBOX_EN > 0u)
    24     void            *OSTCBMsg;              /* Message received from OSMboxPost() or OSQPost()         */
    25 #endif
    26 
    27 #if (OS_FLAG_EN > 0u) && (OS_MAX_FLAGS > 0u)
    28 #if OS_TASK_DEL_EN > 0u
    29     OS_FLAG_NODE    *OSTCBFlagNode;         /* Pointer to event flag node                              */
    30 #endif
    31     OS_FLAGS         OSTCBFlagsRdy;         /* Event flags that made task ready to run                 */
    32 #endif
    33 
    34     INT32U           OSTCBDly;              /* Nbr ticks to delay task or, timeout waiting for event   */
    35     INT8U            OSTCBStat;             /* Task      status                                        */
    36     INT8U            OSTCBStatPend;         /* Task PEND status                                        */
    37     INT8U            OSTCBPrio;             /* Task priority (0 == highest)                            */
    38 
    39     INT8U            OSTCBX;                /* Bit position in group  corresponding to task priority   */
    40     INT8U            OSTCBY;                /* Index into ready table corresponding to task priority   */
    41     OS_PRIO          OSTCBBitX;             /* Bit mask to access bit position in ready table          */
    42     OS_PRIO          OSTCBBitY;             /* Bit mask to access bit position in ready group          */
    43 
    44 #if OS_TASK_DEL_EN > 0u
    45     INT8U            OSTCBDelReq;           /* Indicates whether a task needs to delete itself         */
    46 #endif
    47 
    48 #if OS_TASK_PROFILE_EN > 0u
    49     INT32U           OSTCBCtxSwCtr;         /* Number of time the task was switched in                 */
    50     INT32U           OSTCBCyclesTot;        /* Total number of clock cycles the task has been running  */
    51     INT32U           OSTCBCyclesStart;      /* Snapshot of cycle counter at start of task resumption   */
    52     OS_STK          *OSTCBStkBase;          /* Pointer to the beginning of the task stack              */
    53     INT32U           OSTCBStkUsed;          /* Number of bytes used from the stack                     */
    54 #endif
    55 
    56 #if OS_TASK_NAME_EN > 0u
    57     INT8U           *OSTCBTaskName;
    58 #endif
    59 
    60 #if OS_TASK_REG_TBL_SIZE > 0u
    61     INT32U           OSTCBRegTbl[OS_TASK_REG_TBL_SIZE];
    62 #endif
    63 } OS_TCB;

     这个结构体乍一看还是挺大的,不过我们现在只关注核心数据,去掉那些被宏开关包围的成员,加上一些注释,再看一下:

    typedef struct os_tcb {
        OS_STK          *OSTCBStkPtr;           /* 指向任务堆栈的指针                   */
        struct os_tcb   *OSTCBNext;             /* 指向下一个节点的指针                 */
        struct os_tcb   *OSTCBPrev;             /* 指向上一个节点的指针                 */
        INT32U           OSTCBDly;              /* 任务的延时参数                      */
        INT8U            OSTCBStat;             /* 任务的状态                          */
        INT8U            OSTCBStatPend;         /* 任务的阻塞状态                      */
        INT8U            OSTCBPrio;             /* 任务的优先级                        */
                              /* 下面4个参数是有关优先级算法的,作用三两句说不清楚,可以参考我上一篇讲任务调度的文章 */
        INT8U            OSTCBX;                /* Bit position in group  corresponding to task priority   */
        INT8U            OSTCBY;                /* Index into ready table corresponding to task priority   */
        OS_PRIO          OSTCBBitX;             /* Bit mask to access bit position in ready table          */
        OS_PRIO          OSTCBBitY;             /* Bit mask to access bit position in ready group          */
    } OS_TCB;

    简化后明了多了,我们由定义的那两个成员可知,这是一个双向链表(如果对链表这种数据结构还不是很清楚,建议先去百度了解一下)。

    因此,上面那个函数的作用也就是建立一个链表(空闲链表),长度根据自己的配置。

    #define OS_MAX_TASKS             10u   /* Max. number of tasks in your application, MUST be >= 2       */
    #define  OS_N_SYS_TASKS          2u    /* Number of system tasks  

    我这里把宏定义为10(最大支持10个用户任务),再加上系统本身的保留任务2个(空闲任何、统计任务),那么这个链表的长度就是12。

    这个链表有什么用?

    我认为是为了预留出将来的空间,先建立一个空表在内存中占地方,等今后需要建立任务的时候,就直接从里面拿取空间,以避免内存不够的尴尬情况。

    这个函数的作用,是对任务相关的数据进行初始化,最重要的生成一个名为空闲链表的链表(这个空闲链表链接事先定义好的任务数组)。

    UCOSI操作系统里面有好几个结构完全一样的链表,比如任务链表,优先级链表,空闲链表等等,也是由这几个链表组合管理任务的信息。

    ※至于这几个链表的具体应用,暂时不用纠结,会在后面详细解释。

      五、函数OS_InitEventList()

      定义如下:

    static  void  OS_InitEventList (void)
    {
    #if (OS_EVENT_EN) && (OS_MAX_EVENTS > 0u)
    #if (OS_MAX_EVENTS > 1u)
        INT16U     ix;
        INT16U     ix_next;
        OS_EVENT  *pevent1;
        OS_EVENT  *pevent2;
    
    
        OS_MemClr((INT8U *)&OSEventTbl[0], sizeof(OSEventTbl)); /* Clear the event table                   */
        for (ix = 0u; ix < (OS_MAX_EVENTS - 1u); ix++) {        /* Init. list of free EVENT control blocks */
            ix_next = ix + 1u;
            pevent1 = &OSEventTbl[ix];
            pevent2 = &OSEventTbl[ix_next];
            pevent1->OSEventType    = OS_EVENT_TYPE_UNUSED;
            pevent1->OSEventPtr     = pevent2;
    #if OS_EVENT_NAME_EN > 0u
            pevent1->OSEventName    = (INT8U *)(void *)"?";     /* Unknown name                            */
    #endif
        }
        pevent1                         = &OSEventTbl[ix];
        pevent1->OSEventType            = OS_EVENT_TYPE_UNUSED;
        pevent1->OSEventPtr             = (OS_EVENT *)0;
    #if OS_EVENT_NAME_EN > 0u
        pevent1->OSEventName            = (INT8U *)(void *)"?"; /* Unknown name                            */
    #endif
        OSEventFreeList                 = &OSEventTbl[0];
    #else
        OSEventFreeList                 = &OSEventTbl[0];       /* Only have ONE event control block       */
        OSEventFreeList->OSEventType    = OS_EVENT_TYPE_UNUSED;
        OSEventFreeList->OSEventPtr     = (OS_EVENT *)0;
    #if OS_EVENT_NAME_EN > 0u
        OSEventFreeList->OSEventName    = (INT8U *)"?";         /* Unknown name                            */
    #endif
    #endif
    #endif
    }

      此函数主要用来对系统的事件机制(邮箱、队列、信号量等等)做一些初始化,里面处理和任务初始化差不多,作用也是事先占领一些内存空间,以备今后使用。

      管理各种消息的也是链表(定义就不贴出来了),事件最大的个数也是通过宏定义来实现,如果没有使用的话,把宏定义关掉可以节约一部分空间(不建议)。

      六、七、八、函数OS_FlagInit(),OS_MemInit(),OS_QInit()

      这三个函数就是对具体的消息机制、内存管理功能进行初始化,内部的处理也都大同小异,都是根据系统的配置,然后先占领一些空间,以备今后使用(每个事件的链表的结构体不同)。

      函数定义这里不贴出来,具体可以查看源代码,在今后专门会推出讲解消息量的文章,到时候再详细说。

      九、函数OS_InitTaskIdle()

      定义如下:

     1 static  void  OS_InitTaskIdle (void)
     2 {
     3 #if OS_TASK_NAME_EN > 0u
     4     INT8U  err;
     5 #endif
     6 
     7 
     8 #if OS_TASK_CREATE_EXT_EN > 0u
     9     #if OS_STK_GROWTH == 1u
    10     (void)OSTaskCreateExt(OS_TaskIdle,
    11                           (void *)0,                                 /* No arguments passed to OS_TaskIdle() */
    12                           &OSTaskIdleStk[OS_TASK_IDLE_STK_SIZE - 1u],/* Set Top-Of-Stack                     */
    13                           OS_TASK_IDLE_PRIO,                         /* Lowest priority level                */
    14                           OS_TASK_IDLE_ID,
    15                           &OSTaskIdleStk[0],                         /* Set Bottom-Of-Stack                  */
    16                           OS_TASK_IDLE_STK_SIZE,
    17                           (void *)0,                                 /* No TCB extension                     */
    18                           OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR);/* Enable stack checking + clear stack  */
    19     #else
    20     (void)OSTaskCreateExt(OS_TaskIdle,
    21                           (void *)0,                                 /* No arguments passed to OS_TaskIdle() */
    22                           &OSTaskIdleStk[0],                         /* Set Top-Of-Stack                     */
    23                           OS_TASK_IDLE_PRIO,                         /* Lowest priority level                */
    24                           OS_TASK_IDLE_ID,
    25                           &OSTaskIdleStk[OS_TASK_IDLE_STK_SIZE - 1u],/* Set Bottom-Of-Stack                  */
    26                           OS_TASK_IDLE_STK_SIZE,
    27                           (void *)0,                                 /* No TCB extension                     */
    28                           OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR);/* Enable stack checking + clear stack  */
    29     #endif
    30 #else
    31     #if OS_STK_GROWTH == 1u
    32     (void)OSTaskCreate(OS_TaskIdle,
    33                        (void *)0,
    34                        &OSTaskIdleStk[OS_TASK_IDLE_STK_SIZE - 1u],
    35                        OS_TASK_IDLE_PRIO);
    36     #else
    37     (void)OSTaskCreate(OS_TaskIdle,
    38                        (void *)0,
    39                        &OSTaskIdleStk[0],
    40                        OS_TASK_IDLE_PRIO);
    41     #endif
    42 #endif
    43 
    44 #if OS_TASK_NAME_EN > 0u
    45     OSTaskNameSet(OS_TASK_IDLE_PRIO, (INT8U *)(void *)"uC/OS-II Idle", &err);
    46 #endif
    47 }

      这个函数的作用是建立一个空闲任务,所谓的空闲任务就是啥也不干的任务,为什么必须要有这么一个吃干饭的家伙呢?

      试想一下,等UCOSII操作系统跑起来以后,如果我现在所有任务都处在延时状态中,也就是未就绪的状态,那么系统应该执行什么代码呢?

           CPU的动力来源于晶振,只要晶振不停,那CPU也是不会停下来的啊。

      这就是空闲任务的作用,当所有的任务都没有执行的时候,系统就会执行它,虽然空闲任务啥也不干,但可以避免CPU一脸懵逼加茫然无措。

      这个函数我们需要重点分析,因为所有任务的建立原理和过程都是一样的,只要完全理解了空闲任务的建立过程后,那么建立别的任务也就明白了。

      因为我们创建任务使用的是标准的create函数,并未使用拓展的create函数(宏定义OS_TASK_CREATE_EXT_EN == 0),所以我们只需要关注下面那个部分:

      OSTaskCreateExt()为OSTaskCreate()拓展版本,功能大同小异,只不过多了一些设置,这些设置基本上不会用到。

     1 #else
     2     #if OS_STK_GROWTH == 1u
     3     (void)OSTaskCreate(OS_TaskIdle,
     4                        (void *)0,
     5                        &OSTaskIdleStk[OS_TASK_IDLE_STK_SIZE - 1u],
     6                        OS_TASK_IDLE_PRIO);
     7     #else
     8     (void)OSTaskCreate(OS_TaskIdle,
     9                        (void *)0,
    10                        &OSTaskIdleStk[0],
    11                        OS_TASK_IDLE_PRIO);
    12     #endif
    13 #endif
    #define  OS_STK_GROWTH        1      /* Stack grows from HIGH to LOW memory on ARM    */

      由于我们使用mcu是m3内核,它堆栈的增长方向是由高到低,所以只需要看第一个宏里面的代码。

      看看函数的定义和形参:

    INT8U  OSTaskCreate (void   (*task)(void *p_arg),
                         void    *p_arg,
                         OS_STK  *ptos,
                         INT8U    prio)

       看看我们传递进去的实参:

        (void)OSTaskCreate(OS_TaskIdle,
                           (void *)0,
                           &OSTaskIdleStk[OS_TASK_IDLE_STK_SIZE - 1u],
                           OS_TASK_IDLE_PRIO);

    void   (*task)(void *p_arg):任务函数名               赋值=OS_TaskIdle(空闲任务函数名,系统已经实现了这个任务,不需要自己添加)

    void    *p_arg                    :给任务传入的参数   赋值= 0

    OS_STK  *ptos                 :任务的堆栈空间       赋值= OSTaskIdleStk[OS_TASK_IDLE_STK_SIZE - 1u](空闲任务的堆栈空间和大小也是系统定义的)

    INT8U    prio                     :任务的优先级          赋值= OS_TASK_IDLE_PRIO(既然都叫空闲任务,那优先级肯定是最低的,这个宏的值是63,由系统定义)

    大家快来看啊,原来创建一个任务这么简单,只需要给4个参数就可以了。

    传递了4个参数进去后,那里面具体又是怎么实现处理的呢?

    待续……

  • 相关阅读:
    LeetCode (Two Sum & Add Two nums)
    打包时记录编译开始时间并在App中获取
    CocoaPods
    不知所云
    苹果审核相关内容
    iOS权限的一些备注
    触发网络权限弹窗的一些特别方式记录
    iOS13和iOS14里面访问相册选取图片的两种方式的区别
    @Configuration@Bean
    ACID
  • 原文地址:https://www.cnblogs.com/han-bing/p/9015134.html
Copyright © 2011-2022 走看看