从零开始学习UCOSII操作系统13--系统移植理论篇

从零开始学习UCOSII操作系统13--系统移植理论篇

1、什么是系统移植?

(1)UCOSII移植到不同的处理器上,所谓的移植就是将一个实时的内核能在其他的微处理器或者微控制器上运行。

为了方便移植,UCOSII的大部分的代码都是C语言写的,因为不同机器的汇编代码是不一样的。这是由于UCOSII在设计的时候已经充分考虑到了可移植性这一点。

但是仍然有一部分的代码是需要用C语言和汇编语言写一些与处理器有关的代码。

(2)要使用UCOSII正常的运行,处理器必须满足以下的条件
2.1、处理器的C编译器能产生可重入型的代码

原因:如果不行的话,那么就不能在任务之间随意的切换,因为当你切换到别的任务的时候,该任务在这个函数的数据就会被破坏。

2.2、处理器支持中断,并能产生定时中断

2.3、用C语言就可以开关中断

原因:连中断都没有的话,是不可能进行任务切换的。

2.4、处理器能够支持一定数量的数据存储硬件堆栈,也就是栈

2.5、处理器有将堆栈指针以及其他的CPU的寄存器的内容读出,并存储到堆栈或者内存中去的指令。

原因:因为我们在任务切换的时候,需要将当前的CPU指针保存到刚刚执行的任务当中。然后切换到优先级更高的任务当中。

扫描二维码关注公众号,回复: 1626453 查看本文章

2、移植UCOSII实际上需要移植什么文件?

其实我们移植UCOSII额时候,大部分的代码是基于底层进行编写的,所以我们不需要进行移植。有一些是对这个UCOSII进行的配置的,所以我们不需要进行移植。

我们唯一需要关注的三个文件就是下面的三个文件。
OS_CPU.h
OS_CPU_A.ASM
OS_CPU_C.c

一般我们在开发的时候,我们会把所有的头文件定义在同一个头文件当中,因为这样我们只需要包含一个头文件就可以了,不会重复的包含多个头文件。

需要移植的几个文件:
INCLUDES.H
是一个主头文件,出现在每个.c 文件的第一行。

使得每个.c的文件中无需分别考虑它实际上需要哪些头文件,使用INCLUDE.H唯一的缺点就是,它可能包含一些与当前的编译的.c文件不相干的头文件。

OS_CPU.H
包含了用#define 语句定义的,与处理器相关的常数,宏以及类型。
OS_CPU.H的大体结构如程序清单所列。

这里面使用typedef用的比#define更加的好,因为#define仅仅相当于字符串的拷贝,但是typedef相当于命名了一个别名。

/*        数据类型       */

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()  ??  //定义软件任务切换的函数。

最关键的移植文件:CPU的文件
移植文件3:OS_CPU_C.c
UCOSII的移植范例要求用户编写10个简单的C函数:

OSTaskStkInit();
OSTaskCreateHook();
OSTaskDelHook();
OSTaskSwHook();
OSTaskIdleHook();
OSTaskStatHook();
OSTimeTickHook();
OSInitHookBegin();
OSInitHookend();
OSTCBInitHook();

PS:唯一必要的函数是OSTaskStkinit();其余的9个函数必须声明,但是并不一定要包含任何代码,这些函数的原型放在本章的末尾。

(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的这种低功耗的模式:

PS:其实这里真的设计的非常的巧妙,因为你没有任何的任务进行调度的时候,应该是没有什么执行的,但是我们UCOSII系统,规定当没有任何的任务需要强占CPU的时候,我们应该让其进入低功耗的模式,真的设计的很好。

void OS_TaskIdle(void *pdata)
{
    pdata = pdata;
    for(;;)
    {
        OS_ENTER_CRITICAL();
        OSIdleCtr++;
        OS_EXIT_CRITICAL();
        OSTaskIdleHook();
    }
}

void OSTaskIdleHook(void)
{
    asm("STOP");
    //收到中断并完成中断服务。
}

OS_CPU_A.ASm
UCOSII的移植实例就是要求用户编写4个简单的汇编语言函数:
OSStartHighRdy(); //使得最高优先级的任务运行的函数
OSCtxSw(); //任务的切换的函数
OSIntCtxSw();
OSTickISR();

如果编译器支持插入行汇编代码就可以将所有的与处理器相关的代码放置到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)OSTickISR()
UCOSII要求用户提供一个周期性的时钟源,来实现时间延迟和超时功能,时钟节拍应该每秒发生10或者100次每秒,为了完成任务,可以使用硬件定时器,也可以从交流电中获得50~60Hz的时钟频率。

必须在开启多任务后,即调用OSStart()后,启动时钟节拍中断,但是由于OSStart()函数不会返回。不能在还没有运行第一个任务的时候,启动时钟节拍中断。会导致程序跑飞。

在没有执行OSStart()之前不能打开时钟节拍中断。千万不能在这里开中断。

因为UCOSII此时仍然处于未知的状态,所以一旦跳入中断,就会跑飞。

定时器允许用户被挂起一定的时间:

void OSTickISR(void)
{
    //保存处理器的寄存器
    //调用OSIntEnter或者直接给OSIntNesting加1
    if(OSIntNesting ==1)
    {
        OSTCBCur->OSTCBStkPtr = Stack Pointer;
    }
    //给产生中断的设备清中断
    //重新允许中断
    OSTimeTick();   //硬件的产生中断的原理
    OSIntExit();    //中断退出
    //恢复处理器寄存器
    //执行中断返回指令
}

OSIntCtxSw()

void OSintCtxSw(void)
{
    //调整堆栈指针
    OSintExit();
    OSINTCtxSw();
}

猜你喜欢

转载自blog.csdn.net/dhauwd/article/details/79823958
今日推荐