一,移植UCOSII操作系统的CPU前提条件
1,处理器的C编译器能产生可重入型的代码,如果不行的话,那么就不能在任务之间随意的切换,因为当你切换到别的任务的时候,该任务在这个函数的数据就会被破坏。
2,处理器CPU支持中断,并能产生定时中断
3,用C语言就可以开关中断,进行任务切换
4,处理器CPU能够支持一定数量的数据存储硬件堆栈,也就是栈
5,处理器有将堆栈指针以及其他的CPU的寄存器的内容读出,并存储到堆栈或者内存中去的指令。任务切换的时候,需要将当前的CPU指针保存到刚刚执行的任务当中。然后切换到优先级更高的任务当中。
二,UCOSII实际移植的文件:
UCOSII的移植实例就是要求用户编写4个简单的汇编语言函数,如果编译器支持插入行汇编代码就可以将所有的与处理器相关的代码放置到OS_CPU_C.c里面种,就不需要适用汇编文件。
(1)OSStartHighRdy(); //使得最高优先级的任务运行的函数
OSStart()函数调用OSStartHighRdy()来使得就绪太任务中最高优先级的任务开始运行
void OSStartHighRdy()
{
调用用户定义的OSTaskSwHook();
OSRunning = TRUE;
//得到将要恢复运行的任务的堆栈指针。
stack pointer = OSTcbHighRdy->OSTCBStkPtr;
//从新的堆栈中恢复处理器的所有的寄存器,就是把刚刚切换的堆栈保存到别的地方当中
//执行中断返回,然后跳转PC指针到别的地方中去。
}
(2)OSCtxSw(); 任务级的切换是通过执行软中断指令,或者依据处理器的不同,执行TPAP陷阱指令执行的。中断服务子程序,陷阱或者异常处理的向量的地址必须指向OSCtxSw();
//任务的切换的函数:
void OSCtxSw()
{
保存处理器寄存器;
在当前的任务的任务控制块中保存当前任务的堆栈指针;
OSTCBCur->OSTCBStkPtr = stack pointer;
OSTaskSwHook();
OSTCBCur = OSTCBHighRdy;
OSPrioCur = OSPrioHighRdy;
//得到将要重新开始运行的任务的堆栈指针:
stack pointer = OSTCBHighRdy->OSTCBstkPtr;
//从新的任务堆栈中恢复所有的寄存器的值;
//执行中断返回的指令。
}
(3)OSIntCtxSw();
void OSTickISR(void)
{
//保存处理器的寄存器
//调用OSIntEnter或者直接给OSIntNesting加1
if(OSIntNesting ==1)
{
OSTCBCur->OSTCBStkPtr = Stack Pointer;
}
//给产生中断的设备清中断
//重新允许中断
OSTimeTick(); //硬件的产生中断的原理
OSIntExit(); //中断退出
//恢复处理器寄存器
//执行中断返回指令
}
(4)OSTickISR();
void OSintCtxSw(void)
{
//调整堆栈指针
OSintExit();
OSINTCtxSw();
}
移植UCOSII,大部分的代码是基于底层进行编写的,不需要进行移植。移植UCOSII操作系统的时候需要移植的几个文件:INCLUDES.h是一个主头文件,出现在每个.c 文件的第一行。下面讲移植UCOSII操作系统的时候需要实际移植的三个关键文件:
1,OS_CPU.h
/* 数据类型 */
typedef unsigned char BOOLEAN;
typedef unsigned int OS_STK; //堆栈入口的宽度为16位
typedef unsigned short OS_CPU_SR; //定义CPU状态寄存器的宽度为16位
/* 与处理器有关的代码 */
#define OS_ENTER_CRITICAL() //进入临界区的代码
#define OS_EXIT_CRITICAL() //跳出临界区的代码
#define OS_STK_GHOWTH 1 定义堆栈的方向:1=向下递减,0=向上递增
#define OS_TASK_SW() //定义软件任务切换的函数。
2,OS_CPU_C.c (最关键的移植文件):要求用户编写10个简单的C函数:
OSTaskStkInit();OSTaskCreateHook();OSTaskDelHook();OSTaskSwHook();OSTaskIdleHook();
OSTaskStatHook();OSTimeTickHook();OSInitHookBegin();OSInitHookend();OSTCBInitHook();
(1)OSTaskStkInit()的示例代码
OS_STK * OSTaskStkInit(void (*task)(void * pd),
void * pada,
OS_STK *ptos,
INT16U opt);
{
pada = pada;
模拟ISR向量;
//按照预先设计的寄存器值初始化堆栈结构;
//不断的在堆栈中相应的位置填入你要传递的参数
//返回栈顶指针给调用该函数的函数
//在这里假定堆栈是从上往下递减的,下面讨论同样适用于以相反方向从下到上递增的堆栈结构。
}
(2)OSTaskCreateHook():
添加任务的时候OS_TCBInit()函数都会调用OSTaskCreateHook()函数,该函数允许扩展UCOSII的功能,当UCOSII设置完任务控制块OS_TCB初始化的绝大部分的工作后,但是在任务控制块被链接到相应的任务链中之前,以及在该任务就绪运行之前,UCOSII会调用OSTaskCreateHook(),该函数被调用的时候中断是打开的。
(3)OSTaskIdleHook()
很多微处理器都允许执行相应的指令,将CPU置于低功耗模式。而当接收到中断信号的时候,CPU就会退出低功耗模式,OSTaskIdle()函数可调用OSTaskIdleHook()函数,实现CPU的这种低功耗的模式:
void OS_TaskIdle(void *pdata)
{
pdata = pdata;
for(;;)
{
OS_ENTER_CRITICAL();
OSIdleCtr++;
OS_EXIT_CRITICAL();
OSTaskIdleHook();
}
}
void OSTaskIdleHook(void)
{
asm("STOP");
//收到中断并完成中断服务。
}
三,UCOSII三个关键文件:
1,OS_CPU.h
typedef unsigned char BOOLEAN; //声明一个布尔变量
typedef unsigned char INT8U; //无符号的8位整型变量
typedef signed char INT8S; //有符号的8位整型变量
//关键移植部分:
typedef INT32U OS_STK; //堆栈是32位宽度
typedef INT32U OS_CPU_SR; //定义SR的寄存器为32位
//进入临界区函数,此处是通过汇编实现的
#define OS_ENTER_CRITICAL() (cpu_sr = OSCPUSaveSR())
#define OS_EXIT_CRITICAL() (OSCPURestoreSR(cpu_sr))
#define OS_STK_GROWTH 1 //堆栈的生长的方向是从高地址到低地址
//关键的任务切换的函数
#define OS_TASK_SW() OSCtxSw()
2,OS_CPU_C.C
#include "ucos_ii.h"
OS_STK *OSTaskStkInit (void (*task)(void *pd), void *p_arg, OS_STK *ptos, INT16U opt)
{
OS_STK *stk;
opt = opt;
stk = ptos; //定义一个局部变量
*(stk) = (OS_STK)task; //Entry Point
*(--stk) = (INT32U)0; //LR
*(--stk) = (INT32U)0; //R12
*(--stk) = (INT32U)0; //R11
*(--stk) = (INT32U)0; //R10
*(--stk) = (INT32U)0; //R9
*(--stk) = (INT32U)0; //R8
*(--stk) = (INT32U)0; //R7
*(--stk) = (INT32U)0; //R6
*(--stk) = (INT32U)0; //R5
*(--stk) = (INT32U)0; //R4
*(--stk) = (INT32U)0; //R3
*(--stk) = (INT32U)0; //R2
*(--stk) = (INT32U)0; //R1
*(--stk) = (INT32U)p_arg; //R0
*(--stk) = (INT32U)0x000000131L; //CPSR
/* 这个就是配置CPU的堆栈的寄存器,SVC的用户模式,
同时的打开普通中断和快速中断
*/
return (stk);
}
因为其他的函数不是必须要移植的,所以这里就不进行移植了。
此段代码的意义是初始化堆栈,堆栈是用OS_STK定义的数组。
进行堆栈操作时注意S3C2440是满减栈,此代码后面会有所讲解。
3,OS_CPU_A.s:实现核心的任务切换需要的基本的函数
SRCPND EQU 0x4a000000 ; 源中断的寄存器
INTPND EQU 0x4a000010 ; 中断挂起寄存器:IRQ可以读取此寄存器决定服务32个中断源的哪个源
rEINTPEND EQU 0x560000a8
INTOFFSET EQU 0x4a000014
USERMODE EQU 0x10 //用户模式
FIQMODE EQU 0x11 //快速中断模式
IRQMODE EQU 0x12 //中断模式
SVCMODE EQU 0x13 //普通模式
ABORTMODE EQU 0x17 //异常模式
UNDEFMODE EQU 0x1b
MODEMASK EQU 0x1f
NOINT EQU 0xc0
;*********************************************************************************************************
; EXPORT and EXTERNAL REFERENCES
;*********************************************************************************************************/
IMPORT OSRunning
IMPORT OSTCBCur
IMPORT OSTCBHighRdy
IMPORT OSPrioCur
IMPORT OSPrioHighRdy
IMPORT OSIntNesting
IMPORT OSIntEnter
IMPORT OSIntExit
IMPORT OSTaskSwHook
IMPORT OSTimeTick
IMPORT EINT0_Handle
EXPORT OSStartHighRdy
EXPORT OSCtxSw
EXPORT OSTickISR
EXPORT OSIntCtxSw
EXPORT OSCPUSaveSR
EXPORT OSCPURestoreSR
EXPORT OS_CPU_IRQ_ISR
PRESERVE8
AREA UCOS_ARM, CODE, READONLY
;*********************************************************************************************************
; START MULTITASKING
; void OSStartHighRdy(void)
;
; The stack frame is assumed to look as follows:
;
; Entry Point(Task Name) (High memory)
; LR(R14)
; R12
; R11
; R10
; R9
; R8
; R7
; R6
; R5
; R4
; R3
; R2
; R1
; R0 : argument
; OSTCBHighRdy->OSTCBStkPtr --> CPSR (Low memory)
;
; Note : OSStartHighRdy() MUST:
; a) Call OSTaskSwHook() then,
; b) Set OSRunning to TRUE,
; c) Switch to the highest priority task.
;********************************************************************************************************** */
OSStartHighRdy
;----------------------------------------------------------------------------------
; OSRunning = TRUE;
;----------------------------------------------------------------------------------
MSR CPSR_cxsf,#(SVCMODE :OR: NOINT) ;Switch to SVC mode with IRQ&FIQ disable
BL OSTaskSwHook ;Call user define Task switch hook
LDR R0, =OSRunning ; OSRunning =TRUE
MOV R1, #1
STRB R1, [R0]
;----------------------------------------------------------------------------------
; SP = OSTCBHighRdy->OSTCBStkPtr;
;----------------------------------------------------------------------------------
LDR R0, =OSTCBHighRdy
LDR R0, [R0]
LDR SP, [R0]
;----------------------------------------------------------------------------------
; Prepare to return to proper mode
;----------------------------------------------------------------------------------
LDMFD SP!, {R0}
MSR SPSR_cxsf, R0
LDMFD SP!, {R0-R12, LR, PC}^
;**********************************************************************************************************
; PERFORM A CONTEXT SWITCH (From task level)
; void OSCtxSw(void)
;
; Note(s): 1) Upon entry:
; OSTCBCur points to the OS_TCB of the task to suspend
; OSTCBHighRdy points to the OS_TCB of the task to resume
;
; 2) The stack frame of the task to suspend looks as follows:
;
; PC (High memory)
; LR(R14)
; R12
; R11
; R10
; R9
; R8
; R7
; R6
; R5
; R4
; R3
; R2
; R1
; R0
; OSTCBCur->OSTCBStkPtr ----> CPSR (Low memory)
;
;
; 3) The stack frame of the task to resume looks as follows:
;
; PC (High memory)
; LR(R14)
; R12
; R11
; R10
; R9
; R8
; R7
; R6
; R5
; R4
; R3
; R2
; R1
; R0
; OSTCBHighRdy->OSTCBStkPtr ----> CPSR (Low memory)
;*********************************************************************************************************/
OSCtxSw
STMFD SP!, {LR} ;PC
STMFD SP!, {R0-R12, LR} ;R0-R12 LR
MRS R0, CPSR ;Push CPSR
STMFD SP!, {R0}
;----------------------------------------------------------------------------------
; OSTCBCur->OSTCBStkPtr = SP
;----------------------------------------------------------------------------------
LDR R0, =OSTCBCur
LDR R0, [R0]
STR SP, [R0]
;----------------------------------------------------------------------------------
; OSTaskSwHook();
;---------------------------------------------------------------------------------
BL OSTaskSwHook
;----------------------------------------------------------------------------------
; OSTCBCur = OSTCBHighRdy;
;----------------------------------------------------------------------------------
LDR R0, =OSTCBHighRdy
LDR R1, =OSTCBCur
LDR R0, [R0]
STR R0, [R1]
;----------------------------------------------------------------------------------
; OSPrioCur = OSPrioHighRdy;
;----------------------------------------------------------------------------------
LDR R0, =OSPrioHighRdy
LDR R1, =OSPrioCur
LDRB R0, [R0]
STRB R0, [R1]
;----------------------------------------------------------------------------------
; SP = OSTCBHighRdy->OSTCBStkPtr;
;----------------------------------------------------------------------------------
LDR R0, =OSTCBHighRdy
LDR R0, [R0]
LDR SP, [R0]
;----------------------------------------------------------------------------------
;Restore New task context
;----------------------------------------------------------------------------------
LDMFD SP!, {R0} ;POP CPSR
MSR SPSR_cxsf, R0
LDMFD SP!, {R0-R12, LR, PC}^
;*********************************************************************************************************
; TICK HANDLER
;
; Description:
; This handles all the Timer0(INT_TIMER0) interrupt which is used to generate the uC/OS-II tick.
;*********************************************************************************************************/
OSTickISR
MOV R5,LR
MOV R1, #1
MOV R1, R1, LSL #10 ; Timer0 Source Pending Reg.
LDR R0, =SRCPND
LDR R2, [R0]
ORR R1, R1,R2
STR R1, [R0]
LDR R0, =INTPND
LDR R1, [R0]
STR R1, [R0]
;----------------------------------------------------------------------------------
; OSTimeTick();
;----------------------------------------------------------------------------------
BL OSTimeTick
MOV PC, R5 ; Return
;*********************************************************************************************************
; PERFORM A CONTEXT SWITCH (From an ISR)
; void OSIntCtxSw(void)
;
; Description: 1) This code performs a context switch if a higher priority task has been made ready-to-run
; during an ISR.
;
; 2) The stack frame of the task to suspend looks as follows:
;
; PC (High memory)
; LR(R14)
; R12
; R11
; R10
; R9
; R8
; R7
; R6
; R5
; R4
; R3
; R2
; R1
; R0
;
; OSTCBCur->OSTCBStkPtr ----> CPSR (Low memory)
;
;
; 3) The stack frame of the task to resume looks as follows:
;
; PC (High memory)
; LR(R14)
; R12
; R11
; R10
; R9
; R8
; R7
; R6
; R5
; R4
; R3
; R2
; R1
; R0
; OSTCBHighRdy->OSTCBStkPtr ----> CPSR (Low memory)
;*********************************************************************************************************/
OSIntCtxSw
;----------------------------------------------------------------------------------
; Call OSTaskSwHook();
;----------------------------------------------------------------------------------
BL OSTaskSwHook
;----------------------------------------------------------------------------------
; OSTCBCur = OSTCBHighRdy;
;----------------------------------------------------------------------------------
LDR R0, =OSTCBHighRdy
LDR R1, =OSTCBCur
LDR R0, [R0]
STR R0, [R1]
;----------------------------------------------------------------------------------
; OSPrioCur = OSPrioHighRdy;
;----------------------------------------------------------------------------------
LDR R0, =OSPrioHighRdy
LDR R1, =OSPrioCur
LDRB R0, [R0]
STRB R0, [R1]
;----------------------------------------------------------------------------------
; SP = OSTCBHighRdy->OSTCBStkPtr;
;----------------------------------------------------------------------------------
LDR R0, =OSTCBHighRdy
LDR R0, [R0]
LDR SP, [R0]
;----------------------------------------------------------------------------------
; Restore New Task context
;----------------------------------------------------------------------------------
LDMFD SP!, {R0} ;POP CPSR
MSR SPSR_cxsf, R0
LDMFD SP!, {R0-R12, LR, PC}^
OS_CPU_IRQ_ISR
STMFD SP!, {R1-R3} ; We will use R1-R3 as temporary registers
;----------------------------------------------------------------------------
; R1--SP
; R2--PC
; R3--SPSR
;------------------------------------------------------------------------
MOV R1, SP
ADD SP, SP, #12 ;Adjust IRQ stack pointer
SUB R2, LR, #4 ;Adjust PC for return address to task
MRS R3, SPSR ; Copy SPSR (Task CPSR)
MSR CPSR_cxsf, #SVCMODE:OR:NOINT ;Change to SVC mode
; SAVE TASK''S CONTEXT ONTO OLD TASK''S STACK
STMFD SP!, {R2} ; Push task''s PC
STMFD SP!, {R4-R12, LR} ; Push task''s LR,R12-R4
LDMFD R1!, {R4-R6} ; Load Task''s R1-R3 from IRQ stack
STMFD SP!, {R4-R6} ; Push Task''s R1-R3 to SVC stack
STMFD SP!, {R0} ; Push Task''s R0 to SVC stack
STMFD SP!, {R3} ; Push task''s CPSR
LDR R0,=OSIntNesting ;OSIntNesting++
LDRB R1,[R0]
ADD R1,R1,#1
STRB R1,[R0]
CMP R1,#1 ;if(OSIntNesting==1){
BNE %F1
LDR R4,=OSTCBCur ;OSTCBHighRdy->OSTCBStkPtr=SP;
LDR R5,[R4]
STR SP,[R5] ;}
1
MSR CPSR_c,#IRQMODE:OR:NOINT ;Change to IRQ mode to use IRQ stack to handle interrupt
LDR R0, =INTOFFSET
LDR R0, [R0]
LDR R1, IRQIsrVect
MOV LR, PC ; Save LR befor jump to the C function we need return back
LDR PC, [R1, R0, LSL #2] ; Call OS_CPU_IRQ_ISR_handler();
MSR CPSR_c,#SVCMODE:OR:NOINT ;Change to SVC mode
BL OSIntExit ;Call OSIntExit
LDMFD SP!,{R4} ;POP the task''s CPSR
MSR SPSR_cxsf,R4
LDMFD SP!,{R0-R12,LR,PC}^ ;POP new Task''s context
IRQIsrVect DCD EINT0_Handle
;*********************************************************************************************************
; 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 OSCPUSaveSR(void);
; void OSCPURestoreSR(OS_CPU_SR cpu_sr);
;
;
; Note(s) : 1) These functions are used in general like this:
;
; void Task (void *p_arg)
; {
; #if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */
; OS_CPU_SR cpu_sr;
; #endif
;
; :
; :
; OS_ENTER_CRITICAL(); /* cpu_sr = OSCPUSaveSR(); */
; :
; :
; OS_EXIT_CRITICAL(); /* OSCPURestoreSR(cpu_sr); */
; :
; :
; }
;
; 2) OSCPUSaveSR() is implemented as recommended by Atmel''s application note:
;
; "Disabling Interrupts at Processor Level"
;*********************************************************************************************************
OSCPUSaveSR
MRS R0, CPSR ; Set IRQ and FIQ bits in CPSR to disable all interrupts
ORR R1, R0, #0xC0
MSR CPSR_c, R1
MRS R1, CPSR ; Confirm that CPSR contains the proper interrupt disable flags
AND R1, R1, #0xC0
CMP R1, #0xC0
BNE OSCPUSaveSR ; Not properly disabled (try again)
MOV PC, LR ; Disabled, return the original CPSR contents in R0
OSCPURestoreSR
MSR CPSR_c, R0
MOV PC, LR
END