UcosII移植之os_cpu_a.asm详


文件os_cpu_a.asm跟处理器的汇编级编程密切相关,该文件时移植的重点文件

变量引用

       IMPORT  OSRunning            

       IMPORT  OSPrioCur

       IMPORT  OSPrioHighRdy

       IMPORT  OSTCBCur

       IMPORT  OSTCBHighRdy

       IMPORT  OSIntNesting

       IMPORT  OSIntExit

       IMPORT  OSTaskSwHook

          

       EXPORT  OSStartHighRdy              

       EXPORT  OSCtxSw

       EXPORT  OSIntCtxSw

        EXPORT  OS_CPU_SR_Save                                      ;

        EXPORT OS_CPU_SR_Restore   

/*****************PendV_Handler修改过***********************/  

       EXPORT  PendSV_Handler

使用IMPORT定义的表示这个是外部标号,不是在本文件中定义的,EXPORT表示这些标号是在本文件中定义,可供外部其他文件调用

EXPORT关键字跟编译器有关,能被keil识别,不能被IAR识别

二 常量定义

/*********中断控制及状态寄存器ICSR的地址*******************/

NVIC_INT_CTRL       EQU     0xE000ED04 

/************系统优先级寄存器地址***************************/

NVIC_SYSPRI2         EQU     0xE000ED20 

/************PendSV中断和系统节拍中断**********************/

NVIC_PENDSV_PRI    EQU     0xFFFF0000 

/************触发软件中断的值*******************************/                                        

NVIC_PENDSVSET    EQU     0x10000000 

EQU指令类似于c语言的#define,

这里涉及到中断控制及状态寄存器NVIC_INT_CTRL,系统异常优先级寄存器NVIC_SYSPR12,以及PendSv中断。

具体分析ucosii为什么涉及到PendSV中断

现在的cpu都是按照程序指针pc的指向来运行程序的,当ucos进行任务切换时候,如果一个高优先级任务就绪,在进行任务调度切换时,将打断正在运行的低优先级任务,为了保证高优先级任务运行完成后,能够回到低优先级任务被打断处继续运行,需要将断点处的程序指针压入堆栈,当高优先级任务结束后,再从堆栈中将断点程序指针放回程序指针PC中,即可实现任务切换无缝衔接。可现在大部分cpu 对pc寄存器压堆栈,弹堆栈的指令,因此当ucosii进行任务切换时,要考虑迂回操作,实现对pc寄存器的改写。还好目前cpu有中断处理机制,能实现对pc寄存器的改写,当cpu响应到一个中断时,系统会自动将断点指针压入堆栈,而中断返回指令,自动将堆栈中的断点指针弹回到pc寄存器,恢复中断的程序,实现对断点的保护与恢复。

Ucosii使用了这个机制,当需要进行任务切换的时候,通过软指令,触发一次中断,间接的保存和恢复了pc寄存器,当然stm32在响应中断时也会保存与恢复除了pc以往的其他寄存器,但不是全部。Ucosii触发的这个中断即是PendSV中断,任务切换时,只需要通过软指令触发PendSV中断,在PendSV中断服务程序中,保护被中断的任务现场,恢复将要运行的任务现场,同时cpu在响应中断过程中自动改写pc指针,即通过人为触发中断,欺骗cpu改写pc寄存器,完美实现任务切换。

NVIC_INT_CTRL:

       地址为0xE000ED04,改寄存器可设置一个挂起的NMI,设置或清除一个挂起PendSV,设置或清除一个挂起systick,查找挂起异常等功能。对于ucos移植,相关的为该寄存器的bit28,即PENDSVSET,当设置为1,则挂起PendSV,为0则不挂起。因此当需要通过软指令触发一个PendSV中断时,可将NVIC_PENDSVET写入中断控制状态寄存器NVIC_INT_CTRL

 NVIC_SYSPRI2:

系统异常优先级是用来设置系统异常的优先级的,地址范围是0xE000ED81-0xE000ED23.

系统异常优先级寄存器位分布图



跟移植相关的为PendSV中断的优先级,上面移植代码中设定系统异常优先级寄存器NVIC_SYSPRI2 地址为0xE000ED20,设定掩码NVIC_PENDSV_PRI为0xFFFF0000,将掩码写入地址0xE000ED20,即将systick的优先级与PendSV的优先级均设定为0xFF,即设定优先级最低。

开关中断


