新闻  |   论坛  |   博客  |   在线研讨会
单片机程序的多任务和资源复用举例
lionwq | 2008-01-28 21:25:06    阅读:1930   发布文章

有一台机电设备,有两个按键,控制设备的两个不同部分。
现要求:
 每个按键按下,相应控制程序运行。但两个按键可以同时按下,就是说两个控制程序可能
需要同时运行。使用一个89C52,如何编写程序?

注:此程序不使用RTOS等操作系统。
/*程序说明:
 一)产生波形可以使用中断中计数来产生精确的波形。
   本答案中为更能体现程序的多任务和资源复用问题,采用主程序循环产生。
 二)请特别注意,题意是两个程序在并发运行,实际按本答案可以扩展到N个不同任务同时运行,在此就不讨论。
   (对大程序结构增加了很多其它的概念)
 三)因为在论坛上直接贴出,所以程序放在一个文件中。
   应该按Timer.c, Key.c, Const.h(存放常量定义),Task1, Task2, Answer.c存放
*/
#include <REG52.h>

/*Timer*/
bit fTimer0_2ms;  /*T0中断产生的标志,准备传递给主循环*/
bit fSYS_2ms;   /*系统T0中断产生的标志,12M,主循环使用*/
bit fSYS_20ms;   /*每20MS产生一次的消息*/

#define INT2MSCOUNT   10           /*产生2MS所需要的时间次数*/
unsigned char data mTimer_2msReg=INT2MSCOUNT;  /*产生2MS所需要的寄存器*/
#define INT20MSCOUNT  10           /*产生20MS所需要的时间次数,在20MS基础上*/
unsigned char data mTimer_20msReg=INT20MSCOUNT; /*产生20MS所需要的寄存器,在20MS基础上*/

/*KEY*/
unsigned char data mKey1SwapTask;  /***按键任务寄存器***/
unsigned char data mKey2SwapTask;  /***按键任务寄存器***/
sbit iKey1=P1^0;           /*按键的输入口*

sbit iKey2=P1^1;

bit fKey1;             /*为简单化,没使用队列保存键值,使用标志*/
bit fKey2;             /*为简单化,没使用队列保存键值,使用标志*/


/*Task1*/
unsigned char data mTask1Id;    /*任务一的任务号*/
unsigned char data mTask1_1HzReg;  /*1hz时间寄存器*/
unsigned int data mTask1_2SReg;   /*2S时间寄存器*/
sbit oTask1=P1^2;          /*输出方波口*/

/*Task2*/
unsigned char data mTask2Id;    /*任务二的任务号*/
unsigned char data mTask2_1p2HzReg; /*1.2hz时间寄存器*/
sbit oTask2=P1^3;          /*输出方波口*/

/*---------------------------------------------------------------------------*/
/*产生以1MS为基础的系统定时信号,T0作为基准定时器*/

/*************************************************
定时器T0初始化0.2MS,12M
*************************************************/
void Timer0_Init()
{
 TMOD|=0x2;      /*8位定时器*/

 TL0=TH0=~(200)+1;   /*12M*/

 TR0=1;
 ET0=1;
}

/*************************************************
定时器0的中断服务,产生fTimer0_2ms
*************************************************

void timer0(void) interrupt 1 /*T0中断*/
{
 mTimer_2msReg--;
 if(mTimer_2msReg==0){
  mTimer_2msReg=INT2MSCOUNT; /*产生1MS所需要的寄存器*/
  fTimer0_2ms=1;
 }
}

/*************************************************
控制消息fSYS_2ms
*************************************************/
void Timer0_MainLoop()
{
 fSYS_2ms=0;
 fSYS_20ms=0;

 if(fTimer0_2ms){
  fTimer0_2ms=0;       /*接收中断过来的时间标志,转换为消息*/
  fSYS_2ms=1;         /*此消息在一周内有效,被外部程序复用*/
  /*产生20MS的消息*/
  mTimer_20msReg--;
  if(mTimer_20msReg==0){
    mTimer_20msReg=INT20MSCOUNT;  /*产生20MS所需要的寄存器,在20MS基础上*/
    fSYS_20ms=1;
  }
 }
}

