IMPORT OSRunning;参数指示内核是否运行的布尔型变量
IMPORT OSPrioCur;参数当前任务的优先级
IMPORT OSPrioHighRdy;参数优先级最高任务的优先级
IMPORT OSTCBCur;任务控制块指针指向当前正在运行的任务
IMPORT OSTCBHighRdy;任务控制块指针指向最高优先级任务的任务控制块
IMPORT OSIntNesting;参数保存中断嵌套级
IMPORT OSIntExit;函数退出软中断进行任务切换
IMPORT OSTaskSwHook;勾函数任务切换时运行自己的函数
;IMPORT外部文件定义的引入供本文件使用(输入)
;下面是六个需要自己编写的汇编函数
EXPORT OSStartHighRdy;运行优先级最高的函数供OSstart调用
EXPORT OSCtxSw;任务切换函数
EXPORT OSIntCtxSw;中断结束时进行任务切换供OSintexit调用
EXPORT OS_CPU_SR_Save;OS_ENTER_CRITICAL()进入临界中断
EXPORT OS_CPU_SR_Restore;OS_EXIT_CRITICAL()退出临界中断
EXPORT PendSV_Handler;
;EXPORT本文件定义的供外部文件引用(输出)
NVIC_INT_CTRL EQU 0xE000ED04 ; 中断控制及状态寄存器ICSR P135
NVIC_SYSPRI2 EQU 0xE000ED22 ; 系统优先级寄存器 P132*写错?ED22?好像两个都可以
NVIC_PENDSV_PRI EQU 0xFFFF0000 ; PendSV中断和系统节拍中断
; (都为最低,0xff).
NVIC_PENDSVSET EQU 0x10000000 ; 触发软件中断的值.挂起PENDSV
PRESERVE8;保证8字节对齐
AREA |.text|, CODE, READONLY;只读代码
THUMB;16位THUMB指令
;********************************************************************************************************
; CRITICAL SECTION METHOD 3 FUNCTIONS
;
; Description: Disable/Enable interrupts by preserving the state of interrupts. Generally speaking you
; would store the state of the interrupt disable flag in the local variable 'cpu_sr' and then
; disable interrupts. 'cpu_sr' is allocated in all of uC/OS-II's functions that need to
; disable interrupts. You would restore the interrupt disable state by copying back 'cpu_sr'
; into the CPU's status register.
;
; Prototypes : OS_CPU_SR OS_CPU_SR_Save(void);
; void OS_CPU_SR_Restore(OS_CPU_SR cpu_sr);
;OS_CPU_SR 大小取决于CPU状态寄存器大小32位
;
; Note(s) : 1) These functions are used in general like this:
;
; void Task (void *p_arg)
; {
; #if OS_CRITICAL_METHOD == 3
; OS_CPU_SR cpu_sr;
; #endif
;
; :
; :
; OS_ENTER_CRITICAL();
; :
; :
; OS_EXIT_CRITICAL();
; :
; :
; }
;********************************************************************************************************
;0S_CPU_A.ASM(STM32)这俩个函数是进出中断延用的就是防止任务切换时被意外打断
;0S_CPU_A.ASM(STM32)没啥特别的开关中断返回。
OS_CPU_SR_Save
MRS R0, PRIMASK ;读取PRIMASK到R0,R0为返回值
CPSID I ;PRIMASK=1,关中断(NMI和硬件FAULT可以响应)屏蔽所有可屏蔽异常
BX LR ;返回
OS_CPU_SR_Restore
MSR PRIMASK, R0 ;读取R0到PRIMASK中,R0为参数
BX LR ;返回
;0S_CPU_A.ASM(STM32)这个函数是一开始启动时用的,设置PENDSV优先级最低,这样只有在没有其他异常(中断,事件)运行下进行挂起。
;PendSV可悬起系统调用确保任务切换不会打断中断
OSStartHighRdy
LDR R4, =NVIC_SYSPRI2 ; set the PendSV exception priority
LDR R5, =NVIC_PENDSV_PRI ;设置系统服务优先级为最低优先级
STR R5, [R4] ;只有在没有其他异常的情况下才会触发系统服务中断进行上下文切换
;0S_CPU_A.ASM(STM32)R4写零这样第一次运行在PendSV_Handler中将调用nosave函数,psp一开始为零如果保存psp-32将为不确定值,退出中断后会跑飞。
MOV R4, #0 ; set the PSP to 0 for initial context switch call
MSR PSP, R4 ;设置进程堆栈寄存器为0为初始化上下文切换指令
;0S_CPU_A.ASM(STM32)告诉系统ucos初始化完毕可以启动了
LDR R4, =OSRunning ; OSRunning = TRUE
MOV R5, #1 ;
STRB R5, [R4] ;STRB位传送指令
;0S_CPU_A.ASM(STM32)STM32有PENDSV 任务切换是在PENDSV完成的,所以这里要触发PENDSV
LDR R4, =NVIC_INT_CTRL ;rigger the PendSV exception (causes context switch)
LDR R5, =NVIC_PENDSVSET ;悬起PENDSV触发系统调用中断进行任务调度
STR R5, [R4]
CPSIE I ;enable interrupts at processor level快速开中断
;0S_CPU_A.ASM(STM32)这个函数就是告诉你出错了,系统崩溃的都会到这里
OSStartHang
B OSStartHang ;should never get here好吧,正常的程序是不会跑到这里的。
;0S_CPU_A.ASM(STM32)管他是中断切换还是任务切换,要做的就是触发PENDSV
OSCtxSw
PUSH {R4, R5} ;PUSH {R4, R5,LR}
LDR R4, =NVIC_INT_CTRL ;触发PendSV异常 (causes context switch)
LDR R5, =NVIC_PENDSVSET
STR R5, [R4]
POP {R4, R5} ;POP {R4, R5,PC}改成这样可以省略BX
BX LR
;BX跳转并改变指令集
;
OSIntCtxSw
PUSH {R4, R5}
LDR R4, =NVIC_INT_CTRL ;触发PendSV异常 (causes context switch)
LDR R5, =NVIC_PENDSVSET
STR R5, [R4]
POP {R4, R5}
BX LR
NOP
;0S_CPU_A.ASM(STM32)这个就是任务切换的地方了,不能被打断,所以先关中断
PendSV_Handler
CPSID I ; Prevent interruption ;during context switch 关闭中断防止在切换任务时打断任务切换
;0S_CPU_A.ASM(STM32)硬件仿真的时候进中断都是用MSP 所以保存还是有必要的。
MRS R0, PSP ; PSP is process stack pointer 如 ;果在用PSP堆栈,则可以忽略保存寄存器,参考CM3权威中的双堆栈-白菜注
CBZ R0, PendSV_Handler_Nosave ; Skip register save the first time
;0S_CPU_A.ASM(STM32)如果PSP的值为零则运行PendSV_Handler_Nosave
SUBS R0, R0, #0x20 ; Save remaining regs r4-11 on process stack
;将PSP的值-32保存在R0
STM R0, {R4-R11} ;一个批量入栈操作将R4到R11全部保存在进程堆栈中地址保存在R0内
;MSR MSP,R0
;PUSH {R4-R11}
;0S_CPU_A.ASM(STM32) STM R0, {R4-R11} 可以用上面两句代替。上面一段把准备被切掉的函数的寄存器R4-R7都保存了。
;0S_CPU_A.ASM(STM32) 因为其他寄存器都是自动保存的所以不要管。
LDR R1, =OSTCBCur ; OSTCBCur->OSTCBStkPtr = SP
;将OSTCBCur指针的地址保存在R1中
LDR R1, [R1]
;从R1中的地址处读数据读到得就是OSTCBCur指针存储在R1中
STR R0, [R1] ; R0 is SP of process being switched out R0是当前进程堆栈的PSP指针即即将被切换任务的进程的PSP指针
;将R0中的数据(即PSP-32的值)存放在OSTCBCur指针指向的地址中
; At this point, entire context of process has been saved
;0S_CPU_A.ASM(STM32)到这里进程的所有内容都保存完毕保存了,包括R4-R11的值和旧的任务堆栈的PSP地址
;0S_CPU_A.ASM(STM32)这里郁闷了我好久,原来汇编没有跳转指令会按代码顺序一直运行下去。只要有运行PENDSV HANDLER,一定会运行NOSAVE.仿真时候发现的。
;0S_CPU_A.ASM(STM32)这里要调用一个C函数,为了防止LR在C函数中被改必须要保存下。
PendSV_Handler_Nosave
PUSH {LR} ; Save LR exc_return value
;将R14压栈 ;保存回调地址
LDR R0, =OSTaskSwHook ; OSTaskSwHook();
;取勾函数名所在的地址
BLX R0
;0S_CPU_A.ASM(STM32)BLX 带链接跳转并切换指令集 用户代码都是ARM代码
POP {R14}
;出栈
;0S_CPU_A.ASM(STM32)这一块完成任务优先级的变换,运行什么任务是OS判断这个值决定的了。我之前一直以为这里会跳到将要
;0S_CPU_A.ASM(STM32)运行的任务
LDR R0, =OSPrioCur ; OSPrioCur = OSPrioHighRdy
;将参数OSPrioCur的地址存在R0中
LDR R1, =OSPrioHighRdy
;将参数OSPrioHighRdy的地址存放在R1中
LDRB R2, [R1] ;R2中保存OSPrioHighRdy的值
;把R1地址中的数据取出来存在R2中
STRB R2, [R0]
;把R2中的数据存到R0地址对应的内存空间
;0S_CPU_A.ASM(STM32)任务换了任务控制块也要换了
LDR R0, =OSTCBCur ; OSTCBCur = OSTCBHighRdy;
LDR R1, =OSTCBHighRdy
LDR R2, [R1] ;R2中保存的是OSTCBHighRdy这个指针
STR R2, [R0]
;同上
;0S_CPU_A.ASM(STM32)新的任务,有新的进程堆栈
LDR R0, [R2] ; R0 is new process SP; SP = OSTCBHighRdy->OSTCBStkPtr;
;将R2地址中的数据存到R0中OSTCBHighRdy这个指针(将要运行任务的指针)
;0S_CPU_A.ASM(STM32)恢复新任务进程堆栈的值。
LDM R0, {R4-R11} ; Restore r4-11 from new process stack
;将R4-R11寄存器的值恢复到新进程的堆栈下
;POP{R4-R11}
;可以用这句替换上句
ADDS R0, R0, #0x20 ;+32得到新的PSP地址
MSR PSP, R0 ; Load PSP with new process SP
;0S_CPU_A.ASM(STM32)最后把PSP指向新的堆栈位置
;0S_CPU_A.ASM(STM32)到这里又把PSP指针,和R4-R7重新恢复了 ;0S_CPU_A.ASM(STM32)下面这句应该是为了防止意外吧,正常退出都会用PSP堆栈
ORR LR, LR, #0x04 ; Ensure exception return uses process stack
;将LR的第二位置为1并保存到LR确保返回使用进程堆栈。
;0S_CPU_A.ASM(STM32)结束了,开中断吧
CPSIE I ; 开中断
;0S_CPU_A.ASM(STM32)记得跳转
BX LR ; Exception return will restore remaining context
end