当前位置: 首页> 房产> 建材 > 知名公司_百度导航下载安装手机导航_网站维护中是什么意思_线上销售方案

知名公司_百度导航下载安装手机导航_网站维护中是什么意思_线上销售方案

时间:2025/7/12 10:28:12来源:https://blog.csdn.net/Zevalin/article/details/146083070 浏览次数:0次
知名公司_百度导航下载安装手机导航_网站维护中是什么意思_线上销售方案

参考教程:【正点原子】手把手教你学UCOS-III实时操作系统_哔哩哔哩_bilibili

一、µC/OS-III的初始化

1、OSInit函数简介

(1)OSInit函数用于初始化µC/OS-III,必须在调用任何其它µC/OS-III函数之前调用它,仅调用一次即可。

(2)该函数的执行内容:

①对一些全局变量赋初始值。

②初始化就绪列表以及Tick列表等。

③创建三个任务:空闲任务(必须创建),统计任务(条件创建),软件定时器任务(条件创建)。

[1]空闲任务:任务优先级最低31,当系统无其它就绪任务,那么空闲任务将会执行(空闲任务不能被阻塞)。

[2]统计任务:任务优先级为30,用来统计CPU使用率和各个任务的堆栈使用量。

[3]软件定时器任务:任务优先级为29,主要用于在特定的时间段内处理单次或周期性的软件定时器。

2、OSInit函数源码概览

void  OSInit (OS_ERR  *p_err)
{
#if (OS_CFG_ISR_STK_SIZE > 0u)CPU_STK      *p_stk;CPU_STK_SIZE  size;
#endif#ifdef OS_SAFETY_CRITICALif (p_err == (OS_ERR *)0) {OS_SAFETY_CRITICAL_EXCEPTION();return;}
#endifOSInitHook();                               //调用端口特定初始化代码OSIntNestingCtr = 0u;                       //中断嵌套计数器初始化为0OSRunning = OS_STATE_OS_STOPPED;       //多任务调度初始化为未开启OSSchedLockNestingCtr = 0u;                 //任务调度锁初始化为关闭OSTCBCurPtr = (OS_TCB *)0;    //当前执行任务的任务控制块初始化为空OSTCBHighRdyPtr = (OS_TCB *)0;//当前优先级最高的任务的任务控制块初始化为空OSPrioCur = 0u;                 //当前运行的任务的优先级初始化为0OSPrioHighRdy = 0u;             //当前优先级最高的任务的优先级初始化为0#if (OS_CFG_SCHED_LOCK_TIME_MEAS_EN > 0u)OSSchedLockTimeBegin = 0u;OSSchedLockTimeMax = 0u;OSSchedLockTimeMaxCur = 0u;
#endif#ifdef OS_SAFETY_CRITICAL_IEC61508OSSafetyCriticalStartFlag = OS_FALSE;
#endif#if (OS_CFG_SCHED_ROUND_ROBIN_EN > 0u)OSSchedRoundRobinEn = OS_FALSE;OSSchedRoundRobinDfltTimeQuanta = OSCfg_TickRate_Hz / 10u;
#endif#if (OS_CFG_ISR_STK_SIZE > 0u)p_stk = OSCfg_ISRStkBasePtr;if (p_stk != (CPU_STK *)0) {size  = OSCfg_ISRStkSize;while (size > 0u) {size--;*p_stk = 0u;p_stk++;}}
#if (OS_CFG_TASK_STK_REDZONE_EN > 0u)OS_TaskStkRedzoneInit(OSCfg_ISRStkBasePtr, OSCfg_ISRStkSize);
#endif
#endif#if (OS_CFG_APP_HOOKS_EN > 0u)
#if (OS_CFG_TASK_STK_REDZONE_EN > 0u)OS_AppRedzoneHitHookPtr = (OS_APP_HOOK_TCB )0;
#endifOS_AppTaskCreateHookPtr = (OS_APP_HOOK_TCB )0;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 = 0u;
#endifOS_PrioInit();                    //就绪列表中每个优先级的任务列表全初始化为空OS_RdyListInit();                 //就绪列表初始化#if (OS_CFG_FLAG_EN > 0u)
#if (OS_CFG_DBG_EN > 0u)OSFlagDbgListPtr = (OS_FLAG_GRP *)0;OSFlagQty = 0u;
#endif
#endif#if (OS_CFG_MEM_EN > 0u)OS_MemInit(p_err);if (*p_err != OS_ERR_NONE)return;
#endif#if (OS_MSG_EN > 0u) OS_MsgPoolInit(p_err);if (*p_err != OS_ERR_NONE) return;
#endif#if (OS_CFG_MUTEX_EN > 0u)
#if (OS_CFG_DBG_EN > 0u)OSMutexDbgListPtr = (OS_MUTEX *)0;OSMutexQty = 0u;
#endif
#endif#if (OS_CFG_Q_EN > 0u)
#if (OS_CFG_DBG_EN > 0u)OSQDbgListPtr = (OS_Q *)0;OSQQty = 0u;
#endif
#endif#if (OS_CFG_SEM_EN > 0u) 
#if (OS_CFG_DBG_EN > 0u)OSSemDbgListPtr = (OS_SEM *)0;OSSemQty = 0u;
#endif
#endif#if defined(OS_CFG_TLS_TBL_SIZE) && (OS_CFG_TLS_TBL_SIZE > 0u)OS_TLS_Init(p_err);if (*p_err != OS_ERR_NONE) return;
#endifOS_TaskInit(p_err);                       //任务数、任务切换次数均初始化为0if (*p_err != OS_ERR_NONE) return;#if (OS_CFG_TASK_IDLE_EN > 0u)OS_IdleTaskInit(p_err);                                  //创建空闲任务if (*p_err != OS_ERR_NONE) return;
#endif#if (OS_CFG_TICK_EN > 0u)OS_TickInit(p_err);           //Tick列表初始化(包括但不限于系统节拍初始化为0)if (*p_err != OS_ERR_NONE) return;
#endif#if (OS_CFG_STAT_TASK_EN > 0u)OS_StatTaskInit(p_err);                                  //创建统计任务if (*p_err != OS_ERR_NONE) return;
#endif#if (OS_CFG_TMR_EN > 0u)OS_TmrInit(p_err);                                      //创建软件定时器任务if (*p_err != OS_ERR_NONE) return;
#endif#if (OS_CFG_DBG_EN > 0u)OS_Dbg_Init();
#endifOSCfg_Init();OSInitialized = OS_TRUE;
}

