//改程序将实现电子时钟的显示
//依靠定时计数器0的中断进行时间参数的确定

#include
#include
#include

#define uchar unsigned char
#define Data P0

//端口的定义
sbit WEI = P2^7;
sbit DUAN = P2^6;
sbit RS = P2^1;
sbit RW = P1^5;
sbit E = P1^6;
sbit BF = P0^7;

uchar code num[] = {"0123456789"};
uchar time;     //计时器

//用到函数的声明
void Delay(uchar);           //延时函数
void Busy();                 //忙时检测函数
void InitLCD();                 //液晶屏初始化函数
void InitTimer0();      //定时计数器初始化函数
void Write(uchar, bit);      //写指令/数据
void ShowData(uchar, bit, uchar);   //显示字符函数
void ShowString(uchar, bit, uchar *);  //显示字符串函数

void main()
{
    //生命变量时分秒,并给每个变量赋予;一个十位和一个个位
    uchar shi, fen, miao, ss, sg, fs, fg, ms, mg;
    //开始执行前关闭数码管
    Data = 0x0;
 WEI = 1;
 WEI = 0;
 Data = 0xff;
 DUAN = 1;
 DUAN  = 0;
    //液晶屏显示信息
 InitLCD();
 InitTimer0();
 ShowString(2, 0, "BeiJing Time");
 while(1)
 {
     if(time == 20)       //到了一秒钟
  {
      time = 0;
   miao ++;
   if(miao == 60)
   {
       miao = 0;
    fen ++;
    if(fen == 60)
    {
        fen = 0;
     shi ++;
     if(shi == 24)
         shi = 0;
    }
   }
  }
  ss = shi / 10;
  sg = shi % 10;
  fs = fen / 10;
  fg = fen % 10;
  ms = miao / 10;
  mg = miao % 10;
  ShowData(4, 1, num[ss]);
  ShowData(5, 1, num[sg]);
  ShowData(6, 1, 0x3a);        //冒号
  ShowData(7, 1, num[fs]);
  ShowData(8, 1, num[fg]);
  ShowData(9, 1, 0x3a);        //冒号
  ShowData(0x0a, 1, num[ms]);
  ShowData(0x0b, 1, num[mg]);
 }
}


void Delay(uchar i)
{
    uchar j;
 for(; i > 0; i --)
     for(j = 155; j > 0; j --);     //最少延时1ms
}

void Busy()
{
    Delay(5);       //只有在不忙时才能进行检测
 RS = 0;         //根据规定,当RS = 0; RW = 1;读取指令
 RW = 1;
 E = 0;          //在读取指令时,E要处于高脉冲,就是将E从0状态跳至1状态
 _nop_();
 _nop_();        //两个空操作给硬件一定的反应时间
 E = 1;
 while(BF);      //忙时检测
 _nop_();
 _nop_();        //两个空操作给硬件一定的反应时间
 E = 0;          //当E从1状态跳至0状态时,开始执行指令
}

void Write(uchar msg, bit flag)        //msg是要写入的指令/数据的相对位移,flag的值决定写入的是数据还是指令
{
    Busy();          //只有在不忙时才能进行读写操作
 RS = flag;       //flag的值决定是对数据/指令操作
 RW = 0;          //RW = 0决定写数据/指令
 E = 0;           //在读取指令时,E要处于高脉冲,就是将E从0状态跳至1状态
 _nop_();
 _nop_();        //两个空操作给硬件一定的反应时间
 E = 1;
 _nop_();
 _nop_();        //两个空操作给硬件一定的反应时间
 Data = msg;     //往数据端口写如数据
 _nop_();
 _nop_();        //两个空操作给硬件一定的反应时间
 E = 0;          //当E从1状态跳至0状态时,执行指令
}

void InitLCD()
{
    Busy();
 Delay(10);      //首次操作给硬件较长的延时时间
 Write(0x01, 0);  //清平指令
 Delay(5);
 Write(0x06, 0);  //【输入方式显示指令】光标从左向右移动,内容不移动
 Delay(5);
 Write(0x0c, 0);  //【显示开关控制指令】显示开,光标不显示,字符不闪烁
 Delay(5);
 Write(0x38, 0);  //【功能设置指令】8位数,双行显示,5*7点阵
 Delay(5);
}

void InitTimer0()
{
    time = 0;
    TMOD = 0x01;           //选择定时计数器的中断方式1
 EA = 1;             //打开总中断
 ET0 = 1;            //打开定时计数器0的中断
 TR0 = 1;            //打开定时计数器0开始计时
}

void timer0() interrupt 1
{
    //每隔50ms计时一次
    TH0 = (65536 - 50000) / 256;
 TL0 = (65536 - 50000) % 256;
 time ++;
}


void ShowData(uchar addr, bit flag, uchar msg)      //addr表示相对位移,flag指定进行操作所在的行,msg表示要显示的信息
{
    if(flag == 1)          //表示显示在第二行
 {
     Write((addr | 0xc0), 0);
  Write(msg, 1);     //写如信息
 }
 else
 {
     Write((addr | 0x80), 0);
  Write(msg, 1);
 }
}

void ShowString(uchar addr, bit flag, uchar *ptr)      //addr表示相对位移,flag指定进行操作所在的行,*ptr表示要显示的信息
{
    if(flag == 1)          //表示显示在第二行
 {
     while(*ptr)
  {
      Write((addr | 0xc0), 0);
   Write(*ptr, 1);
   ptr ++;
   addr ++;
  }
 }
 else                 //在第一行显示信息
 {
     while(*ptr)
  {
      Write((addr | 0x80), 0);
   Write(*ptr, 1);
   ptr ++;
      addr ++;
  }
 }
}