基于AT89C51的电子钟(C语言编程)

来源:互联网 发布:linux shell while 编辑:程序博客网 时间:2024/05/22 11:38

 先看看电路图


P3.0是设置选择位,按一次选择一个,再按一次,顺序往下执行

#include<reg52.h>
#include "intrins.h"
#define uchar unsigned char
#define uint  unsigned int
int i,sec,min,h,date,month,year,flag;
uchar code digit[10]={"0123456789"}; 
sbit RS=P2^0;           //寄存器选择位,将RS位定义为P2.0引脚
sbit RW=P2^1;           //读写选择位,将RW位定义为P2.1引脚
sbit E=P2^2;            //使能信号位,将E位定义为P2.2引脚
sbit BF=P0^7;           //忙碌标志位,,将BF位定义为P0.7引脚
sbit key_ch=P3^0;
sbit key_add=P3^1;
sbit key_minus=P3^2;
sbit key5=P1^5;
sbit key6=P1^6;
sbit key7=P1^7;
void control(void);
void delay1ms()
{
    unsigned char i,j;
for(i=0;i<10;i++)
  for(j=0;j<33;j++)
  ;  
 }
 /*****************************************************
函数功能:延时若干毫秒
入口参数:n
***************************************************/
 void delay(uint n)
 {
    uint i;
for(i=0;i<n;i++)
delay1ms();
 }
/*****************************************************
函数功能:判断液晶模块的忙碌状态
返回值:result。result=1,忙碌;result=0,不忙
***************************************************/
 bit BusyTest(void)
  {
    bit result;
RS=0;       //根据规定,RS为低电平,RW为高电平时,可以读状态
    RW=1;
    E=1;        //E=1,才允许读写
    _nop_();   //空操作
    _nop_();
    _nop_(); 
    _nop_();   //空操作四个机器周期,给硬件反应时间
    result=BF;  //将忙碌标志电平赋给result
E=0;
    return result;
  }
/*****************************************************
函数功能:将模式设置指令或显示地址写入液晶模块
入口参数:dictate
***************************************************/
void Write_com (uchar dictate)
{   
    while(BusyTest()==1);   //如果忙就等待
RS=0;                  //根据规定,RS和R/W同时为低电平时,可以写入指令
RW=0;   
E=0;                   //E置低电平(写指令时,就是让E从0到1发生正跳变,所以应先置"0"
_nop_();
_nop_();               //空操作两个机器周期,给硬件反应时间
P0=dictate;            //将数据送入P0口,即写入指令或地址
_nop_();
_nop_();
_nop_();
_nop_();               //空操作四个机器周期,给硬件反应时间
E=1;                   //E置高电平
_nop_();
_nop_();
_nop_();
_nop_();               //空操作四个机器周期,给硬件反应时间
 E=0;                  //当E由高电平跳变成低电平时,液晶模块开始执行命令
 }
/*****************************************************
函数功能:指定字符显示的实际地址
入口参数:x
***************************************************/
 void WriteAddress(uchar x)
 {
     Write_com(x|0x80); //显示位置的确定方法规定为"80H+地址码x"
 }
/*****************************************************
函数功能:将数据(字符的标准ASCII码)写入液晶模块
入口参数:y(为字符常量)
***************************************************/
 void WriteData(uchar y)
 {
    while(BusyTest()==1);  
 RS=1;           //RS为高电平,RW为低电平时,可以写入数据
 RW=0;
 E=0;            //E置低电平(写指令时,就是让E从0到1发生正跳变,所以应先置"0"
 P0=y;           //将数据送入P0口,即将数据写入液晶模块
 _nop_();
 _nop_();
   _nop_();
      _nop_();        //空操作四个机器周期,给硬件反应时间
 E=1;            //E置高电平
 _nop_();
 _nop_();
 _nop_();
 _nop_();        //空操作四个机器周期,给硬件反应时间
 E=0;            //当E由高电平跳变成低电平时,液晶模块开始执行命令
 }
/*****************************************************
函数功能:对LCD的显示模式进行初始化设置
***************************************************/
void LcdInt(void)
{
    delay(15);             //延时15ms,首次写指令时应给LCD一段较长的反应时间
    Write_com(0x38);  //显示模式设置:16×2显示,5×7点阵,8位数据接口0011 1000
delay(5);   //延时5ms 
Write_com(0x38);
delay(5);
Write_com(0x38); //3次写 设置模式
delay(5);
Write_com(0x0c);  //显示模式设置:显示开,有光标,光标闪烁
delay(5);
Write_com(0x06);  //显示模式设置:光标右移,字符不移  0000 0110
delay(5);  
Write_com(0x01);  //清屏幕指令,将以前的显示内容清除
delay(5);
 }
