在MC68HC908GP32上移植μC/OS-II
4.OS_CPU08.C文件
本文引用地址:https://www.eepw.com.cn/article/201609/305220.htmμC/OS-II的移植需要用户在OS_CPU08.C中定义6个函数:
OSTaskStkInit()
OSTaskCreateHook()
OSTaskDelHook()
OSTaskSwHook()
OSTaskStatHook()
OSTimeTickHook()
实际需要定义的只有OSTaskStkInit()函数,其他5个函数需要声明,但不一定有实际内容。这5个函数都是用户定义,所以OS_CPU08.C中只有定义,没有给出代码。如果用户需要使用这些函数,请将文件OS_CFG.H中的#define constant OS_CPU_HOOKS_EN设为1,设为0表示不使用这些函数。
OSTaskStkInit()函数由任务创建函数 OSTaskCreate()或OSTaskCreateExt()调用,用来初始化任务的堆栈。初始状态的堆栈模拟发生一次中断后的堆栈结构,按照中断后的进栈次序预留各个寄存器存储空间;而中断返回地址指向任务代码的起始地址。当调用OSTaskCreate()或OSTaskCreateExt()创建一个新任务时,需要传递的参数是:任务代码的起始地址、参数指针(pdata)、任务堆栈顶端的地址、任务的优先级。 OSTaskCreateExt()还需要一些其他参数,但与OSTaskStkinit()没有关系。OSTaskStkInit()只需要以上提到的 3个参数(task、pdata和ptos)。堆栈初始化工作结束后,OSTaskStkInit()返回新的堆栈栈顶指针,OSTaskCreate()OSTaskCreateExt()将指针保存在任务的OS_TCB中。
Void*OSTaskStklint(void(*task)(void*pd),void*pdata,void*ptos,INT16U opt)
{
INT16U *stk;
stk=(INT16U*)ptos; /*保存堆栈指针*/
*--stk=(INT16U)(task); /保存程序计数器内容*/
*--stk=(INT16U)(0x00); /初始化X和A寄存器内容*/
--stk=(INT16U)(0x00); /*初始化CCR和H寄存器*/
return((void*)stk);
}
其余的几个函数:OSTaskCreateHook()、OSTaskDelHook()、OSTaskSwHook()、OSTaskStatHook和OSTimeTickHook()均由用户自定义。
四、制作用户自己的项目
在为内核编写了上述与硬件相关的代码以后,用户就可以为自己的项目编写实际的代码了。在本例中,用户任务共有两个。任务1在初始化时钟中断以后,就进入了一人死循环。在这个循环里,任务1一方面以1s(秒)为周期改变并行I/O口 PORTA第0个引脚的输出电压,另一方面每隔4s便向任务2发送1个信号。而任务2则始终等待任务1发来的信号,一旦收到信号,便改变并行I/O口 PORTA第1个引脚的输出电压。具体的代码如下:
/*****************************************
* EXE2.C
*******************************************/
#include
#include includes.h
Byte PORTA @0x0000; /*并口A地址$0000*/
Byte DDRA @0x0004; /*并口A方向寄存器地址$0004*/
Byte T1SC @0x0020; /*定时器控制寄存器地址$0020*/
Byte T1MODH@0x0023; /*定时器模式寄存器地址$0023*/
OS_EVENT *Semaphore;
#define TASK_STK_SIZE 64 /*任务堆栈大小64字节*/
INT8U Task1Stk[TASK_STK_SIZE]; /*定义任务1堆栈*/
INT8U Task2Stk[TASK_STK_SIZE]; /*定义任务2堆栈*/
Void Hardwareinit(void);
Void Task1(void*pdata)
{int count=0;
/*int count=0;
/*初始化定时器*/
asm{
LDA #0x50
STA T1SC
LDHX #0x0333 //设定定时器间隔100ms
STHX T1MODH
CLI
}
for(;;){
PORTA=0xFE;
OSTimeDly(5); /*延时0.5s*/
PORTA|=0x01;
/*延时0.5s*/
DSTimeDly(5);
Count++;
If(count= =4){
OSSemPost(Semaphore);
Count=0;
}
}}
void Task2(void *pdata)
{
Byte err;
For(;;){
OSSemPend(Semaphore,0,err);
PORTA=0xFD:
OSSemPend(Semaphore,0,err);
PORTA|=0x02;
}
}
void main(void){
Hardwarelnit(); /*完成硬件的初始化工作*/
Oslint(); /*初始化多任务环境*/
Semaphore=OSSemCreate(0);
OSTaskCreate(Task1,(void*)0,(void*)Task1Stk
[TASK_STK_SIZE],10);
OSTaskCreate(Task2,void*)0,(void*)Task2Stk
[TASK_STK_SIZE],9);
OSStart();
}
在主程序main()中,用户必须先调用OSInit(),然后创建各个任务和信号量等,最后调用OSStart(),以启动内核运行,开始正常的任务调度。
本例中尽量减小了对RAM的需求:假如中断嵌套层数不超过三层,所需事件只有一个,即只需要一个事件控制块;应用中对μC/OS-II提供的功能进行最大限度的裁剪,能不用的尽量不用。采用了上述措施后,μC/OS-II的RAM使用情况大致如下:μC/OC-II所使用的全局变量占用22字节,事件控制块占用12字节。此外,当系统初始化时,还需要最小30字节的系统堆栈用于初始化TCB,并传递参数。以上为μC/OS-II中系统所必需的RAM,计64字节。
假设给每个任务分配64个字节的堆栈空间,其中用于任务控制块17字节,允许三层中断嵌套要用18字节,任务切换时的栈结构要用8字节。由于程序是用C语言编写的,确切的子函数嵌套调用层数是不知道的。如果不使用本身需要缓冲区的C 语言函数,如printf()等,任务中程序调用所产生的嵌套层数不超过10层,则64字节中还能分析给任务局部变量的空间只剩下1字节了。这是能分配任务的最小RAM空间了。
综上所述,GP32的512字节RAM可分为8个64字节的RAM块。如果运行 4个任务,能留给应用程序的RAM也只剩下128字节了。如果在GP32上运行μC/OS-II,且不多于8个任务,则任务调度表可以再简化,不需要调度 64个任务,只调度8个任务就可以了。
评论