从零开始学习UCOSII操作系统14--系统移植实战篇
前言:本篇文章就是为了简单的学习使用的,知道原理之后就可以随意的移植到你自己想要的CPU中,所以我使用最简单的芯片S3C2440,目的就是为了让人们了解怎么移植UCOSII。
1、移植UCOSII的关键的三个文件
根据我们上一讲说过的部分:我们需要移植的部分就是OS_CPU.h
OS_CPU_A.ASM,OS_CPU_C.c
(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是满减栈,此代码后面会有所讲解。
移植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