暴走的指压师

为何中年妇女对14岁正太不理不睬?28岁大妈是如何保持14岁身材与脸颊?三无蓝毛究竟身在何处?为何少年抛弃妹子去寻找基友的菊花,大妈抛弃正太去和眼镜妹百合? 一切的一切,请看《Q ヱヴァンゲリヲン新劇場版:Q 》
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

Ucos ii 移植到LPC2148

Posted on 2011-04-18 20:07  晓彻  阅读(1039)  评论(0编辑  收藏  举报

所谓"移植",就是使一个实时内核能在其它的微处理器或微控制器上运行。尽管大部分μC/OS-II的代码是用C语言编写的,但是在编写与处理器硬件相关的代码时还是不得不使用汇编语言。移植的主要工作就是编写这些与处理器硬件相关的代码。

Ucos软件体系结构如下所示:

 

根据μC/OS-II的要求,移植μC/OS-II到一个新的体系结构上需要提供2个或3个文件:

OS_CPU.H(C语言头文件)

OS_CPU_C.C(C程序源文件)

OS_CPU_A.ASM(汇编程序源文件)

其中OS_CPU_A.ASM在某些情况下不需要,但极其罕见。不需要OS_CPU_A.ASM的必须满足以下苛刻条件,而同时满足这些条件的微控制器几乎没有:

1.可以直接使用C语言开关中断;

2.可以直接使用C语言编写中断服务程序;

3.可以直接使用C语言操作堆栈指针;

4.可以直接使用C语言保存CPU的所有寄存器。

 

Include.hconfig.h

μC/OS-II要求所有.C文件的都要包含头文件includes.h,这样使得用户项目中的每个.C文件不用分别去考虑它实际上需要哪些头文件。使用INCLUDES.H的缺点是它可能会包含一些实际不相关的头文件,这意味着每个文件的编译时间可能会增加,但却增强了代码的可移植性。

在本移植中另外增加了一个头文件config.h,我们要求所有用户程序必须包含config.h,在config.h中包含includes.h和特定的头文件和配置项。而μC/OS-II的系统文件依然只是包含includes.h,即μC/OS-II的系统文件完全不必改动。所有的配置改变包括头文件的增减均在config.h中进行,而includes.h定下来后不必改动(μC/OS-II的系统文件需要包含的东西是固定的)。这样,μC/OS-II的系统文件需要编译的次数大大减少,编译时间随之减少。

 

OS_CPU.H

(1)可移植性类型定义

μCOS-II不使用C语言中的short、int、long等数据类型的定义,因为它们与处理器类型有关,隐含着不可移植性。代之以移植性强的整数数据类型,这样,既直观又可移植,不过这就成了必须移植的代码。

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    float         FP32;

typedef    double         FP64;

typedef    INT32U         OS_STK;

(2)软中断底层接口

为了使底层接口函数与处理器状态无关,同时在任务调用相应的函数不需要知道函数位置,本移植使用软中断指令SWI作为底层接口,使用不同的功能号区分不同的函数。软中断功能号分配如下表所示,未列出的为保留功能。

用软中断作为操作系统的底层接口就需要在C语言中使用SWI指令。在IAR中,有一个关键字__swi,用它声明一个不存在的函数,则调用这个函数就在调用这个函数的地方插入一条SWI指令,并且可以指定功能号。同时,这个函数也可以有参数和返回值,其传递规则与一般函数一样。

 

(3)堆栈生长方式

μCOS-II使用结构常量OS_STK_GROWTH中指定堆栈的生长方式:

置OS_STK_GROWTH为0表示堆栈从下往上长。

置OS_STK_GROWTH为1表示堆栈从上往下长。

虽然ARM处理器核对于两种方式均支持,但IAR的C语言编译器仅支持一种方式,即从上往下长,并且必须是满递减堆栈,所以OS_STK_GROWTH的值为1。

#define OS_STK_GROWTH 1

 

 

Os_cpu_c.c

  1. OSTaskStkInit( ) 接口

该函数用于初始化任务堆栈,使任务的堆栈看起来就像刚发生中断一样。即任务被执行时,就像从中断返回一样。在编写此函数之前,必须先确定任务的堆栈结构。而任务的堆栈结构是与CPU的体系结构、编译器有密切的关联。本移植的堆栈结构如下图所示。

 

  1. 软件中断异常服务程序

操作系统与硬件相关的底层函数使用软件中断作为接口,如下表所示。移植代码中一个重要的工作就是为这些软件中断编写服务程序。

 

  1. Hook( )函数

    在Os_cpu_c.c文件中还有许多钩子函数,它们在某个特定的系统动作时被调用,允许执行函数中的用户代码。这些函数默认是空函数,用户根据实际情况添加相关代码。它们分别如下表所示。

 

 

OS_CPU_A.ASM

  1. 软中断

Os_cpu_a.s文件中有软件中断的汇编接口程序、任务切换程序、OS启动时运行就绪最高优先级任务的程序。

  当发生软件中断时,程序通过异常向量表跳转到软中断的汇编与C接口程序SoftwareInterrupt处,下图为SoftwareInterrupt的流程图。 

                           

  因为执行任务切换时(执行新任务)堆栈指针会指向用户的堆栈,这样下一次进入管理模式(在osintctxsw中就会用到管理模式)就会破坏用户堆栈,从而导致程序执行不正确。所以程序在一开始设置堆栈指针。软中断指令使处理器进入管理模式,而用户程序处于系统/用户模式,其它异常也有自己的处理器模式,都有各自的堆栈指针,不会因为给堆栈指针赋值而破坏其它处理器模式的堆栈而影响其它程序的执行。返回的地址已经存储在连接寄存器LR中而不是存储在堆栈中。由于进人管理模式自动关中断,所以这段程序不会被其它程序同时调用,设置的堆栈指针指向的位置肯定是空闲位置,后一次调用不会影响前一次调用。这样就可以保证"LDR  SP, StackSvc"进行正确的堆栈指针设置。

    因为ARM处理器核具有两个指令集,在执行Thumb指令的状态时不是所有寄存器都可见(参考ARM的相关资料),而且任务又可能不在特权模式(不能改变CPSR)。为了兼容任意一种模式,本移植使用软中断指令SWI使处理器进入管理模式和ARM指令状态,并使用功能0实现OS_TASK_SW()的功能,

Ø附: 1LR没有入栈:

         SWIUndef异常返回时使用:

movs pc, LR;从FIQIRQ和预取终止返回时使用:

SUBS PC, LR#4 从数据异常返回时使用:

SUBS PC, LR#8

        2LR有入栈:

在使用上述指令异常返回时,如果LR之前被压栈的话使用LDM "",达到同样效果。 例如:

LDMFD SP!, {PC}

 

   

(2) OSIntCtxSw的编写

μC/OS-中,任务切换只是简单的将处理器寄存器保存到将被挂起的任务的堆栈中,并且将更高优先级的任务从堆栈中恢复出来。处于就绪状态的任务的堆栈结构看起来就像刚发生过中断并将所有的寄存器保存到堆栈中的情形一样。换句话说,μC/OS-要运行处于就绪状态的任务必须要做的事就是将所有处理器寄存器从任务堆栈中恢复出来,并且执行中断的返回。其执行流程如下图所示:

 

μC/OS-中,用户级任务调度时会调用宏(或者函数)OS_TASK_SW(),它是在μC/OS-从低优先级任务切换到最高优先级任务时被调用的,μC/OS-建议OS_TASK_SW()通过某种途径最终调用函数OSCtxSw()。函数OSCtxSw()是与系统相关的,μC/OS-提供的OSCtxSw()函数原型如下:

   

OSCtxSw()原型的程序清单

void OSCtxSw(void)

{

保存处理器寄存器;            

将当前任务的堆栈指针保存到当前任务的OS_TCB中;

OSTCBCur->OSTCBStkPtr = Stack pointer

  调用用户定义的OSTaskSwHook()

  OSTCBCur  = OSTCBHighRdy

  OSPrioCur = OSPrioHighRdy

  得到需要恢复的任务的堆栈指针;

    Stack pointer = OSTCBHighRdy->OSTCBStkPtr

  将所有处理器寄存器从新任务的堆栈中恢复出来;

  执行中断返回指令;

}

 

  1. OSStartHighRdy编写

μC/OS-II的多任务环境由函数OSStart( ) 启动。用户在调用该函数之前,必须已经建立了一个或更多任务。OSStart()最终调用函数OSStartHighRdy( )运行多任务启动前优先级最高的任务,而它最终是调用__OSStartHighRdy实现的,其代码如下所示:

__OSStartHighRdy

MSR CPSR_c, #(NoInt | SYS32Mode)

;告诉uC/OS-II自身已经运行

LDR R4, =OSRunning

MOV R5, #1

STRB R5, [R4]

;调用钩子函数

BL OSTaskSwHook

LDR R6, =OSTCBHighRdy

;取得新任务的TCB指针

LDR R6, [R6]

B OSIntCtxSw_1

 

关于时钟节拍

IRQ是受μC/OS-II管理的中断,而对于FIQ不做处理,这是为了提高FIQ的响应速度。由于各种ARM芯片的中断系统不一样,各个用户的目标板也不一样,对于中断和时钟节拍是需要进一步移植的代码。为此编写了一个汇编宏,它是μC/OS-II for ARM7通用的中断服务程序的汇编与C函数接口代码。

 

中断服务程序

因为中断发生时肯定是允许中断的,所以如果用户在清除中断源之前调用μC/OS-II的系统服务函数就很可能会造成芯片的中断系统工作异常而使程序工作异常。因此在函数开始处关闭中断,或者直接给变量OSEnterSum赋1。如果用户程序没有这种情况,则不需要这个操作。在执行OS_EXIT_CRITICAL( )后,中断重新打开,如果在接下来的用户处理程序中发生中断,就可以实现中断嵌套。中断服务程序过程伪码如下图所示:

void ISR(void)

{

OS_ENTER_CRITICAL()或直接给变量OsEnterSum1;

清除中断源;    

通知中断控制器中断结束:

开中断:

OS_EXIT_CRITICAL();

用户处理程序;

}

 

编写用户任务

进过以上步骤,ucos液晶可以在开发板上运行了,移植是一个艰辛而又漫长的过程,不可能在几天之内就完成。到了这一步的话,就可以开发用户程序了。