ucosiii内核源码解析---os_core.c

os_core.c是OS的核心函数,里面有OSInit(),OSSched()任务级别调度等函数,下面列出其中每段代码的意思。

栈的设置:栈是单片机 RAM 里面一段连续的内存空间,栈的大小由启动文件里面的代码配置。

 在多任务系统中,每个任务都是独立的,互不干扰的,所以要为每个任务都分配独立的栈空间,这个栈空间通常是一个预先定义好的全局数组。这些一个个的任务栈也是存在于 RAM 中,能够使用的最大的栈也是由启动文件中Stack_Size 决定。只是多任务系统中任务的栈就是在统一的一个栈空间里面分配好一个个独立的房间,每个任务只能使用各自的房间,而裸机系统中需要使用栈的时候则可以天马行空,随便在栈里面找个空闲的空间使用。
堆也是一样的:

在这里插入图片描述

 注:
在 μC/OS-III 中,空闲任务的栈最小应该大于 128

1、OSInit()调用初始化勾子函数,初始化系统全局变量,初始化优先级、就绪队列、任务管理器等相关变量。

void  OSInit (OS_ERR  *p_err)//调用初始化钩子函数  (设置中断使用的堆栈地址)初始化系统全局变量 初始化Prio、ReadList
{
    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  将OS_TCB指针初始化为已知状态          */
    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) {					/* 这里是对堆栈初始化,清零 同时 堆栈指针p_stk会指向堆栈数组的尾部*/
                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();
}

2、OSIntExit() 中断级调度器函数, 在 OS_CPU_SysTickHandler()中用到了,当中断处理完成之后,并且没有中断嵌套,查看是否有新的高优先级任务,如果有启动任务调度。

        其代码的大概意思是如果内核没有工作则退出,如果中断嵌套为零则退出,反则自减一,减后如果中断嵌套计数器依旧大于零,则说明还有中断,调用CPU_INT_EN()进入中断,不需要任务切换。任务调度器依旧被锁,则继续进入中断,判断完后获得最高优先级的就绪列表中的任务,增加任务切换计数器的值并跟踪任务切换总数,最后执行中断级任务切换。

如果内核没有工作则退出,如果中断嵌套为零则退出,反则自减一,减后如果中断嵌套计数器依旧大
于零,则说明还有中断,调用CPU_INT_EN()进入中断,不需要任务切换。任务调度器依旧被锁,则
继续进入中断,判断完后获得最高优先级的就绪列表中的任务,增加任务切换计数器的值并跟踪任务
切换总数,最后执行中断级任务切换。
void  OSIntExit (void)
{
    CPU_SR_ALLOC();

    if (OSRunning != OS_STATE_OS_RUNNING) {           /* Has the OS started? */
        return;                                             /* No */
    }

    CPU_INT_DIS();
    if (OSIntNestingCtr == (OS_NESTING_CTR)0) {             /* Prevent OSIntNestingCtr                                                                                 
                                                             from wrapping                  */
        CPU_INT_EN();
        return;
    }
    OSIntNestingCtr--;
    if (OSIntNestingCtr > (OS_NESTING_CTR)0) {              /* ISRs still nested?                                     */
        CPU_INT_EN();                                       /* Yes                                                    */
        return;
    }

    if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0) {        /* Scheduler still locked?    调度程序依旧被锁定                            */
        CPU_INT_EN();                                       /* Yes                                                    */
        return;
    }

    OSPrioHighRdy   = OS_PrioGetHighest();                  /* Find highest priority   寻找最高优先级                               */
    OSTCBHighRdyPtr = OSRdyList[OSPrioHighRdy].HeadPtr;     /* Get highest priority task ready-to-run 获得最高优先级的任务准备执行                */
    if (OSTCBHighRdyPtr == OSTCBCurPtr) {                   /* Current task still the highest priority?   查看当前任务是否为最高优先级            */
        CPU_INT_EN();                                       /* Yes                                                    */
        return;
    }

#if OS_CFG_TASK_PROFILE_EN > 0u
    OSTCBHighRdyPtr->CtxSwCtr++;                            /* Inc. # of context switches for this new task  增加新任务的上下文切换次数         */