二、开启任务调度器

1、OSStart函数简介

(1)OSStart函数用于启动任务调度器,任务调度器启动后,µC/OS-III便会开始进行任务调度。

(2)该函数的执行内容:

①进行一些安全关键代码判断。

②获取当前最高优先级任务。

③将调度器运行状态标志设置为开启状态。

④获取最高优先级任务的任务控制块。

⑤调用函数OSStartHighRdy,启动第一个任务。

(3)前导置零指令:CPU_CntLeadZeros(x),其中x为32位的变量,它能返回一个32位二进制数头部0的个数。

2、OSStart函数源码概览

void  OSStart (OS_ERR  *p_err)
{OS_OBJ_QTY  kernel_task_cnt;          //用于当前任务总数统计#ifdef OS_SAFETY_CRITICALif (p_err == (OS_ERR *)0) {OS_SAFETY_CRITICAL_EXCEPTION();return;}
#endifif (OSInitialized != OS_TRUE) {*p_err = OS_ERR_OS_NOT_INIT;return;}kernel_task_cnt = 0u;
#if (OS_CFG_STAT_TASK_EN > 0u)   //如果创建了统计任务,需要计入任务总数kernel_task_cnt++;
#endif
#if (OS_CFG_TMR_EN > 0u)         //如果创建了软件定时器任务,需要计入任务总数kernel_task_cnt++;
#endif
#if (OS_CFG_TASK_IDLE_EN > 0u)         //如果创建了空闲任务,需要计入任务总数kernel_task_cnt++;
#endifif (OSTaskQty <= kernel_task_cnt)  //如果没有创建应用任务,将无法开启任务调度器{*p_err = OS_ERR_OS_NO_APP_TASK;return;}if (OSRunning == OS_STATE_OS_STOPPED)         //调度器仅开启一次即可{  OSPrioHighRdy = OS_PrioGetHighest(); //通过前导置零指令获取当前最高优先级任务的优先级OSPrioCur = OSPrioHighRdy;          //更改当前执行任务的优先级为当前最高优先级任务的优先级OSTCBHighRdyPtr = OSRdyList[OSPrioHighRdy].HeadPtr;   //获取最高优先级就绪列表第一个任务的任务控制块OSTCBCurPtr = OSTCBHighRdyPtr;      //更改任务调度器启动后运行的任务为最高优先级就绪列表的第一个任务OSRunning = OS_STATE_OS_RUNNING;  //调度器运行状态置为运行中OSStartHighRdy();                       //启动第一个任务*p_err           = OS_ERR_FATAL_RETURN;} else {*p_err           = OS_ERR_OS_RUNNING; }
}

