本篇文章基于STM32f107芯片进行移植,使用相同内核cortex-m3的都可参考
移植要做的就是和cpu相关的代码,cpu上层的代码都是固定的。ucosii中与cpu相关的代码在那里呢?
移植相关的文件就是上述三个文件。
1、头文件os_cpu.h,主要功能:
(1)声明数据类型,增强代码的移植行
typedef unsigned char BOOLEAN;
typedef unsigned char INT8U; /* Unsigned 8 bit quantity */
typedef signed char INT8S; /* Signed 8 bit quantity */
typedef unsigned short INT16U; /* Unsigned 16 bit quantity */
typedef signed short INT16S; /* Signed 16 bit quantity */
typedef unsigned int INT32U; /* Unsigned 32 bit quantity */
typedef signed int INT32S; /* Signed 32 bit quantity */
typedef float FP32; /* Single precision floating point*/
typedef double FP64; /* Double precision floating point*/
//STM32是32位位宽的,这里OS_STK和OS_CPU_SR都应该为32位数据类型
typedef unsigned int OS_STK; /* Each stack entry is 32-bit wide*/
typedef unsigned int OS_CPU_SR; /* Define size of CPU status register*/
(2)定义临界段,也就是开中断关中断函数
#define OS_CRITICAL_METHOD 3 //进入临界段的方法
#if OS_CRITICAL_METHOD == 3
#define OS_ENTER_CRITICAL() {cpu_sr = OS_CPU_SR_Save();}
#define OS_EXIT_CRITICAL() {OS_CPU_SR_Restore(cpu_sr);}
#endif
(3)定义栈的增长方向
栈的增长需要实现约定好,ARM中一般采取满减的方式。
//定义栈的增长方向.
//CM3中,栈是由高地址向低地址增长的,所以OS_STK_GROWTH设置为1
#define OS_STK_GROWTH 1 /* Stack grows from HIGH to LOW memory on ARM */
(4)定义任务切换的宏
//任务切换宏,由汇编实现.
#define OS_TASK_SW() OSCtxSw()
任务切换的代码在os_cpu.asm中实现。
2、os_cpu_c.c 文件
os_cpu_c.c中最重要的函数是栈的初始化函数,用于在创建任务时初始化任务栈,对R0-R12通用寄存器,sp指针,lr寄存器,程序状态寄存器xPSR,以及程序入口地址初始化。
OS_STK *OSTaskStkInit (void (*task)(void *p_arg), void *p_arg, OS_STK *ptos, INT16U opt)
{
OS_STK *stk;
(void)opt; /* 'opt' is not used, prevent warning */
stk = ptos; /* Load stack pointer */
/* Registers stacked as if auto-saved on exception */
*(stk) = (INT32U)0x01000000L; /* xPSR */
*(--stk) = (INT32U)task; /* Entry Point */
*(--stk) = (INT32U)OS_TaskReturn; /* R14 (LR) (init value will cause fault if ever used)*/
*(--stk) = (INT32U)0x12121212L; /* R12 */
*(--stk) = (INT32U)0x03030303L; /* R3 */
*(--stk) = (INT32U)0x02020202L; /* R2 */
*(--stk) = (INT32U)0x01010101L; /* R1 */
*(--stk) = (INT32U)p_arg; /* R0 : argument */
/* Remaining registers saved on process stack */
*(--stk) = (INT32U)0x11111111L; /* R11 */
*(--stk) = (INT32U)0x10101010L; /* R10 */
*(--stk) = (INT32U)0x09090909L; /* R9 */
*(--stk) = (INT32U)0x08080808L; /* R8 */
*(--stk) = (INT32U)0x07070707L; /* R7 */
*(--stk) = (INT32U)0x06060606L; /* R6 */
*(--stk) = (INT32U)0x05050505L; /* R5 */
*(--stk) = (INT32U)0x04040404L; /* R4 */
return (stk);
}
3. os_cpu_a.asm文件
该文件主要处理任务切换、中断相关的函数。有以下三个函数需要处理:
OSStartHighRdy
OSCtxSw
OSIntCtxSw
这三个函数最终都会用到PendSV_Handler。
OSStartHighRdy():运行优先级最高的就绪任务
这个函数只会执行一次。是在OSStart()函数当中运行的。函数运行的时间点是在创建任务完毕,开始执行任务时从当前所有就绪的任务当中找到优先级最高的任务来执行。
;/**************************************************************************************
;* 函数名称: OSStartHighRdy
;*
;* 功能描述: 使用调度器运行第一个任务
;*
;* 参 数: None
;*
;* 返 回 值: None
;**************************************************************************************/
OSStartHighRdy
LDR R4, =NVIC_SYSPRI2 ; set the PendSV exception priority 设置PENDSV中断优先级,最低
LDR R5, =NVIC_PENDSV_PRI
STR R5, [R4]
MOV R4, #0 ; set the PSP to 0 for initial context switch call。设置PSP栈指针为0,PendSV_Handler中用来判断是否是第一次执行任务切换
MSR PSP, R4
LDR R4, =OSRunning ; OSRunning = TRUE 设置OSRunnind为真,表明os已经运行起来了,在一些地方会用到
MOV R5, #1
STRB R5, [R4]
;切换到最高优先级的任务
LDR R4, =NVIC_INT_CTRL ;rigger the PendSV exception (causes context switch)
LDR R5, =NVIC_PENDSVSET ;这里就要置PENDSV中断触发,中断开启后就能进入PENDSV中断了。
STR R5, [R4]
CPSIE I ;enable interrupts at processor level
OSStartHang
B OSStartHang ;should never get here
OSCtxSw()任务切换函数
;/**************************************************************************************
;* 函数名称: OSCtxSw
;*
;* 功能描述: 任务级上下文切换
;*
;* 参 数: None
;*
;* 返 回 值: None
;***************************************************************************************/
OSCtxSw
PUSH {R4, R5}
LDR R4, =NVIC_INT_CTRL ;触发PendSV异常 (causes context switch)
LDR R5, =NVIC_PENDSVSET
STR R5, [R4]
POP {R4, R5}
BX LR
OSIntCtxSw()中断中切换任务
这里是在中断中切换函数时的入口,最终任务切换在PendSV_Handler中完成,代码和上面的代码是基本一样的,区别在于在执行该函数时硬件无需再保存cpu寄存器,因为在调用之前已经发生了中断,已经保存过了。
;/**************************************************************************************
;* 函数名称: OSIntCtxSw
;*
;* 功能描述: 中断级任务切换
;*
;* 参 数: None
;*
;* 返 回 值: None
;***************************************************************************************/
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
PendSV_Handler,真正的任务切换函数
这里是任务真正的切换函数,任务切换要完成的工作是,
1、保存当前任务的R4-R11和PSP到任务栈中,(R0-R3,R12,LR,PC,xPSR已经被硬件自动压栈保存)。
2、找到当前就绪任务中优先级最高的任务,找到这个任务的PSP
3、以找到的任务PSP为线索,将该任务的R4-R11出栈
4、最后,LR记得设置成合适的值,才能回到线程模式下,使用线程栈PSP。LR的具体意义参考这篇文章cortex-m4内核知识讲解
出中断,硬件自动将当前任务的R0-R3,R12,LR,PC,xPSR出栈。完成任务切换
;/**************************************************************************************
;* 函数名称: OSPendSV
;*
;* 功能描述: OSPendSV is used to cause a context switch.
;*
;* 参 数: None
;*
;* 返 回 值: None
;***************************************************************************************/
PendSV_Handler
CPSID I ; 关中断,防止被打断
MRS R0, PSP ; PSP is process stack pointer 如果在用PSP堆栈,则可以忽略保存寄存器,参考CM3权威中的双堆栈-白菜注
CBZ R0, PendSV_Handler_Nosave ; Skip register save the first time,若是第一次任务切换(psp=0,在OSStartHighRdy中设置过),直接执行下一段程序,
SUBS R0, R0, #0x20 ; Save remaining regs r4-11 on process stack
STM R0, {R4-R11} ;将R4-R11入栈保存
LDR R1, =OSTCBCur ; OSTCBCur->OSTCBStkPtr = SP;
LDR R1, [R1]
STR R0, [R1] ; R0 is SP of process being switched out将PSP指针保存到任务块中
; At this point, entire context of process has been saved
PendSV_Handler_Nosave
PUSH {R14} ; Save LR exc_return value
LDR R0, =OSTaskSwHook ; OSTaskSwHook()找到最高优先级任务
BLX R0
POP {R14}
LDR R0, =OSPrioCur ; OSPrioCur = OSPrioHighRdy;
LDR R1, =OSPrioHighRdy
LDRB R2, [R1]
STRB R2, [R0]
LDR R0, =OSTCBCur ; OSTCBCur = OSTCBHighRdy;
LDR R1, =OSTCBHighRdy
LDR R2, [R1]
STR R2, [R0] ;这里找到下一个任务的栈指针PSP
LDR R0, [R2] ; R0 is new process SP; SP = OSTCBHighRdy->OSTCBStkPtr;
LDM R0, {R4-R11} ; Restore r4-11 from new process stack,将R4-R11出栈
ADDS R0, R0, #0x20
MSR PSP, R0 ; Load PSP with new process SP R4-R11出栈后,更新一下PSP
ORR LR, LR, #0x04 ; Ensure exception return uses process stack
CPSIE I
BX LR ; Exception return will restore remaining context
NOP
end
任务切换的压栈出栈细节,这篇文章讲的不错,一目了然
http://blog.csdn.net/_xiao/article/details/78481598