|
发表于 2009-4-19 22:05:01
|
显示全部楼层
/*
简单的多任务操作系统
其实只有个任务调度切换,把说它是OS有点牵强,但它对于一些简单的开发应用来说,简单也许就是最好的.尽情的扩展它吧.别忘了把你的成果分享给大家.
这是一个最简单的OS,一切以运行效率为重,经测试,切换一次任务仅20个机器周期,也就是在标准51(工作于12M晶振)上20uS.
而为速度作出的牺牲是,为了给每个任务都分配一个私有堆栈,而占用了较多的内存.作为补偿,多任务更容易安排程序逻辑,从而可以节省一些用于控制的变量.
任务槽越多,占用内存越多,但任务也越好安排,以实际需求合理安排任务数目.一般来说,4个已足够.况且可以拿一个槽出来作为活动槽,换入换入一些临时任务.
task_load(函数名,任务槽号)
装载任务
os_start(任务槽号)
启动任务表.参数必须指向一个装载了的任务,否则系统会崩溃.
task_switch()
切换到其它任务
.编写任务函数注意事项:
KEIL C编译器是假定用户使用单任务环境,所以在变量的使用上都未对多任务进行处理,编写任务时应注意变量覆盖和代码重入问题.
1.覆盖:编译器为了节省内存,会给两个没用调用关系的函数分配同一内存地址作为变量空间.这在单任务下是很合理的,但对于多任务来说,两个进程会互相干扰对方.
解决的方法是:凡作用域内会跨越task_switch()的变量,都使用static前辍,保证其地址空间分配时的唯一性.
2.重入:重入并不是多任务下独有的问题,在单任务时,函数递归同样会导致重入,即,一个函数的不同实例(或者叫作"复本")之间的变量覆盖问题.
解决的方法是:使用reentrant函数后辍(例如:void function1() reentrant{...}).当然,根本的办法还是避免重入,因为重入会带来巨大的目标代码量,并极大降低运行效率.
3.额外提醒一句,在本例中,任务函数必须为一个死循环.退出函数会导致系统崩溃.
.任务函数如果是用汇编写成或内嵌汇编,切换任务时应该注意什么问题?
由于KEIL C编译器在处理函数调用时的约定规则为"子函数有可能修改任务寄存器",因此编译器在调用前已释放所有寄存器,子函数无需考虑保护任何寄存器.
这对于写惯汇编的人来说有点不习惯: 汇编习惯于在子程序中保护寄存器.
请注意一条原则:凡是需要跨越task_switch()的寄存器,全部需要保护(例如入栈).根本解决办法还是,不要让寄存器跨越任务切换函数task_switch()
事实上这里要补充一下,正如前所说,由于编译器存在变量地址覆盖优化,因此凡是非静态变量都不得跨越task_switch().
任务函数的书写:
void 函数名(void){//任务函数必须定义为无参数型
while(1){//任务函数不得返回,必须为死循环
//....这里写任务处理代码
task_switch();//每执行一段时间任务,就释放CPU一下,让别的任务有机会运行.
}
}
任务装载:
task_load(函数名,任务槽号)
装载函数的动作可发生在任意时候,但通常是在main()中.要注意的是,在本例中由于没考虑任务换出,
所以在执行os_start()前必须将所有任务槽装满.之后可以随意更换任务槽中的任务.
启动任务调度器:
os_start(任务槽号)
调用该宏后,将从参数指定的任务槽开始执行任务调度.本例为每切换一次任务需额外开销20个机器周期,用于迁移堆栈.
*/
#include"iom8v.h"
#include"fun.h"
//#include"def.h"
//#include"var.h"
char led;
char d8[16]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};
unsigned char seg[5];
char b[10];
#define MAX_TASKS 2
#define MAX_TASK_DEP 35
/*============================以下为任务管理器代码============================*/
//任务槽个数.在本例中并未考虑任务换入换出,所以实际运行的任务有多少个,就定义多少个任务槽,不可多定义或少定义
//任务的栈指针
#define MAX_TASKS 2
#define MAX_TASK_DEP 35
unsigned int task_sp[MAX_TASKS];
//最大栈深.最低不得少于2个,保守值为12.
//预估方法:以2为基数,每增加一层函数调用,加2字节.如果其间可能发生中断,则还要再加上中断需要的栈深.
//减小栈深的方法:1.尽量少嵌套子程序 2.调子程序前关中断.
unsigned char task_stack[MAX_TASKS][MAX_TASK_DEP];//任务堆栈.
unsigned char task_id;//当前活动任务号
//任务切换函数(任务调度器)
void task_switch(){
asm("push r0");
asm("push r1");
asm("push r2");
asm("push r3");
asm("push r4");
asm("push r5");
asm("push r6");
asm("push r7");
asm("push r8");
asm("push r9");
asm("push r10");
asm("push r11");
asm("push r12");
asm("push r13");
asm("push r14");
asm("push r15");
asm("push r16");
asm("push r17");
asm("push r18");
asm("push r19");
asm("push r20");
asm("push r21");
asm("push r22");
asm("push r23");
asm("push r24");
asm("push r25");
asm("push r26");
asm("push r27");
asm("push r28");
asm("push r29");
asm("push r30");
asm("push r31");
asm("in r16,0x3f");
asm("push r16");
task_sp[task_id] = SP;
task_id++;
if(task_id == MAX_TASKS)task_id = 0;
SP = task_sp[task_id];
asm("pop r16");
asm("out 0x3f,r16");
asm("pop r31");
asm("pop r30");
asm("pop r29");
asm("pop r28");
asm("pop r27");
asm("pop r26");
asm("pop r25");
asm("pop r24");
asm("pop r23");
asm("pop r22");
asm("pop r21");
asm("pop r20");
asm("pop r19");
asm("pop r18");
asm("pop r17");
asm("pop r16");
asm("pop r15");
asm("pop r14");
asm("pop r13");
asm("pop r12");
asm("pop r11");
asm("pop r10");
asm("pop r9");
asm("pop r8");
asm("pop r7");
asm("pop r6");
asm("pop r5");
asm("pop r4");
asm("pop r3");
asm("pop r2");
asm("pop r1");
asm("pop r0");
asm("ret");
//return;
}
void delay(long c)
{
long i;
for(i=0;i<c;i++)
{
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
}
}
//任务装入函数.将指定的函数(参数1)装入指定(参数2)的任务槽中.如果该槽中原来就有任务,则原任务丢失,但系统本身不会发生错误.
void task_load(void(*task)(),unsigned char tid)
{
task_sp[tid] = (unsigned int)(task_stack[tid]-1);
task_stack[tid][MAX_TASK_DEP -1] = (unsigned char)(((unsigned int)task)&0xFF);
task_stack[tid][MAX_TASK_DEP -2] = (unsigned char)(((unsigned int)task)>>8);
}
//从指定的任务开始运行任务调度.调用该宏后,将永不返回.
//#define os_start(tid) {task_id = tid,SP = task_sp[tid];return;}
void os_start(unsigned char tid)
{
task_id = tid;
SP = task_sp[tid]+MAX_TASK_DEP - 2;
return;
}
/*============================以下为测试代码============================*/
void task1(void)
{
for(;;)
{
PORTC=0x10;
delay(20000);
task_switch();
}
}
void task2(void)
{
for(;;)
{
PORTC=0x20;
delay(30000);
task_switch();
}
}
void main(){
//在这个示例里并没有考虑任务的换入换出,所以任务槽必须全部用完,否则系统会崩溃.
//这里装载了三个任务,因此在定义MAX_TASKS时也必须定义为3
DDRD=0Xff;
DDRC=0x07;
task_load(task1, 0);//将task1函数装入0号槽
task_load(task2, 1);//将task2函数装入1号槽
os_start(0);//启动任务调度,并从0号槽开始运行.参数改为1,则首先运行1号槽.
//调用该宏后,程序流将永不再返回main(),也就是说,该语句行之后的所有语句都不被执行到.
}
编译器是ICC ,MCU是mega8;这个系统有点问题,就是 在使用task_load(task1, 0)将task1函数装入0号槽时,task1 的值并不是任务1的入口地址,经调试(unsigned int)task1的值是40(0x28);各位大侠,帮帮忙,看看问题出在哪了?小弟不胜感激!!!!!!!!!!!! |
|