- OSStartHighRdy()用于启动任务切换,即配置PendSV异常为最低优先级,然后触发PendSV异常
- PendSV函数内进行任务切换,无返回值,使用汇编程序编写
- 常见ARM汇编指令详解:
- 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类似
- PendSV_Handler函数中用到的ARM汇编指令
- MRS:加载特殊功能寄存器的值 到通用寄存
- MSR:存储通用寄存器的值到特殊功能寄存器
- CBZ:比较,如果结果为0就转移
- CBNZ:比较,非0转移
- LDR:从存储器中加载字到一个寄存器中
- LDR[伪指令]:加载一个立即数或者一个地址值到一个寄存器。例如:LDR Rd, = label,如果label是立即数,那Rd等于立即数,如果label是一个标识 符,比如指针,那存到Rd的就是label这个标识 符的地址
- 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打过来
- 例程:
;************************************************************************************************************************ ; 全局变量、函数 ;************************************************************************************************************************ IMPORT OSTCBCurPtr ;外部文件引入的参考 IMPORT OSTCBHighRdyPtr ;同上 EXPORT OSStartHighRdy ;本文件定义的函数 EXPORT PendSV_Handler ;同上 ;************************************************************************************************************************ ; 常亮 ;有关内核外设寄存器定义可参考官方文档:STM32F10xxx Crotex_M3 programing manual ;系统控制块外设SCB地址范围:0xE000 ED00 - 0xE000 ED3F ;************************************************************************************************************************ 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 ;触发PendSVn异常的值 BIT28:PENDSVSET ;************************************************************************************************************************ ; 代码产生指令 ;************************************************************************************************************************ PRESERVE8 THUMB AREA CODE, CODE, READONLY ;************************************************************************************************************************ ; 开始第一次上下文切换 ;1、配置PendSV异常的优先级为最低 ;2、在开始第一次上、下文切换之前,设置PSP = 0 ;3、触发PendSV异常,开始上、下文切换 ;************************************************************************************************************************ OSStartHighRdy LDR R0, = NVIC_SYSPRI14 ;设置PendSV异常优先级为最低 LDR R1, = NVIC_PENDSV_PRI STRB R1, [R0] MOVS R0, #0 ;设置psp的值为0,开始第一次上、下文切换 MSR PSP, R0 LDR R0, =NVIC_INT_CTRL ;触发PendSV异常 LDR R1, =NVIC_PENDSVSET STR R1, [R0] CPSIE I ;开中断 OSStartHang B OSStartHang ;程序应 永远不会运行到此处 ;************************************************************************************************************************ ; PendSVHandler异常 ;************************************************************************************************************************ PendSV_Handler ;任务的保存,即把CPU寄存器的值存储到任务的堆栈中 CPSID I ;关中断,NMI和HardFault除外,防止上、下文切换被中断 MRS R0, PSP ;将PSP的值加载到R0 CBZ R0, OS_CPU_PendSVHandle_nosave ;判断R0,如果值为0则跳转到OS_CPU_PendSVHandle_nosave ;进行第一次任务切换的时候,r0肯定为0 ;在进入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存的是任务空闲栈的栈顶 ;任务的切换,即把下一个要运行任务的堆栈内容加载到CPU寄存器中 OS_CPU_PendSVHandle_nosave ;OSTCBCurPtr = OSTCBHighRdyPtr; LDR R0, = OSTCBCurPtr ;加载OSTCBCurPtr指针的地址到R0,这里LDR属于伪指令 LDR R1, = OSTCBHighRdyPtr ;加载OSTCBHighRdyPtr指针的地址到R1,这里LDR属于伪指令 LDR R2, [R1] ;加载OSTCBHighRdyPtr指针到R2, STR R2, [R0] ;存储OSTCBHighRdyPtr到OSTCBCurPtr LDR R0, [R2] ;加载OSTCBHighRdyPtr到R0 LDMIA R0!, {R4-R11} ;加载需要手动保存的信息到CPU寄存器R4-R11 ;更新PSP的值,这时PSP指向下一个要执行的任务的堆栈的栈底(这个栈底已经加上刚刚手动加载到CPU寄存器R4-R11的偏移) MSR PSP, R0 ORR LR, LR, #0x04 ;确保异常返回使用的堆栈指针是PSP,即LR寄存器的bit2为1 CPSIE I ;开中断 ;异常返回,这个时候任务堆栈中的剩下内容将会自动加载到xPSR,PC(任务入口地址)R14\R12\R3\R2\R1\R0(任务的形参) ;同时PSP的值也将更新,即指向任务堆栈的栈顶,在STM32中,堆栈是由高地址向低地址生长 BX LR NOP ;汇编指令对齐,不然会有警告 END ;汇编文件结束
- 总结:涉及到ARM汇编指令,略显复杂
- ARM汇编指令电脑壁纸1920*1080,相当炫酷,来自硬汉论坛
【9】从0到1教你写uC/OS-III————>任务切换PendSV异常
最新推荐文章于 2022-10-03 19:55:36 发布