找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 5397|回复: 8
收起左侧

注释很详细的51单片机简易的电子钟程序+Proteus仿真电路

  [复制链接]
ID:631727 发表于 2019-11-23 22:15 | 显示全部楼层 |阅读模式
有需要自己借鉴
仿真原理图如下(proteus仿真工程文件可到本帖附件中下载)
51hei截图20191123221417.png

单片机源程序如下:
  1. /*------------------------------------------------------------------------------------------------
  2. 程序功能:采用LCD1602液晶显示,显示项目有:年月日、星期、时分秒,带按键调整功能。
  3. 按键操作说明:
  4. 1、S3按键:是调整时间日期键,该按键选择对秒、分、时、星期、日、月、年选中操作,
  5. 当其中一个被选中的时候,可以进行时间调整和日期的调整。每按一次调整一个时间或日期,直到"年"再按一次退出设置界面。
  6. 2、S1按键:是时间、日期增加操作。
  7. 3、S2按键:是时间、日期减少操作。

  8. ------------------------------------------------------------------------------------------------*/

  9. #include <reg51.h>
  10. #include <intrins.h>
  11. #define uchar unsigned char
  12. #define uint unsigned int
  13. sbit ds1302_sclk= P1^4;    //DS1302的Sclk时钟线位定义
  14. sbit ds1302_io  = P1^5;    //DS1302的i/o数据线位定义
  15. sbit ds1302_ce = P1^6;     //DS1302的ce片选线位定义
  16. sbit rd=P3^7;

  17. sbit lcd1602_rs = P1^2;          //位定义1602液晶数据命令选择端
  18. sbit lcd1602_rw = P1^1;                 //位定义1602液晶读写选择端
  19. sbit lcd1602_en  = P1^0;          //位定义1602液晶使能端
  20. sbit set = P3^4;               //定义S9按键_时间设置选择键
  21. sbit up  = P3^2;               //定义S13按键_增加键
  22. sbit down= P3^3;               //定义S17按键_减小键

  23. uchar time_data[]={19,6,11,23,13,14,0};   //设置时间初值(年、周、月、日、时、分、秒)
  24. uchar write_add[]={0x8c,0x8a,0x88,0x86,0x84,0x82,0x80};  //年、周、月、日、时、分、秒的寄存器写入地址

  25. uchar code table1[]={"20  -  -   Week"}; //液晶一直显示的字符
  26. uchar code table2[]={"      :  :      "}; //用空格补齐16位
  27. uchar miao,fen,shi,nian,yue,ri,week;
  28. uchar flag=1,count=0,flag_up=0,flag_down=0,done=0;

  29. void delay(uchar x)  //延时函数
  30. {
  31.         uchar y,z;
  32.         for(z=x;z>0;z--)
  33.                 for(y=110;y>0;y--);
  34. }

  35. /*****************************与ds1302相关函数*********************************/

  36. void write_ds1302_byte(uchar dat)   //ds1302写一字节
  37. {
  38.         uchar i;
  39.         for(i=0;i<8;i++)   //分8次循环,把(add)一位一位的写入
  40.         {
  41.                 ds1302_sclk=0;       
  42.                 ds1302_io =dat&0x01;   //dat和0x01进行按位与,保留最低位。即从低位开始写入
  43.                 dat=dat>>1;   //add右移一位
  44.                 ds1302_sclk=1;
  45.         }
  46. }

  47. void ds1302_write(uchar add,uchar date)  //单字节写操作函数(包含地址和数据)
  48. {
  49.         ds1302_ce=0;      
  50.         ds1302_sclk=0;     
  51.         ds1302_ce=1;     //ce变为高电平,允许单片机向DS1302中的寄存器写入地址命令和数据
  52.         _nop_();         
  53.         write_ds1302_byte(add);    //写入控制命令(指明写入的寄存器地址及后续的操作为写操作)
  54.         write_ds1302_byte(date);   //写入一个字节数据
  55.         ds1302_ce=0;     //地址命令和数据写完之后,把ce拉低,防止对写入的数据进行误操作
  56.         _nop_();         
  57.         ds1302_io =1;    //释放IO数据线
  58.         ds1302_sclk=1;   //释放sclk时钟线
  59. }

  60. uchar ds1302_read(uchar add)  //单字节读操作函数
  61. {
  62.         uchar i,value;
  63.         ds1302_ce=0;     
  64.         ds1302_sclk=0;   
  65.         ds1302_ce=1;  //ce变为高电平,单片机向DS1302中的寄存器写入地址命令,并读出数据
  66.         _nop_();        //稍微延时(一个机器周期)
  67.         write_ds1302_byte(add);   //写入控制命令,(指明要读取的寄存器地址及后续的操作为读操作)
  68.         for(i=0;i<8;i++)   //分8次循环,把数据一位一位的读出
  69.         {
  70.                 value=value>>1;     //value右移一位
  71.                 ds1302_sclk=0;    //把sclk时钟置为低电平,开始读取数据
  72.                 if(ds1302_io)
  73.                 {
  74.                         value=value|0x80;    //把I0读到的值,放到value 的最高位
  75.                 }
  76.                 ds1302_sclk=1;
  77.         }
  78.         ds1302_ce=0;      //一个字节的数据读完之后,把ce拉低
  79.         _nop_();
  80.         ds1302_sclk=0;     //释放sclk时钟线
  81.         _nop_();
  82.         ds1302_sclk=1;
  83.         ds1302_io=1;      //释放io数据线
  84.         return value;    //把 value值返回
  85. }

  86. void set_rtc()   //设置时间初值函数
  87. {
  88.         uchar i,j,k;
  89.         for(i=0;i<7;i++)  //把数组time_data[]中的7个十进制数转换成BCD码
  90.         {
  91.                 j=time_data[i]/10;
  92.                 k=time_data[i]%10;
  93.                 time_data[i]=k+j*16;
  94.         }
  95.         ds1302_write(0x8e,0x00);  //在往寄存器写之前,要去除写保护(8e为写保护寄存器的地址)
  96.         for(i=0;i<7;i++)   //分7次循环,向年、周、月、日、时、分、秒的寄存器写入时间数据
  97.         {
  98.                 ds1302_write(write_add[i],time_data[i]);
  99.         }
  100.         ds1302_write(0x8e,0x80);  //加写保护
  101. }


  102. /***************************与lcd1602相关函数***************************/
  103. void lcd_write_com(uchar com)   //写命令函数
  104. {
  105.         lcd1602_rs=0;     //数据命令选择端,写命令时设为0
  106.         lcd1602_rw=0;    //读写选择端,写时设为0
  107.         lcd1602_en=0;  
  108.         P0=com;        //将要写的命令送到数据总线上
  109.         lcd1602_en=1;   //使能端电平变换,形成一个高脉冲将命令写入到液晶控制器
  110.         delay(5);       //延时
  111.         lcd1602_en=0;               
  112. }

  113. void lcd_write_date(uchar date)    //写数据函数
  114. {
  115.     lcd1602_rs=1;      //数据命令选择端,写数据时设为1
  116.         lcd1602_rw=0;     //读写选择端,写时设为0
  117.         lcd1602_en=0;
  118.         P0=date;       //将要写的数据送到数据总线上
  119.         lcd1602_en=1;  //使能端电平变换,形成一个高脉冲将命令写入到液晶控制器
  120.         delay(5);       //延时
  121.         lcd1602_en=0;       
  122. }

  123. void lcd1602_init()                 //LCD1602初始化函数       
  124. {
  125.         uchar lcdnum;
  126.         lcd_write_com(0x38);  
  127.         lcd_write_com(0x0c);   
  128.         lcd_write_com(0x06);   
  129.         lcd_write_com(0x01);   

  130.         lcd_write_com(0x80);                 //写第一行数据
  131.         for(lcdnum=0;lcdnum<16;lcdnum++)             
  132.         {
  133.                 lcd_write_date(table1[lcdnum]);
  134.                 delay(2);
  135.         }
  136.         lcd_write_com(0x80+0x40);                         //写入第二行数据
  137.         for(lcdnum=0;lcdnum<16;lcdnum++)
  138.         {
  139.                 lcd_write_date(table2[lcdnum]);
  140.                 delay(2);
  141.         }
  142. }

  143. /***********************************显示部分********************************/

  144. void write_time1(uchar add,uchar date)    //日期送显示
  145. {       
  146.         uchar shi,ge;
  147.         shi=date/10;
  148.         ge=date%10;
  149.         lcd_write_com(0x80+add);
  150.         lcd_write_date(0x30+shi);
  151.         lcd_write_date(0x30+ge);
  152. }

  153. void write_time2(uchar add,uchar date)    //时间送显示
  154. {       
  155.         uchar shi,ge;
  156.         shi=date/10;
  157.         ge=date%10;
  158.         lcd_write_com(0x80+0x44+add);
  159.         lcd_write_date(0x30+shi);
  160.         lcd_write_date(0x30+ge);
  161. }

  162. uchar bcd_decimal(uchar bcd)              //bcd转换十进制
  163. {
  164.         uchar decimal;
  165.         decimal=bcd>>4;
  166.         decimal=decimal*10+(bcd&=0x0f);
  167.         return decimal;        
  168. }

  169. void write_week(uchar we)        //星期送显示
  170. {
  171.         lcd_write_com(0x80+0x0f);


  172.         switch(we)
  173.         {
  174.                 case 1: lcd_write_date('1');
  175.                                  break;
  176.                 case 2:        lcd_write_date('2');
  177.                                  break;
  178.                 case 3:        lcd_write_date('3');
  179.                                  break;
  180.                 case 4: lcd_write_date('4');
  181.                                  break;       
  182.                 case 5: lcd_write_date('5');
  183.                                  break;
  184.                 case 6: lcd_write_date('6');
  185.                                  break;
  186.                 case 7: lcd_write_date('7');
  187.                                  break;                                                                                                                  
  188.         }       
  189. }

  190. /******************************按键处理******************************/

  191. uchar key_bcd(uchar key_decimal)    //转成ds1302所需的BCD码
  192. {
  193.         uchar temp;
  194.         temp=(((key_decimal/10)&0x0f)<<4)|(key_decimal%10);
  195.         return temp;       
  196. }

  197. void key_up_down()     //加减键处理函数
  198. {
  199.         if(up==0)   //加键处理
  200.         {
  201.                 delay(50);
  202.                 flag_up=1;     //"加"更新标志
  203.                 while(!up);
  204.                 switch(count)
  205.                 {
  206.                         case 1:
  207.                                         miao++;
  208.                                         if(miao>59)
  209.                                                 miao=0;
  210.                                         break;
  211.                         case 2:
  212.                                         fen++;
  213.                                         if(fen>59)
  214.                                                 fen=0;
  215.                                         break;
  216.                         case 3:
  217.                                         shi++;
  218.                                         if(shi>23)
  219.                                                 shi=0;
  220.                                         break;                               
  221.                         case 4:
  222.                                         week++;
  223.                                         if(week>7)
  224.                                                 week=1;
  225.                                         break;                               
  226.                         case 5:
  227.                                         ri++;
  228.                                         if(ri>31)
  229.                                                 ri=1;
  230.                                         break;                                       
  231.                         case 6:
  232.                                         yue++;
  233.                                         if(yue>12)
  234.                                                 yue=1;
  235.                                         break;                                       
  236.                         case 7:       
  237.                                         nian++;
  238.                                         if(nian>99)
  239.                                                 nian=0;
  240.                                         break;                                                                                               
  241.                 }
  242.         }
  243.         if(down==0)    //减键处理
  244.         {
  245.                 delay(50);
  246.                 flag_down=1;           //"减"更新标志                       
  247.                 while(!down);
  248.                 switch(count)
  249.                 {
  250.                         case 1:
  251.                                         miao--;
  252.                                         if(miao==255)
  253.                                                 miao=59;
  254.                                         break;
  255.                         case 2:
  256.                                         fen--;
  257.                                         if(fen==255)
  258.                                                 fen=59;
  259.                                         break;
  260.                         case 3:
  261.                                         shi--;
  262.                                         if(miao==255)
  263.                                                 shi=23;
  264.                                         break;                               
  265.                         case 4:
  266.                                         week--;
  267.                                         if(week<1)
  268.                                                 week=7;
  269.                                         break;                               
  270.                         case 5:
  271.                                         ri--;
  272.                                         if(ri<1)
  273.                                                 ri=31;
  274.                                         break;                                       
  275.                         case 6:
  276.                                         yue--;
  277.                                         if(yue<1)
  278.                                                 yue=12;
  279.                                         break;                                       
  280.                         case 7:       
  281.                                         nian--;
  282.                                         if(nian==255)
  283.                                                 nian=99;
  284.                                         break;                                                                                               
  285.                 }
  286.         }                       
  287. }


  288. void key_set()   //ste键扫描函数
  289. {
  290.         if(set==0 )
  291.         {
  292.                 delay(50);
  293.                 if(set==0)
  294.                 {       
  295.                         while(!set);      //等待set释放
  296.                         count++;        //按set一下,count加1,进入时间调速扫描程序  
  297.                         lcd_write_com(0x0f);  //把1602液晶显示光标并闪烁
  298.                         if(flag==1)
  299.                                 {
  300.                                         done=1;
  301.                                         flag=0;
  302.                                         ds1302_write(0x8e,0x00);        //写允许       
  303.                                         ds1302_write(0x80,key_bcd(miao)|0x80); //BIT7为1,晶振停止工作
  304.                                         ds1302_write(0x8e,0x80);        //写保护       
  305.                                 }               
  306.                 }
  307.         }

  308. }

  309. void keyjpress()        //按键处理
  310. {       
  311.         key_set();
  312.         if(count!=0)           //count不为0,进入时间调整扫描
  313.         {
  314.                        
  315.                 switch(count)
  316.                 {
  317.                         case 1:do   
  318.                                    {
  319.                                                 lcd_write_com(0x80+0x40+11);     //调整秒
  320.                                                 key_up_down();
  321.                                                 if(flag_up || flag_down)
  322.                                                 {       
  323.                                                         flag_up=0;
  324.                                                         flag_down=0;
  325.                                                         ds1302_write(0x8e,0x00);                       
  326.                                                         ds1302_write(0x80,key_bcd(miao) | 0x80);
  327.                                                         ds1302_write(0x8e,0x80);        
  328.                                                     write_time2(6,miao);
  329.                                                     lcd_write_com(0x80+0x40+7);  //液晶写字符后光标自动
  330.                                                          //向右移动一位,把光标
  331.                                                          //左移一位
  332.                                             }
  333.                                }
  334.                                     while(count==2);
  335.                                         break;

  336.                         case 2:do   
  337.                                    {
  338.                                                 lcd_write_com(0x80+0x40+8);      //调整分
  339.                                                 key_up_down();                                               
  340.                                                 if(flag_up || flag_down)
  341.                                                 {       
  342.                                                         flag_up=0;                                          
  343.                                                         flag_down=0;
  344.                                                         ds1302_write(0x8e,0x00);                       
  345.                                                         ds1302_write(0x82,key_bcd(fen));
  346.                                                         ds1302_write(0x8e,0x80);        
  347.                                                     write_time2(3,fen);
  348.                                                     lcd_write_com(0x80+0x40+4);
  349.                                             }       
  350.                                     }
  351.                                     while(count==3);       
  352.                                         break;

  353.                         case 3:do                                 
  354.                                    {       
  355.                                                 lcd_write_com(0x80+0x40+5);   //调整时
  356.                                                 key_up_down();
  357.                                                 if(flag_up || flag_down)
  358.                                                 {       
  359.                                                         flag_up=0;                                          
  360.                                                         flag_down=0;
  361.                                                         ds1302_write(0x8e,0x00);                       
  362.                                                         ds1302_write(0x84,key_bcd(shi));
  363.                                                         ds1302_write(0x8e,0x80);        
  364.                                                     write_time2(0,shi);
  365.                                                     lcd_write_com(0x80+0x40+1);
  366.                                             }
  367.                                     }
  368.                                     while(count==4);
  369.                                         break;                       
  370.                         case 4: do
  371.                                    {
  372.                                                 lcd_write_com(0x80+0x0f);    //调整星期
  373.                                                 key_up_down();
  374.                                                 if(flag_up || flag_down)
  375.                                                 {       
  376.                                                         flag_up=0;                                          
  377.                                                         flag_down=0;                                               
  378.                                                         ds1302_write(0x8e,0x00);                       
  379.                                                         ds1302_write(0x8a,key_bcd(week));
  380.                                                         ds1302_write(0x8e,0x80);        
  381.                                                     write_week(week);
  382.                                                     lcd_write_com(0x80+0x0e);
  383.                                             }       
  384.                                     }
  385.                                     while(count==5);
  386.                                         break;                       
  387.                         case 5:do
  388.                                    {       
  389.                                                 lcd_write_com(0x80+9);   //调整日
  390.                                                 key_up_down();
  391.                                                 if(flag_up || flag_down)
  392.                                                 {       
  393.                                                         flag_up=0;                                          
  394.                                                         flag_down=0;
  395.                                                         ds1302_write(0x8e,0x00);                       
  396.                                                         ds1302_write(0x86,key_bcd(ri));
  397.                                                         ds1302_write(0x8e,0x80);        
  398.                                                     write_time1(8,ri);
  399.                                                     lcd_write_com(0x80+9);
  400.                                             }
  401.                                     }
  402.                                         while(count==6);
  403.                                         break;                       
  404.                         case 6:do
  405.                                    {                    
  406.                                                 lcd_write_com(0x80+6);   //调整月
  407.                                                 key_up_down();
  408.                                                 if(flag_up || flag_down)
  409.                                                 {       
  410.                                                         flag_up=0;                                          
  411.                                                         flag_down=0;                                               
  412.                                                         ds1302_write(0x8e,0x00);                       
  413.                                                         ds1302_write(0x88,key_bcd(yue));
  414.                                                         ds1302_write(0x8e,0x80);        
  415.                                                     write_time1(5,yue);
  416.                                                     lcd_write_com(0x80+6);
  417.                                             }
  418.                                     }
  419.                                     while(count==7);
  420.                                         break;                       
  421.                         case 7:
  422.                                                 lcd_write_com(0x80+3);     //调整年
  423.                                                 key_up_down();
  424.                                                 if(flag_up || flag_down)
  425.                                                 {       
  426.                                                         flag_up=0;                                          
  427.                                                         flag_down=0;
  428.                                                         ds1302_write(0x8e,0x00);                       
  429.                                                         ds1302_write(0x8c,key_bcd(nian));
  430.                                                         ds1302_write(0x8e,0x80);        
  431.                                                     write_time1(2,nian);
  432.                                                     lcd_write_com(0x80+3);
  433.                                             }
  434.                                     break;
  435.                         case 8:       
  436.                                         lcd_write_com(0x0c);         //调整结束,关闭显示光标
  437.                                          flag=1;
  438.                                          done=0;
  439.                                         count=0;
  440.                                         ds1302_write(0x8e,0x00);                       
  441.                                         ds1302_write(0x80,key_bcd(miao)&0x7f); //BIT7为0,晶振开始工作
  442.                                         ds1302_write(0x8e,0x80);
  443.                                         break;                                                           
  444.                         default:break;                                                                                       
  445.                 }       
  446.         }

  447. }

  448. void xianshi()  //1602液显示函数
  449. {
  450.   //读秒分时,日月年,星期               
  451.         miao=bcd_decimal(ds1302_read(0x81));  
  452.         fen =bcd_decimal(ds1302_read(0x83));
  453.         shi =bcd_decimal(ds1302_read(0x85));
  454.         ri  =bcd_decimal(ds1302_read(0x87));
  455.         yue =bcd_decimal(ds1302_read(0x89));
  456.         nian=bcd_decimal(ds1302_read(0x8d));
  457.         week=bcd_decimal(ds1302_read(0x8b));
  458.   //送液晶显示               
  459.         write_time2(6,miao);
  460.         write_time2(3,fen);
  461.         write_time2(0,shi);
  462.         write_time1(8,ri);
  463.         write_time1(5,yue);
  464.         write_time1(2,nian);
  465.         write_week(week);       

  466. }

  467. main()  //主函数
  468. {
  469.         uchar ch;  
  470.         lcd1602_en=0;  //关闭1602液晶使能,防止1602数据端影响单片机P0口输出
  471.         P0=0xff;       
  472.         lcd1602_init();    //1602液晶初始化

  473.         ch=ds1302_read(0x81)>>7;  //读取DS1302时钟的秒寄存器的值,并保留最高位
  474.         if(ch)   //判断:若ch为0,不设置初值;若ch为1,重新设置初值
  475.         {
  476.         set_rtc();   //对DS1302设置时间初值
  477.         }
  478.         while(1)
  479.         {
  480.                 if(done==1)
  481.                 {       
  482.                         keyjpress();
  483.                 }
  484.                 if(done==0)
  485.                 {
  486.                         xianshi();        //取得并显示日历、时间
  487.                         key_set();
  488.                
  489.                 }                               
  490.         }
  491. }  