void init()//初始化子函数
{
i=0;
sec=0;
min=0;
h=12;
date=28;
month=4;
year=14;
flag=0;
EA=1;
ET0=1;
TMOD=0x01;
TH0=(65536-50000)/256;
TL0=(65536-50000)%256;
TR0=1;
}
 /*****************************************************
函数功能:显示秒
入口参数:x
***************************************************/ 
void DisplaySecond(uchar x)
{
    uchar i,j;       //j,k,l分别储存温度的百位、十位和个位
i=x/10;     //取十位
j=x%10; //取个位     
WriteAddress(0x4c);    //写显示地址,将在第2行第7列开始显示
WriteData(digit[i]);    //将百位数字的字符常量写入LCD
WriteData(digit[j]);    //将十位数字的字符常量写入LCD
delay(50);         //延时1ms给硬件一点反应时间    
 }


/*****************************************************
函数功能:显示分钟
入口参数:x
***************************************************/ 
void DisplayMinute(uchar x)
{
  uchar i,j;    //j,k,l分别储存温度的百位、十位和个位
i=x/10; //取十位
j=x%10; //取个位     
WriteAddress(0x49);   //写显示地址,将在第2行第7列开始显示
WriteData(digit[i]);   //将百位数字的字符常量写入LCD
WriteData(digit[j]);   //将十位数字的字符常量写入LCD
delay(50);        //延时1ms给硬件一点反应时间    
 }
/*****************************************************
函数功能:显示小时
入口参数:x
***************************************************/ 
void DisplayHour(uchar x)
{
  uchar i,j;     //j,k,l分别储存温度的百位、十位和个位
i=x/10; //取十位
j=x%10; //取个位     
WriteAddress(0x46);    //写显示地址,将在第2行第7列开始显示
WriteData(digit[i]);    //将百位数字的字符常量写入LCD
WriteData(digit[j]);    //将十位数字的字符常量写入LCD
delay(50);         //延时1ms给硬件一点反应时间    
 }
 /*****************************************************
函数功能:显示日
入口参数:x
***************************************************/ 
void DisplayDay(uchar x)
{
  uchar i,j;    //j,k,l分别储存温度的百位、十位和个位
i=x/10;   //取十位
j=x%10;   //取个位     
WriteAddress(0x0c);    //写显示地址,将在第2行第7列开始显示
WriteData(digit[i]);   //将百位数字的字符常量写入LCD
WriteData(digit[j]);   //将十位数字的字符常量写入LCD
delay(500);          //延时1ms给硬件一点反应时间    
 }
 /*****************************************************
函数功能:显示月
入口参数:x
***************************************************/ 
void DisplayMonth(uchar x)
{
  uchar i,j;    //j,k,l分别储存温度的百位、十位和个位
i=x/10; //取十位
j=x%10; //取个位     
WriteAddress(0x09);    //写显示地址,将在第2行第7列开始显示
WriteData(digit[i]);     //将百位数字的字符常量写入LCD
WriteData(digit[j]);     //将十位数字的字符常量写入LCD
delay(50);            //延时1ms给硬件一点反应时间    
 }
/*****************************************************
函数功能:显示年
入口参数:x
***************************************************/ 
void DisplayYear(uchar x)
{
  uchar i,j;    //j,k,l分别储存温度的百位、十位和个位
i=x/10;  //取十位
j=x%10;  //取个位     
WriteAddress(0x06);       //写显示地址,将在第2行第7列开始显示
WriteData(digit[i]);      //将百位数字的字符常量写入LCD
WriteData(digit[j]);      //将十位数字的字符常量写入LCD
delay(50);             //延时1ms给硬件一点反应时间    
 }
