UC/OS -II 移植对比

本文介绍了UC/OS-II在S1C33209和ARM核S3C44BOX上的移植步骤,包括修改配置文件、汇编代码及C文件等内容。文章深入分析了关键函数的实现原理,帮助开发者理解并掌握UC/OS-II的移植方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

转载于:httpzq2007.blog.hexun.com9534277_d.html

 

UC/OS-II 的移植步骤分析

                                             zqcumt    07-4-15

关于UC/OS-II 的移植网上介绍的已经很多了,比较流行的几款处理器(例如ARM )在网上都可以直接下载移植好的代码。由于最近选修了一门嵌入式系统的课,用的处理器是EPSON 公司的S1C33 系列,做实验的时候要进行操作系统的移植,这个周末花了一天半的时间学习了一下,因为毕业设计的时候做过ARM 上的移植,于是将两者比较了一下,给出一般的移植要点。由于将来实验还要设计到GUI 的移植以及文件系统的移植和网络协议的移植,我会将自己的学习笔记都记录下来。

大家下载到源码后,针对Intel 80x86 的代码在uCOS-II/Ix86L 目录下。代码是80x86 实模式,且在编译器大模式下编译的。移植部分的代码可在下述文件中找到:OS_CPU.H , OS_CPU_C.C , OS_CPU_A.ASM 。大家可以参考这个例子,对它进行修改。

INCLUDES.H 是主头文件,在所有后缀名为.C 的文件的开始都包含INCLUDES.H 文件。使用INCLUDES.H 的好处是所有的.C 文件都只包含一个头文件,程序简洁,可读性强。缺点是.C 文件可能会包含一些它并不需要的头文件,额外的增加编译时间。与优点相比,多一些编译时间还是可以接受的。用户可以改写INCLUDES.H 文件,增加自己的头文件,但必须加在文件末尾。

///////////////////////////////////////////////////////////////////////////////

一、(1)OS_CPU.H 文件的移植 ( 针对S1C33209)

//////////////////////////////////////////////////////////////////////////

OS_CPU.H 文件中包含与处理器相关的常量,宏和结构体的定义。

#ifdef  OS_CPU_GLOBALS

#define OS_CPU_EXT   // 全局变量

#else

#define OS_CPU_EXT  extern

#endif

///////////////////////////////////////////////////////////////////////////////

由于不同的处理器有不同的字长,µC/OS-II 的移植需要重新定义一系列的数据结构。这部分是和处理器相关的.

typedef unsigned char  BOOLEAN;

typedef unsigned char  INT8U;

typedef signed   char  INT8S;

typedef unsigned short   INT16U;

typedef signed   short   INT16S;

typedef   unsigned int  INT32U;

typedef   signed   int  INT32S;

// 因为没有浮点运算所以删掉

typedef unsigned int OS_STK;// 定义 堆栈的 宽度为 16

typedef unsigned int   OS_CPU_SR;// 定义 状态寄存器的宽度为16

///////////////////////////////////////////////////////////////////////////////

下面的部分主要是为了和UC/OS 第一版的兼容

#define BYTE           INT8S          

#define UBYTE          INT8U  

#define WORD           INT16S 

#define UWORD          INT16U

#define LONG           INT32S

#define ULONG          INT32U

///////////////////////////////////////////////////////////////////////////////

与其他实时系统一样,µC/OS-II 在进入系统临界代码区之前要关闭中断,等到退出临界区后再打开。从而保护核心数据不被多任务环境下的其他任务或中断破坏。Borland C/C++ 支持嵌入汇编语句,所以加入关闭/ 打开中断的语句是很方便的。µC/OS-II 定义了两个宏用来关闭/ 打开中断:OS_ENTER_CRITICAL()OS_EXIT_CRITICAL() 。下面定义了三种方法, 具体的可以查阅相关书籍.

//////////////////////////////////////////////////////////////

#define  OS_CRITICAL_METHOD    2 // 使用第二种方法

///////////////////////////////////////////////////////////////////////////////

