(学习日记)2024.03.03:UCOSIII第五节:常用汇编指令+OS初始化+启动任务+任务切换

本文主要是介绍(学习日记)2024.03.03:UCOSIII第五节:常用汇编指令+OS初始化+启动任务+任务切换,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

写在前面:
由于时间的不足与学习的碎片化,写博客变得有些奢侈。
但是对于记录学习(忘了以后能快速复习)的渴望一天天变得强烈。
既然如此
不如以天为单位,以时间为顺序,仅仅将博客当做一个知识学习的目录,记录笔者认为最通俗、最有帮助的资料,并尽量总结几句话指明本质,以便于日后搜索起来更加容易。


标题的结构如下:“类型”:“知识点”——“简短的解释”
部分内容由于保密协议无法上传。


点击此处进入学习日记的总目录

2024.03.03

  • 十、UCOSIII:常用汇编指令
  • 十一、UCOSIII:OS系统初始化
  • 十二、UCOSIII:启动系统
  • 十三、UCOSIII:任务切换
    • 1、PendSV异常
    • 2、PendSV异常服务函数

十、UCOSIII:常用汇编指令

指令名称作用
EQU给数字常量取一个符号名,相当于C语言中的define
AREA汇编一个新的代码段或者数据段
SPACE分配内存空间
PRESERVE8当前文件栈需按照8字节对齐
EXPORT声明一个标号具有全局属性,可被外部的文件使用
DCD以字为单位分配内存,要求4字节对齐,并要求初始化这些内存
PROC定义子程序,与ENDP成对使用,表示子程序结束
WEAK弱定义,如果外部文件声明了一个标号,则优先使用外部文件定义的标号,如果外部文件没有定义也不出错。要注意的是:这个不是ARM的指令,是编译器的,这里放在一起只是为了方便。
IMPORT声明标号来自外部文件,跟C语言中的EXTERN关键字类似
B跳转到一个标号
ALIGN编译器对指令或者数据的存放地址进行对齐,一般需要跟一个立即数,缺省表示4字节对齐。要注意的是:这个不是ARM的指令,是编译器的,这里放在一起只是为了方便。
END到达文件的末尾,文件结束
IF,ELSE,ENDIF汇编条件分支语句,跟C语言的if else类似
MRS加载特殊功能寄存器的值到通用寄存器
MSR存储通用寄存器的值到特殊功能寄存器
CBZ比较,如果结果为0 就转移
CBNZ比较,如果结果非0 就转移
LDR从存储器中加载字到一个寄存器中
LDR[伪指令]加一个立即数或者一个地址值到一个寄存器。举例:LDR
LDRH从存储器中加载半字到一个寄存器中
LDRB从存储器中加载字节到一个寄存器中
STR把一个寄存器按字存储到存储器中
STRH把一个寄存器存器的低半字存储到存储器中
STRB把一个寄存器的低字节存储到存储器中
LDMIA加载多个字,并且在加载后自增基址寄存器
STMIA存储多个字,并且在存储后自增基址寄存器
ORR按位或
BX直接跳转到由寄存器给定的地址
BL跳转到标号对应的地址,并且把跳转前的下条指令地址保存到LR
BLX跳转到由寄存器REG给出的的地址,并根据REG的LSB切换处理器状态,还要把转移前的下条指令地址保存到LR。ARM(LSB=0),Thumb(LSB=1)。CM3 只在Thumb中运行,就必须保证reg 的LSB=1,否则一个fault 打过来

为了快速地开关中断, CM3 专门设置了一条 CPS 指令,有 4 种用法
在这里插入图片描述
PRIMASK和FAULTMAST是CM3里面三个中断屏蔽寄存器中的两个,还有一个是BASEPRI, 有关这三个寄存器的详细用法见表

