浙江省工程建设协会网站广告制作行业
news/
2025/9/28 3:32:33/
文章来源:
浙江省工程建设协会网站,广告制作行业,临淄房产信息网123,做文字logo的网站写在前面#xff1a; 由于时间的不足与学习的碎片化#xff0c;写博客变得有些奢侈。 但是对于记录学习#xff08;忘了以后能快速复习#xff09;的渴望一天天变得强烈。 既然如此 不如以天为单位#xff0c;以时间为顺序#xff0c;仅仅将博客当做一个知识学习的目录 由于时间的不足与学习的碎片化写博客变得有些奢侈。 但是对于记录学习忘了以后能快速复习的渴望一天天变得强烈。 既然如此 不如以天为单位以时间为顺序仅仅将博客当做一个知识学习的目录记录笔者认为最通俗、最有帮助的资料并尽量总结几句话指明本质以便于日后搜索起来更加容易。 标题的结构如下“类型”“知识点”——“简短的解释” 部分内容由于保密协议无法上传。 点击此处进入学习日记的总目录 2024.03.03 十、UCOSIII常用汇编指令十一、UCOSIIIOS系统初始化十二、UCOSIII启动系统十三、UCOSIII任务切换1、PendSV异常2、PendSV异常服务函数 十、UCOSIII常用汇编指令
指令名称作用EQU给数字常量取一个符号名相当于C语言中的defineAREA汇编一个新的代码段或者数据段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[伪指令]加一个立即数或者一个地址值到一个寄存器。举例LDRLDRH从存储器中加载半字到一个寄存器中LDRB从存储器中加载字节到一个寄存器中STR把一个寄存器按字存储到存储器中STRH把一个寄存器存器的低半字存储到存储器中STRB把一个寄存器的低字节存储到存储器中LDMIA加载多个字并且在加载后自增基址寄存器STMIA存储多个字并且在存储后自增基址寄存器ORR按位或BX直接跳转到由寄存器给定的地址BL跳转到标号对应的地址并且把跳转前的下条指令地址保存到LRBLX跳转到由寄存器REG给出的的地址并根据REG的LSB切换处理器状态还要把转移前的下条指令地址保存到LR。ARM(LSB0)Thumb(LSB1)。CM3 只在Thumb中运行就必须保证reg 的LSB1否则一个fault 打过来
为了快速地开关中断 CM3 专门设置了一条 CPS 指令有 4 种用法 PRIMASK和FAULTMAST是CM3里面三个中断屏蔽寄存器中的两个还有一个是BASEPRI 有关这三个寄存器的详细用法见表
名字功能描述PRIMASK这是个只有单一比特的寄存器。在它被置1后就关掉所有可屏蔽的异常只剩下NMI和硬FAULT可以响应。它的缺省值是0表示没有关中断。FAULTMASK这是个只有1个位的寄存器。当它置1时只有NMI才能响应所有其他的异常甚至是硬FAULT也通通闭嘴。它的缺省值也是0表示没有关异常。BASEPRI这个寄存器最多有9位由表达优先级的位数决定。它定义了被屏蔽优先级的阈值。当它被设成 某个值后所有优先级号大于等于此值的中断都被关优先级号越大优先级越低。但若被设成0则不关闭任何中断0也是缺省值。
十一、UCOSIIIOS系统初始化
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的xPSRPC任务入口地址R14R12R3R2R1R0会自动存储到当前任务堆栈同时递减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 ; 异常返回这个时候任务堆栈中的剩下内容将会自动加载到xPSRPC任务入口地址R14R12R3R2R1R0任务的形参; 同时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寄存器xPSRPC任务入口地址R14R12R3R2R1R0会自动存储到当前的任务栈同时递减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 异常返回这个时候任务栈中的剩下内容将会自动加载到xPSRPC任务入口地址 R14R12R3R2R1R0任务的形参这些寄存器。同时PSP的值也将更新即指向任务栈的栈顶。 这样就切换到了新的任务。 这个时候栈空间的分布具体见 刚切换完成即将运行的任务的栈空间分布和栈指针指向图
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/920214.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!