当前位置: 首页 > news >正文

uCOS3实时操作系统(系统初始化和任务启动)

文章目录

  • ucos初始化
  • 任务创建
  • 任务启动

ucos初始化

  • 系统运行的过程如下:OSInit -> OSTaskCreate -> OSStart
  • ucos初始化主要在 OSInit 中进行,下面列举了该初始化过程中比较重要的几个步骤:
    OSInit()OSInitHook();OS_CPU_ExceptStkBase //将异常栈的栈底(OS_CPU_ExceptStkBase)按 8 字节对齐OS_KA_BASEPRI_Boundary	//初始化全局变量 OS_KA_BASEPRI_Boundary 的值,该全局变量用于设置 BASEPRI 寄存器OSIntNestingCtr = 0u;//中断嵌套计数OSRunning = OS_STATE_OS_STOPPED; //内核运行状态OSSchedLockNestingCtr = 0u;//任务调度器锁定嵌套计数OSTCBCurPtr = (OS_TCB *)0;/* 指向当前任务的任务控制块的指针 */OSTCBHighRdyPtr = (OS_TCB *)0;/* 指向优先级最高的任务的任务控制块的指针 */OSPrioCur = 0u;/* 当前任务的任务优先级 */OSPrioHighRdy = 0u;/* 优先级最高的任务的任务优先级 */p_stk = OSCfg_ISRStkBasePtr;  // 任务异常栈空间清零处理,该栈空间是一个全局变量…… //一些其他的必要的初始化(但是对于程序分析不是很重要没写入进来)OS_PrioInit();for (i = 0u; i < OS_PRIO_TBL_SIZE; i++) {  //OS_PRIO_TBL_SIZE此变量通过设置的最大优先级数量动态调整OSPrioTbl[i] = 0u;			//我们设置32,刚好一个数组只需要一个字即可装下32bit,此处是进行清零操作}							//若后续创建了新的任务则对应的bit会置1,bit31对应优先级0OS_RdyListInit();p_rdy_list = &OSRdyList[i];   //这 OS_CFG_PRIO_MAX 个就绪态任务链表被存放在数组 OSRdyList[OS_CFG_PRIO_MAX中p_rdy_list->HeadPtr    = (OS_TCB *)0;  //数组 OSRdyList 的一个元素就代表一个就绪态任务链表p_rdy_list->TailPtr    = (OS_TCB *)0;OS_TaskInit(p_err);  // 任务管理的初始化,将任务数量计数器清零,将任务切换次数计数器清零OS_IdleTaskInit(p_err);  //创建空闲任务及其相关初始化变量OS_TickInit(p_err);OSTickCtr             = 0u;//将系统时钟节拍计数器(OSTickCtr)清零OSTickList.NbrEntries = 0u;  //将 Tick 任务链表(OSTickList)中指向下一个 Tick 任务控制块的指针清空OSTickList.NbrUpdated = 0u;OS_StatTaskInit(p_err);  //创建统计任务及其相关初始化变量OS_TmrInit(p_err);  //创建软件定时器任务及其相关初始化变量OSCfg_Init();  //主要用于保证一些未使用到的变量不会被编译器优化掉,中途可能会用到OSInitialized = OS_TRUE; //µC/OS-III 内核初始化状态
    