三、启动第一个任务

1、任务的栈

(1)要想启动第一个任务,就需要将该任务的值恢复到CPU的寄存器中,而任务的值在创建任务时已经保存到任务堆栈中。

①中断产生时,硬件自动将xPSR、PC(R15)、LR(R14)、R12、R3-R0保存和恢复,而R4-R11需要手动保存和恢复。

②进入中断后硬件会强制使用MSP指针,此时LR(R14)的值将会被自动被更新为特殊的EXC_RETURN。

(2)任务在被创建时,系统会调用OSTaskStkInit函数初始化任务的堆栈,其源码如下:

CPU_STK  *OSTaskStkInit (OS_TASK_PTR    	p_task,void          		*p_arg,CPU_STK       	*p_stk_base,CPU_STK       	*p_stk_limit,CPU_STK_SIZE   	stk_size,OS_OPT         	opt)
{CPU_STK    *p_stk;(void)opt;          /* 'opt' is not used, prevent warning */p_stk = &p_stk_base[stk_size];                           //获取任务堆栈末尾地址p_stk = (CPU_STK *)((CPU_STK)(p_stk) & 0xFFFFFFF8u); //8字节对齐/* 任务相关内容入栈 */
*(--p_stk) = (CPU_STK)0x01000000u;              	/* xPSR */
//寄存器xPSR被初始为0x01000000,其中bit24被置1,表示使用Thumb指令
*(--p_stk) = (CPU_STK)p_task;                   	/* Entry Point */
//寄存器PC被初始化为任务函数指针vTask_x,这样当某次任务切换后,任务x获得CPU控制权,任务函数vTask_x被出栈到PC寄存器,之后会执行任务x的代码
*(--p_stk) = (CPU_STK)OS_TaskReturn;          	/* R14 (LR) */
//LR寄存器初始化为函数指针OS_TaskReturn,这是由移植层提供的一个出错处理函数
*(--p_stk) = (CPU_STK)0x12121212u;              	/* R12 */
//子函数的调用通过寄存器R0~R3传递参数,创建任务时,传入的参数被保存到R0中,用来向任务函数传递参数*(--p_stk) = (CPU_STK)0x03030303u;              	/* R3 */*(--p_stk) = (CPU_STK)0x02020202u;              	/* R2 */*(--p_stk) = (CPU_STK)p_stk_limit;                	/* R1 */*(--p_stk) = (CPU_STK)p_arg;                    	/* R0 : argument */*(--p_stk) = (CPU_STK)0xFFFFFFFDuL;   /* R14: EXEC_RETURN; See Note 5 *//* Remaining registers saved on process stack */*(--p_stk) = (CPU_STK)0x11111111uL;              /* R11 */*(--p_stk) = (CPU_STK)0x10101010uL;             /* R10 */*(--p_stk) = (CPU_STK)0x09090909uL;             /* R9 */*(--p_stk) = (CPU_STK)0x08080808uL;             /* R8 */*(--p_stk) = (CPU_STK)0x07070707uL;              /* R7 */*(--p_stk) = (CPU_STK)0x06060606uL;              /* R6 */*(--p_stk) = (CPU_STK)0x05050505uL;              /* R5 */*(--p_stk) = (CPU_STK)0x04040404uL;             	/* R4 */return (p_stk);
}