名字功能描述
PRIMASK这是个只有单一比特的寄存器。在它被置1后,就关掉所有可屏蔽的异常,只剩下NMI和硬FAULT可以响应。它的缺省值是0,表示没有关中断。
FAULTMASK这是个只有1个位的寄存器。当它置1时,只有NMI才能响应,所有其他的异常,甚至是硬FAULT,也通通闭嘴。它的缺省值也是0,表示没有关异常。
BASEPRI这个寄存器最多有9位(由表达优先级的位数决定)。它定义了被屏蔽优先级的阈值。当它被设成 某个值后,所有优先级号大于等于此值的中断都被关(优先级号越大,优先级越低)。但若被设成0,则不关闭任何中断,0也是缺省值。

十一、UCOSIII:OS系统初始化

OS系统初始化一般是在硬件初始化完成之后来做的,主要做的工作就是初始化μC/OS-III中定义的全局变量。
OSInit()函数在文件 os_core.c中定义
在这里插入图片描述

/* RTOS初始化
** 初始化全局变量
*/
void OSInit (OS_ERR *p_err)
{OSRunning =  OS_STATE_OS_STOPPED;OSTCBCurPtr = (OS_TCB *)0;OSTCBHighRdyPtr = (OS_TCB *)0;OS_RdyListInit();*p_err = OS_ERR_NONE;
}
  • OSRunning = OS_STATE_OS_STOPPED;
    系统用一个全局变量OSRunning来指示系统的运行状态, 刚开始系统初始化的时候,默认为停止状态,即OS_STATE_OS_STOPPED。
    在这里插入图片描述
  • OSTCBCurPtr = (OS_TCB *)0;
    全局变量OSTCBCurPtr是系统用于指向当前正在运行的任务的TCB指针,在任务切换的时候用得到。
  • OSTCBHighRdyPtr = (OS_TCB *)0;
    全局变量OSTCBHighRdyPtr用于指向就绪任务中优先级最高的任务的TCB,在任务切换的时候用得到。 此处暂时不支持优先级,则用于指向第一个运行的任务的TCB。
  • OS_RdyListInit();
    OS_RdyListInit()用于初始化全局变量OSRdyList[],即初始化就绪列表。 OS_RdyListInit()在os_core.c文件中定义
  • *p_err = OS_ERR_NONE;
    代码运行到这里表示没有错误,即OS_ERR_NONE。
  • 全局变量OSTCBCurPtr和OSTCBHighRdyPtr均在os.h中定义
    在这里插入图片描述

十二、UCOSIII:启动系统

任务创建好,系统初始化完毕之后,就可以开始启动系统了。系统启动函数OSStart()在os_core.c中定义
在这里插入图片描述

  • if ( OSRunning == OS_STATE_OS_STOPPED )
    系统是第一次启动的话,if 肯定为真,则继续往下运行。

  • OSTCBHighRdyPtr = OSRdyList[0].HeadPtr;
    OSTCBHIghRdyPtr 指向第一个要运行的任务的TCB。 因为暂时不支持优先级,所以系统启动时先手动指定第一个要运行的任务。

  • OSStartHighRdy();
    OSStartHighRdy()用于启动任务切换,即配置PendSV的优先级为最低,然后触发PendSV异常, 在PendSV异常服务函数中进行任务切换。该函数不再返回,在文件os_cpu_a.s中定义,由汇编语言编写
    在这里插入图片描述

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

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

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

  • CPSIE I
    开中断,因为有些用户在main()函数开始会先关掉中断, 等全部初始化完成后,在启动OS的时候才开中断。
    汇编常用指令作用如下

十三、UCOSIII:任务切换

1、PendSV异常

当调用OSStartHighRdy()函数,触发PendSV异常后,就需要编写PendSV异常服务函数,然后在里面进行任务切换。

PendSV异常服务函数名称必须与启动文件里面向量表中PendSV的向量名一致, 如果不一致则内核是响应不了用户编写的PendSV异常服务函数的,只响应启动文件里面默认的PendSV异常服务函数。