#endif
    OSTaskCtxSwCtr++;                                       /* Keep track of the total number of ctx switches  跟踪 ctx 开关的总数       */

#if defined(OS_CFG_TLS_TBL_SIZE) && (OS_CFG_TLS_TBL_SIZE > 0u)
    OS_TLS_TaskSw();
#endif

    OSIntCtxSw();                                           /* Perform interrupt level ctx switch  执行中断级ctx切换                   */
    CPU_INT_EN();
}

        在中断级调度器中真正完成任务切换的就是中断级任务切换函数OSIntCtxSw(),由于在进入中断前现场已经保存过了,所以其不像OSCtxSW(任务级调度器)需要先保存现场,只需要执行任务堆栈恢复CPU寄存器的值就可以。在进入中断的时候需要调用OSIntEnter ()函数其主要功能就是为OSIntNestingCtr自加一,这个变量的意思是中断嵌套计数器,用来记录中断嵌套的深度。

3、 OSSched ():任务级调度,与中断级调度器有所不同,这个函数是在任务中调用,目的是切换到更高优先级的任务真正起到切换任务的是函数OS_TASK_SW(),OS_TASK_SW()就是函数OSCtxSW(),OSCtxSW()是os_cpu_a.asm中用汇编写的一段代码,其主要作用就是将当前任务的CPU寄存器的值保存在任务堆栈中,就是保存现场,之后将新任务的堆栈指针加载到CPU的堆栈指针寄存器中,就实现了任务切换,最后还要从新任务的堆栈中恢复CPU寄存器的值。

void  OSSched (void)//在任务中调用,切换到更高优先级的任务
{
    CPU_SR_ALLOC();



    if (OSIntNestingCtr > (OS_NESTING_CTR)0) {              /* ISRs still nested?                                     */
        return;                                             /* Yes ... only schedule when no nested ISRs              */
    }

    if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0) {        /* Scheduler locked?                                      */
        return;                                             /* Yes                                                    */
    }

    CPU_INT_DIS();
    OSPrioHighRdy   = OS_PrioGetHighest();                  /* Find the highest priority ready  找到最高优先级的就绪任务                      */
    OSTCBHighRdyPtr = OSRdyList[OSPrioHighRdy].HeadPtr;
    if (OSTCBHighRdyPtr == OSTCBCurPtr) {                   /* Current task is still highest priority task?  当前任务是否还是最高优先级         */
        CPU_INT_EN();                                       /* Yes ... no need to context switch                      */
        return;
    }

#if OS_CFG_TASK_PROFILE_EN > 0u
    OSTCBHighRdyPtr->CtxSwCtr++;                            /* Inc. # of context switches to this task  上下文切换到此任务              */
#endif
    OSTaskCtxSwCtr++;                                       /* Increment context switch counter   递增上下文切换计数器                    */

#if defined(OS_CFG_TLS_TBL_SIZE) && (OS_CFG_TLS_TBL_SIZE > 0u)
    OS_TLS_TaskSw();
#endif

    OS_TASK_SW();                                           /* Perform a task level context switch                    */
    CPU_INT_EN();
}

这是任务级调度器,结合前面的中断级调度器我们可以知道有两种切换任务的函数为OSCtxSW()、OSIntCtxSW()。他们才是真正进行任务切换的,从以下代码看出他们两个主要做的事情就是触发PendSV异常,也是一段汇编代码。


这里看起来一样是因为Cortex-m3系列进入异常时自动压栈了R0-R3、R12、LR、PSR、PC寄存器,
并在返回时自动弹出,不需要用汇编来写了。
OSCtxSw   保存现场,并产生PendSV切换任务
    LDR     R0, =NVIC_INT_CTRL     ; Trigger the PendSV exception (causes context switch)
    LDR     R1, =NVIC_PENDSVSET
    STR     R1, [R0]
    BX      LR


OSIntCtxSw 因为是中断级调度,所以之前已经保护过现场了,直接产生PendSV切换任务
    LDR     R0, =NVIC_INT_CTRL      ; Trigger the PendSV exception (causes context switch)
    LDR     R1, =NVIC_PENDSVSET
    STR     R1, [R0]
    BX      LR



