经典单片机C51多任务代码(改进版)

时间:2021-01-20 19:53:51
经典单片机C51多任务代码(改进版)  
作者:佚名   
以前曾经发布过一篇http://www.51hei.com/mcu/1616.html  这个是改进版,修正了几个小问题.
//说明本源码来自网络(DIY超轻量级多任务操作系统一文)
//============================================================================
//51实用多任务源码,欢迎引用改进,有新改进功能请回发一份给我谢谢!(请发邮箱)
//改进为定时中断内切换任务
//任务内可以设定时间片长短,通过改定时器0初置的方法实现
//置定时器0 TL0=0xFF;的方法可以让出CPU使用权
//七彩惊云改进,QQ540953860 13880880726  胥先生
//采用本源码请保留各改进人的联系信息,以便共享和完善,请将各改进前版本备注在代码后面以便了解发展
//步骤和各种不同档次单片机下的灵活引用。
//请对每一句代码进行备注谢谢!
//============================================================================
//============================================================================
//你的改进说明写这儿
//============================================================================
#include <reg51.h>


#define MAX_TASKS 2       //任务槽个数.必须和实际任务数一至 
#define MAX_TASK_DEP 12   //最大栈深.最低不得少于2个,保守值为12.
//根据程序嵌套层数调整该值大小,同时要算一下会不会溢出,这里的设置很重要。
//还有就是重入问题,局部变量在任务切换时有可能被另外的任务改写。 
//#define MAX_TASK_DEP 24   //最大栈深.最低不得少于2个,24. 
unsigned char idata task_stack[MAX_TASKS][MAX_TASK_DEP];//任务堆栈数组. 
unsigned char task_id;    //当前活动任务号 
unsigned char idata task_sp[MAX_TASKS];//栈指标存放数组
bit RWCH_BZ=0;//任务切换标志变量


/*
//任务切换函数(任务调度器) 
void task_switch(){ //本子程序可以用来让出CPU使用权
        task_sp[task_id] = SP; //存当前任务SP的值


        if(++task_id == MAX_TASKS) //任务号++并比较,到最大置0以便重复切换任务
                task_id = 0;


        SP = task_sp[task_id]; //读取下一任务SP值,
} */


//任务装入函数.将指定的函数(参数1)装入指定(参数2)的任务槽中.如果该槽中原来就有任务,则原任务丢失,但系统本身不会发生错误. 
void task_load(unsigned int fn, unsigned char tid){ 
        task_sp[tid] = task_stack[tid] + 1; //装入任务SP数据
        task_stack[tid][0] = (unsigned int)fn & 0xff; //装入任务首地址低位
        task_stack[tid][1] = (unsigned int)fn >> 8; //装入首地址高位
}


//从指定的任务开始运行任务调度.调用该宏后,将永不返回. 
#define os_start(tid) {task_id = tid,SP = task_sp[tid];return;} 
//这里其实就是以改写SP指针的方法,返回时不再返回调用本子程序的地方,而返回被新设定的SP指针指向的任务1首地址处运行。


 


/*============================以下为测试代码============================*/


void task1(){ //任务1
        static unsigned char i; 
        while(1){ 
                i++; 
        } 
}


void task2(){//任务2 
        static unsigned char j; 
        while(1){ 
                j+=2; 
           TL0=0xFF;//用置TL0的办法来进行让定时中断动作从而进行任务切换,让出CPU使用权给下一任务的方法更好
   
        } 
}


