找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 5010|回复: 1
收起左侧

我的小制作基于51单片机的数字时钟

[复制链接]
ID:72519 发表于 2015-1-21 17:44 | 显示全部楼层 |阅读模式
通过几天的调试用,单片机AT89C52,LCD液晶屏LC1602,温度采集模块DS18B20,时钟模块DS1302,和三个按键,以及一些电子元件完成了数字时钟系统的设计。在一块液晶显示屏上实现动态,时、分、秒、年、月、日、星期、温度数据的显示, 通过三个按键,S1调时键、S2时间加键、S3时间减键、实现时钟的年、月、日、星期、时、分、秒、的调整。本系统设计大部分功能有软件来实现,使电路简单明了。由于以前从没有过单片机相关产品的制作经验,所以这个设计共用了约30多个钟。在做这设计之前软件思路非常模糊代码都是想到哪写到哪,结果写到最后调试时出现了大把错误,在调试程序中花了大把的时间,调程序真的是个考验耐心的工作搞的我砸电脑的想法都有了。因为是自己一个人制作的出现了错误只好在网上找资料,阅读别人写过的代码,经过长时间的修改终于完成了这次制作。设计因为是业余学习的单片机加上这断时间工作比较忙,所以为了完成这个产品的制作可没少睡过觉,几乎每天都没在2点钟前睡过。附原代码
  1. #include"reg52.h"

  2. #include"12864.h"

  3. #include"ds1302.h"

  4. #include"ds18b20.h"

  5. #include"intrins.h"//_nop_声明



  6. #define uchar unsigned char

  7. #define uint unsigned int

  8. sbit DQ=P3^7;

  9. sbit rs=P3^0;

  10. sbit rw=P3^1;

  11. sbit en=P3^2;

  12. uchar tou;



  13. sbit io=P3^6;

  14. sbit rst=P3^4;      //定义各芯片跟单片机连接的定义

  15. sbit sck=P3^5;

  16. sbit s1=P1^0;      //定义各芯片跟单片机连接的定义

  17. sbit s2=P1^4;

  18. sbit s3=P1^7; //定义各芯片跟单片机的连接的定义

  19. uchar tung=0;  //定义

  20. uchar tab4[]={0x82,0x85,0x88,0x8f,0xc0,0xc3,0xc6};//用来存放年月日星期时分秒的地址

  21. char tab[]={13,5,4,5,00,59,33};//年 月 日 周 时 分 秒的初值

  22. uchar tab1[]; //

  23. uchar write_add[7]={0x8c,0x88,0x86,0x8a,0x84,0x82,0x80}; //写寄存器地址

  24. uchar read_add[7]={0x8d,0x89,0x87,0x8b,0x85,0x83,0x81};//存放从1302中读数寄存器地址

  25. void write_byte(uchar);            //写数据函数

  26. uchar read_byte(void);          //读数据函数

  27. uchar readtemperature(void);//一次性读出温度



  28. void write_ds1302_byte(uchar);    //单字节写函数

  29. void write_ds1302(uchar add,uchar dat);//写 /写地址数据+写数据函数

  30. uchar read_ds1302(uchar);//         单字节读函数

  31. void flx(uchar j);           //

  32. void key(void);     //按键函数

  33. void write_ds1302_byte(uchar dat)

  34. {

  35.          uchar i;

  36.          for(i=0;i<8;i++)                      //单字节写

  37.          {

  38.                    sck=0;

  39.                    io=dat&0x01;

  40.                    dat=dat>>1;

  41.                    sck=1;

  42.          }

  43.                   

  44. }

  45. void write_ds1302(uchar add,uchar dat)

  46. {

  47.         rst=0;

  48.          _nop_();

  49.          sck=0;

  50.          _nop_();

  51.          rst=1;                      //写地址数据+写数据函数

  52.          _nop_();

  53.          write_ds1302_byte(add);               //写地址

  54.          write_ds1302_byte(dat);        //写数据

  55.          rst=0;

  56.          _nop_();

  57.          io=1;

  58.          sck=1;



  59. }                                                                                             

  60. uchar read_ds1302(uchar add)

  61. {        uchar i,value;

  62.          rst=0;                      //

  63.          _nop_();

  64.          sck=0;

  65.          _nop_();     //单字节读数据函数

  66.          rst=1;

  67.          _nop_();

  68.          write_ds1302_byte(add );

  69.          for(i=0;i<8;i++)

  70.          {

  71.                    value=value>>1;

  72.                    sck=0;

  73.                    if(io)                                         //因为是串口所以要分8次读数据

  74.                    value=value|0x80;

  75.                    sck=1;

  76.          }

  77.          rst=0;

  78.          _nop_();

  79.          sck=0;

  80.          _nop_();

  81.          sck=1;

  82.          io=1;

  83.          return value;

  84.          }

  85. void read_ds1302_1(void)//一次读出年月日 时分秒函数

  86. {

  87.          uchar i,shi,ge,tun;

  88.          write_ds1302(0x8e,0x00);//去除写保护

  89.          for(i=0;i<7;i++)

  90.          {

  91.                    tun=read_ds1302(read_add[i]);     //一次将年月日从DS1302中读出送到数组中

  92.                    ge=tun%16;

  93.                    shi=tun/16;                                                           //将BCD码分成十位和个位

  94.                    tab[i]=shi*10+ge;                                     //再换成十进制存到数组tab中



  95.          }

  96.                    write_ds1302(0x8e,0x00);//加写保护

  97. }

  98. void write_ds1302_1(void)

  99. {                                                                                              //将10进制数送到1602;

  100.        write_com(0x80+0x40+6);//秒的位置

  101.          write_dat(tab[6]/10+0x30);

  102.          write_dat(tab[6]%10+0x30);



  103.          write_com(0x80+0x40+3);//

  104.          write_dat(tab[5]/10+0x30);  //分的位置

  105.          write_dat(tab[5]%10+0x30);

  106.                               //将十进制的数据换成1602的显示码送到显示屏

  107.          write_com(0x80+0x40+0);// //时的地址

  108.          write_dat(tab[4]/10+0x30);    //显示屏的第二排要加0x80+40

  109.          write_dat(tab[4]%10+0x30);



  110.          write_com(0x80+2);//

  111.          write_dat(tab[0]/10+0x30);//年的地址

  112.          write_dat(tab[0]%10+0x30);



  113.          write_com(0x80+5);// 显示屏 的第一排地址要加0x80;

  114.          write_dat(tab[1]/10+0x30);

  115.          write_dat(tab[1]%10+0x30);                   //月的地址

  116.                                                                                    

  117.          write_com(0x80+8);//   日的地址

  118.          write_dat(tab[2]/10+0x30);

  119.          write_dat(tab[2]%10+0x30);



  120.          write_com(0x80+15);                        //星期的地址

  121.          write_dat(tab[3]%10+0x30);









  122. }

  123. void set_rtc(void)         //一次设置初始时间年月日函数

  124. {

  125.          uchar i,j;

  126.         

  127.          for(i=0;i<7;i++)

  128.          {

  129.                  j=tab[i]/10;         //将设置的年月日时间2013—4-25 00-14-00中分解出十位放入J中

  130.                    tab1[i]=tab[i]%10; //将设置的年月日时间2013—4-25 00-14-00中分解出个位再放入time_data[]数组中

  131.                    tab1[i]=tab1[i]+j*16;//将十进制的个位和十位转换为BCD码



  132.          }

  133.          write_ds1302(0x8e,0x00);//去除写保护

  134.          for(i=0;i<7;i++)

  135.          {

  136.          write_ds1302(write_add[i],tab1[i]); //写要设置初始时间日期的操作寄存器的地址

  137.          }

  138.          write_ds1302(0x8e,0x00);//加写保护

  139. }

  140.   

  141.   void ds1302_init(void)

  142.   {



  143.   



  144.         key();          //按键函数

  145.    read_ds1302_1();//一次读出年月日 时分秒函数

  146.    write_ds1302_1(); //时间的显示函数

  147.                   

  148. }      

  149. void key(void)

  150. {      

  151.                             //按键函数

  152.          if(s1==0)             //如果S1按下

  153.          {

  154.                    delay(3);    //延时去抖

  155.                   if(s1==0)                                                  

  156.                    {      

  157.                             while(!s1);   //

  158.                               tung=0;            //tung用来确定光标的位置

  159.                                     

  160.                             while(tung<=6)            //循环7次   时分秒年月日星期

  161.                             {      

  162.                                                

  163.                              

  164.                                          if(s2==0)

  165.                                                {

  166.                                                         delay(3);

  167.                                                         if(s2==0)//去抖动延时

  168.                                                         {

  169.                                                                  while(!s2);    // 检测按键是否放开

  170.                                                                  tab[tung]++; //如果+键按下加1

  171.                                                         }

  172.                     }

  173.                                                if(s3==0)

  174.                                                {

  175.                                                         delay(3);    // 检测按键是否放开

  176.                                                         if(s3==0)

  177.                                                         {

  178.                                                                  while(!s3); //按键是否松手

  179.                                                                  tab[tung]--;        //如果—键按下减1

  180.                                                         }

  181.                                                }

  182.                                       

  183.                                         switch(tung)

  184.                                         {                                                 //通过tung的值确定光标的位置

  185.                                         case 0:

  186.                                                           if(tab[tung]<3)         //如果tung=0;则表示正在调年让年在2004到2098这个范围内

  187.                                                           tab[tung]=98;

  188.                                                           if(tab[tung]>98)

  189.                                                           tab[tung]=4;

  190.                                                           break;

  191.                                                          

  192.                                        case 1:

  193.                                                        if(tab[tung]<1)

  194.                                                                tab[tung]=12;

  195.                                                           if(tab[tung]>12)       //如果tung=1;则表示正在调月让月在1到12这个范围内

  196.                                                             tab[tung]=1;

  197.                                                            break;

  198.                                        case 2:

  199.                                                        if(tab[tung]==0)

  200.                                                                tab[tung]=31;

  201.                                                           if(tab[tung]==32)       //如果tung=2;则表示正在调日让在1到31这个范围内

  202.                                                                tab[tung]=1;

  203.                                                           break;

  204.                                        case 3:

  205.                                                       if(tab[tung]<1)

  206.                                                           tab[tung]=7;

  207.                                                          if(tab[tung]>7)   //如果tung =3则表示下在调星期让在1到7这个范围内

  208.                                                           tab[tung]=1;

  209.                                                           break;

  210.                                    case 4:

  211.                                                       if(tab[tung]<0)

  212.                                                 tab[tung]=23;

  213.                                                          if(tab[tung]>23)         //如果tung=4则表示在调时让时在1到23这个范围内

  214.                                                                 tab[tung]=0;

  215.                                                            break;

  216.                                       case 5:

  217.                                                       if(tab[tung]<0)

  218.                                                                 tab[tung]=59;

  219.                                                          if(tab[tung]>59)           //如果tung=5;则表示正在调分让分在1到59这个范围内

  220.                                                                 tab[tung]=0;

  221.                                                            break;

  222.                                       default:

  223.                                                       if(tab[tung]<0)

  224.                                                                 tab[tung]=59;

  225.                                                          if(tab[tung]>59)           //如果tung=6;则表示正在调秒让秒在1到59这个范围内

  226.                                                                 tab[tung]=0;

  227.                                                         break;

  228.                                         }

  229.                                        write_com(tab4[tung]);    //显示正在调的位置         

  230.                                        flx(tab[tung]);    //将数据分离成1602的显示码并送到1602显示屏

  231.                                          

  232.                                     

  233.                             if(s1==0)

  234.                                       {

  235.                                                 while(!s1);  //判断是否调好了时间如果好了则跳出key

  236.                                               tung++;    //每按一下S1调式模式键则加一次如果到了7次跳出KEY函数

  237.                                       }                                 

  238.                                              

  239.                             }

  240.                            

  241.                    }

  242.                    write_com(0x0c);//调完了时间关闭光标      

  243.                    set_rtc(); //把调好的时间数据送到DS1302芯片中

  244.          }

  245. }

  246. void flx(uchar j)

  247. {

  248.          uchar k,a;

  249.           write_com(0x0f);       //开光标显示指令

  250.           k=j/10;                 //分离十位跟个位

  251.           a=j%10;

  252.           if(tung!=3)        //年月日时分秒分离

  253.           {

  254.         

  255.                write_dat(k+0x30);    //将分离的十位跟个位换成LC1602能显示的码

  256.                     write_com(0x0c);//关光标指令   

  257.                write_dat(a+0x30);



  258.          }

  259.          else

  260.         

  261.                    write_dat(a+0x30);      //星期分离

  262.                     

  263. }



  264. void delay1(uchar i)

  265. {

  266. while(i--);

  267. }

  268. void init_ds18b20(void)

  269. {

  270.   uchar n;

  271.   DQ=1;

  272.   delay1(8);

  273.   DQ=0;

  274.   delay1(80);                 //初始化函数

  275.   DQ=1;

  276.   delay1(8);

  277.   n=DQ;

  278.   delay(25);

  279. }

  280. void write_byte(uchar dat)

  281. {

  282.          uchar i;

  283.          for(i=0;i<8;i++)

  284.          {

  285.                    DQ=0;                              //写函数

  286.                    DQ=dat&0x01;

  287.                    delay(4);

  288.                    DQ=1;

  289.                    dat>>=1;               //移一位

  290.         

  291.          }

  292.                    delay(4);

  293. }

  294. uchar read_byte(void)

  295. {

  296.          uchar i,value;

  297.          for(i=0;i<8;i++)

  298.          {

  299.                    DQ=0;                                     //读数据函数

  300.                    value>>=1;                       //移1位

  301.                    DQ=1;

  302.                    if(DQ)

  303.                    value|=0x80;

  304.                    delay(4);

  305.          }

  306.          return value;

  307. }

  308. uchar readtemperature(void)//一次性读出温度

  309. {

  310.          uchar a,b;

  311.          init_ds18b20();

  312.          write_byte(0xcc);//跳过ROM

  313.          write_byte(0x44);//启动温度测量

  314.          delay1(300);

  315.          init_ds18b20();               //初始化



  316.          write_byte(0xcc);

  317.          write_byte(0xbe);//读取温度

  318.          a=read_byte();//高8位

  319.          b=read_byte();//

  320.         

  321.          b<<=4;                             //因不用小数部分所以移4位

  322.          b+=(a&0xf0)>>4;          //将存放低8位的个位和高8位的十位合到一块

  323.        return b;

  324. }

  325. void xianshi(void)

  326. {

  327.          uchar temp,k,f;

  328.          temp=readtemperature();//将温度保存到temp中

  329.          f=temp/10;                            //分离出十位跟个位

  330.          k=temp%10;

  331. write_com(0x80+0x40+10);//温度显示的位置                     

  332. write_dat(f+0x30);          //

  333. write_dat(k+0x30);





  334. }

  335. uchar tab1[]="20  -  -   week  ";  //屏上要固定显示的字符

  336. uchar tab2[]="  :  :  ";

  337. void delay(uint x)

  338. {

  339.          uint a,b;

  340.          for(a=x;a--;a>0)                     //延时函数

  341.                    for(b=110;b>0;b--);

  342. }

  343. void write_com(uchar com)

  344. {

  345.          rs=0;

  346.          rw=0;                                                  //写数据指令函数

  347.          P2=com;

  348.          delay(4);

  349.          en=1;

  350.          delay(4);

  351.          en=0;

  352. }

  353. void write_dat(uchar dat)

  354. {

  355.          rs=1;

  356.          rw=0;

  357.     P2=dat;

  358.          delay(4);                                  //写数据函数

  359.          en=1;

  360.          delay(4);

  361.          en=0;



  362. }

  363. void init(void)

  364. {

  365.     write_com(0x38);

  366.          write_com(0x0c);//     //1602初始化函数

  367.          write_com(0x06);

  368.          write_com(0x01);

  369.          write_com(0x80);

  370. write_com(0x80);                           //第一行显示的位置地址

  371. for(tou=0;tou<16;tou++)

  372.          {

  373.                    write_dat(tab1[tou]);  //显示固定字符

  374.          }

  375. write_com(0x80+0x40);        //第二行显示的位置地址

  376. for(tou=0;tou<8;tou++)

  377. {

  378.          write_dat(tab2[tou]);

  379.         

  380. }

  381. write_com(0x80+0x40+12);  

  382. write_dat(0xdf);//显示温度的小圆圈符号,0xdf是液晶屏字符库的该符号地址码

  383. write_dat(0x43); //显示"C"符号,0x43是液晶屏字符库里大写C的地址码   

  384. }

  385. main()

  386. {

  387.          init();//初始化显示屏

  388.          set_rtc();       //设置ds1302时钟芯片时间

  389. while(1)



  390.              ds1302_init();//初始化ds1302时钟芯片

  391.                    xianshi();      //温度显示函数

  392.                   

  393. }

  394.         



  395. }

  396. 通过这次的制作让我全面的了解单片机相关产品的开发流程,及一些常用芯片驱动程序的编写。始我在单片机上的学习更上一层楼!
复制代码




回复

使用道具 举报

ID:260199 发表于 2017-12-11 15:58 | 显示全部楼层
666,有没有整点提醒功能
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表