//PendSV_Handler			
		/*
PendSV_Handler
    CPSID   I                                                   ; 关所有中断
    MRS     R0, PSP                                             ; PSP是当前进程堆栈的指针,将PSP赋值给R0
    CBZ     R0, PendSV_Handler_Nosave                     ; 如果R0为0时跳转到PendSV_Handler_Nosave
 
    ;已经存在任务  保存正在运行的任务 
    SUBS    R0, R0, #0x20                                       ; 偏移0x20的位置用来保存R4至R11
    STM     R0, {R4-R11} ;将剩下的R4至R11寄存器保存在此进程的堆栈中
 
    LDR     R1, =p_OSTCBCur                      
    ; OSTCBCur->OSTCBStkPtr = SP; 即OSTCBCur->OSTCBStkPtr这个保存当前的栈尾,以便下次弹出
    LDR     R1, [R1]
    STR     R0, [R1]                                            ; R0 is SP of process being switched out
   ;此时,整个上下文的过程已经被保存
 ;—————————切换上下文—————————————
 ; 实现 OSTCBCurPtr = OSTCBHighRdyPtr
 ; 把下一个要运行的任务的堆栈OSPrioHighRdy加载到 CPU 寄存器中   
PendSV_Handler_Nosave
    PUSH    {R14}                                               ; Save LR exc_return value
    LDR     R0, =OSTaskSwHook                                   ; OSTaskSwHook();  这里用于用户扩展
    BLX     R0
    POP     {R14}
 
    ;将最高优先级任务赋给当前优先级
    LDR     R0, =OSPrioCur                                      ; OSPrioCur = OSPrioHighRdy;
    LDR     R1, =OSPrioHighRdy ;将当前优先级变量指向最高优先级
    LDRB    R2, [R1]
    STRB    R2, [R0]
 
    LDR     R0, =p_OSTCBCur                                     ; OSTCBCur  = OSTCBHighRdy;
    LDR     R1, =p_OSTCBHighRdy ;TCB表也一样
    LDR     R2, [R1]
    STR     R2, [R0]
    ;到这里,[R2]保存的是新的进程的堆栈指针SP
    LDR     R0, [R2]                                            ;  SP = OSTCBHighRdy->OSTCBStkPtr;
    LDM     R0, {R4-R11}                                        ;  弹出其它寄存器,和前面的是一个逆过程
    ADDS    R0, R0, #0x20                                       ;和前面的逆过程对比可知
    MSR     PSP, R0                                             ; 将R0中的SP赋值给PSP寄存器
    ORR     LR, LR, #0x04                                       ; 确保异常返回时使用进程堆栈
    CPSIE   I ;开中断
    BX      LR                                                  ; 异常返回将恢复那些自动出栈的剩余寄存器
    ;跳到当前任务 开始运行
		这里是系统默认中断做的事:主要工作有判断是否第一次运行、将最高级任务赋给当前任务,然后将放到cpu寄存器运行,最高优先级任务开始运行。
		*/

PendSV异常服务要完成两个工作就是保存上文和切换下文,PendSV还可以由OSStartHighRdy触发。uCOS操作系统里(其实实时操作系统都一样)各类异常/中断的优先级关系:SYSTICK异常>中断>PendSv异常,所以PendSV会自动的延迟上下文切换的请求,直到他的中断都处理完了后才触发,这保证了实时性内核的实时性也保证了操作系统的正常轮转。

        PendSV的配置是在OSStartHighRdy()中完成的,这个函数用于启动任务切换,即配置PendSV、触发PendSV、在PendSV异常服务函数中完成任务切换。

4、OSStartHighRdy()

常量说明:
NVIC_INT_CTRL EQU 0xE000ED04 ; 中断控制及状态寄存器 SCB_ICSR。
NVIC_SYSPRI14 EQU 0xE000ED22 ; 系统优先级寄存器 SCB_SHPR3:
; bit16~23
NVIC_PENDSV_PRI EQU 0xFF ; PendSV 优先级的值(最低)。
NVIC_PENDSVSET EQU 0x10000000 ; 触发 PendSV 异常的值 Bit28: PENDSVSET


