新闻中心

EEPW首页 > 嵌入式系统 > 设计应用 > 嵌入式实时操作系统 μC/OS-II 在S12单片机上的移植是怎样的?

嵌入式实时操作系统 μC/OS-II 在S12单片机上的移植是怎样的?

作者: 时间:2018-08-06 来源:网络 收藏

本文引用地址://www.cazqn.com/article/201808/385460.htm

typedef unsigned char BOOLEAN; /* 布尔变量*/

typedef unsigned char INT8U; /* 无符号8 位整型变量*/

typedef signed char INT8S; /* 有符号8 位整型变量 */

typedef unsigned int INT16U; /* 无符号16 位整型变量*/

typedef signed int INT16S; /* 有符号16 位整型变量*/

……

用户还必须将任务堆栈的数据类型告诉给μC/OS-II。S12CPU 的是堆栈是16 位的,所以定义OS_STK 为INT16U。所有的任务堆栈都必须用OS_STK 来声明数据类型。

#define OS_STK INT16U /* 堆栈是16 位宽度*/

对于不同的处理器而言,数据入堆栈时堆栈指针的增长方向也是不一样的,MC9S12DG128 单片机的堆栈指针是由高地址向低地址增长的,所以,要预先设定堆栈的

增长方向:

#define OS_STK_GROWTH 1 /*堆栈指针由高地址向低地址增长*/

μC/OS-II 需要先禁止中断再访问代码的临界段,并且在访问完毕后重新允许中断。这就使得μC/OS-II 能够保护临界段代码免受多任务或中断服务例程的破坏。禁止和允

许中断的宏是OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL(),定义这两个宏的有三种方法,移植时采用的是方法1,进入临界代码前关中断,脱离临界代码后开中断[2]。方法1在OS_CPU.H 中是这样定义的:

#if OS_CRITICAL_METHOD == 1 //方法一

#define OS_ENTER_CRITICAL( ) asm SEI

#defien OS_EXIT_CRITICAL () asm CLI

#endif

3.2 编写与硬件相关的代码

接下来需要编写与硬件相关的代码。这部分代码可以用C 语言,也可以用汇编语言。移植中与硬件相关的文件中最主要的是OS_CPU_C.C 和汇编文件OS_CPU_A.ASM。由于移植使用的是Metrowerks 公司提供的CodeWarrior CW12 V4.6 版本的C 交叉编译工具,而CW12 V4.6 允许在C 代码中插入汇编语句,所以可以把OS_CPU_A.ASM 这个文件合并

到OS_CPU_C.C 文件中去。以下是具体的移植过程。

3.2.1 中断服务子程序OSTickISR()

中断服务子程序所使用的中断可以用实时时钟产生,也可以用单片机片内的定时器模块来产生。本次移植采用的是用模数计数器产生精确时钟节拍中断,用S12 的模数计数器可以实现任意时间的精确中断,这里的中断为每秒30 次。

时钟节拍中断发生时,CPU12 会自动CPU 把CPU 寄存器推入堆栈,然后是清中断标志。但是页面寄存器PPAGE 并没有被推入堆栈,如果CPU12 的寻址范围超过了64KB,则要把PPAGE 也推入堆栈,本文中没有用到PPAGE 寄存器。

时钟节拍中断服务子程序可能激活一个优先级高于当前被中断任务的优先级的任务。时钟节拍中断服务子程序要连续调用:OSIntEnter()、OSTimerTick()和OSIntExit()

这三个函数。OSIntEnter()通知μC/OS-II 进入中断服务子程序了。OSTimerTick()给要求延迟若干时钟节拍的任务延迟计数器减1,减1 后为0 则该任务进入就绪态。

OSIntExit()函数告诉μC/OS-II 时钟节拍中断服务子程序结束了,如果这时有更高优先级的任务进入了就绪态,OSIntExit()就会调用中断级的任务切换函数OSIntCtxSw()

做任务切换,以便让更高的优先级的任务运行。以下是函数代码:

void OSTickISR(void)

{

/*根据需要决定是否保存PPAGE 寄存器,此处没有保存*/

OSIntEnter();

MCFLG_MCZF=1; //清除模计数器中断标志位

OSTimeTick();

OSIntExit(); //退出中断并进行任务切换

}

3.2.2 任务堆栈初始化函数OSTaskStkInit()

这个C语言写的函数是与CPU硬件相关的。这个函数初始化任务的堆栈,由建立任务的函数OSTaskCreate()或扩展的建立任务函数OSTaskCreatExit()调用。建立任务的函数带有4个形式参数,扩展的建立任务的函数有8个参数。其中pdata用于向任务传递参数。利用了这个参数将页面寄存器PPAGE 参数传给建立的任务。在改写该函数的时候一定要深刻了解S12CPU在中断发生时各个CPU寄存器的入栈的顺序,否则,μC/OS-II是运行不起来的。中断发生时S12CPU各个寄存器入栈的顺序如图3所示。由于该函数是被建立任务的函数所调用的,所以各个CPU寄存器的初始值并不重要。但要CCR寄存器的内容需要注意:如果选择任务启动后允许中断发生,则所有的任务运行期间中断都允许;同样,如果选择任务启动后禁止中断,则所有的任务都禁止中断发生,而不能有所选择。本文选择在任务启动时开启中断。以下是函数代码:

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

{

INT16U *stk;

pt = opt; // 'opt'未使用,此处可防止编译器的警告

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

*--stk = (INT16U)(pdata); //放置向函数传递的参数pdata

*--stk = (INT16U)(task); //函数返回地址PC

*--stk = (INT16U)(0x1122); //寄存器 Y

*--stk = (INT16U)(0x3344); //寄存器 X

((INT8U *)stk)--; // 寄存器A 仅需要1 个字节

*(INT8U *)stk = (INT8U)(0x55); //寄存器 A

((INT8U *)stk)--; // 寄存器B 仅需要1 个字节

*(INT8U *)stk = (INT8U)(0x66); //寄存器 B

((INT8U *)stk)--; // 寄存器CCR 仅需要1 个字节

*(INT8U *)stk = (INT8U)(0x00); //寄存器 CCR,开中断

return ((void *)stk);

}

3.2.3 让优先级最高的就绪态任务开始运行OSStartHightRdy()

OSStartHighRdy()是在多任务启动时被OSStart()调用的,μC/OS-II 做完所有的初始化工作之后,OSStart()就启动运行多任务,而OSStart()调用OSStartHighRdy()

函数运行多个就绪任务中优先级最高的任务。注意,堆栈指针总是储存在任务控制块的开头。


图3 中断发生时S12CPU寄存器入栈的顺序



评论


相关推荐

技术专区

关闭