手把手,嘴对嘴,讲解UCOSII嵌入式操作系统的初始化过程(一)

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

  在写单片机逻辑程序之时,我的初始化过程大概分为两种:

  ①外围硬件的初始化(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系统初始化的标准格式,7~39行是定义任务的基本信息,优先级,堆栈大小,堆栈空间,任务函数等等。

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

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

扫描二维码关注公众号,回复: 236659 查看本文章

  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。

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。

  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这是一个结构体,里面记录优先级最高的那个任务的信息。

  OSTCBCur也是一个结构体,里面记录当前正在执行的那个任务的信息。

  

  四:函数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 }

这个函数的作用,是对任务相关的信息进行初始化,比如清空任务管理链表。

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

猜你喜欢

转载自www.cnblogs.com/han-bing/p/9015134.html