开始第一次上下文切换
配置 PendSV 异常的优先级为最低
在开始第一次上下文切换之前,设置 psp=0
触发 PendSV 异常,开始上下文切换
;*******************************************************************
OSStartHighRdy
 LDR R0, = NVIC_SYSPRI14 ; 设置 PendSV 异常优先级为最低(1)
 LDR R1, = NVIC_PENDSV_PRI
 STRB R1, [R0]

 MOVS R0, #0 ; 设置 psp 的值为 0,开始第一次上下文切换 (2)
 MSR PSP, R0

 LDR R0, =NVIC_INT_CTRL ; 触发 PendSV 异常(3)
 LDR R1, =NVIC_PENDSVSET
 STR R1, [R0]

 CPSIE I ; 启用总中断, NMI 和 HardFault 除外(4)

 OSStartHang
 B OSStartHang ; 程序应永远不会运行到这里

1)配置 PendSV 的优先级为 0XFF,即最低。在 μC/OS-III 中,上下文切换是在 PendSV 异常服务程序中执行的,配置 PendSV 的优先级为最低,从而消灭了在中断服务程序中执行上下文切换的可能。

2)设置 PSP 的值为 0,开始第一个任务切换。在任务中,使用的栈指针都是 PSP,后面如果判断出 PSP 为 0,则表示第一次任务切换

3)触发 PendSV 异常,如果中断启用且有编写 PendSV 异常服务函数的话,则内核会响应PendSV 异常,去执行 PendSV 异常服务函数。

4)开中断,因为有些用户在 main() 函数开始会先关掉中断,等全部初始化完成后,在启动 OS 的时候才开中断。

CPSID I ;PRIMASK=1 ; 关中断
CPSIE I ;PRIMASK=0 ; 开中断
CPSID F ;FAULTMASK=1 ; 关异常
CPSIE F ;FAULTMASK=0 ; 开异常

OSStartHighRdy ()函数由OSStart()调用,OSStart函数用来启用多任务,如果多任务开始正常进入OSStartHighRdy 函数触发PendSV异常就会进行任务切换。

5、OSStart(OS_ERR *p_err):此函数用于启动多任务,让ucosiii接管任务,在调用 OSStart() 之前,你必须调用 OSInit() 并且你必须至少创建一项申请任务

系统使用全局变量OSRunning来表示当前的系统运行状态,刚开始系统初始化的时候是停止状态,进入if首先寻找最高优先级的就绪任务,再OSStartHighRdy() 用于启动任务切换,操作系统开始执行最高优先级的任务。

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

    if (OSRunning == OS_STATE_OS_STOPPED) {
        OSPrioHighRdy   = OS_PrioGetHighest();              /* Find the highest priority  寻找最高优先级的任务                            */
        OSPrioCur       = OSPrioHighRdy;           /*将最高优先级付给当前优先级*/
        OSTCBHighRdyPtr = OSRdyList[OSPrioHighRdy].HeadPtr;/* 从TCB链表中取出最高优先级 赋给 最高级TCB   */
        OSTCBCurPtr     = OSTCBHighRdyPtr;//最高优先级赋给 当前优先级
        OSRunning       = OS_STATE_OS_RUNNING; //将OSRunning标记为1表示已经开始运行内核
        OSStartHighRdy();  //一长段汇编代码/* Execute target specific code to start task  执行目标特定代码以启动任务           */
 		*p_err           = OS_ERR_FATAL_RETURN;              /* OSStart() is not supposed to returnOSStart() 不应该返回                    */
    } else {
       *p_err           = OS_ERR_OS_RUNNING;                /* OS is already running  操作系统已经在运行                                */
    }
}

5.1以上知道了任务切换的大致过程,那么什么会发送任务切换(任务调度)呢?

1.使用延时函数OSTimeDly()或者OSTimeDlyHMSM();
2.OSStart()启动任务调度器
3.删除任务;
4.任务通过调用OSTaskSuspend()将自身挂起;
5.任务解挂某个挂起的任务;
6.用户调用OSSched();

7.释放信号量或者发送消息,也可通过配置相应的参数不发生任务调度;
8.任务等待的事情还没发生(等待信号量,消息队列等);
9.任务取消等待;
10.删除一个内核对象;
11.任务改变自身的优先级或者其他任务的优先级;
12.退出所有的嵌套中断;
13.通过OSSchedUnlock()给调度器解锁;
14.任务调用OSSchedRoundRobinYield()放弃其执行时间片。

