公告:

基于51单片机的实时时钟(89c52+DS12c887+LCD1602)

作者:智凡单片机 / 时间:4个月前 (06/14) / 分类:51单片机 / 阅读:37038 / 评论:0

       迷茫了好长时间,不知道自己该学点什么,正值暑假来临,仔细斟酌电子信息工程专业的前景和发展,最终选择好好学习一下单片机,由于之前就学过,所以对其不是那么陌生,只不过先前学习的不够透彻,而且书本上和老师讲授用的都是汇编语言,学习起来特别困难,指令难记,程序更加难以理解。使得本来对单片机学习的兴趣锐减了不少。

       自从同学那里听到郭天祥这位高人,单片机届的高人,用c语言编程序,又激发了自己学习单片机的兴趣。因为对c语言不陌生,而且它学习来就简单多了,并且学的时间也长,上手快。为了方便学习暑假就在家待了十天,之后返校,学校不让住就在外面租房,又从网上购了51单片机开发板,接着从那时起开始从网上查有关单片机和郭天祥的资料。幸运的找到了郭天祥的《十天学会单片机和c语言编程》,于是就立即下载下来。看过之后,发现正是自己所需要的,郭老师的授课方式特别适合初学者,甚至适合没接触过或者说没学过单片机课程的人群。记得那段时间每天都在听郭老师的视频教程,然后仔细琢磨程序的思想,认真做课后练习,当然自己也产生一些想法,然后按照自己的思想去编写,去调试,那些天每天待在实验室,坐在电脑前面,听教程,写程序,调程序。虽然很累很累,但是每天过得很充实,感觉学到了很多东西,同时又感到还有好多东西不会。对单片机的兴趣越来越浓。

       转眼学习半月有余了,已经将郭老师的课程听完了,于是按照郭老师安排的进程,需要制作一个时钟,于是就从网上购51芯片,时钟芯片,电阻,电容,电路板之类等等,之前都是在学理论,没有实践机会。这次一定要好好练习一下,连连焊接技术,同时积累一下实物连接的经验。

       下面是我做的实时时钟

   基于51单片机的实时时钟(89c52+DS12c887+LCD1602) 51单片机 第1张

基于51单片机的实时时钟(89c52+DS12c887+LCD1602) 51单片机 第2张

基于51单片机的实时时钟(89c52+DS12c887+LCD1602) 51单片机 第3张

基于51单片机的实时时钟(89c52+DS12c887+LCD1602) 51单片机 第4张

基于51单片机的实时时钟(89c52+DS12c887+LCD1602) 51单片机 第5张

用手机拍的,像素不够高,光线不太好!用开发板给其供电(5v),单片机的P0口接液晶的数据口(7-14),单片机的P1口接ds12c887的数据口(4-11),液晶的3脚一般接10k的滑动变阻器,在此我直接接3.4K的电阻(测量出来的),液晶的1脚和16脚直接接地,15脚接电源,4脚接P2^3,6脚接P2^2,由于只读不写,所以5脚直接接地。DS12c887的1,2,3,16,23悬空,12脚接地,13接P3^6,14接P3^4,15脚P3^7,17脚接P3^5,19脚接P3^3(外部中断1),温度传感器数据口接P2^0,蜂鸣器接P2^1。

 

 

程序代码:

#include<reg51.h>
#include<intrins.h>
#define uchar unsigned char
#define uint unsigned int
sbit DQ =P2^0;
sbit rs=P2^3;
sbit lcden=P2^2;
//sbit s4=P2^4;//用来设置闹钟
sbit s1=P2^6;//功能键
sbit s2=P2^7;//+
sbit s3=P2^5;//-
sbit beep=P2^1;//蜂鸣器
sbit dscs=P3^6; //13脚
sbit dsas=P3^4; //14脚
sbit dsrw=P3^7;//15脚
sbit dsds=P3^5; //17脚
sbit dsirq=P3^3;//19脚,外部中断1
uchar s1num,flag;
char miao,shi,fen,zhou,nian,yue,ri;
uchar code table[]="  20  -  -      ";
uchar code table1[]="     :  :  ";
void write_ds(uchar,uchar);
uchar read_ds(uchar);
//void set_time();
//void set_alarm(uchar,uchar,uchar);
void delay(uint z)
{
 uint x,y;
 for(x=z;x>0;x--)
  for(y=110;y>0;y--);
}
void delay_us(uint z)
{
    while(z--); 
}
void write_com(uchar com)
{
 rs=0;
 lcden=0;
 P0=com;
 delay(5);
 lcden=1;
 delay(5);
 lcden=0; 
}

void write_data(uchar date)
{
 rs=1;
 lcden=0;
 P0=date;
 delay(5);
 lcden=1;
 delay(5);
 lcden=0; 
}
void init_1602()
{
 uchar num;
 lcden=0;
 EA=1;
 EX0=1;
 IT0=1;
//    set_alarm(21,02,00);
    write_ds(0x0A,0x20);//A寄存器
 write_ds(0x0B,0x06);//B寄存器DM设置成2进制,最高位为0
//    set_time(); 
 read_ds(0x0c);
 write_com(0x38);
 write_com(0x0c);
 write_com(0x06);
 write_com(0x01);
 write_com(0x80);
 for(num=0;num<16;num++)
  {
   write_data(table[num]);
   delay(5);
  }
 write_com(0x80+0x40);
 for(num=0;num<11;num++)
  {
   write_data(table1[num]);
   delay(5);
  }
}
void write_nyr(uchar add,uchar date)
{
  uchar shi,ge;
  write_com(0x80+add);
  shi=date/10;
  ge=date;
  write_data(shi+0x30);
  write_data(ge+0x30);
 }
void write_zhou(uchar zhou)
{
    write_com(0x80+13);
    switch(zhou)
 {
  case 1:write_data('M');
         write_data('o');
   write_data('n');break;
  case 2:write_data('T');
         write_data('u');
   write_data('e');break;
  case 3:write_data('W');
         write_data('e');
   write_data('d');break;
  case 4:write_data('T');
         write_data('h');
   write_data('u');break;
  case 5:write_data('F');
         write_data('r');
   write_data('i');break;
  case 6:write_data('S');;
         write_data('a');
   write_data('t');break;
  case 7:write_data('S');
         write_data('u');
   write_data('n');break;   
 }
}
void write_sfm(uchar add,uchar date)
{
 uchar shi,ge;
 shi=date/10;
 ge=date;
 write_com(0x80+0x40+add);
 write_data(0x30+shi);
 write_data(0x30+ge);
}
void write_ds(uchar add,uchar date)
{
 dscs=0;
 dsas=1;
 dsds=1;
 dsrw=1;
 P1=add;
 dsas=0;
 dsrw=0;
 P1=date;
 dsrw=1;
 dsas=1;
 dscs=1;    
}

uchar read_ds(uchar add)
{
  uchar ds_date;
 dscs=0;
 dsas=1;
 dsds=1;
 dsrw=1;
 P1=add;
 dsas=0;
 dsds=0;
 P1=0xff;
 ds_date=P1;
 dsds=1;
 dsas=1;
 dscs=1;
 return ds_date; 
}