#if      OS_CRITICAL_METHOD == 1 // 第一种方法, 由于没有用到, 我们不用去修改, 可以注释掉

#define  OS_ENTER_CRITICAL()  asm  CLI

#define  OS_EXIT_CRITICAL()   asm  STI  

#endif

///////////////////////////////////////////////////////////////////////////////

#if      OS_CRITICAL_METHOD == 2 // 第二种方法, 这个是我们用到的, 要修改, 一般用汇编写, 根据各个处理器的不同而不同, 下面是S1C33 系列的汇编

 

#define  OS_ENTER_CRITICAL() 

asm(" ld.w %r4, %psr");

asm(" xld.w %r5, 0xffffffef");

asm(" and %r4, %r5");// 关中断, 保持状态寄存器的其它状态不变

asm(" ld.w %psr, %r4"); 

             

#define  OS_EXIT_CRITICAL()

asm(" ld.w %r4, %psr");

asm(" or %r4, 0b10000");

asm(" ld.w %psr, %r4"); // 开中断, 保持状态寄存器的其它状态不变

#endif

///////////////////////////////////////////////////////////////////////////////

#if     OS_CRITICAL_METHOD == 3  // 第三种方法, 由于没有用到, 我们不用去修改, 可以直接注视掉

 

#define  OS_ENTER_CRITICAL()

  (cpu_sr = OSCPUSaveSR()) 

#define  OS_EXIT_CRITICAL()

  (OSCPURestoreSR(cpu_sr))   

#endif

///////////////////////////////////////////////////////////////////////////

#define  OS_STK_GROWTH        1     // 堆栈的增长方向, 由高相低, 这个也是和处理器相关的, 有的处理器堆栈是由低向高变, 只要定义为零即可

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////

µC/OS-II, 就绪任务的堆栈初始化应该模拟一次中断发生后的样子,堆栈中应该按进栈次序设置好各个寄存器的内容。OS_TASK_SW() 函数模拟一次中断过程,在中断返回的时候进行任务切换。 中断服务程序(ISR )(也称为例外处理过程)的入口点必须指向汇编函数OSCtxSw(), 参看文件OS_CPU_A.ASM. 在中断向量表vector.c 的代码中修改向量表如下

    (unsigned long)  OSCtxSw,               // 48   12  software exception 0

///////////////////////////////////////////////////////////////////////

#define  uCOS                 0                    

#define  OS_TASK_SW()   asm(" int 0"); / / 使用零号中断来进行任务切换

///////////////////////////////////////////////////////////////////////////////

可以注释掉, 主要是用于在PC 机上模拟时钟节拍

OS_CPU_EXT  INT8U  OSTickDOSCtr;  // 全局变量

////////////////////////////////////////////////////////////////////////////

可以注释掉

#if OS_CRITICAL_METHOD == 3                 

OS_CPU_SR  OSCPUSaveSR(void);

void       OSCPURestoreSR(OS_CPU_SR cpu_sr);

#endif

///////////////////////////////////////////////////////////////////////////////

(2)OS_CPU.H 文件的移植 ( 针对ARM 核的S3C44BOX )

///////////////////////////////////////////////////////////////////////////////
typedef unsigned char BOOLEAN;
typedef unsigned char INT8U; /*8
位无符号整数*/
typedef signed char INT8S; /*8
位有符号整数*/
typedef unsigned short INT16U; /*16
位有符号整数*/
typedef signed short INT16S; /*16
位无符号整数*/
typedef unsigned long INT32U; /*32
位无符号整数*/
typedef signed long INT32S; /*32
位有符号整数*/
typedef float FP32; /*
单精度浮点数*/
typedef double FP64; /*
双精度浮点数*/
///////////////////////////////////////////////////////////////////////////
typedef unsigned int OS_STK;/*
堆栈入口宽度为16*/ARM 处理器相关的代码:

///////////////////////////////////////////////////////////////////////////////

具体的实现见第二步, OS_CPU_A.ASM 中实现
#define OS_ENTER_CRITICAL () ARMDisableInt() /*
关中断在 OS_CPU.A.S 中定义,可以参

     考下面的程序 */