(3)出栈/压栈汇编指令详解:

2、OSStartHighRdy函数简介

(1)OSStartHighRdy函数用于启动第一个任务。

(2)该函数的执行内容:

①屏蔽中断,防止在启动第一个任务时被打断。

②将PendSV设置为最低优先级。

③将PSP清0,PSP将用于任务栈。

④将MSP设置为OS_CPU_ExceptStkBas,指向异常栈的栈底(按8字节对齐)。

⑤获取当前最高优先级的任务的任务优先级,以及任务控制块。

⑥将最高优先级任务的任务栈出栈。

⑦开中断,并跳转到第一个运行任务的任务函数执行。

(3)程序在运行过程中需要一定的栈空间来保存局部变量等一些信息,当有信息保存到栈中时,MCU会自动更新SP指针,使SP指针指向最后一个入栈的元素,那么程序就可以根据SP指针来从栈中存取信息。

        ARMCortex-M提供了两个栈空间,这两个栈空间的堆栈指针分别是MSP(主堆栈指针)和PSP(进程堆指针)。

        ①MSP由OS内核、异常服务例程以及所有需要特权访问的应用程序代码来使用,在µC/OS-III中,中断使用MSP。

        ②PSP用于常规的应用程序代码(不处于异常服务例程中时),在µC/OS-III中,中断以外使用PSP。

当使用不同的堆栈指针时,SP会等于当前使用的堆栈指针。

3、OSStartHighRdy函数源码概览

OSStartHighRdyCPSID   I                           ; 屏蔽中断,防止在启动第一个任务时被打断MOV32   R0, NVIC_SYSPRI14 MOV32   R1, NVIC_PENDSV_PRISTRB    R1, [R0]                    ; 设置PENDSV的中断优先级为最低MOVS    R0, #0 MSR     PSP, R0                    ; 将PSP清0,PSP将用于任务栈MOV32   R0, OS_CPU_ExceptStkBaseLDR     R1, [R0]MSR     MSP, R1                   ; 将MSP设置为OS_CPU_ExceptStkBas,指向异常栈的栈底(按8字节对齐)BL      OSTaskSwHookMOV32   R0, OSPrioCurMOV32   R1, OSPrioHighRdyLDRB    R2, [R1]STRB    R2, [R0]   ; 获取当前最高优先级的任务的任务优先级MOV32   R0, OSTCBCurPtrMOV32   R1, OSTCBHighRdyPtrLDR     R2, [R1]STR     R2, [R0]    ; 获取当前最高优先级的任务的任务控制块LDR     R0, [R2]MSR     PSP, R0    ; PSP指向最高优先级任务的任务栈栈底MRS     R0, CONTROLORR    R0, R0, #2  ; CONTROL寄存器的bit1置1,选择PSP指针BIC     R0, R0, #4  ; 如果使用了FPU,CONTROL寄存器的bit2会置1,需要清零MSR     CONTROL, R0ISB; SP此时指向任务栈栈底LDMFD    SP!, {R4-R11, LR}        ; 将SP存储地址里面的内容手动加载到CPU寄存器r4-r11以及LR,过程中SP地址上移; 以下出栈操作也需手动,因为这里不是中断LDMFD    SP!, {R0-R3}             ; 将SP存储地址里面的内容手动加载到CPU寄存器r0-r3,过程中SP地址上移LDMFD    SP!, {R12, LR}           ; 将SP存储地址里面的内容手动加载到CPU寄存器r12以及LR,过程中SP地址上移LDMFD    SP!, {R1, R2}            ; 将SP存储地址里面的内容手动加载到CPU寄存器r1-r2,过程中SP地址上移CPSIE    I           ; 开中断BX       R1         ; 返回任务函数地址(此时r1中的内容就是任务函数地址)

四、任务切换

1、任务切换概述

(1)任务切换的本质其实就是CPU寄存器的切换(又叫上下文切换),这个过程在PendSV中断服务函数里完成。