//18b20初始化
void init_18b20(void)
  
    
     DQ=1;
     _nop_();//高电平持续一会(大概1us)
     DQ=0; //然后拉低
     delay_us(80);//延时
     DQ=1;  //释放总线
     delay_us(300);//延时采样  
}
//18b20写
void write_b20(uchar date)
{
   uchar i;
  for(i=0;i<8;i++)//循环8次,一位一位写,先写最低位,最后写最高位,当出现需要延时1us时就没必要了,因为单片机每条指令大概就是1-2us
    {
       DQ=0;
       DQ=date&0x01;
       delay_us(4);
       DQ=1;
       date>>=1;
    }
    delay_us(4);
}
//18b20读
uchar read_b20(void)
{
   uchar i,value;
   for(i=0;i<8;i++)
       {
           DQ=0;
           value>>=1;//起到一定延时作用
           DQ=1;//释放总线,否则由于线与总为0
           if(DQ==1)//如果为1,接着执行,采样 
           value|=0x80;
          delay_us(4);
       }
    return value;
  
}
//18b20读温度,带返回值
uchar readtemperature(void)
{
     uchar a,b;
     init_18b20();
     write_b20(0xcc);//跳过rom
     write_b20(0x44);//启动温度测量
//   delay_us(300);
    init_18b20();
    write_b20(0xcc);
    write_b20(0xbe);
    a=read_b20();
    b=read_b20();
    b<<=4;
    b+=(a&0xf0)>>4;
    return b;
}
//显示函数
void display(uchar temp)
{
    write_com(0x80+0x40+13);
 write_data(0x30+temp/10);
 write_data(0x30+temp);
    write_data('C');  
 
void keyscan()
  {
  // s4=0;
   if(s1==0)
  {
  delay(5);
  if(s1==0)
   
      s1num++;
   flag=1;
   while(!s1) ;
   if(s1num==1)
       {
      write_com(0x80+0x40+9);
      write_com(0x0f);
    }
   if(s1num==2)
       {
     write_com(0x80+0x40+6);
    }
   if(s1num==3)
       {
     write_com(0x80+0x40+3);
    }
   if(s1num==4)
      {
     write_com(0x80+13);
   }
   if(s1num==5)
      {
     write_com(0x80+11);
   }
   if(s1num==6)
      {
     write_com(0x80+8);
   }
   if(s1num==7)
      {
     write_com(0x80+5);
   }
   if(s1num==8)
      {
     s1num=0;
     write_com(0x0c);
     flag=0;
     write_ds(0,miao);
     write_ds(2,fen);
     write_ds(4,shi);
     write_ds(6,zhou);
     write_ds(7,ri);
     write_ds(8,yue);
     write_ds(9,nian);
   }
    }
   }
     if(s1num!=0)
    {
      if(s2==0)
   {
     delay(1);
     if(s2==0)
     {
     while(!s2);
     switch(s1num)
     {
     case 1:miao++;if(miao==60)miao=0;write_sfm(9,miao);write_com(0x80+0x40+10);break;
     case 2:fen++;if(fen==60)fen=0;write_sfm(6,fen);write_com(0x80+0x40+7);break;
     case 3:shi++;if(shi==24)shi=0;write_sfm(3,shi);write_com(0x80+0x40+4);break;
          case 4:zhou++;if(zhou==8)zhou=1;write_zhou(zhou);write_com(0x80+15);break;
     case 5:ri++;if(ri==32)ri=1;write_nyr(10,ri);write_com(0x80+11);break;
     case 6:yue++;if(yue==13)yue=1;write_nyr(7,yue);write_com(0x80+8);break;
     case 7:nian++;if(nian==100)nian=0;write_nyr(4,nian);write_com(0x80+5);break;
     }     
     }
    }
   if(s3==0)
   {
     delay(1);
     if(s3==0);
     {
       while(!s3) ;
    switch(s1num)
     {
     case 1:miao--;if(miao==-1)miao=59;write_sfm(9,miao); write_com(0x80+0x40+9);break;
     case 2:fen--;if(fen==-1)fen=59;write_sfm(6,fen);write_com(0x80+0x40+6);break;
     case 3:shi--;if(shi==-1)shi=23;write_sfm(3,shi);write_com(0x80+0x40+3);break;
        case 4:zhou--;if(zhou==0)zhou=7;write_zhou(zhou);write_com(0x80+13);break;
     case 5:ri--;if(ri==0)ri=31;write_nyr(10,ri);write_com(0x80+10);break; 
     case 6:yue--;if(yue==0)yue=12;write_nyr(7,yue);write_com(0x80+7);break;
     case 7:nian--;if(nian==-1)nian=99;write_nyr(4,nian);write_com(0x80+4);break;
     }
     }
         }
  }
}