6、OSSchedLock (OS_ERR  *p_err):

这允许您的应用程序防止上下文切换,直到您准备好允许上下文切换。

void  OSSchedLock (OS_ERR  *p_err)//当任务调度发生时,阻止任务调度,直到调用了 OSSchedUnlock()。  主要是 OSSchedLockNestingCtr++;
{
    CPU_SR_ALLOC();

    CPU_CRITICAL_ENTER();
    OSSchedLockNestingCtr++;                                /* Increment lock nesting level  递增嵌套级别                         */
#if OS_CFG_SCHED_LOCK_TIME_MEAS_EN > 0u
    OS_SchedLockTimeMeasStart();
#endif
    CPU_CRITICAL_EXIT();
   *p_err = OS_ERR_NONE;
}


#if OS_CFG_SCHED_LOCK_TIME_MEAS_EN > 0u   
void  OS_SchedLockTimeMeasStart (void)
{
    if (OSSchedLockNestingCtr == 1u) {
        OSSchedLockTimeBegin = CPU_TS_TmrRd();
    }
}

进入临界区,递增嵌套级别(主要目的),判断是否设置了测量锁定时间的代码,有的话进入OS_SchedLockTimeMeasStart(),这个函数用于测量计划程序锁定的峰值时间,CPU_TS_TmrRd()是一个16位的计时器,不希望锁定调度程序的时间超过65536,这不是个好事。开头的CPU_SR_ALLOC()是定义的一个局部变量,用来存 CPU 关中断前的中断状态。

7、OSSchedUnlock (OS_ERR  *p_err)

void  OSSchedUnlock (OS_ERR  *p_err)
{
    CPU_CRITICAL_ENTER();
    OSSchedLockNestingCtr--;                                /* Decrement lock nesting level  递减锁嵌套级别                         */
    if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0) {
        CPU_CRITICAL_EXIT();                                /* Scheduler is still locked   调度器依旧被锁                           */
       *p_err = OS_ERR_SCHED_LOCKED;
        return;
    }
#if OS_CFG_SCHED_LOCK_TIME_MEAS_EN > 0u
    OS_SchedLockTimeMeasStop();
#endif

    CPU_CRITICAL_EXIT();                                    /* Scheduler should be re-enabled                         */
    OSSched();                                              /* Run the scheduler                                      */
   *p_err = OS_ERR_NONE;
}


void  OS_SchedLockTimeMeasStop (void)
{
    CPU_TS_TMR  delta;


    if (OSSchedLockNestingCtr == (OS_NESTING_CTR)0) {       /* Make sure we fully un-nested scheduler lock            */
        delta = CPU_TS_TmrRd()                              /* Compute the delta time between begin and end           */
              - OSSchedLockTimeBegin;
        if (OSSchedLockTimeMax    < delta) {                /* Detect peak value                                      */
            OSSchedLockTimeMax    = delta;
        }
        if (OSSchedLockTimeMaxCur < delta) {                /* Detect peak value (for resettable value)               */
            OSSchedLockTimeMaxCur = delta;
        }
    }
}

进入临界区,递减锁嵌套级别,判断如果还有锁嵌套,异常退出,反之判断是否设置测量锁定时间的代码,有的话进入OS_SchedLockTimeMeasStop函数计算出调度器被锁时间,再判断值是否有效,退出临界区,在调度器被锁期间可能有就绪任务所以通过调用OSSched()函数打开调度。

8、OSSchedRoundRobinCfg (CPU_BOOLEAN   en,OS_TICK    dflt_time_quanta,

 OS_ERR       *p_err)

调用该函数改变轮询调度参数,如是否打开时间片轮循,时间片大小的设置。代码的流程是如果开启了时间片轮循,进入临界区判断参数en是否开启了时间片轮循,1开启、0关闭。判断参数dflt_time_quanta是否大于0,大于0则时间片大小为dflt_time_quanta,小于0则选取系统默认的时间片大小(OS_TICK)(OSCfg_TickRate_Hz / (OS_RATE_HZ)10)。退出临界区,并正确返回。