void displaytime()
{
  WriteAddress(0x00);  //写Date的显示地址,将在第1行第1列开始显示
  WriteData('D');      //将字符常量写入LCD
  WriteData('a');      //将字符常量写入LCD
  WriteData('t');      //将字符常量写入LCD
  WriteData('e');      //将字符常量写入LCD
  WriteData(':');      //将字符常量写入LCD
  WriteAddress(0x08);  //写年月分隔符的显示地址, 显示在第1行第9列
  WriteData('-');      //将字符常量写入LCD
  WriteAddress(0x0b);  //写月日分隔符的显示地址, 显示在第1行第12列
  WriteData('-');      //将字符常量写入LCD
  WriteAddress(0x40);  //写Date的显示地址,将在第1行第2列开始显示
  WriteData('T');      //将字符常量写入LCD
  WriteData('i');      //将字符常量写入LCD
  WriteData('m');      //将字符常量写入LCD
  WriteData('e');      //将字符常量写入LCD
  WriteData(':');      //将字符常量写入LCD
  WriteAddress(0x48);  //写小时与分钟分隔符的显示地址, 显示在第2行第6列
  WriteData(':');      //将字符常量写入LCD
  WriteAddress(0x4b);  //写分钟与秒分隔符的显示地址, 显示在第2行第9列
  WriteData(':');      //将字符常量写入LCD
  while(1)
    {
         DisplaySecond(sec);
DisplayMinute(min);
DisplayHour(h);
DisplayDay(date);
DisplayMonth(month);
DisplayYear(year);
control();
}
}
void control(void)   //时间日期调整
{
if(!key_ch)   //set键按下
{ key5=0;
delay(5);   //去抖
if(!key_ch)
{        
  flag++;
  if(flag==7)
flag=0;
}
}
while(!key_ch);//松手检测
if(flag==1 && key_add==0)//设置秒加1
{  
   
while(!key_add);//松手检测
sec++;
if(sec==60)
sec=0;
key6=0;
}
if(flag==1 && key_minus==0)//设置秒减1
{
   
while(!key_minus);//松手检测
sec--;
if(sec==-1)
sec=59;
key7=0;
}
if(flag==2 && key_add==0)//设置分加1
{
while(!key_add);//松手检测
    min++;
    if(min==60)
min=0;
}
if(flag==2 && key_minus==0)//设置分减1
{ delay(5);   //去抖1
while(!key_minus);//松手检测
    min--;
    if(min==-1)
min=59;
}
if(flag==3 && key_add==0)//设置时加1
{
while(!key_add);//松手检测
    h++;
    if(h==24)
h=0;
}
if(flag==3 && key_minus==0)//设置时减1
{
while(!key_minus);//松手检测
h--;
    if(h==-1)
h=23;
}
if(flag==4 && key_add==0)//设置日加1
{
while(!key_add);//松手检测
    date++;
if((date==29))//平年2月判断
if((year%4!=0)&&(month==2))
date=1;
if((date==30))//闰年2月判断
if((year%4==0)&&(month==2))
date=1;
if((date==31))//小月判断
if((month==4)||(month==6)||(month==9)||(month==11))
date=1;
if((date==32))//大月判断  
if((month==1)||(month==3)||(month==5)||(month==7)||(month==8)||(month==10)||(month==12))
date=1;
}
if(flag==5 && key_add==0)//设置月加1
{  
while(!key_add);//松手检测
    month++;
    if(month==13)
month=1;
}
if(flag==6 && key_add==0)//设置年加1
{
while(!key_add);//松手检测
    year++;
if(year==99)
year=0;
}
if(flag==6 && key_minus==0)//设置年减1
{
while(!key_minus);//松手检测
    year--;
if(year==0)
year=99;
}
}


void time()
{
if(i==20)
{
    i=0;
    sec++;
    if(sec==60)
    {
    sec=0;
    min++;
    if(min==60)
      {
      min=0;
      h++;
      if(h==24)
      {
      h=0;
      min=0;
      sec=0;
      date++;
if((date==29))//平年2月判断
if((year%4!=0)&&(month==2))
{
date=1;
month++;
if(month==13)
{
month=1;
year++;
}
}
if((date==30))//闰年2月判断
if((year%4==0)&&(month==2))
{
date=1;
month++;
if(month==13)
{
month=1;
year++;
}
}
if((date==31))//小月判断
  if((month==4)||(month==6)||(month==9)||(month==11))
{
date=1;
month++;
if(month==13)
{
month=1;
year++;
}
}
if((date==32))//大月判断
                       if((month==1)||(month==3)||(month==5)||(month==7)||(month==8)||(month==10)||(month==12))  
{
date=1;
month++;
if(month==13)
{
month=1;
year++;
}
}
      }
      }
    }
    }
}
void T0_rpt() interrupt 1  //定时器0中断子函数
{
TH0=(65536-50000)/256;//重新装入初始值50000us=50ms    100ms
TL0=(65536-50000)%256;
i++;
time();
}
void main()
{
init();
    LcdInt();
while(1)
{
   displaytime();
}
}

这是程序,已经可以实现大部分需要的功能!按键需要按下1~2s,才有改变!

但是LCD上面更改时间和日期的时候没有闪烁,可以通过延迟显示的方法实现!

再加一个按键OK,在显示函数里面加入即可完美实现!

0 0
原创粉丝点击