【例Y19.1】蜂鸣器奏乐、数码管循环显示、LED流水显示的并行操作（C51）
//例Y19.1
//该程序的功能是同时做3件节奏各不相同的工作,即:无源蜂鸣器演奏,数码管循环显示和16个LED的流水点亮。
//程序可随时通过INT1键(外中断1)控制LED流水点亮的方向。
//程序中共包含4个任务函数:
//任务0——完成系统初始化,启动任务1-3,然后自身撤退。
//任务1——控制16个LED按方向标志的指示进行左/右流水点亮。
//任务2——控制6个数码管不断循环显示”0”~”F”。
//任务3——控制无源蜂鸣器演奏陕北民歌”三十里铺”。
//3个任务的节奏控制均通过os_wait2()函数及K_IVL事件实现。
//编程时要避免对定时器T0和CPU中断标志EA进行操作。
//因为在RTCX51-Tiny中,定时器T0由系统进行管理,是各种系统定时的硬件基础。
#include <reg51.h>
#include <rtx51tny.h>
#include <absacc.h>
#include <intrins.h>
#define uchar unsigned char
#define uint unsigned int
struct music_type
{ uchar tone;
  uchar delay;
};
char code seg[]={						//七段码表
0x3f,0x06,0x5b,0x4f,0x66,					//0~4
0x6d,0x7d,0x07,0x7f,0x6f,					//5~9
0x77,0x7c,0x39,0x5e,0x79,					//A~E
0x71,0x40,0x46,0x00						//F,-,-1,灭
};
uint code tonetab[]={						//D调7声音阶3个8度
63967,64138,64291,64360,64489,64603,64704,			//低音1~7
64752,64837,64913,64948,65012,65070,65120,			//中音1~7
65144,65186,65225,65242,65274,65303,65328			//高音1~7
};
//曲谱,每个音符两项数据:第1项为音高,第2项为节拍(以1/4拍为单位)
struct music_type code notetab[]={				//”三十里铺”曲谱
1+14,0x02,2+14,0x04,2+14,0x02,5+14,0x02,1+14,0x04,6+7,0x02,
5+7,0x03,6+7,0x01,5+7,0x02,2+7,0x02,5+7,0x08,
1+14,0x02,2+14,0x04,2+14,0x02,5+14,0x02,1+14,0x04,6+7,0x02,
5+7,0x03,6+7,0x01,5+7,0x02,2+7,0x02,5+7,0x08,
1+14,0x02,4+7,0x04,5+7,0x02,1+14,0x02,1+14,0x04,6+7,0x02,
5+7,0x03,6+7,0x01,5+7,0x02,2+7,0x02,5+7,0x08,
4+7,0x03,4+7,0x01,3+7,0x02,2+7,0x02,
1+7,0x03,2+7,0x01,5+7,0x02,2+7,0x02,1+7,0x08,
1+14,0x02,2+14,0x04,2+14,0x02,5+14,0x02,1+14,0x04,6+7,0x02,
5+7,0x03,6+7,0x01,5+7,0x02,2+7,0x02,5+7,0x08,
1+14,0x02,2+14,0x04,2+14,0x02,5+14,0x02,1+14,0x04,6+7,0x02,
5+7,0x03,6+7,0x01,5+7,0x02,2+7,0x02,5+7,0x08,
1+14,0x02,4+7,0x04,5+7,0x02,1+14,0x02,1+14,0x04,6+7,0x02,
5+7,0x03,6+7,0x01,5+7,0x02,2+7,0x02,5+7,0x08,
4+7,0x03,4+7,0x01,3+7,0x02,2+7,0x02,
1+7,0x03,2+7,0x01,5+7,0x02,2+7,0x02,1+7,0x08,
0,0x00
};
sbit P13=P1^3;							//连接无源蜂鸣器
sbit SDAT=P3^4;							//连接显示数码的移位寄存器
sbit SCLK=P3^5;
volatile uchar tl1,th1;						//存控制定时器1定时参数
uint led=0x0001;						//LED初始显示数据
bit dir=0;							//LED流水方向控制标志
void T1_sev() interrupt 3					//定时器T1中断:控制蜂鸣器发声频率
{ TL1=tl1;							//重装定时数据
  TH1=th1;
  P13=!P13;							//蜂鸣器驱动倒相
}
void INT1_serv() interrupt 2					//外中断1,改变LED流水方向标志
{ dir=!dir;							//流水方向控制标志改变
}
void sendchar(char ch) 						//通过串行传输送出1个显示数码
{ unsigned char i;
  for(i=0;i<8;i++)						//七段码中的8位数据
  { if(ch&0x01) SDAT=1;
    else SDAT=0;
    SCLK=0;
    ch>>=1;
    SCLK=1;
  }
}
void init() _task_ 0						//任务0:初始化,激活任务1~3
{ TMOD|=0x10; 							//系统硬件初始化
  ET1=1;
  IT1=1;
  EX1=1;
  os_create_task(1); 						//启动任务1~3
  os_create_task(2);
  os_create_task(3);
  os_delete_task(0); 						//自身撤退
}
void disled() _task_ 1						//任务1:16个LED流水显示
{ while(1)
  { XBYTE[0x2000]=led&0xff;					//低8位LED数据显示
    XBYTE[0x4000]=led>>8;					//高8位LED数据显示
    led=dir?_iror_(led,1):_irol_(led,1);			//根据dir指示进行左/右循环移位
    os_wait2(K_IVL,10);						//控制LED流水显示的节奏
  }
}
void disdigit() _task_ 2					//任务2:数码管循环显示'0'~'F'
{ unsigned char i; 
  while(1)
  { for(i=0;i<16;i++)						//循环显示'0'~'F'
    { sendchar(seg[i]); 					//送出1个显示数码
      os_wait2(K_IVL,40); 					//控制数码管循环移位显示的节奏
    }
  }
}
void musicplay() _task_ 3					//任务3:蜂鸣器演奏”三十里铺”
{ uchar i,j;
  while(1) 
  { i=0;
    while(notetab[i].delay)
    { if(!notetab[i].tone) TR1=0;				//音高数据为0
      else							//音高数据不为0
      { th1=TH1=tonetab[notetab[i].tone-1]/256;			//用定时参数控制T1的振荡频率(音高)
        tl1=TL1=tonetab[notetab[i].tone-1]%256;
        TR1=1; 							//该音符发声
      } 
      for(j=0;j<notetab[i].delay;j++)				//控制音符的持续时间(节拍)
        os_wait2(K_IVL,20);
      i++;							//拟换下个音符
    }
    TR1=0;							//停奏
    os_wait2(K_IVL,100);					//奏完1遍后的等待
  }
}