void main(){ 
        //这里装载了两个任务,因此在定义MAX_TASKS时也必须定义为2


//=============================================================
//单片机初始化
//    mov tmod,#20h  ;串口通讯设置 串口方式3 波特9600 晶振11.59适用
//TMOD=0x20;//定时器1方式2
TMOD=0x22;//定时器0方式2,定时器1方式2
//    mov Tl1,#0fdh ;
TL1=0xfa;
//    mov th1,#0fdh  ; TEL:028-89951522 转载请勿删除,谢谢!
TH1=0xfa;
//    mov pcon,#128d  ;QQ:540953860
//PCON=128;
PCON=0x80;
//    mov sp,#60h
//SP=0x60;
IP=16;//串口中断设为高优先级
//    mov scon,#0e0h
SCON=0xf0;//为e0时禁止接收数据
//setb tr1
TR1=1;//开启定时器1
TR0=1;//开启定时器0
ET0=1;//开定时器0中断
 TL0=0x50;//定时器0初值设定
 TH0=0x50;//在中断中设置它可实现时间片调整
//    setb ea


//=============================================================
task_load(task1, 0);//将task1函数装入0号槽 
task_load(task2, 1);//将task2函数装入1号槽 
EA=1;//开中断
os_start(0); //进入任务1

ckzd() interrupt 4 using 1//串行中断入口
{


}


timer0zd() interrupt 1 using 2//定时器0中断入口
  {


  // clr tr1  ;关闭定时器
   TR0=0;
// clr et1  ;关闭定时中断
   ET0=0;
//  task_switch();//切换任务
//===========================================
//切换任务
      task_sp[task_id] = SP;


        if(++task_id == MAX_TASKS) 
          {
                task_id = 0; 
                RWCH_BZ=1;//当task_id==0时说明所有任都切换了一次,置标志不再对栈进行调整。
    }
        SP = task_sp[task_id];
   
if(RWCH_BZ==0)
  {//栈调整代码
  SP+=2;//第一次切换程序时原栈没有(POP PSW 和POP ACC 数据调整一下,为什么请查汇编代码)
//为什么要加上面代码的分析
 //中断中其实隐含了以下代码
//PUSH ACC
//PUSH PSW
//----------------


//中断内的实际代码


//----------------
//下面两句的数据在每个任务第一次切换时任务栈内其实并没有它的数据,如果不调整,这两句将会把返回地址数据给POP掉
//所以进行SP+2处理才会正常返回切换后的任务,从而骗过CPU
//POP PSW
//POP ACC
//RET
  }
//============================================
// setb et1
  ET0=1;//开中断
// setb tr1
  TR0=1;//开定时器


 }
 //===================================


//=======================================================================
/*
//网络原码
#include <reg51.h>


#define MAX_TASKS 2       //任务槽个数.必须和实际任务数一至 
#define MAX_TASK_DEP 12   //最大栈深.最低不得少于2个,保守值为12. 
unsigned char idata task_stack[MAX_TASKS][MAX_TASK_DEP];//任务堆栈. 
unsigned char task_id;    //当前活动任务号 
unsigned char idata task_sp[MAX_TASKS];




//任务切换函数(任务调度器) 
void task_switch(){ 
        task_sp[task_id] = SP;


        if(++task_id == MAX_TASKS) 
                task_id = 0;


        SP = task_sp[task_id]; 
}


//任务装入函数.将指定的函数(参数1)装入指定(参数2)的任务槽中.如果该槽中原来就有任务,则原任务丢失,但系统本身不会发生错误. 
void task_load(unsigned int fn, unsigned char tid){ 
        task_sp[tid] = task_stack[tid] + 1; 
        task_stack[tid][0] = (unsigned int)fn & 0xff; 
        task_stack[tid][1] = (unsigned int)fn >> 8; 
}


//从指定的任务开始运行任务调度.调用该宏后,将永不返回. 
#define os_start(tid) {task_id = tid,SP = task_sp[tid];return;}


void task1(){ 
        static unsigned char i; 
        while(1){ 
                i++; 
                task_switch();//编译后在这里打上断点 
        } 
}


void task2(){ 
        static unsigned char j; 
        while(1){ 
                j+=2; 
                task_switch();//编译后在这里打上断点 
        } 
}


void main(){ 
        //这里装载了两个任务,因此在定义MAX_TASKS时也必须定义为2 
        task_load(task1, 0);//将task1函数装入0号槽 
        task_load(task2, 1);//将task2函数装入1号槽 
        os_start(0); 

*/

//=======================================================================


原文地址:http://www.51hei.com/mcu/1701.html

---ps:我转载时候找到了DIY超轻量级多任务操作系统的原文,见:http://www.amobbs.com/thread-1398508-1-1.html