#if OS_CFG_SCHED_ROUND_ROBIN_EN > 0u    //当启动了相同任务优先级时间片轮询(OS_CFG_SCHED_ROUND_ROBIN_EN==1 )
void  OSSchedRoundRobinCfg (CPU_BOOLEAN   en,
                            OS_TICK       dflt_time_quanta,
                            OS_ERR       *p_err)
{// 决定是否开启时间片轮询,设置任务的默认时间片时间(多少个SysTick),默认为 OSCfg_TickRate_Hz / 10
    CPU_SR_ALLOC();



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

    CPU_CRITICAL_ENTER();
    if (en != DEF_ENABLED) {   //是否开启时间片轮循
        OSSchedRoundRobinEn = DEF_DISABLED;
    } else {
        OSSchedRoundRobinEn = DEF_ENABLED;
    }

    if (dflt_time_quanta > (OS_TICK)0) {			//设置任务的默认时间片,如果未设置就选取默认值
        OSSchedRoundRobinDfltTimeQuanta = dflt_time_quanta;
    } else {
        OSSchedRoundRobinDfltTimeQuanta = (OS_TICK)(OSCfg_TickRate_Hz / (OS_RATE_HZ)10);
    }
    CPU_CRITICAL_EXIT();
   *p_err = OS_ERR_NONE;
}
#endif

9、OSSchedRoundRobinYield (OS_ERR  *p_err)

该函数在时间片到期前执行完毕,被调用放弃CPU。进入临界区前需要判断,同优先级的任务数是否大于1、轮循是否启用、调度器是否锁定、判断是否在中断中。判断完后,将当前任务控制块移至就绪列表尾部,将执行的权利给就绪列表的表头指向的任务,确定要使用的时间片大小,退出临界区。

#if OS_CFG_SCHED_ROUND_ROBIN_EN > 0u
void  OSSchedRoundRobinYield (OS_ERR  *p_err)
{// 当一个任务在给定的时间片之前完成了任务,可以调用此函数放弃CPU让给相同优先级的等待运行的任务。即放弃未使用的时间片给同等级的任务    

    OS_RDY_LIST  *p_rdy_list;
    OS_TCB       *p_tcb;
    CPU_SR_ALLOC();



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

#if OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u
    if (OSIntNestingCtr > (OS_NESTING_CTR)0) {              /* Can't call this function from an ISR                   */
       *p_err = OS_ERR_YIELD_ISR;
        return;
    }
#endif

    if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0) {        /* Can't yield if the scheduler is locked                 */
       *p_err = OS_ERR_SCHED_LOCKED;
        return;
    }

    if (OSSchedRoundRobinEn != DEF_TRUE) {                  /* Make sure round-robin has been enabled                 */
       *p_err = OS_ERR_ROUND_ROBIN_DISABLED;
        return;
    }

    CPU_CRITICAL_ENTER();
    p_rdy_list = &OSRdyList[OSPrioCur];                     /* Can't yield if it's the only task at that priority 如果这是该优先级队列的唯一任务,则无法让步    */
    if (p_rdy_list->NbrEntries < (OS_OBJ_QTY)2) {
        CPU_CRITICAL_EXIT();
       *p_err = OS_ERR_ROUND_ROBIN_1;
        return;
    }

    OS_RdyListMoveHeadToTail(p_rdy_list);                   /* Move current OS_TCB to the end of the list 将当前 OS_TCB 移动到列表末尾,从而实现任务切换            */
    p_tcb = p_rdy_list->HeadPtr;                            /* Point to new OS_TCB at head of the list    指向列表头部的新 OS_TCB            */
    if (p_tcb->TimeQuanta == (OS_TICK)0) {                  /* See if we need to use the default time slice  看看我们是否需要使用默认的时间片         */
        p_tcb->TimeQuantaCtr = OSSchedRoundRobinDfltTimeQuanta;//使用默认时间片
    } else {
        p_tcb->TimeQuantaCtr = p_tcb->TimeQuanta;           /* Load time slice counter with new time 用新时间加载时间片计数器                 */
    }

    CPU_CRITICAL_EXIT();

    OSSched();                                              /* Run new task  执行任务调度                                        */
   *p_err = OS_ERR_NONE;//正确执行
}
#endif

