关于MC9S12XEP100上的UCOS移植记录

关于MC9S12XEP100上的UCOS移植记录
关于MC9S12XEP100上的UCOS移植记录

关于ucos在MC9S12XEP100上的移植总结

BY : 安勇攀移植的话主要是针对于CPU相关的几个函数的实现,这几个函数如果编译器支持混合编译的话可以放在一个c文件中。

OS_STK *OSTaskStkInit(void (*task)(void *pd),void *pdata,OS_STK *ptos,INT16U opt);//栈初始化void OSStartHighRdy(void);//运行当前最高优先级任务

void OSCtxSw(void);//任务切换函数

void OSIntCtxSw(void);//中断几任务切换函数。

以及几个钩子函数,钩子函数在这里可以不予实现。但是必须将他们的函数写出来。

void OSTaskCreateHook(OS_TCB * pctb){

}

void OSTaskDelHook(OS_TCB * pctb) {

}

void OSTaskSwHook(){

}

void OSTaskIdleHook(){

}

void OSTaskStatHook(){

}

void OSTimeTickHook() {

}

void OSInitHookBegin(){

}

void OSInitHookEnd(){

}

void OSTCBInitHook(OS_TCB * pctb){

}

钩子函数主要是想给用户在某些操作之前实现一些自己需要预先进行的东西。

还有一个与CPU相关的一个os_cpu.h的实现,文件内部主要是对系统使用的数据类型的重定义以及中断方式,软中断指令的实现等。

首先需要理解单片机CPU对于中断前后现场的保护机制。

对于绝大多数单片机CPU来说。在RAM的顶端(这里仅限于栈区从RAM区域顶端,栈的生长方向向下)存在一个栈去,对于使用飞思卡尔CW编译器的话,可以在prm文件中进行定义栈区的大小或者指定栈区的栈顶地址。指定好栈顶地址以后,对于单片机而言CPU 中的SP寄存器的值就已经确定了,栈区主要的作用是函数参数的传递执行还有中断现场的保护和恢复,我这里只说关于中断现场的保护。至于单片机的中断机制,包括中断的向量等等我在这里也不再赘述。需要提醒的是由于中断通常意义上来讲是指异步中断,异步中断的发生时间对于单片机的CPU来讲是没有实现预知的。所以对于一个单片机来说,中断现场的保护一般是由硬件进行完成的。中断发生时,CPU会自动将PC寄存器,变址寄存器X,变址寄存器Y,累加器B,累加器A的当前值自动的放入堆栈中,随着这些中断现场寄存器值的放入。SP寄存器依然要保持指向栈顶,所以每压栈一次,SP指针就会加一,以保证SP永远指向栈顶。下面就来说一下关于ucos移植中的任务堆栈OSTaskStkInit初始化函数。在ucos 中,系统对于任务的调度和管理是通过对一个OSTCB(任务控制块)链表的维护实现的,根据C语言的知识我们知道链表是存在内存四区的堆区。在任务控制块链表的每一个节点上,

都存在一个OSTCB结构体,ucos实质上是通过对这个结构体来进行任务控制。在该结构体中的第一个元素就是一个OS_STK型的指向RAM中某个地址,在ucos中,就认为该地址就是该任务的栈顶指针SP,所以在编写OSTaskStkInt函数时,我们要利用该指针区初始化该任务的栈。栈的初始化指导思想是认为我们的初始化函数就是单片机在发生堆栈时硬件自动将中断现场入栈的过程。OSTaskStkInit函数是被OSTaskCreate函数调用的。但是我本人在移植过程中发现,仅仅按照手册上给出的中断入栈方法入栈,系统是不能运行的。为什么不能运行呢,经过对比代码我发现这是由于芯片的Flash映射机制导致的,在大多数的单片机中,中断函数都是存储在单片机flash中的非分页区,为什么要存在非分页区域,因为针对xep100来说cpu可寻址的物理地址范围是64KB(16位单片机),中断发生的时候单片机中的cpu

需要跳转的相应的中断函数区执行的。XEP100单片机的flash大小是1M。那CPU怎么寻址呢? 这就要说到关于CPU的分页机制,在XEP100单片机中,他提供了一种分页机制,就是利用PPAGE寄存器去指出当前的页,PPAGE的值不同,就可以将不同的页提供给CPU。这里每页大小是64K,也就是说对于XEP100来说,他根据PPAGE提供的16个不同值来寻址16页,每页大小是64K,对于cpu来说,可寻址的范围就是这一页下的64K空间。好了,说了这么多,我就是想表达,因为使用ucos的话,我们在大多数清况下是不去关心每个函数具体存放在哪里的,及时我们使用#pragma编译命令声明了函数存储的地方,大多数时候后面最多也都是DEFAULT,这个DEFAULT可是代表了16个页。所以我们的任务代码也可能会存放在不同的页中,那么,我们任务模仿中断的时候我们是不是也要讲PPAGE的值也要保存起来呢?答案是肯定的。但是我们还要用EEPROM呢,我们是不是还要用RAM呢?他们也要分页的,那我们就必须将这些寄存器也进行保存,需要注意的是一定要管理好我们的栈,不然这几个寄存的的出栈入栈都不是硬件完成的,我们弹出和压入的时候一定要注意。OSTaskStkInit函数的最后我们需要将当前任务的栈顶指针返回给OSTaskCreate函数。OSStartHighRdy函数被OSStart函数调用,并不在意返回OSStart函数。主要功能是将OSRunning标志置一,说明已经在运行os,接下来就是讲当前坐高优先级任务控制块中的堆栈指针赋值给CPU中的SP,然后执行中断返回指令,将最近一次保存的当前最高优先级任务的堆栈区弹出到CPU相应的寄存器中。

OSCtxSw函数是任务级的切换函数,该函数是由软中断指令触发,函数体中主要执行的是将高优先级任务的TCB控制块中的栈顶指针交给CPU中的SP指针。然后执行中断返回指令将高优先级任务的栈内容弹出到CPU中。

OSIntCtxSw函数主要是执行中断级的任务切换。原理上与OSCtxSw相同。代码作用也基本相似。

还有一个比较重要的就是操作系统心跳systick的实现,XEP100是双核单片机,在其内部有一个XGATE协处理器,该处理器主要的任务是处理系统的中断,我们这里的心跳时钟的实现实际上应该由该处理器实现,但是我这里并没有使用双核机制,所以也就不再使用XGATE。取而代之的是使用主处理器的定时器来实现心跳,在中断服务函数里面去调用OSTimeTick 滴答函数。需要注意的是我们进出定时器中断服务函数是,需要通知操作系统已经进入中断服务程序,使系统对调度器上锁。OS在每次心跳期间都要遍历整个链表,并将整个链表中的延时函数值减一。如果某个任务的延时函数减到0证明延时时间到,那么该任务进入就绪态。此时如果该任务的优先级比正在运行的任务的优先级高,由于ucos是剥夺性内核那么在延时函数中会触发软中断指令,进而进行一次任务切换。

相关主题
相关文档
最新文档