启动文件里面为每个异常都编写好默认的异常服务函数,函数体都是一个死循环,当你发现代码跳转到这些启动文件里面默认的异常服务函数的时候, 就要检查下异常函数名称是否写错了,没有跟向量表里面的一致。

  • PendSV异常服务中主要完成两个工作,
    一是保存上文,即保存当前正在运行的任务的环境参数;
    二是切换下文, 即把下一个需要运行的任务的环境参数从任务栈中加载到CPU寄存器,从而实现任务的切换。

PendSV异常服务中用到了OSTCBCurPtr和OSTCBHighRdyPtr这两个全局变量,这两个全局变量在os.h中定义, 要想在汇编文件os_cpu_a.s中使用,必须将这两个全局变量导入到os_cpu_a.s中
在这里插入图片描述

  • IMPORT OSTCBCurPtr
    IMPORT OSTCBHighRdyPtr
    使用IMPORT关键字将os.h中的OSTCBCurPtr和OSTCBHighRdyPtr这两个全局变量导入到该汇编文件, 从而该汇编文件可以使用这两个变量。如果是函数也可以使用IMPORT导入的方法。

  • EXPORT OSStartHighRdy
    EXPORT PendSV_Handler
    使用EXPORT关键字导出该汇编文件里面的OSStartHighRdy和PendSV_Handler这两个函数, 让外部文件可见。除了使用EXPORT导出外,还要在 某个C的头文件里面声明下这两个函数(在μC/OS-III中是在os_cpu.h中声明),这样才可以在C文件里面调用这两个函数。

2、PendSV异常服务函数