任务创建

  • 下面是一个简单的任务创建函数
    OSTaskCreate(   (OS_TCB        *)&StartTask_TCB,(CPU_CHAR      *)"start_task",(OS_TASK_PTR    )start_task,(void          *)0,(OS_PRIO        )START_TASK_PRIO,(CPU_STK       *)StartTask_STK,(CPU_STK_SIZE   )START_STK_SIZE / 10,(CPU_STK_SIZE   )START_STK_SIZE,(OS_MSG_QTY     )0,(OS_TICK        )0,(void          *)0,(OS_OPT         )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR),(OS_ERR        *)&err);
    
  • 接下面简要说明一下创建具体过程
    OSTaskCreate……	//各种可能的错误检查,如栈空间大小检测,中断中非法调用等等OS_TaskInitTCB(p_tcb);  //初始化任务控制块中成员变量的值为0#if (CPU_CFG_STK_GROWTH == CPU_STK_GROWTH_HI_TO_LO)  //栈的生长方向是与硬件有关的p_stk_limit = p_stk_base + stk_limit;  //根据栈的生长方向确定 “水位”限制的大小,因为传入是一个值不是地址#elsep_stk_limit = p_stk_base + (stk_size - 1u) - stk_limit;#endifp_sp = OSTaskStkInit(p_task, //初始化任务栈,详细内容参考后面注释1p_arg,p_stk_base,p_stk_limit,stk_size,opt);p_tcb->StkPtr        = p_sp; //将更新后的栈指针更新至任务控制块的栈指针中…… //根据函数传入的参数初始化任务控制块中的成员变量CPU_CRITICAL_ENTER();  //进入临界区OS_PrioInsert(p_tcb->Prio);   //更新在init函数中创建的优先级位图表OS_RdyListInsertTail(p_tcb);  //任务插入就绪态任务链表,将对应的链表的前后指针都设置为任务控制块地址OSTaskQty++;   //任务数量+1CPU_CRITICAL_EXIT();  //退出临界区OSSched();  //任务切换,这个是下一章节的内容,此时OSRunning还是stoppped状态,会直接退出该函数,需要经过OSStart()函数改变此状态才行。
    
  • 注释1 初始化任务栈,任务创建时会在栈的最开始位置创建一个如下图所示的任务栈空间方便后期进行切换,且任务栈创建成功后会将更新后的栈指针更新至任务控制块的栈指针中,便于后期进行出栈操
    在这里插入图片描述
    在这里插入图片描述
    对应的函数实现如下图所示:
    在这里插入图片描述