void main()
{

    uchar temp;
    init_1602();
 init_18b20();
 while(1)
 
    temp=readtemperature();
          display();
    keyscan();
    if(flag==0)
   {
          if((read_ds(0x0B)&0x80)==0)//对最高位进行判断,为0则显示时间
    {
     nian=read_ds(9);
     yue=read_ds(8);
     ri=read_ds(7);
     zhou=read_ds(6);
     shi=read_ds(4);
     fen=read_ds(2);
     miao=read_ds(0);
     write_nyr(4,nian);
     write_nyr(7,yue);
     write_nyr(10,ri);
     write_zhou(zhou);
     write_sfm(3,shi);
     write_sfm(6,fen);
     write_sfm(9,miao);
      }
      } 
 }
}

总结分析

       这几天一直在忙着做基于ds12c887实时时钟,从听教程到自己改程序,一次次的尝试,接着用焊接电路,将ds12c887跟单片机最小系统连接起来,最后在液晶1602上显示出实时时间。看似简单的电路,可焊接起来很麻烦,整整用了两天才基本焊好(按键没有连接),装上ds12c887芯片发现有问题,原来我把I/O口接反了,应该是887的4-11接P1^0---P1^7;我接成了P1^7-P1^0,因为在焊接887的时候,没有考虑太严谨,只想着尽量把887的数据口和单片机的I/O口凑得近点,以便连接,却忽视了数据口是有严格要求的,必须有顺序。

       还有,887的掉电保护问题,必须初始化的时候初始化B寄存器(如果还是不正确的话,可以再尝试设置A寄存器(write_ds(0x0A,0x20)),使最高位SET位为0,然后DM为设置为1(二进制形式),不然的话很可能秒会走到90,而且中间还有时间跳变(可这样写write_ds(0x0B,0x06)),然后在出函数中加个判断最高位SET是0还是1,是0的话执行掉电保护(if(read_ds(0x0B)&0X80)==0)。其实之前,1602也出现了类似的问题。后来及时调整过来了,顺利显示出了应有的效果。关于1602的3脚电位器(用来调节1602的亮度),如果可以测出电位器的阻值,直接连上相应的直流电阻也是可以的。15脚接电阻的问题一定慎重考虑。

       写程序时必须严格按照时序图写,先给887一个初始值,就是实时时间,如果显示正常,可关掉电源,过一会儿再打开,开是否起到掉电保护,如果没有,则需要(先给887初值,然后下载到单片机,然后再把初始化时间函数去掉,再下载一次应该就没问题了。)当然,若有按键的话,如果时间不准确,也可以调节一下时间。

       对于时间的显示,如果你把887跟单片机的某根连线接的不好或者就没接上,很可能会在1602上显示I5,所以如果你的显示时I5也不用大惊小怪,先检查一下线路问题。如果线路确定没有问题,再去认真查程序,调试。

       另外,在接数据口时一定要慎重,特别是P0口,因为其没有上拉电阻,如果要用的话,一定要想办法搞清楚到底用不用加上拉电阻。在布局按键时,最好加一个复位键。因为当单片机受到外界干扰(像电源接反之类等等)时,有可能会使程序混乱,显示出来的时间不准确。这时,只要按一下复位键就能恢复正常时间。复位键接到9脚。

 

       对于温度传感器ds18b20,一定要按照时序图严格编写,特别是初始化函数,延时不对,采集不到,读不出来。当然刚开始显示的数字都是随机的,经常会出来85,不过瞬间就会恢复正常显示。

 

 

注:这个程序还不完整,还没把闹钟程序加到里面!


没有评论,留下你的印记,证明你来过。


发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。