#define OS_EXIT_CRITICAL () ARMEnableInt() /*
开启中断*/
#define OS_STK_GROWTH 1 /*
堆栈由高地址向低地址增长*/

///////////////////////////////////////////////////////////////////////////////

定义宏OS_TASK_SW (),这个宏实际上被定义为os_CPU_a.s 中的函数OSCtxSw ()。由此可以了解OSCtxSw ()的任务:保存当前任务上下文,装入新任务上下文。这里并没有用到模拟软中断

#define OS_TASK_SW          OSCtxSw

///////////////////////////////////////////////////////////////////////////////

// Definitions specific to ARM/uHAL

#define SVC32MODE   0x13

// 定义空闲任务堆栈的大小,可以不用定义这部分

#define SEMIHOSTED_STACK_NEEDS 1024

// idle task stack size (words)

#ifdef SEMIHOSTED

#define OS_IDLE_STK_SIZE        (32+SEMIHOSTED_STACK_NEEDS)

#else

#define OS_IDLE_STK_SIZE        32

#endif

 

// defined in os_cpu_a.s 声明这些函数,在后面都有所定义

extern void OSCtxSw(void);           // task switch routine

extern void OSIntCtxSw(void);           // interrupt context switch

extern void ARMDisableInt(void);        // disable global interrupts

extern void ARMEnableInt(void);         // enable global interrupts

extern void OSTickISR(void);        // timer interrupt routine

///////////////////////////////////////////////////////////////////////////////

二、(1)OS_CPU.A.S 文件的移植 ( 针对S1C33209)

///////////////////////////////////////////////////////////////////////////////

µC/OS-II 的移植需要用户改写OS_CPU_A.ASM 中的四个函数:

OSStartHighRdy()

OSCtxSw()

OSIntCtxSw()

OSTickISR()

////////////////////////////////////////////////////////////////

该函数由SStart() 函数调用,功能是运行优先级最高的就绪任务,在调用OSStart() 之前,用户必须先调用OSInit() ,并且已经至少创建了一个任务(请参考OSTaskCreate() OSTaskCreateExt() 函数)。OSStartHighRdy() 默认指针OSTCBHighRdy 指向优先级最高就绪任务的任务控制块(OS_TCB )(在这之前OSTCBHighRdy 已由OSStart() 设置好了)。OSTCBHighRdy->OSTCBStkPtr 指向的是任务堆栈的顶端

 

OSStartHighRdy:

  xcall   OSTaskSwHook  // 调用OSTaskSwHook ,此时OSRunningFALSE

ld.w    %r4, 0x1

xld.w    %r5, OSRunning

xld.b   [%r5], %r4  // 使OSRunning 的状态为TRUE ,以后调用OSTaskSwHook 时会先保存寄存器再恢复

xld.w  %r5, [OSTCBHighRdy];

ld.w   %sp, %r5;// 得到最高优先级任务的堆栈指针

xld.w  %r4, [%sp+0x0];

ld.w   %sp, %r4;// 偏移量为0

popn   %r15     // 恢复r15-r0, 这个是S1C33209 的汇编语句

reti;      // 返回,此命名执行时,处理器会自动恢复PC 和状态寄存器的值,至此新任务

///////////////////////////////////////////////////////////////////////////////

OSCtxSw() 是一个任务级的任务切换函数(在任务中调用,区别于在中断程序中调用的OSIntCtxSw() )。 它通过执行一条软中断的指令来实现任务切换。软中断向量指向OSCtxSw() 。在 µC/OS-II 中,如果任务调用了某个函数,而该函数的执行结果可能造成系统任务重新调度(例如试图唤醒了一个优先级更高的任务),则在函数的末尾会调用OSSched() , 如果OSSched() 判断需要进行任务调度,会找到该任务控制块OS_TCB 的地址,并将该地址拷贝到OSTCBHighRdy ,然后通过宏OS_TASK_SW() 执行软中断进行任务切换。。注意到在此过程中,变量OSTCBCur 始终包含一个指向当前运行任务OS_TCB 的指针。大部分解释同上,只是多了寄存器的保存这一段。

OSCtxSw:   

xcall   OSTaskSwHook // 中断时,PC 和寄存器的值S1C33209 处理器已经自动保存了

pushn   %r15;// Save current task's context

xld.w   %r4, [OSTCBCur];// 指向当前的运行任务

ld.w    %r5, %sp; Save the SP to R5

ld.w    %sp, %r4;// 保存当前任务的堆栈指针

ld.w    [%sp+0x0], %r5  ; //Save the SP to OSTCBCur

xld.w   %r4, [OSTCBHighRdy] ; //OSTCBCur = OSTCBHighRdy

xld.w   %r5, OSTCBCur;

ld.w    [%r5], %r4 ;

xld.w    %r4, [OSPrioHighRdy]; //OSPrioCur = OSPrioHighRdy ,把任务优先级也保存

xld.w    %r5, OSPrioCur ;

ld.b     [%r5], %r4

xld.w   %r4, [OSTCBCur];// 载入新的任务

ld.w    %sp, %r4

ld.w    %r5, [%sp+0x0];// 恢复新任务的堆栈

ld.w    %sp, %r5  

popn     %r15   ;

reti   ; // 运行新的任务

///////////////////////////////////////////////////////////////////////////////

µC/OS-II 中,由于中断的产生可能会引起任务切换,在中断服务程序的最后会调用OSIntExit() 函数检查任务就绪状态,如果需要进行任务切换,将调用OSIntCtxSw() 。所以OSIntCtxSw() 又称为中断级的任务切换函数。由于在调用OSIntCtxSw() 之前已经发生了中断,OSIntCtxSw() 将默认CPU 寄存器已经保存在被中断任务的堆栈中了。因此在中断服务程序中要保存寄存器,PC 和状态寄存器的值已经被处理器自动保存。OSIntCtxSw() 大部分程序和OSCtxSw ()相同只是不用保存寄存器,它也可直接用OSCtxSw ()来实现

OSIntCtxSw:

xcall  OSTaskSwHook   ; //Call user defined task switch hook

xld.w   %r4, [OSTCBHighRdy]   ;// OSTCBCur = OSTCBHighRdy

xld.w   %r5, OSTCBCur ;

ld.w    [%r5], %r4  ;

xld.w   %r4, [OSPrioHighRdy] ; //OSPrioCur = OSPrioHighRdy ,把任务优先级也保存

xld.w   %r5, OSPrioCur  ;

ld.b    [%r5], %r4

xld.w   %r4, [OSTCBCur]  // 载入新的任务

ld.w    %sp, %r4

ld.w    %r5, [%sp+0x0]

ld.w    %sp, %r5       

popn     %r15                              ;

reti     //Return to new task

///////////////////////////////////////////////////////////////////////////////

µC/OS-II 中的其他中断服务程序一样,OSTickISR() 首先在被中断任务堆栈中保存CPU 寄存器的值,然后调用OSIntEnter() µC/OS-II 要求在中断服务程序开头调用OSIntEnter() ,其作用是将记录中断嵌套层数的全局变量OSIntNesting1 。如果不调用OSIntEnter() ,直接将OSIntNesting1 也是允许的。OSTickISR() 调用OSTimeTick() ,检查所有处于延时等待状态的任务,判断是否有延时结束就绪的任务。 OSTickISR() 的最后调用OSIntExit() ,如果在中断中(或其他嵌套的中断)有更高优先级的任务就绪,并且当前中断为中断嵌套的最后一层。OSIntExit() 将进行任务调度。注意如果进行了任务调度,OSIntExit() 将不再返回调用者,而是用新任务的堆栈中的寄存器数值恢复CPU 现场,然后用IRET 实现任务切换。如果当前中断不是中断嵌套的最后一层,或中断中没有改变任务的就绪状态,OSIntExit() 将返回调用者OSTickISR() ,最后OSTickISR() 返回被中断的任务。如果编译器支持C 语言和汇编的混合编程,则这段代码可以放到 OS_CPU_C.C 中,针对S1C33209 的移植这部分放在OS_CPU_C.C 中。为了连续性就在这里顺便写吧。

void OSTickISR()

{   asm( " pushn %r15");// 保存中断的任务的寄存器

///////////////////////////////////////////////////////////////////////////////

在这个移植中以8 位定时器TIME2 作为时钟节拍,2MS 发生一次中断,在中断向量表vector.c 中在timer2 的入口地址处放入(unsigned long)OSTickISR,   发生中断后程学将会跳到此程序处执行。

*(volatile unsigned char*)0x40285 |= 0x04;   // 清除timer2 的中断标志位

    OSIntEnter();// 处理中断嵌套曾数的增加也可以直接 给OSIntNesting 加一

        if (OSIntNesting == 1) {

      asm(" ld.w %r4, %sp");// 如果嵌套层数为1 则在当前的任务控制块中保存堆栈指针

            asm(" xld.w %r10, [OSTCBCur]");

            asm(" ld.w %sp, %r10");

            asm(" ld.w [%sp+0x0], %r4");

            asm(" ld.w %sp, %r4");

           }

    OSTimeTick();    // 给等待延迟时间的任务的参数减1

OSIntExit(); // 调用这个函数,如果ISR 使更高优先级的任务进入就绪态或者ISR 脱离

             // 了中断嵌套,则此函数不会返回,而是由进行中断级任务切换,否

// 此函数返回OSTickISR ,然后恢复寄存器

    asm(" popn %r15");// 恢复寄存器

    asm(" reti");// 返回中断的任务继续运行

}

///////////////////////////////////////////////////////////////////////////////

为了更清楚一点这里面的过程,顺便付上这里用到TIMER2 的程序,最好有个感性的认识

这部分程序应该在驱动程序里或者放在初始化程序里。始终节拍中断的启动(定时器2 的启动)应该放在OSStart() 运行之后,但是OSStart() 不会返回,所以应该放在OSStart() 之前建立的任务中的优先级最高的任务中启动,如果放在 OSInit()OSStart() 之间启动,程序容易崩溃。

/* Prototype */

void init_timer(void);

void Start_Timer(void);

/////////////////////////////////////////////////////////////////////////////

定时器2 的初始化,完成定时时间等一下设置,每隔2ms 发生一次的中断

void init_timer(void)

{

*(volatile unsigned char *)0x4014E |= 0x0F;

*(volatile unsigned char *)0x40169 = 0x92;/

*(volatile unsigned char *)0x40168 |= 0x02;

*(volatile unsigned char *)0x40285 &= 0xFB;

*(volatile unsigned char *)0x40275 |= 0x04;

}

///////////////////////////////////////////////////////////////////////////////

启动定时器2

void Start_Timer(void)

{

    *(volatile unsigned char*)0x40168 |= 0x01;

}

//////////////////////////////////////////////////////////////////////////////

(2)OS_CPU.A.S 文件的移植 ( 针对ARM 核的S3C44BOX )

/////////////////////////////////////////////////////////////////////////////

µC/OS-II 的移植需要用户改写OS_CPU_A.ASM 中的四个函数:

OSStartHighRdy()

OSCtxSw()

OSIntCtxSw()

OSTickISR()