;********************************************************************************************************
;                                          PendSVHandler异常
;********************************************************************************************************
PendSV_Handler
; 任务的保存,即把CPU寄存器的值存储到任务的堆栈中	CPSID   I                                 ; 关中断,NMI和HardFault除外,防止上下文切换被中断	MRS     R0, PSP                           ; 将psp的值加载到R0CBZ     R0, OS_CPU_PendSVHandler_nosave   ; 判断R0,如果值为0则跳转到OS_CPU_PendSVHandler_nosave; 进行第一次任务切换的时候,R0肯定为0;-----------------------一、保存上文-----------------------------
; 任务的切换,即把下一个要运行的任务的栈内容加载到CPU寄存器中
; 在进入PendSV异常的时候,当前CPU的xPSR,PC(任务入口地址),R14,R12,R3,R2,R1,R0会自动存储到当前任务堆栈,同时递减PSP的值
;--------------------------------------------------------------STMDB   R0!, {R4-R11}                     ; 手动存储CPU寄存器R4-R11的值到当前任务的堆栈LDR     R1, = OSTCBCurPtr                 ; 加载 OSTCBCurPtr 指针的地址到R1,这里LDR属于伪指令LDR     R1, [R1]                          ; 加载 OSTCBCurPtr 指针到R1,这里LDR属于ARM指令STR     R0, [R1]                          ; 存储R0的值到	OSTCBCurPtr->OSTCBStkPtr,这个时候R0存的是任务空闲栈的栈顶;-----------------------二、切换下文-----------------------------
; 实现 OSTCBCurPtr = OSTCBHighRdyPtr
; 把下一个要运行的任务的栈内容加载到CPU寄存器中
; 任务的切换,即把下一个要运行的任务的堆栈内容加载到CPU寄存器中
;--------------------------------------------------------------
OS_CPU_PendSVHandler_nosave  ; OSTCBCurPtr = OSTCBHighRdyPtr;LDR     R0, = OSTCBCurPtr                 ; 加载 OSTCBCurPtr 指针的地址到R0,这里LDR属于伪指令LDR     R1, = OSTCBHighRdyPtr             ; 加载 OSTCBHighRdyPtr 指针的地址到R1,这里LDR属于伪指令LDR     R2, [R1]                          ; 加载 OSTCBHighRdyPtr 指针到R2,这里LDR属于ARM指令STR     R2, [R0]                          ; 存储 OSTCBHighRdyPtr 到 OSTCBCurPtrLDR     R0, [R2]                          ; 加载 OSTCBHighRdyPtr 到 R0LDMIA   R0!, {R4-R11}                     ; 加载需要手动保存的信息到CPU寄存器R4-R11MSR     PSP, R0                           ; 更新PSP的值,这个时候PSP指向下一个要执行的任务的堆栈的栈底(这个栈底已经加上刚刚手动加载到CPU寄存器R4-R11的偏移)ORR     LR, LR, #0x04                     ; 确保异常返回使用的堆栈指针是PSP,即LR寄存器的位2要为1CPSIE   I                                 ; 开中断BX      LR                                ; 异常返回,这个时候任务堆栈中的剩下内容将会自动加载到xPSR,PC(任务入口地址),R14,R12,R3,R2,R1,R0(任务的形参); 同时PSP的值也将更新,即指向任务堆栈的栈顶。在STM32中,堆栈是由高地址向低地址生长的。NOP                                       ; 为了汇编指令对齐,不然会有警告END                                       ; 汇编文件结束
  • CPSID I
    关中断,NMI和HardFault除外,防止上下文切换被中断。 在上下文切换完毕之后,会重新开中断。

  • MRS R0, PSP
    将PSP的值加载到R0寄存器。MRS是ARM 32位数据加载指令, 功能是加载特殊功能寄存器的值到通用寄存器。

  • CBZ R0, OS_CPU_PendSVHandler_nosave
    判断R0,如果值为0则跳转到OS_CPU_PendSVHandler_nosave。进行第一次任务切换的时候, PSP在OSStartHighRdy初始化为0,所以这个时候R0肯定为0,则跳转到OS_CPU_PendSVHandler_nosave。 CBZ是ARM16位转移指令,用于比较,结果为0则跳转。

  • STMDB R0!, {R4-R11}
    手动存储CPU寄存器R4-R11的值到当前任务的栈。
    当异常发生,进入PendSV异常服务函数的时候, 当前CPU寄存器xPSR,PC(任务入口地址),R14,R12,R3,R2,R1,R0会自动存储到当前的任务栈,同时递减PSP的值, 这个时候当前任务的栈空间分布具体见 进入PendSV异常时当前任务的栈空间分布图。
    在这里插入图片描述

  • 当执行STMDB R0!, {R4-R11}代码后, 当前任务的栈空间分布具体见 当前任务执行完上文保存时的栈空间分布图
    在这里插入图片描述

  • STR R0, [R1]
    存储R0的值到OSTCBCurPtr->OSTCBStkPtr, 这个时候R0存的是任务空闲栈的栈顶。到了这里,上文的保存就总算完成。
    这个时候当前任务的栈空间分布和栈指针指向具体见 当前任务执行完上文保存时的栈空间分布和StkPtr指向图
    在这里插入图片描述

  • OS_CPU_PendSVHandler_nosave
    当第一次任务切换的时候,会跳转到这里运行。当执行过一次任务切换之后, 则顺序执行到这里。这个标号以后的内容属于下文切换。

  • LDR R0, = OSTCBCurPtr
    加载 OSTCBCurPtr 指针的地址到R0。在ARM汇编中,操作变量都属于间接操作, 即要先获取到这个变量的地址。这里LDR属于伪指令,不是ARM指令。举例:LDR Rd, = label,如果label是立即数, 那Rd等于立即数,如果label是一个标识符,比如指针,那存到Rd的就是label这个标识符的地址。

  • STR R2, [R0]
    存储 OSTCBHighRdyPtr 到 OSTCBCurPtr, 实现下一个要运行的任务的TCB存储到OSTCBCurPtr。

  • LDR R0, [R2]
    加载 OSTCBHighRdyPtr 到 R0。TCB中第一个成员是栈指针StkPtr, 所以这个时候R0等于StkPtr,后续操作任务栈都是通过操作R0来实现,不需要操作StkPtr。

  • LDMIA R0!, {R4-R11}
    LDMIA中的I是increase的缩写,A是after的缩小, R0后面的感叹号“!”表示会自动调节R0里面存的指针。
    将任务栈中需要手动加载的内容加载到CPU寄存器R4-R11,同时会递增R0, 让R0指向空闲栈的栈顶,栈空间的分布情况具体见 任务创建成功后栈空间的分布图。
    在这里插入图片描述

  • 当任务被创建的时候,任务的栈会被初始化, 初始化的流程是:
    先让栈指针StkPtr指向栈顶, 然后从栈顶开始依次存储 异常退出时会自动加载到CPU寄存器 的值和 需要手动加载到CPU寄存器 的值, 具体代码实现见OSTaskStkInit()函数。
    当把需要手动加载到CPU的栈内容加载完毕之后,栈空间的分布图和栈指针指向具体见图 手动加载栈内容到CPU寄存器后的栈空间分布图 , 注意这个时候StkPtr不变,变的是R0。
    在这里插入图片描述

  • MSR PSP, R0
    更新PSP的值,这个时候PSP与图3‑4中R0的指向一致。

  • ORR LR, LR, #0x04
    设置LR寄存器的位2为1,确保异常退出时使用的栈指针是PSP。
    当异常退出后,就切换到就绪任务中优先级最高的任务继续运行。

  • CPSIE I
    开中断。上下文切换已经完成了四分之三,剩下的就是异常退出时自动保存的部分。

  • BX LR
    异常返回,这个时候任务栈中的剩下内容将会自动加载到xPSR,PC(任务入口地址), R14,R12,R3,R2,R1,R0(任务的形参)这些寄存器。同时PSP的值也将更新,即指向任务栈的栈顶。
    这样就切换到了新的任务。 这个时候栈空间的分布具体见 刚切换完成即将运行的任务的栈空间分布和栈指针指向图
    在这里插入图片描述