(2)假设由任务A切换到任务B,这个过程主要分为两步:

①需暂停任务A的执行,并将此时任务A的寄存器值保存到任务堆栈,这个过程叫做保存现场。

②将任务B的各个寄存器值(被存于任务堆栈中)恢复到CPU寄存器中,这个过程叫做恢复现场。

2、PendSV中断

(1)执行µC/OS-III提供的API函数——OSCtxSw和OSIntCtxSw,可以触发PendSV中断。

函数

描述

OSSched

任务中使用

OSIntExit

中断中使用

(2)启动PendSV中断的底层逻辑:通过向中断控制和状态寄存器ICSR的bit28写入1,挂起PendSV,以启动PendSV中断。

(3)触发任务切换(PendSV中断)的时机:

①任务向另外一个任务发送信号或消息。

②任务调用延时函数(OSTimeDly或OSTimeDlyHMSM)。

③任务等待一个还未发生的事件发生。

④任务的挂起状态被终止。

⑤有任务被创建或被删除。

⑥内核对象被删除。

⑦中断嵌套结束。

⑧任务修改了自身或其它任务的任务优先级。

⑨任务调用函数OSTaskSuspend挂起自身,或者任务调用函数OSTaskResue恢复其它被函数OSTaskSuspend挂起的任务。

⑩使用函数OSSchedUnblock解锁任务调度器。

⑪任务调用函数OSSchedRoundRobinYield弃用剩余的时间片。

⑫在应用程序中调用函数OSSched。

(4)PendSV中断服务函数源码:

OS_CPU_PendSVHandlerCPSID   I  ; 关中断(Cortex-M7写入BASEPRI后不能立即生效,需要先关中断)MOV32  R2, OS_KA_BASEPRI_Boundary LDR     R1, [R2]MSR     BASEPRI, R1  ; 屏蔽μC/OS-III可管理的全部中断(优先级4-15的中断)DSBISBCPSIE   I  ; 开中断; 硬件已自动压栈,现PSP指向任务栈的R14,需说明Cortex-M3压栈(以及出栈)的过程在中断以外,使用的是PSP指针MRS     R0, PSP                 ; 获取PSP的指向地址IF {FPU} != "SoftVFP"TST       R14, #0x10IT        EQVSTMDBEQ  R0!, {S16-S31}ENDIFSTMFD   R0!, {R4-R11, R14}  ; 从R14开始压栈,将CPU寄存器的内容压回任务栈MOV32   R5, OSTCBCurPtr       ; 获取任务控制块的地址LDR     R1, [R5]STR     R0, [R1]                ; 将任务栈顶指针保存到任务控制块的成员中MOV     R4, LR                  BL      OSTaskSwHook            MOV32   R0, OSPrioCur    MOV32   R1, OSPrioHighRdyLDRB    R2, [R1]STRB    R2, [R0]                ; 获取当前最高优先级任务的最高优先级MOV32   R1, OSTCBHighRdyPtrLDR     R2, [R1]STR     R2, [R5]                ; 获取当前最高优先级任务的任务控制块ORR     LR,  R4, #0x04         ; R14恢复特殊值,bit2要置1,表示从进程堆栈中做出栈操作,返回后使用PSPLDR     R0,  [R2]               ; 获取切换后任务的任务栈栈底地址LDMFD   R0!, {R4-R11, R14}     ; 将任务栈中的部分内容手动恢复至CPU寄存器(剩下的内容由硬件自动恢复)IF {FPU} != "SoftVFP"TST       R14, #0x10IT        EQVLDMIAEQ  R0!, {S16-S31}ENDIFMSR     PSP, R0                 ; 使PSP指向任务栈的R0MOV32   R2, #0CPSID   I   ; 关中断(Cortex-M7写入BASEPRI后不能立即生效,需要先关中断)MSR     BASEPRI, R2  ; 打开μC/OS-III可管理的全部中断(优先级4-15的中断)DSBISBCPSIE   I   ; 开中断BX      LR                                    ALIGN                                         END
关键字:知名公司_百度导航下载安装手机导航_网站维护中是什么意思_线上销售方案

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

责任编辑: