UC/OS-II的详细移植笔记 两种处理器的移植比较(S1C33209和S3C44B0X)

时间:2022-06-07 23:36:58
关于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核的S3C44B0X ) ///////////////////////////////////////////////////////////////////////////////
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 ,此时OSRunning为FALSE 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(),其作用是将记录中断嵌套层数的全局变量OSIntNesting加1。如果不调用OSIntEnter(),直接将OSIntNesting加1也是允许的。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核的S3C44B0X ) ///////////////////////////////////////////////////////////////////////////// µ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特殊只能用MRSMSR在寄存器间操作         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}              //这里的sp是IRQ 模式下的,将r4压入堆栈 /////////////////////////////////////////////////////////////////////////// 另存IRQ模式下的SPSR和LR,以便在SVC模式下也能访问,相当于一个中介作用     LDR     r4, =LINK_SAVE     STR     lr, [r4]                //LINK_SAVE = lr,保存lr,此lr为IRQ模式下     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()里清除S3C44B0xTick_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,即从堆栈中恢复PC和SPR以及寄存器中的内容,因此第一次堆栈中的放的值决定了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);       //返回堆栈指针所指向的地址,恢复寄存器时候要用 } ///////////////////////////////////////////////////////////////////////////////