复制代码

所有资料51hei提供下载:
time1.zip (82.72 KB, 下载次数: 113)

评分

参与人数 1黑币 +50 收起 理由
admin + 50 共享资料的黑币奖励!

查看全部评分

回复

使用道具 举报

ID:282431 发表于 2019-11-24 10:05 | 显示全部楼层
这注释真的非常这详细,非常棒,谢谢分享!
回复

使用道具 举报

ID:614253 发表于 2019-12-18 16:11 | 显示全部楼层

这注释真的非常这详细,非常棒,谢谢分享!
回复

使用道具 举报

ID:27825 发表于 2019-12-18 20:13 来自手机 | 显示全部楼层
谢谢老师,好好学习
回复

使用道具 举报

ID:770377 发表于 2020-6-5 15:12 | 显示全部楼层
太强了
回复

使用道具 举报

ID:655556 发表于 2020-6-5 22:31 | 显示全部楼层
很详细,非常感谢
回复

使用道具 举报

ID:765729 发表于 2020-6-7 09:51 来自手机 | 显示全部楼层
大神厉害,我学到了
回复

使用道具 举报

ID:1073996 发表于 2023-4-26 10:23 | 显示全部楼层
为什么我的搞完之后,数码管显示2005/5/05-05:05:05.ds1302显示的年和日期反了
回复

使用道具 举报

ID:863362 发表于 2023-4-27 13:48 | 显示全部楼层
为什么运行起来时间误差很大呢?每分钟误差10秒。
回复

使用道具 举报

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

本版积分规则

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

Powered by 单片机教程网

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