以上两段代码为开关中断的程序段,OS_CPU_SR_Save段为关闭中断,并返回PRIMASK寄存器数值的程序段。OS_CPU_SR_Restore段为将堆栈中保存的中断开关状态回写到PRIMASK寄存器中。

调用此函数之前,必须先定义局部变量cpu_sr,进入中断之前保存CPU状态,退出时恢复状态


四 调度器


OSStartHighRdy程序段被ucos的OSStart调用,实现运行第一个优先级最高的任务,前提是任务已创建。

程序首先设定PendSV优先级,设定PSP堆栈指针,设定系统运行标志OSRunning

然后通过软指令触发PendSV中断,在PendSV中断服务程序中实现任务现场保护与切换,即完成任务调度,开始运行第一个程序。

第一段:设定systick优先级与PendSV优先级设定均为0xFF,即设定优先级最低。

第二段:设定堆栈指针PSP为0,因为此处是系统运行的第一个程序,在PendSV中断服务程序中不需要保持上一个任务的运行环境(没有上一个任务),在这里设定PSP为0,是通知PendSV中断服务程序不需要保存上一个任务的运行环境。在PendSV中断服务程序会判断PSP的值,后面会分析。

第三段:设定系统运行标志OSRunning为运行标志

第四段:通过设定中断控制状态寄存器NVIC_INT_CTRL实现触发PendSV中断,然后在PendSV中断服务程序中实现任务切换。在退出中断服务程序时,系统会自动将第一个任务的指针写入PC寄存器,开始运行第一个任务代码

第五段:中断使能,触发PendSV中断

 

任务级上下切换


OSCtxSw是任务级的上下文切换程序段,在os_cpu.h中的OS_TASK_SW()宏封装的即是OSCtxSw程序段

OSCtxSw程序段只是简单的通过设定中断控制状态寄存器实现触发PendSV中断,然后返回。OSCtxSw中设定触发PendSv中断时,PendSV中断服务程序不会立即运行,因为在调用OS_TASK_SW()时,中断为关闭状态,当使能中断后,PendSV中断服务程序才会有机会运行。

  一般是OS_Sched函数调用OS_TASK_SW()宏

中断级上下切换


OSIntCtxSw是中断级的上下文切换程序段,当一个中断服务程序结束后,OSIntExit()查看是否还有优先级更高的程序就绪,若有则调用OSIntCtxSw程序段进行任务切换,OSIntCtxSw也只是简单的通过设定中断控制状态寄存器实现触发PendSV中断,然后返回。

 OSIntCtxSw与OSCtxSw程序代码一样,但意义不一样。OSCtxSw是任务级切换(如任务A因为等待某个资源或是做延时切换到任务B),OSIntCtxSw是中断级切换(中断退出时,由中断状态切换到另一个任务),此时,被中断打断的任务运行环境已经缓存至堆栈中,因此在从中断切换到新的任务时,无需重新将被中断打断的任务的运行环境入堆栈

  PendSV中断

 

中断服务程序由汇编语言实现上下任务切换。CM3架构处理器在响应中断时自动将xPSR,PC,LR,R12,R3,R2,R1,R0这8个寄存器压入堆栈,在退出中断时,自动将这8个寄存器弹出堆栈,其余R4-R11寄存器就要程序压入堆栈。因此PendSV中断服务程序需要将R4-R11寄存器压入堆栈以及弹出堆栈

方便理解,看一下PendSV_Handler的伪代码


首先判断PSP堆栈指针是否为NULL,若PSP为0,说明将运行系统第一个任务,无需缓存R4-R11寄存器,否则要缓存R4-R11寄存器(即前一个运行任务的运行环境),前一运行任务的xPSR,PC,LR,R12,R3,R2,R1,R0已结在响应PendSV中断时自动压入堆栈,将前一任务的堆栈指针存入任务控制块中。

然后调用任务切换钩子函数OSTsakSwHook()

再然后设定OSPrioCur为当前就绪任务中优先级最高的任务优先级,设定OSTCBCur指向当前就绪任务中优先级最高的任务的控制块。设定PSP指向当前就绪任务中优先级最高的任务的堆栈

然后从当前就绪任务中优先级最高的任务的堆栈中弹出R4-R11寄存器,即恢复运行缓存。退出中断,处理器自动将xPSR,PC,LR,R12,R3,R2,R1,R0从当前就绪任务中优先级最高的任务堆栈中弹出,即实现任务上下文切换

 

你可能感兴趣的:(嵌入式学习)