uCOS-II移植需要需要改动的文件主要有有os_cpu_a.asm、 .c、os_cpu.h三个文件,首先来讲一下汇编文件os_cpu_a.asm。
os_cpu_a.asm中用到的汇编知识及语法:
1、EXTERN:用于声明变脸,表示被声明的变量在其他文件中定义,在本文件中使用
2、EXPORT:用于定义变量,且此变量被其他文件所使用的,与之相对的是IMPORT,表示只能在本文件中使用。
3、MRS/MSR:读/写特殊功能寄存器(xPSR,PRIMASK,FAULTMASK,BASEPRI,CONTROL,这些特殊功能寄存器只能被这两个指令访问)
4、CPSID/CPSIE I/F:开启/关闭中断/异常,这是CM3内核专门设置的一条快速开关指令。
.......
其他一些指令为汇编常用指令。
另外再讲一个知识点:PSP和MSP
堆栈,一般是程序运行时用来临时存储数据的存储结构,符合后进先出的原则,就像抽屉一样,后放进的东西总是要先拿出来。
PSP:进程堆栈指针,一般任务运行时使用的是这个堆栈
MSP:主堆栈指针,系统级的代码运行于这个堆栈,比如异常/中断例程,内核。
首先来说一下OS_CPU_SR_Save函数,代码如下:
OS_CPU_SR_Save
MRS R0, PRIMASK ;保存PRIMASK(中断屏蔽寄存器)内容到R0中
CPSID I ;关闭中断
BX LR ;BX为跳转指令,跳转到LR所指向的地址。相当于退出此函数
PRIMASK这个寄存器是只有单一比特的寄存器,置1表示关掉所有可屏蔽的异常,置0则不关闭任何中断。LR寄存器为连接寄存器,它用于在调用子程序时存储返回地址。OS_CPU_SR_Save()函数被OS_ENTER_CRITICAL()函数调用,而OS_ENTER_CRITICAL()的作用是关闭所有中断或者说进入临界段,防止某些不允许被中断的代码收到干扰,所以在关闭中断时需要调用OS_CPU_SR_Save()函数将与中断有关的PRIMASK保存到堆栈中,当下次重新开启中断时再将他从堆栈中恢复到PRIMASK中,就好像没发生什么一样。
既然有OS_CPU_SR_Save()保存函数,就肯定有恢复函数OS_CPU_SR_Restore
OS_CPU_SR_Restore
MSR PRIMASK, R0
BX LR
此函数功能就像之前所述的将保存的内容恢复到PRIMASK中。此函数一般被OS_EXIT_CRITICAL()函数调用,OS_EXIT_CRITICAL()的作用是开启中断或者说退出临界段。
接着来说说OSStartHighRdy函数,代码如下:
OSStartHighRdy
LDR R0, =NVIC_SYSPRI14
LDR R1, =NVIC_PENDSV_PRI
STRB R1, [R0]
MOVS R0, #0 ;
MSR PSP, R0
LDR R0, =OSRunning
MOVS R1, #1
STRB R1, [R0]
LDR R0, =NVIC_INT_CTRL
LDR R1, =NVIC_PENDSVSET
STR R1, [R0]
CPSIE I
OSStartHighRdy由OSStart()调用,用来启动最高优先级任务,当然任务必须在OSStart()调用前被创建。其中PSP=0是用来告诉具体的任务切换程序这是第一次进行任务切换,做过切换后PSP就不会是0了。需要注意的是,程序中出现了=NVIC_SYSPRI14,NVIC_SYSPRI14表示0xE000ED22这个数,所以LDR R0, =NVIC_SYSPRI14这条指令就相当于是吧0xE000ED22这个地址保存到R0中,即RO指向0xE000ED22这个地址中的内容,不加“=”会把0xE000ED22地址中所存储的内容放到R0中去。
另外,需要说明的是在CM3内核中,真正的任务之间的切换是由PendSV中断来处理的,而不是OSCtxSw或者OSIntCtxSw。
既然讲到了OSCtxSw和OSIntCtxSw,那就来讲讲它们。代码如下:
OSCtxSw
LDR R0, =NVIC_INT_CTRL
LDR R1, =NVIC_PENDSVSET
STR R1, [R0]
BX LR
OSIntCtxSw
LDR R0, =NVIC_INT_CTRL
LDR R1, =NVIC_PENDSVSET
STR R1, [R0]
BX LR
这两位是孪生兄弟,长得一模一样,但是它们的含义却不相同。OSCtxSw用于正常的任务切换,切位任务级调度,而OSIntCtxSw用于在中断结束后进行任务调度,称为中断级调度。
最后我们来讲讲真正的任务切换函数OS_CPU_PendSVHandler函数。在此之前先来说明一下CM3的寄存器。
CM3有通用目的寄存器R0~R12,用于暂时存储数据;堆栈指针寄存器R13;链接寄存器R14,即LR;程序寄存器R15,存储着下一条要执行的指令的地址。
函数代码如下:
OS_CPU_PendSVHandler
CPSID I
MRS R0, PSP
CBZ R0, OS_CPU_PendSVHandler_nosave
SUBS R0, R0, #0x20 ; 切换到存储R4~R11寄存器的堆栈地址
STM R0, {R4-R11}
LDR R1, =OSTCBCur
LDR R1, [R1]
STR R0, [R1]
OS_CPU_PendSVHandler_nosave
PUSH {R14}
LDR R0, =OSTaskSwHook
BLX R0
POP {R14}
LDR R0, =OSPrioCur
LDR R1, =OSPrioHighRdy
LDRB R2, [R1]
STRB R2, [R0]
LDR R0, =OSTCBCur
LDR R1, =OSTCBHighRdy
LDR R2, [R1]
STR R2, [R0]
LDR R0, [R2]
LDM R0, {R4-R11}
ADDS R0, R0, #0x20 ; 调整任务堆栈指针
MSR PSP, R0
ORR LR, LR, #0x04 ; Ensure exception return uses process stack
CPSIE I
BX LR
由于CM3内核自动将R0~R3,R12保存到任务堆栈,所以只需要手动保存R4~R11即可。其他说明见代码注释。