这篇关于(学习日记)2024.03.03:UCOSIII第五节:常用汇编指令+OS初始化+启动任务+任务切换的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/774679

相关文章

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

JS常用组件收集

收集了一些平时遇到的前端比较优秀的组件,方便以后开发的时候查找!!! 函数工具: Lodash 页面固定: stickUp、jQuery.Pin 轮播: unslider、swiper 开关: switch 复选框: icheck 气泡: grumble 隐藏元素: Headroom

这15个Vue指令,让你的项目开发爽到爆

1. V-Hotkey 仓库地址: github.com/Dafrok/v-ho… Demo: 戳这里 https://dafrok.github.io/v-hotkey 安装: npm install --save v-hotkey 这个指令可以给组件绑定一个或多个快捷键。你想要通过按下 Escape 键后隐藏某个组件,按住 Control 和回车键再显示它吗?小菜一碟: <template

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

学习hash总结

2014/1/29/   最近刚开始学hash,名字很陌生,但是hash的思想却很熟悉,以前早就做过此类的题,但是不知道这就是hash思想而已,说白了hash就是一个映射,往往灵活利用数组的下标来实现算法,hash的作用:1、判重;2、统计次数;

MySQL数据库宕机,启动不起来,教你一招搞定!

作者介绍:老苏,10余年DBA工作运维经验,擅长Oracle、MySQL、PG、Mongodb数据库运维(如安装迁移,性能优化、故障应急处理等)公众号:老苏畅谈运维欢迎关注本人公众号,更多精彩与您分享。 MySQL数据库宕机,数据页损坏问题,启动不起来,该如何排查和解决,本文将为你说明具体的排查过程。 查看MySQL error日志 查看 MySQL error日志,排查哪个表(表空间

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

常用的jdk下载地址

jdk下载地址 安装方式可以看之前的博客: mac安装jdk oracle 版本:https://www.oracle.com/java/technologies/downloads/ Eclipse Temurin版本:https://adoptium.net/zh-CN/temurin/releases/ 阿里版本: github:https://github.com/