找回密码
 立即注册

QQ登录

只需一步,快速开始

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

ds1302和1602可调时钟仿真图 含菜单程序(基于51单片机)

[复制链接]
ID:378600 发表于 2018-8-29 23:16 | 显示全部楼层 |阅读模式
新人第一篇帖子,基于51单片机的门禁系统(实验),内附proteus作图以及ds1302和1602程。

仿真原理图如下(proteus仿真工程文件可到本帖附件中下载)

仿真原理图如下(proteus仿真工程文件可到本帖附件中下载)

仿真原理图如下(proteus仿真工程文件可到本帖附件中下载)


单片机原程序如下:
  1. #include<reg51.h>
  2. /********位定义*********/
  3. sbit io=P1^5;                //ds1302的串行数据I/O口
  4. sbit clk=P1^6;               //ds1302的时钟口
  5. sbit rst=P1^7;               //ds1302的复位口
  6. sbit acc0=ACC^0;             //移位时的第0位
  7. sbit acc7=ACC^7;             //移位时用的第7位
  8. sbit busy=P0^7;              //lcd 忙标志位
  9. sbit rs=P2^0;                //lcd 数据状态位
  10. sbit rw=P2^1;                //lcd 读写控件位
  11. sbit re=P2^2;                //lcd 使能位
  12. sbit menu=P3^0;              //菜单
  13. sbit inc=P3^1;               //加一
  14. sbit dec=P3^2;               //减一
  15. sbit quit=P3^3;              //退出
  16. /************全局变量***********/
  17. unsigned char sec,min,hour,day,month,week,year;   //秒,分,时,日,月,星期,年
  18. unsigned char newsec,newmin,newhour,newday,newmonth,newweek,newyear;  //重写数据用的秒,分,时——
  19. unsigned char bksec,bkmin,bkhour,bkweek,bkday,bkmonth,bkyear,down,count; //闪烁标志位
  20. unsigned char flag,daflag; // 加减标志位,时间日期标志位
  21. unsigned char timete[8],timede[10]; //分别存放,时分秒;年月日
  22. unsigned char week1[3]="SUN";        //星期日
  23. unsigned char week2[3]="MON";        //    一
  24. unsigned char week3[3]="TUE";        //    二
  25. unsigned char week4[3]="WED";        //    三
  26. unsigned char week5[3]="THU";        //    四
  27. unsigned char week6[3]="FRI";        //    五
  28. unsigned char week7[3]="SAT";        //    六
  29. unsigned char name[]="Wulai";        //制作者(本人)的名字
  30. /************函数申明***************/
  31. void delay(unsigned char ms);           //延时
  32. void writebyte(unsigned char wdat);        //写一字节到ds1302
  33. unsigned char  readbyte();                //从ds1302中读一字节
  34. unsigned char readdat(unsigned char raddr);                //读ds1302某地址数据
  35. void writedat(unsigned char waddr,unsigned char  x);        //写数据到ds1302的某地址
  36. void ifprotect(flag);                        //是否写允许
  37. void timebuf();                         //时间缓冲区
  38. void checkbusy();                       //测试lcd忙碌状态
  39. void wrcom(unsigned char com);          //写指令到lcd
  40. void wrdat(unsigned char dat);          //写数据到lcd
  41. void lcdinit();                         //lcd初始化
  42. void scanmenu();                        //menu扫描
  43. void funcmenu();                        //menu功能
  44. void scaninc();                         //扫描加能函数
  45. void scandec();                         //扫描减一函数
  46. void scanquit();                        //扫描退出函数
  47. void funcinc();                         //加一功能函数
  48. void funcdec();                         //减一功能函数
  49. void funcquit();                        //退出功能函数
  50. void turn_sub(unsigned char newval,unsigned char newbk,unsigned char  newaddr);   //调时子函数
  51. void sub_buf(unsigned char subk,unsigned char bfte,unsigned char fda);      //时间缓冲子函数
  52. void sub_week(unsigned char num);                                 //显示星期子函数

  53. /**********************ds1302函数部分***************************/
  54. void delay(unsigned char ms)                  //延时函数
  55. {
  56.   unsigned char i;
  57.   unsigned char m=4;
  58.   while(ms--)
  59.      {
  60.        for(i=0;i<5;i++)
  61.           {
  62.              do
  63.                {}while(m--);
  64.           }
  65.      }

  66. }
  67. void writebyte(unsigned char wdat)         //写一字节到ds1302

  68. {
  69.   unsigned char i;
  70.   ACC=wdat;
  71.   for(i=8;i>0;i--)
  72.      {
  73.            io=acc0;   //从最低位开始传送
  74.            clk=1;     //产生一个边沿   在写指令时供产生上升沿,在写数据时供产生紧挨着的下降沿
  75.            clk=0;
  76.            ACC>>=1;
  77.          }
  78. }
  79. unsigned char  readbyte()                 //从ds1302中读一字节

  80. {
  81.    unsigned char i1;
  82.    for(i1=8;i1>0;i1--)
  83.       {
  84.             ACC>>=1;    //保证最低位传送至最低位
  85.             acc7=io;    //从最低位开始传送
  86.             clk=1;      //产生一个边沿   在读数据时供产生上升沿
  87.             clk=0;
  88.       }
  89.    return(ACC);
  90. }
  91. unsigned char readdat(unsigned char raddr)   //读ds1302某地址数据

  92. {
  93.   unsigned char value;
  94.   rst=0;           //在读写前何证rst为低电平
  95.   clk=0;
  96.   rst=1;           //在读写期间保证rst为高电平 且只有在clk为低 电平时才能变为高电平
  97.   writebyte(raddr);
  98.   value=readbyte();
  99.   rst=0;           //读写完毕后保证rst为低,clk为高
  100.   clk=1;
  101.   return(value);
  102. }
  103. /*******写数据到ds1302的某地址********/
  104. void writedat(unsigned char waddr,unsigned char  x)
  105. {
  106.   rst=0;
  107.   clk=0;
  108.   rst=1;
  109.   writebyte(waddr);
  110.   writebyte(x);
  111.   rst=0;
  112.   clk=1;
  113. }

  114. /********是否写保护********/
  115. void  ifprotect(flag)
  116. {
  117.   if(flag)                     //flag=1时禁止写入
  118.     writedat(0x8e,0x10);
  119.   else                         //flag=0时允许写入
  120.     writedat(0x8e,0x00);
  121. }
  122. void readtime()                                            //读时间
  123. {
  124.   unsigned char time;
  125.   time=readdat(0x81);                     //读秒
  126.   sec=((time&0x70)>>4)*10+(time&0x0f);    //将bcd码转换成十进制数
  127.   time=readdat(0x83);                     //读分
  128.   min=((time&0x70)>>4)*10+(time&0x0f);
  129.   time=readdat(0x85);                     //读时
  130.   hour=((time&0x70)>>4)*10+(time&0x0f);
  131.   time=readdat(0x87);                     //读日
  132.   day=((time&0x70)>>4)*10+(time&0x0f);
  133.   time=readdat(0x89);                     //读月
  134.   month=((time&0x70)>>4)*10+(time&0x0f);
  135.   time=readdat(0x8d);                     //读年
  136.   year=(time>>4)*10+(time&0x0f);
  137.   time=readdat(0x8b);                     //读星期
  138.   week=time&0x07;
  139. }
  140. /****************时间缓冲子函数*************************/
  141. void sub_buf(unsigned char subk,unsigned char bfte,unsigned char fda)
  142. {
  143.   if(subk<=1)       //判断是否正常显示
  144.       {
  145.           if(daflag==1)            //判断是日间还是日期
  146.              {
  147.                timete[fda]=bfte%10+48;
  148.                timete[fda+1]=bfte/10+48;
  149.              }
  150.           else
  151.              {
  152.                timede[fda]=bfte%10+48;
  153.                timede[fda+1]=bfte/10+48;
  154.              }
  155.       }
  156.   else                  //显示空
  157.     {
  158.       if(daflag==1)           //判断是时间还是日期
  159.              {
  160.                timete[fda]=0x02;
  161.                timete[fda+1]=0x02;
  162.              }
  163.           else
  164.              {
  165.                timede[fda]=0x02;
  166.                timede[fda+1]=0x02;
  167.              }
  168.      }
  169. }
  170. void timebuf()                            //时间缓冲区
  171. {
  172.   readtime();     //调用读时间函数
  173.   daflag=1;
  174.   sub_buf(bksec,sec,0);       //调用时间缓冲子函数
  175.   timete[2]=':';
  176.   sub_buf(bkmin,min,3);
  177.   timete[5]=':';
  178.   sub_buf(bkhour,hour,6);
  179.   daflag=0;
  180.   sub_buf(bkday,day,0);
  181.   timede[2]='-';
  182.   sub_buf(bkmonth,month,3);
  183.   timede[5]='-';
  184.   if(bkyear<=1)
  185.      {
  186.        timede[6]=year%10+48;       //年
  187.        timede[7]=year/10+48;
  188.        timede[8]=48;
  189.        timede[9]=50;
  190.      }
  191.    else
  192.      {
  193.        timede[6]=0x02;
  194.        timede[7]=0x02;
  195.        timede[8]=0x02;
  196.        timede[9]=0x02;
  197.      }
  198. }
  199. /**************************lm016l显示器函数部分****************************/
  200. void checkbusy()                       // 测试lcd忙碌状态
  201. {
  202.   do
  203.     {
  204.       P0=0xff;
  205.       rs=0;
  206.       rw=1;
  207.       re=0;
  208.       busy=P0&0x80;
  209.       re=1;
  210.     }while(busy==1);
  211. }
  212. void wrcom(unsigned char com)          //写指令到lcd
  213. {
  214.   P0=com;
  215.   rs=0;
  216.   rw=0;
  217.   re=0;
  218.   checkbusy();
  219.   re=1;
  220. }
  221. void wrdat(unsigned char dat)          //写数据到lcd
  222. {
  223.   P0=dat;
  224.   rs=1;
  225.   rw=0;
  226.   re=0;
  227.   checkbusy();
  228.   re=1;
  229. }
  230. void lcdinit()             // lcd初始化
  231. {
  232.   wrcom(0x02);        //光标、画面回home位,AC=0
  233.   wrcom(0x0c);        //显示开,光标不闪不显
  234.   wrcom(0x06);        //对数据读写后,ac自动加1,且屏不动
  235.   wrcom(0x38);       //允许双行显示
  236. }
  237. void display()              //显示
  238. {
  239.   unsigned char i2,i3,i5;
  240.   timebuf();
  241.   wrcom(0x80);               //在第一行显示时,分,秒
  242.   for(i2=10;i2>0;i2--)
  243.      wrdat(timede[i2-1]);
  244.   wrcom(0xc0);              //在第二行显示年月日
  245.   for(i3=8;i3>0;i3--)
  246.      wrdat(timete[i3-1]);
  247.   switch(week)                        //第一行显示星期
  248.       {
  249.         case 1:sub_week(1);break;
  250.         case 2:sub_week(2);break;
  251.         case 3:sub_week(3);break;
  252.         case 4:sub_week(4);break;
  253.         case 5:sub_week(5);break;
  254.         case 6:sub_week(6);break;
  255.         case 7:sub_week(7);break;
  256.       }
  257.    for(i5=0;i5<5;i5++)              //显示作者名字
  258.       {
  259.         wrcom(0xca+i5);
  260.         wrdat(name[i5]);
  261.       }
  262. }
  263. void sub_week(unsigned char num)  //显示星期子函数
  264. {
  265.   unsigned char i4;
  266.   for(i4=0;i4<3;i4++)
  267.      {
  268.        if(bkweek<=1)                     //判断是否正常显示
  269.          {
  270.            wrcom(0x8c+i4);
  271.            switch(num)
  272.                {
  273.                  case 1:wrdat(week1[i4]); break;
  274.                  case 2:wrdat(week2[i4]); break;
  275.                  case 3:wrdat(week3[i4]); break;
  276.                  case 4:wrdat(week4[i4]); break;
  277.                  case 5:wrdat(week5[i4]); break;
  278.                  case 6:wrdat(week6[i4]); break;
  279.                  case 7:wrdat(week7[i4]); break;
  280.                }

  281.           }
  282.         else             //显示空
  283.            {
  284.               wrcom(0x8c+i4);
  285.               wrdat(0x02);
  286.            }
  287.      }
  288. }
  289. /******************* 键盘部分的函数*****************************************/
  290. /***********调时子函数********************/
  291. void turn_sub(unsigned char newval,unsigned char newbk,unsigned char  newaddr)
  292.     {
  293.                    newval=readdat(newaddr);                     //读取当前时间
  294.                    newval=((newval&0x70)>>4)*10+(newval&0x0f);    //将bcd码转换成十进制
  295.                    if(flag==1)                  //判断是加一还是减一
  296.                    newval++;
  297.                    else
  298.                    newval--;
  299.                    switch(count)
  300.                      {
  301.                        case 1: if(newval>59) newval=0;break;
  302.                        case 2: if(newval>59) newval=0;break;
  303.                        case 3: if(newval>23) newval=0;break;
  304.                        case 5: if(newval>31) newval=0;break;
  305.                        case 6: if(newval>12) newval=0;break;
  306.                        case 7: if(newval>79) newval=0;break;
  307.                        default:break;
  308.                      }
  309.                    ifprotect(0);
  310.                    writedat((newaddr-1),((newval/10)<<4)|(newval%10));  //将新数据写入寄存器
  311.                    ifprotect(1);
  312.                    newbk++;                     //修改过程中数值也要闪烁
  313.                    if(newbk>3)
  314.                       newbk=0;
  315.                    display();                   //将修改后的值显示出来

  316.     }
  317. void scanmenu()         //menu扫描
  318. {
  319.   if(menu==0)        //判断menu键是否按下
  320.     {
  321.       delay(1);      //延时消抖(实际中延时要比此长一般约10ms)
  322.       if(menu==0)    //再次判断menu键是否按下
  323.         {
  324.           down=1;        //menu键按下则置其标志位为1
  325.           while(menu!=1);      //等待按键松开
  326.           count++;             //记录menu按下的次数
  327.           if(count>7)
  328.              count=1;
  329.           ifprotect(0);             //按下menu键后禁止ds1302工作
  330.           sec=readdat(0x81);
  331.           writedat(0x80,0x80|sec);
  332.           ifprotect(1);
  333.          }
  334.     }
  335. }
  336. void funcmenu()                      //menu键功能函数
  337. {
  338.   switch(count)
  339.      {
  340.        case 1:{scaninc();scandec();      //每按一次目录键对inc,dec键进行一次扫描
  341.                   do              //根据count的计数来判断对那一值进行修改,并保证这一值闪烁
  342.                      { bkyear=1;
  343.                        bksec++;
  344.                        if(bksec>3)
  345.                           bksec=0;
  346.                       }while(count!=1);
  347.                   scanquit();             //每按一次目录键对quit键进行一次扫描
  348.                   display();              //退出时正常显示
  349.               }break;
  350.        case 2:{scaninc();scandec();
  351.                   do
  352.                     {
  353.                        bksec=1;     //保证退出上一值的闪烁后,让其正常显示
  354.                        bkmin++;
  355.                        if(bkmin>3)
  356.                          bkmin=0;
  357.                     }while(count!=2);
  358.                   scanquit();
  359.                   display();
  360.                }break;
  361.        case 3:{scaninc();scandec();
  362.                   do
  363.                      {
  364.                         bkmin=1;
  365.                         bkhour++;
  366.                         if(bkhour>3)
  367.                             bkhour=0;
  368.                      }while(count!=3);
  369.                   display();
  370.                   scanquit();
  371.               }break;
  372.        case 4:{scaninc();scandec();
  373.                   do
  374.                       {
  375.                         bkhour=1;
  376.                         bkweek++;
  377.                         if(bkweek>3)
  378.                            bkweek=0;
  379.                       }while(count!=4);
  380.                   display();
  381.                   scanquit();
  382.               }break;
  383.        case 5:{scaninc();scandec();
  384.                   do
  385.                      {
  386.                        bkweek=1;
  387.                        bkday++;
  388.                        if(bkday>3)
  389.                          bkday=0;
  390.                      }while(count!=5);
  391.                  display();
  392.                  scanquit();
  393.               }break;
  394.        case 6:{scaninc();scandec();
  395.                    do
  396.                       {
  397.                          bkday=1;
  398.                          bkmonth++;
  399.                          if(bkmonth>3)
  400.                              bkmonth=0;
  401.                        }while(count!=6);
  402.                    display();
  403.                    scanquit();
  404.               }break;
  405.        case 7:{scaninc();scandec();
  406.                    do
  407.                      {
  408.                         bkmonth=1;
  409.                         bkyear++;
  410.                         if(bkyear>3)
  411.                            bkyear=0;
  412.                       }while(count!=7);
  413.                    display();
  414.                    scanquit();
  415.               }break;
  416.        default: break;
  417.      }
  418. }
  419. void scaninc()           //扫描inc按键
  420. {
  421.   if(inc!=1)         //判断inc键是否按下
  422.      {               //延时消拌再次判断
  423.          delay(1);
  424.          if(inc!=1)
  425.             while(inc!=1);     //等待按键松开
  426.          flag=1;
  427.          funcinc();            //调用inc功能函数
  428.       }
  429. }
  430. void scandec()            //扫描dec键
  431. {
  432.   if(dec!=1)
  433.      {
  434.         delay(1);
  435.         if(dec!=1)
  436.           while(dec!=1);
  437.         flag=0;
  438.         funcdec();
  439.      }
  440. }
  441. void scanquit()         //扫描quit键
  442. {
  443.   if(quit!=1)
  444.     {
  445.        delay(1);
  446.        if(quit!=1)
  447.           while(quit!=1);
  448.        funcquit();
  449.     }
  450. }
  451. void funcinc()            //inc功能 键
  452. {
  453.   switch(count)
  454.       {
  455.         case 1:do{         //对秒进行修改
  456.                     turn_sub(newsec,bksec,0x81);
  457.                     sec=readdat(0x81);
  458.                     ifprotect(0);                //禁止ds1302工作
  459.                     writedat(0x80,0x80|sec);
  460.                     ifprotect(1);
  461.                  }while(count!=1);break;
  462.         case 2:do{                         //对分进行修改
  463.                    turn_sub(newmin,bkmin,0x83);
  464.                   }while(count!=2);break;
  465.         case 3:{
  466.                  turn_sub(newhour,bkhour,0x85);
  467.                }while(count!=3);break;
  468.         case 4:do{
  469.                     newweek=readdat(0x8b);
  470.                     newweek++;
  471.                     if(newweek>7)
  472.                       newweek=1;
  473.                     ifprotect(0);
  474.                     writedat(0x8a,newweek);
  475.                     ifprotect(1);
  476.                     bkweek++;
  477.                     if(bkweek>3)
  478.                       bkweek=0;
  479.                     display();
  480.                }while(count!=4);break;
  481.         case 5:do{
  482.                    turn_sub(newday,bkday,0x87);
  483.                }while(count!=5);break;
  484.         case 6:do{
  485.                     turn_sub(newmonth,bkmonth,0x89);
  486.                }while(count!=6);break;
  487.         case 7:do{
  488.                   turn_sub(newyear,bkyear,0x8d);
  489.                }while(count!=7);break;
  490.         default: break;
  491.       }
  492. }
  493. void funcdec()               //dec按键功能函数
  494. {
  495.   switch(count)
  496.       {
  497.         case 1:do{
  498.                     turn_sub(newsec,bksec,0x81);
  499.                     sec=readdat(0x81);
  500.                     ifprotect(0);
  501.                     writedat(0x80,0x80|sec);
  502.                     ifprotect(1);
  503.                  }while(count!=1);break;
  504.         case 2:do{
  505.                     turn_sub(newmin,bkmin,0x83);
  506.                  }while(count!=2);break;
  507.         case 3:do{
  508.                    turn_sub(newhour,bkhour,0x85);
  509.                  }while(count!=3);break;
  510.         case 4:do{
  511.                     newweek=readdat(0x8b);
  512.                     newweek--;
  513.                     if(newweek<1)
  514.                        newweek=7;
  515.                     ifprotect(0);
  516.                     writedat(0x8a,0x00|newweek);
  517.                     ifprotect(1);
  518.                     bkweek++;
  519.                     if(bkweek>3)
  520.                        bkweek=0;
  521.                     display();
  522.                  }while(count!=4);break;
  523.         case 5:do{
  524.                     turn_sub(newday,bkday,0x87);
  525.                  }while(count!=5);break;
  526.         case 6:do{
  527.                    turn_sub(newmonth,bkmonth,0x89);
  528.                  }while(count!=6);break;
  529.         case 7:do{
  530.                     turn_sub(newyear,bkyear,0x8d);
  531.                  }while(count!=7);break;
  532.         default: break;
  533.       }
  534. }
  535. void funcquit()             //quit按键功能函数
  536. {
  537.   bksec=1;          //保证退出后正常显示
  538.   bkmin=1;
  539.   bkhour=1;
  540.   bkweek=1;
  541.   bkday=1;
  542.   bkmonth=1;
  543.   bkyear=1;
  544.   count=0;       //退出后menu按键次数复位
  545.   down=0;        //退出后menu按下标志位复位
  546.   sec=readdat(0x81);
  547.   sec=((sec&0x70)>>4)*10+(sec&0x0f);
  548.   ifprotect(0);
  549.   writedat(0x80,(((sec&0x7f)/10)<<4)|(sec%10));  //保存当前秒并允许ds1302工作
  550.   ifprotect(1);
  551. }

  552. void main()
  553. {
  554.   lcdinit();         //lcd初始化
  555.   down=0;
  556.   count=0;
  557.   while(1)
  558.    {
  559.      scanmenu();     //扫描menu键是否按下
  560.      if(down==1)     //若按下进入调时模式
  561.         funcmenu();
  562.      display();      //若没按下正常显示
  563.      delay(18);

  564.    }
  565. }
复制代码

全部资料51hei下载地址:
小门系统.rar (6.71 MB, 下载次数: 46)
回复

使用道具 举报

ID:1 发表于 2018-8-30 01:44 | 显示全部楼层
注意不要发文不对题的东东 这样是没有积分的
回复

使用道具 举报

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

本版积分规则

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

Powered by 单片机教程网

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