任务启动

  • 开始 µC/OS-III 任务调度是 µC/OS-III 内核开始运行前的最后一步,在此之前需要先调用函数 OSInit()对 µC/OS-III 内核进行初始化,并至少创建一个应用任务。通过调用函数 OSStart()就能够开始 µC/OS-III 的任务调度。

    OSStart(&err);if (OSInitialized != OS_TRUE) {  //判断用户在此之前是否没有初始化操作系统*p_err = OS_ERR_OS_NOT_INIT;return;}if (OSTaskQty <= kernel_task_cnt) { //判断用户在此之前是否没有创建应用任务*p_err = OS_ERR_OS_NO_APP_TASK;return;}if (OSRunning == OS_STATE_OS_STOPPED) {OSPrioHighRdy   = OS_PrioGetHighest();  //就绪态任务的任务优先级以位图的当时记录在数组 OSPrioTbl 中通过计算前导零的方式就能够非常方便地获取就绪态任务中最高的任务优先级OSPrioCur       = OSPrioHighRdy;OSTCBHighRdyPtr = OSRdyList[OSPrioHighRdy].HeadPtr;OSTCBCurPtr     = OSTCBHighRdyPtr;  //获取最高优先级任务控制块的指针OSRunning       = OS_STATE_OS_RUNNING;  //将状态改为启动OSStartHighRdy();  //此函数是任务开始调度的核心,下面会进一步讲解*p_err           = OS_ERR_FATAL_RETURN;}
    
  • 函数 OSStartHighRdy()是在文件 os_cpu_a.asm 中定义的一个标号,注意此处与任务创建函数中的任务栈初始化联系十分紧密,将初始化任务栈中的寄存器全部都出栈到CPU寄存器里面,代码如下:

    OSStartHighRdyCPSID   I                                                   ; Prevent interruption during context switchMOV32   R0, NVIC_SYSPRI14                                   ; Set the PendSV exception priorityMOV32   R1, NVIC_PENDSV_PRISTRB    R1, [R0]MOVS    R0, #0                                              ; Set the PSP to 0 for initial context switch callMSR     PSP, R0MOV32   R0, OS_CPU_ExceptStkBase                            ; Initialize the MSP to the OS_CPU_ExceptStkBaseLDR     R1, [R0]MSR     MSP, R1BL      OSTaskSwHook                                        ; Call OSTaskSwHook() for FPU Push & PopMOV32   R0, OSPrioCur                                       ; OSPrioCur   = OSPrioHighRdy;MOV32   R1, OSPrioHighRdyLDRB    R2, [R1]STRB    R2, [R0]MOV32   R0, OSTCBCurPtr                                     ; OSTCBCurPtr = OSTCBHighRdyPtr;MOV32   R1, OSTCBHighRdyPtrLDR     R2, [R1]STR     R2, [R0]LDR     R0, [R2]                                            ; R0 is new process SP; SP = OSTCBHighRdyPtr->StkPtr;MSR     PSP, R0                                             ; Load PSP with new process SPMRS     R0, CONTROLORR     R0, R0, #2BIC     R0, R0, #4                                          ; Clear FPCA bit to indicate FPU is not in useMSR     CONTROL, R0ISB                                                         ; Sync instruction streamLDMFD    SP!, {R4-R11, LR}                                  ; Restore r4-11, lr from new process stackLDMFD    SP!, {R0-R3}                                       ; Restore r0, r3LDMFD    SP!, {R12, LR}                                     ; Load R12 and LRLDMFD    SP!, {R1, R2}                                      ; Load PC and discard xPSRCPSIE    IBX       R1
    
  • 需要注意的是,在我们使用裸机进行中断任务切换的时候不管主程序还是中断都是使用的是MSP堆栈指针,而在ucos操作系统中我们在主程序的任务切换上使用的是PSP堆栈指针,在中断系统调度上中我们使用的是MSP堆栈指针,此外,在中断进行时,系统会自动出入栈除R4-R11以外的寄存器。
    在这里插入图片描述
    在这里插入图片描述

  • 控制寄存器选择当前使用哪个堆栈指针。
    在这里插入图片描述

  • 在上面的代码中用到了入栈出栈相关的指令,具体的描述如下图所示:
    在这里插入图片描述

http://www.xdnf.cn/news/72217.html

相关文章:

  • close和shutdown
  • el-select+vue-virtual-scroller解决数据量大卡顿问题
  • Python 爬虫如何获取淘宝商品的 SKU 详细信息
  • 用74HC595芯片就可做一个SPI组件
  • 【内容摘要】大模型内容摘要实战 会议摘要 提示词技巧
  • 【Spring】深入解析 Spring AOP 核心概念:切点、连接点、通知、切面、通知类型和使用 @PointCut 定义切点的方法
  • oracle rac时区问题导致远程查询时间不准
  • 从洗衣房到国学课堂:海信冰箱发起跨越千里的山区助学行动
  • 2024年TETCI SCI2区:增强差分进化麻雀搜索算法DSSADE,深度解析+性能实测
  • AI日报 - 2024年04月22日
  • 【Vue】修饰符
  • 进行ecovadis认证有哪些优势?百胜咨询:专业ecovadis认证辅导机构
  • 安全挑战再升级,2025都有哪些备份与恢复挑战?
  • 开箱即用:一款带世界时钟简约好用在线时间戳转换工具源码
  • 【Linux】:UDP协议
  • C++中的未定义详解
  • 在C++业务类和QML之间创建一个数据桥梁
  • 机器视觉lcd屏增光片贴合应用
  • 什么是Manus,国内用户如何订阅Manus
  • FR806HA小板烧录固件
  • Vue.js进阶实践:串行请求管理与优雅中断方案
  • 内核是如何接收网络包的
  • CountAnything 如何驱动木材行业自动库存管理转型
  • 示波器探头状态诊断与维护技术指南
  • 牛行为-目标检测数据集(包括VOC格式、YOLO格式)
  • aws服务(一)S3介绍使用代码集成
  • 薪技术|0到1学会性能测试第19课-参数化技术之导入数据
  • 【第16届蓝桥杯软件赛】CB组第一次省赛
  • 高防服务器适合哪些行业使用
  • vue3 + element-plus中el-dialog对话框滚动条回到顶部