同时对于ARM 的开关中断( ARMEnableInt ARMDisableInt 的定义也是放在这个文件下的

关于ARM 的程序就不用解释那么清楚了,相信英文大家都能看懂,也可以参考上面的程序实现的功能都是相同的

///////////////////////////////////////////////////////////////////////////////

EXPORT OSStartHighRdy

    IMPORT  OSTaskSwHook

    IMPORT  OSTCBHighRdy

    IMPORT  OSRunning

OSStartHighRdy

        BL OSTaskSwHook       //Call user-defined hook function

 

        LDR     r4,=OSRunning     // Indicate that multitasking has started

        MOV     r5, #1                  

        STRB    r5, [r4]        // OSRunning = true

 

        LDR     r4, =OSTCBHighRdy      // Get highest priority task TCB address

        LDR     r4, [r4]               // get stack pointer

          LDR     sp, [r4]              // switch to the new stack

LDMFD   sp!, {r4}           ;// CPSR 特殊 只能用 MRS MSR 在寄存器间操作

        MSR     cpsr_cxsf, r4            // r4 中恢复cpsr

///////////////////////////////////////////////////////////////////////////////SVC 模式下ARM 处理器不会自动保存PC 的所以需要自己保存和恢复

        LDMFD   sp!, {r0-r12,lr,pc}      ; pop new task s r0-r12,lr & pc

///////////////////////////////////////////////////////////////////////////////

        EXPORT OSCtxSw             // 这个函数别的文件要用

        IMPORT  OSPrioCur           // 这是在别的文件定义的变量 当前任务优先级

        IMPORT  OSPrioHighRdy      // 将要恢复执行的任务的优先级

        IMPORT  OSTCBCur           // 当前任务的 TCB 的指针

        IMPORT  OSTaskSwHook       // 调用用户定义 HOOK

         IMPORT  OSTCBHighRdy       // 将要恢复执行的任务的 TCB 指针

  OSCtxSw

        STMFD sp!, {lr}        // push pc (lr is actually be pushed in place of PC) 因为是从 OS_Sched() BL 到这里的

        STMFD   sp!, {r0-r12,lr}       // push lr & register file

        MRS     r4, cpsr                // CPSR 特殊 只能用 MRS MSR 在寄存器间操作

        STMFD   sp!, {r4}             // push current psr      

        LDR     r4, =OSTCBCur          // Get current task TCB address

        LDR     r5, [r4]

        STR     sp, [r5]                // store sp in preempted tasks s TCB

/////////////////////////////////////////////////////////////////////////////

    以下程序段和 OSIntCtxSw 相同,可以共用一段   

BL    OSTaskSwHook               // call Task Switch Hook

       LDR r5, =OSTCBHighRdy       // 得到就绪任务中的最高优先级的任务

       LDR  r5, [r5]

        STR   r5, [r4]         // 使当前任务指针指向最高优先级的任务

OSTCBCur = OSTCBHighRdy

         LDR r6, =OSPrioHighRdy      

       LDRB     r6, [r6]

       LDR r4, =OSPrioCur          

         STRB     r6, [r4]                // 保存优先级到当前的优先级

         LDR sp, [r5]                //get new task s stack pointer

         LDMFD    sp!, {r4}                //pop new task cpsr

         MSR cpsr_cxsf, r4

LDMFD     sp!, {r0-r12,lr,pc}      // 切换到新的任务

  ///////////////////////////////////////////////////////////////////////////////

关 于OSIntCtxSw()就是上面那下半截。这是因为:ARM硬件的中断时并不自动压栈任何寄存器,所以免去了恢复堆栈指针的麻烦;另外,我们最好在进 入ISR保存当前任务现场时一同保存好TCB中的堆栈指针,而不是在OSIntCtxSw()中保存。具体的解释也可以参考上面这里只是用的寄存器不同而 已。

IMPORT OSTaskSwHook

OSIntCtxSw

        BL      OSTaskSwHook     // 调用OSTaskSwHook 函数

        LDR     r4, =OSTCBHighRdy  // 得到当前最高优先级就绪的任务

        LDR     r4, [r4]

        LDR     r5, =OSTCBCur

        STR     r4, [r5]            // OSTCBCur = OSTCBHighRdy

        LDR     r6, =OSPrioHighRdy

        LDRB    r6, [r6]

        LDR     r5, =OSPrioCur

        STRB    r6, [r5]                // OSPrioCur = OSPrioHighRdy

        LDR     sp, [r4]           // 得到新任务的堆栈指针

         LDMFD   sp!, {r4}           // pop new task cpsr

        MSR     cpsr_cxsf, r4

LDMFD   sp!, {r0-r12,lr,pc}      // 切换到新的任务

///////////////////////////////////////////////////////////////////////////////

这 是 UCOS-II 抢占式调度ISR的一个标本。当一个优先级高的任务放弃CPU使用权,例如要休眠 10 个 Tick,系统调度一个低优先级的任务执行之。OSTickISR()为休眠的任务计时,每次执行,就把休眠任务剩余的睡觉时间减去一个Tick数。如果 发现一个任务睡够了,就顺便恢复它为READY态。做完该做的一切,一个对OSIntExit()的调用,使调度发生了。

EXPORT OSTickISR

    IMPORT  OSIntEnter

    IMPORT  OSTimeTick

    IMPORT  tick_hook  

    IMPORT  OSIntExit

///////////////////////////////////////////////////////////////////////////////

注意 ARM IRQ 中断发生后的 PC 保存(处理器自动保存 LR=PC+4 ),而不是前面的 PC LR 。另外,我们保存的是 SVC 模式下的现场,中断后处理器进入 IRQ 模式,访问不到 SVC 模式下的 R13(sp) ,于是在 IRQ 模式下,只好先另存 SPSR LR ,然后尽快退回到 SVC 模式,这时的 R13 才是任务的堆栈指针。在此模式下再将 SPSR LR 保存到堆栈中,立即保存所有寄存器。任务是在 SVC 模式下运行。关于时钟节拍怎么实现的(如果不是很懂就看下一篇文章关于 ARM 中断处理的详细分析)。

LINK_SAVE   DCD     0    // 申请一个字单元用0 来初始化这个字

PSR_SAVE    DCD     0    // 地址为LINK_SAVE+4

OSTickISR

    STMFD   sp!, {r4}                // 这里的spIRQ 模式下的,将r4 压入堆栈

///////////////////////////////////////////////////////////////////////////

另存 IRQ 模式下的 SPSR LR ,以便在 SVC 模式下也能访问,相当于一个中介作用

    LDR     r4, =LINK_SAVE

    STR     lr, [r4]                //LINK_SAVE = lr ,保存lr,lrIRQ 模式下

    MRS     lr, spsr                //lr=spsr

    STR     lr, [r4, #4]            // PSR_SAVE = spsr_irq, 保存spsr

////////////////////////////////////////////////////////////////////////////

    LDMFD   sp!, {r4}                 // 恢复r4 中的内容

    ORR     lr, lr, #0x80   // Mask irq for context switching before

    MSR cpsr_cxsf , lr      // IRQ 模式恢复到SVC 模式

////////////////////////////////////////////////////////////////////////////

    SUB     sp, sp, #4          // Space for PC 保留位置

    STMFD   sp!, {r0-r12, lr}   // 保存寄存器和lr

    LDR     r4, =LINK_SAVE     //r4= lr_irq

    LDR     lr, [r4, #0]        //lr=lr_irq

    SUB     lr, lr, #4              // PC = LINK_SAVE - 4, 这个一定要正确

///////////////////////////////////////////////////////////////////////////////

  PC= LR 4 存回到堆栈中,刚才跳过了 PC 4 字节的空间 (R1 R12 再加 lr 共占了 14 个字 )

    STR     lr, [sp, #(14*4)]   //sp=sp+14*4, 因为堆栈是从高地址向低地址递减

                                    PC=LR -4

    LDR     r4, [r4, #4]            // r4 = PSR_SAVE,

    STMFD   sp!, {r4}              // save CPSR of the task

    LDR     r4, =OSTCBCur   // sp 保存到当前的任务中

    LDR     r4, [r4]

    STR     sp, [r4]                // OSTCBCur -> stkptr = sp 

         BL    OSIntEnter  / / 处理中断嵌套曾数的增加也可以直接 给OSIntNesting 加一

    BL OSTimeTick            // 调用ostimetick()

    BL  tick_hook          // 我们在 Tick_hook() 里清除 S3C44B0x Tick_Int_Pend                           函数在 main.c 里,是另加的

    BL  OSIntExit         // 决定是否进行任务调度

/////////////////////////////////////////////////////////////////////////////

  如果返回则继续运行此任务

    LDMFD   sp!, {r4}              //pop new task cpsr

    MSR     cpsr_cxsf, r4

    LDMFD   sp!, {r0-r12,lr,pc}       // pop new task r0-r12,lr & pc

///////////////////////////////////////////////////////////////////////////////

定义关中断,主要是为了安全访问临界区的资源

  EXPORT     ARMDisableInt

ARMDisableInt

    MRS r0, cpsr

    STMFD   sp!, {r0}         // push current PSR

    ORR r0, r0, #0xC0

    MSR cpsr_c, r0      //disable IRQ Int s

    MOV pc, lr         // 返回

///////////////////////////////////////////////////////////////////////////////

定义开中断

    EXPORT ARMEnableInt

ARMEnableInt

    LDMFD   sp!, {r0}           // pop current PSR

    MSR cpsr_c, r0             //restore original cpsr 

    MOV pc, lr       // 返回

///////////////////////////////////////////////////////////////////////////////

三、(1)OS_CPU.C.C 文件的移植 ( 针对S1C33209)

///////////////////////////////////////////////////////////////////////////////

µC/OS-II 的移植需要用户改写OS_CPU_C.C 中的六个函数:

OSTaskStkInit()

OSTaskCreateHook()

OSTaskDelHook()

OSTaskSwHook()

OSTaskStatHook()

OSTimeTickHook()

实际需要修改的只有 OSTaskStkInit() 函数,其他五个函数需要声明,但不一定有实际内容。这五个函数都是用户定义的,所以OS_CPU_C.C 中没有给出代码。如果用户需要使用这些函数,请将文件OS_CFG.H 中的#define constant OS_CPU_HOOKS_EN 设为1 ,设为0 表示不使用这些函数。

///////////////////////////////////////////////////////////////////////////////

这个函数是很重要的 , 该函数由OSTaskCreate() OSTaskCreateExt() 调用,用来初始化任务的堆栈。初始状态的堆栈模拟发生一次中断后的堆栈结构。 当调用OSTaskCreate() OSTaskCreateExt() 创建一个新任务时,需要传递的参数是:任务代码的起使地址,参数指针(pdata ) ,任务堆栈顶端的地址,任务的优先级。OSTaskCreateExt() 还需要一些其他参数,但与OSTask­StkInit() 没有关系。OSTaskStkInit() 只需要以上提到的3 个参数(task , pdata ,ptos )。在这个堆栈初始化函数中要清楚堆栈中都要保存哪些东西,要留多大的空间,这些都很重要,否则会发生很严重的错误。

 

OS_STK  *OSTaskStkInit (void (*task)(void *pd), void *pdata, OS_STK *ptos, INT16U opt)

{

    INT32U *stk;   // 定义一个指针

    opt    = opt;    /* 这个参数没有用,但是为了防止编译错误*/

stk    = (INT32U*)ptos;                 // 载入堆栈指针

///////////////////////////////////////////////////////////////////////////

S1c33 处理器是在入栈时,先变化sp ,再向当前的sp 指向的地址写入数据。出栈时是先弹出数据,再变化sp

*stk-- = (INT32U)task;             // 存放PC 的地址,s1c33209 的处理器会自动保存

////////////////////////////////////////////////////////////////////////////

存放状态寄存器,同样也会被自动保存,设置为中断开启 参考其PSR 每位的作用。 如果选择任务启动后允许中断发生,则所有的任务运行期间中断都允许;同样,如果选择任务启动后禁止中断,则所有的任务都禁止中断发生,而不能有所选择。知道为什么吗?因为启动的时候,OSStart ()调用的是 OSStartHighRdy ,即从堆栈中恢复PCSPR 以及寄存器中的内容,因此第一次堆栈中的放的值决定了spr ,其它寄存器的值到没有什么关系。

 

*stk-- = (INT32U)0x00000010;    

*stk-- = (INT32U)0;   // R15 中的值

*stk-- = (INT32U)0;   //--R14

*stk-- = (INT32U)0;    //--R13

    *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)0;    //--R0   

    return ((OS_STK *)stk);       // 返回堆栈指针所指向的地址,恢复寄存器时候要用

}

///////////////////////////////////////////////////////////////////////////////

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值