/*---------------------------------------------------------------------------*/
/*按键扫描,包含两个扫描任务*/
/**********************************************
每次系统时间进入一次,20ms.这里把20MS判断放进来,好看点
按键扫描循环
为简单化,没使用队列保存键值,使用标志
那些重复发出N键,在这个结构中非常容易加上
**********************************************/
void Key_MainLoop(

{
  if(fSYS_20ms==0)return;

  switch(mKey1SwapTask){
   case 0:/***有按键按下吗?***/
      if(iKey1==0){
       mKey1SwapTask=1;
      }
      break;
  case 1: /***键按下去抖延时***/
      mKey1SwapTask=2;          /***延时一个系统时间***/
      break;
  case 2: /***键值判断***/
      if(iKey1==0){
       fKey1=1;             /*按键有效*/
       mKey1SwapTask=3;         /*去按键去抖*/
      }
      else mKey1SwapTask=0;        /*抖动*/
      break;
  case 3: /***有松开吗?***/
      if(iKey1==1){
       mKey1SwapTask=4;
      }
      break;
  case 4: /***键松开去抖延时***/
      mKey1SwapTask=5;          /***延时一个系统时间***/
      break;
  case 5: /***键值判断***/
      if(iKey1==1){
       mKey1SwapTask=0;         /*去按键检测开始*/
      }
      else mKey1SwapTask=3;        /*抖动*/
      break;
  }


  switch(mKey2SwapTask){
   case 0:/***有按键按下吗?***/
      if(iKey2==0){
       mKey2SwapTask=1;
      }
      break

        case 1: /***键按下去抖延时***/
      mKey2SwapTask=2;          /***延时一个系统时间***/
      break;
  case 2: /***键值判断***/
      if(iKey2==0){
       fKey2=1;             /*按键有效*/
       mKey2SwapTask=3;         /*去按键去抖*/
      }
      else mKey2SwapTask=0;        /*抖动*/
      break;
  case 3: /***有松开吗?***/
      if(iKey2==1){
       mKey2SwapTask=4;
      }
      break;
  case 4: /***键松开去抖延时***/
      mKey2SwapTask=5;          /***延时一个系统时间***/
      break;
  case 5: /***键值判断***/
      if(iKey2==1){
       mKey2SwapTask=0;         /*去按键检测开始*/
      }
      else mKey2SwapTask=3;        /*抖动*/
      break;
  }
}

/*---------------------------------------------------------------------------*/
/*任务一*/
/**********************************************
一个部分输出1HZ的方波,2S后停止。
**********************************************/
void Task1_MainLoop()
{
  switch(mTask1Id){
   case 0:  if(fKey1)

                                          fKey1=0;                 /*接收该键值*/
           mTask1_1HzReg=500/2;    /*1hz时间寄存器,500ms,以2MS为单位*/
           mTask1_2SReg=2000/2;    /*2S时间寄存器,500ms,以2MS为单位*/
           oTask1=0;
           mTask1Id=1;
        }
        break;
   case 1:  if(fSYS_2ms){
          mTask1_1HzReg--;
          if(mTask1_1HzReg==0){
            oTask1=~oTask1;
            mTask1_1HzReg=500/2;   /*1hz时间寄存器,500ms,以2MS为单位*/
          }

          mTask1_2SReg--;
          if(mTask1_2SReg==0){
            oTask1=1;        /*2S时间到*/
            mTask1Id=0;
          }
        }
        break;
  }
}


/*---------------------------------------------------------------------------*/
/*任务二*/
/**********************************************
一个一直输出1.2hz的方波,直到按键再次按
**********************************************/
void Task2_MainLoop()
{
  switch(mTask2Id){
   case 0:  if(fKey2){
           fKey2=0;          /*接收该键值*
                                          mTask2_1p2HzReg=416/2;   /*1hz时间寄存器,832/2ms,以2MS为单位*/
           oTask2=0;
           mTask2Id=1;
        }
        break;
   case 1:  if(fKey2){
         fKey2=0;
         oTask2=1;
         mTask2Id=0;
        }
        else {
          if(fSYS_2ms){
            mTask2_1p2HzReg--;
            if(mTask2_1p2HzReg==0){
              oTask2=~oTask1;
              mTask2_1p2HzReg=416/2;  /*1hz时间寄存器,832/2ms,以2MS为单位*/
            }
          }
        }
        break;
  }
}

/*---------------------------------------------------------------------------*/
/*主程序*/
void main(){
  Timer0_Init();
  EA=1;

  while(1){
    Timer0_MainLoop();
    Key_MainLoop();
    Task1_MainLoop();
    Task2_MainLoop();
  }
}

参与讨论
登录后参与讨论
淡泊以明志 宁静以致远
推荐文章
最近访客