这里再看看OS_RdyListMoveHeadToTail(p_rdy_list)函数是如何实现的。调用此函数可将列表的当前头部移动到列表的尾部。0和1是没有任务和只有一个任务,不需要移动。如果是两个任务则将任务控制块的指针进行交换则可以,这里的调换实际就是双向链表的删除插入操作

void  OS_RdyListMoveHeadToTail (OS_RDY_LIST  *p_rdy_list)
{
    OS_TCB  *p_tcb1;
    OS_TCB  *p_tcb2;
    OS_TCB  *p_tcb3;



    switch (p_rdy_list->NbrEntries) {
        case 0:
        case 1:
             break;

        case 2:                                             /* SWAP the TCBs                                          */
             p_tcb1              = p_rdy_list->HeadPtr;     /* Point to current head                                  */
             p_tcb2              = p_rdy_list->TailPtr;     /* Point to current tail                                  */
             p_tcb1->PrevPtr     = p_tcb2;
             p_tcb1->NextPtr     = (OS_TCB *)0;
             p_tcb2->PrevPtr     = (OS_TCB *)0;
             p_tcb2->NextPtr     = p_tcb1;
             p_rdy_list->HeadPtr = p_tcb2;
             p_rdy_list->TailPtr = p_tcb1;
             break;

        default:                                            /*仅当列表中有两个以上的OS_TCB时才移动*/
             p_tcb1              = p_rdy_list->HeadPtr;     /*指向当前头部*/
             p_tcb2              = p_rdy_list->TailPtr;     /*指向当前尾部*/
             p_tcb3              = p_tcb1->NextPtr;        /*指向新的列表头*/
             p_tcb3->PrevPtr     = (OS_TCB *)0;             /*调整新列表头的后链接*/
             p_tcb1->NextPtr     = (OS_TCB *)0;             /*调整新列表尾部的前向链接*/
             p_tcb1->PrevPtr     = p_tcb2;                  /*调整新列表尾部的后链接*/
             p_tcb2->NextPtr     = p_tcb1;                  /*调整旧列表尾部的前向链接*/
             p_rdy_list->HeadPtr = p_tcb3;                  /*调整新列表的头和尾指针*/
             p_rdy_list->TailPtr = p_tcb1;
             break;
    }
}

10、OS_IdleTask (void  *p_arg)

并没有做什么。但是里面调用的OSIdleTaskHook()这个函数值得一看,这是空闲任务的钩子函数,在OSIdleTaskHook() 中调用 最终调用的是函数App_OS_IdleTaskHook(),也就是说如果我们想要在空闲任务的钩子函数中做其他处理就需要将代码写在App_OS_IdleTaskHook()。

void  OS_IdleTask (void  *p_arg) //空闲任务
{
    CPU_SR_ALLOC();



    p_arg = p_arg;                                          /* Prevent compiler warning for not using 'p_arg'防止编译器警告不使用'p_arg'          */

    while (DEF_ON) {
        CPU_CRITICAL_ENTER();
        OSIdleTaskCtr++;
#if OS_CFG_STAT_TASK_EN > 0u//大于0则启用统计任务
        OSStatTaskCtr++;
#endif
        CPU_CRITICAL_EXIT();

        OSIdleTaskHook();                                   /* Call user definable HOOK                               */
    }
}

void  OSIdleTaskHook (void)
{
#if OS_CFG_APP_HOOKS_EN > 0u //OS_CFG_APP_HOOKSEN置1,即允许使用空闲任务的钩子函数,
														 
	
    if (OS_AppIdleTaskHookPtr != (OS_APP_HOOK_VOID)0) {
        (*OS_AppIdleTaskHookPtr)();//当使能空闲任务的钩子函数以后每次进入空闲任务就会调用指针OS_ AppIdleTaskHookPtr所指向的函数。
    }
#endif
}

11、OS_IdleTaskInit (OS_ERR  *p_err)

空闲任务的创建即初始化,空闲任务的初始化在 OSInit() 被调用,意味着在系统还没有启动之前空闲任务就已经创建好,必须创建空闲任务(不需要我们手动创建),因为CPU不能停下来,他有最低的优先级。

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

西邮小菜机

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

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

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

打赏作者

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

抵扣说明:

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

余额充值