找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 8278|回复: 7
打印 上一主题 下一主题
收起左侧

带闹钟的51单片机六位数字钟设计 含源码+仿真+调试资料

  [复制链接]
跳转到指定楼层
楼主
一、硬件电路
  • 总电路图
六位数字钟仿真原理图如下(proteus仿真工程文件可到本帖附件中下载)
图6-1总电路图


2、时间调试
  记时时间,如图6-1为“01:13:38”,图6-2所示20秒后的时间“01:13:38”,调试显示正常。
                           
图6-1 01:13:38                                      图 6-2  01:13:38
调整时间,如图6-2到图6-4所示,为修改位闪烁状态,图6-5和图6-6为修改后的时间和退出修改3秒后的时间,调试显示正常。
             
图6-2修改秒个位闪烁“灭”     图6-3修改秒个位闪烁“亮”       图6-4修改分个位闪烁“灭”
           
图6-5修改后时间                                图6-6退出修改



3、闹钟调试
              如图6-7所示,长按key2,右下角红色led亮,表示开启闹钟,并设置闹钟时间为“01:14:00”。若无开启闹钟如图6-8所示,到设定时间时,绿色LED无反应。如图6-9和图6-10所示,开启闹钟,到设定时间,绿色led亮10s后熄灭。如图6-11所示,闹钟“响起”后,按下key2取消“闹铃”,绿色led熄灭。
              
图6-7  设定闹钟01:14:00                       图6-8无开启闹钟,到时无反应
                              
            图6-9到闹钟时间                                                                      图6-10  闹钟10s后停止   
        
                                图6-11闹钟手动停止


4、日期调试
              如图6-12和图6-13所示,在显示模式下流动显示8位数“2022年3月1日”。如图6-14至图6-16所示,当年分为闰年“2020”时,2月末为29日,过24点,变成“2020年03月01日”。如图6-17至图6-19所示,当年分为平年“2021”时,2月末为28日,过24点,变成“2021年03月01日”。

                             
图6-12 显示日期2022年03月          图6-13 显示日期2022年03月01日
         图6-14 闰年2月29日      图6-15 设定当天23:59:50           图6-16 24点后日期
      
   图6-17 平年2月28日        图6-18 设定当天23:59:49          图6-19   24点后日


单片机源程序如下:
  1. #include<reg51.h>                                                        //包含51单片机头文件
  2. #include<intrins.h>                                                        //包含标准函数库
  3. sbit P_alarm              =P1^6;                                           //闹钟开启与关闭指示灯,低电平有效,灭代表闹钟关闭
  4. sbit P_alarming=P1^7;                                          //闹铃指示灯,到时则亮,低电平有效
  5. unsigned char code dis_7[10]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};
  6.                                                                                                   //七段共阴显示数码管“0~9”阴码
  7. unsigned char data time[3]={23,59,58};
  8.                                                                                                   //时间,从左到右分别为“小时、分钟、秒”
  9. unsigned char data clock[3]={23,0,0};
  10.                                                                                                   //闹钟,从左到右分别为“小时、分钟、秒”
  11. unsigned char data date[4]={20,18,02,20};
  12.                                                                                                   //日期,              从左到右分别为“年前两位、年后两位、月、日”
  13. unsigned char data day[12]={31,28,31,30,31,30,31,30,31,30,31,30};
  14.                                                                                                   //12个月对应的天数
  15. unsigned char data *p;                                          //指针变量,指向当前显示地址
  16. unsigned char data mcs,ms_10;              //计数器,mcs每250us加1次记录40次,ms_10记录mcs 100次
  17. unsigned char data position=1;              //编辑模式下,记录当前修改位“1~6”分别表示数码管右到左标号
  18. unsigned char data screen=1;                //显示模式下,记录当前显示屏幕功能,如下:“1”代表time,“2”代表clock,“3”代表date
  19. unsigned int  data fliker;                                          //编辑模式下,修改位闪烁功能定时计数器
  20. unsigned int data flow;                                          //显示模式下,日期流动计数器

  21. bit edit;                                                                                    //“0”代表显示模式,“1”代表编辑模式
  22. bit day_flag=0;                                                        //24点标志位
  23. bit disp_num;                            //显示数据位数切换,“0”代表显示6位,即时间,“1”代表显示日期
  24. bit alarm,alarming;                                                        //闹钟状态和闹铃状态标志位


  25. void display(unsigned char *p);               //显示函数
  26. void clock_c();                                                                      //闹钟功能程序
  27. void  date_c();                                                                      //日期程序
  28. unsigned char keyscan();                                          //键盘扫描程序,另外,K1长按进行模式切换
  29. void key2_function_d();                              //显示状态,按键2功能程序,闹钟取消\启动,闹铃消停
  30. void key2_function_e();                              //编辑状态,按键2功能程序,加1
  31. void key1_function_d();                              //显示状态,按键2功能程序,显示切换
  32. void key1_function_e();                              //编辑状态,按键2功能程序,修改位切换
  33. void delay(unsigned int i);                            //延时函数



  34. void main()
  35. {
  36.               unsigned char keyboard,key;            
  37.               TMOD=0x02;                                                                                    //设置定时器0工作方式为2
  38.               TL0=0x06;                                                                                    //设置低八位初始值
  39.               TH0=0x06;                                                                                    //设置高八位初始值
  40.               EA=1;                                                                                                  //开启总中断
  41.               ET0=1;                                                                                                  //开启定时器0中断允许
  42.               TR0=1;                                                                                                  //启动定时起0
  43.               p=time;                                                                                                  //指向时间地址
  44.               while(1)
  45.               {
  46.                             keyboard=P1&0x03;                                          //读取按键的值,并且屏蔽无关位
  47.                             display(p);                                                                      //调用显示函数
  48.                             clock_c();                                                                                    //调用闹钟函数
  49.                             date_c();                                                                                    //调用日期函数
  50.                             if((screen==1)&&(edit==1))TR0=0;else TR0=1;
  51.                                                                                                                               //修改时间时,关闭定时器
  52.                             if(keyboard!=0x03)                                                        //若有按键按下,则执行以下程序
  53.                             {
  54.                                           display(p);                                                        //显示函数消除按键抖动
  55.                                           if(keyboard!=0x03)                                          //再次检测,若有,则执行以下程序
  56.                                           {
  57.                                                         key=keyscan();                                          //调用按键扫描程序,读取按键值
  58.                                                         if(!edit)                                                        //显示模式
  59.                                                         {
  60. switch(key)    //如果KEY1按下,则KEY1为1,若KEY2按下则为2
  61.                                                                       {
  62.                                                                                     case 1:key1_function_d();break;//显示屏幕切换
  63.                                                                                     case 2:key2_function_d();break;//闹钟功能设置,闹铃取消
  64.                                                                                     default:break;
  65.                                                                       }
  66.                                                         }
  67.                                                         else                                                                      //编辑模式                                                                                                               
  68.                                                         {
  69.                                                                       switch(key)               //如果KEY1按下,则KEY1为1,若KEY2按下则为2
  70.                                                                       {
  71.                                                                                     case 1:key1_function_e();break;//修改位切换
  72.                                                                                     case 2:key2_function_e();break;//加1
  73.                                                                                     default:break;
  74.                                                                       }
  75.                                                         }              }              }              }              }
  76. void display(unsigned char *p)                            //显示子程序,显示参数为无符号字符型指针变量
  77. {
  78.               unsigned char disp[8]={0,0,0,0,0,0,0,0};
  79.                                                                                                                 //显示数据缓存变量
  80.               unsigned char disp_com[6]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf};
  81.                                                                                                                 //数码管位控端分别代表从左到右“1~6”号数码管
  82.               unsigned char a,b,d,dot;
  83.               unsigned char c=0;
  84.               if(!disp_num)a=6;else a=8;                            //显示时间则a=6,否则a=8
  85. for(b=0;b<a;b++)                                                        //将显示数据按位缓存与disp数据中
  86.               {
  87.                                           if(c==0)disp[b]=p[b/2]/10;
  88.                                           else disp[b]=p[b/2]%10;
  89.                                           c++;if(c==2)c=0;
  90.               }
  91.               if(!edit)                                                                                    //显示模式
  92.               {
  93.                             if(!disp_num)                                                        //显示时间
  94.                             {
  95.                                           for(b=0;b<6;b++)
  96.                                           {
  97.                                                         if(b==1||b==3)dot=128;else dot=0;
  98.                                                                                                                 //1为小时个位,3为分钟个位,其后面加小数点
  99.                                                         P0=0xff;                                          //消影
  100.                                                         P2=~dis_7[disp[b]]+dot;//段码刷新
  101.                                                         P0=disp_com[b];              //位选端有效,显示输出
  102.                                                         delay(5);                                          //延时
  103.                                           }
  104.                             }
  105.                             else                                                                                    //显示日期                                         
  106.                             {
  107.                                           b=flow/50;                                          //flow最大为400,每显示50次流动一位
  108.                                           for(c=0;c<6;c++)
  109.                                           {
  110. d=c+b;if(d>7)d-=8;              //每次显示6位,但流动显示起始位,第9位时返回第1位显示
  111.                                                         if(d==3||d==5||d==7)dot=128;else dot=0;
  112.                                                                                                                 //年份、月份、日期个位加小数点
  113.                                                         P0=0xff;                                          //消影
  114.                                                         P2=~dis_7[disp[d]]+dot;//断码刷新
  115.                                                         P0=disp_com[c];              //输出位选端
  116.                                                         delay(5);                                          //延时
  117.                                           }
  118.                                           flow++;                                                        //每显示1次屏幕加1;
  119.                                           if(flow==400)flow=0;              //最大399
  120.                             }
  121.               }
  122.               else                                                                                                  //编辑模式            
  123.               {            
  124.                             fliker++;if(fliker>100)fliker=0;//修改位显示闪烁功能
  125.                             if(!disp_num)                                                 //显示时间
  126.                             {
  127.                                           for(b=0;b<6;b++)                  
  128.                                           {
  129.                                                         if(b==1||b==3)dot=128;else dot=0;
  130.                                                         P0=0xff;
  131.                                                         P2=~dis_7[disp[b]]+dot;
  132. if(b==(6-position))              //修改位闪烁,position=1代表修改第6位数码管,最右边数码管
  133.                                                         {
  134.                                                                       if(fliker<50)P0=disp_com[b];  //亮
  135.                                                                       else P0=0xff;                                                        //灭
  136.                                                         }
  137.                                                         else P0=disp_com[b];
  138.                                                         delay(5);
  139.                                           }
  140.                             }
  141.                             else                                                                                    //显示日期
  142.                             {            
  143.                                           if(position>6)b=0;else b=2;                            //显示日期移动,起始显示状态“年,月”
  144.                                           for(c=0;c<6;c++)
  145.                                           {
  146.                                                         d=c+b;                                                                      //起始位置为disp[0或者2]
  147.                                                         if(d==3||d==5)dot=128;else dot=0;
  148.                                                         P0=0xff;
  149.                                                         P2=~dis_7[disp[d]]+dot;
  150.                                                         if(c==(6-position)||(c==(8-position)&&(position>6)))
  151. //修改位闪烁,年最大位开始修改(显示“年、月”||显示“年、月、日”)
  152.                                                         {
  153.                                                                       if(fliker<50)P0=disp_com[c];
  154.                                                                       else P0=0xff;
  155.                                                         }
  156.                                                         else P0=disp_com[c];
  157.                                                         delay(5);
  158.                                           }              }              }     }

  159. void clock_c()                                                                                    //闹钟子程序
  160. {
  161.               if(alarm)                                                                                    //开启闹钟则允许以下程序
  162.               {
  163.                             int a,b,c;            
  164.                             a=time[0]-clock[0];
  165.                             b=time[1]-clock[1];
  166.                             c=time[2]-clock[2];                                          //比较闹钟时间和当前时间
  167.                             if((a==0)&&(b==0)&&(c==0))              //时间到
  168.                                           alarming=1;                                          //启动闹铃
  169.                             if(alarming)                                                        //闹铃10秒自动取消
  170.                             {
  171.                                           if((b*60+c)>=10)alarming=0;
  172.                             }
  173.               }
  174.               P_alarm=!alarm;                                                        //闹钟状态输出
  175.               P_alarming=!alarming;                                          //闹铃状态输出
  176. }

  177. void  date_c()                                                                                    //日期子程序
  178. {
  179.               unsigned char a,b,c,d;
  180.               d=date[0]*100+date[1];
  181.               a=23-time[0];
  182.               b=59-time[1];
  183.               c=59-time[2];                                                                      //是否到24点
  184.             
  185.               if((d%400==0)||((d%100!=0)&&(d%4==0)))              //闰年
  186.               {              day[1]=29;}
  187.               else
  188.               {              day[1]=28;if(date[3]>28)date[3]=28;}              //平年

  189.               if((a==23)&&(b==59)&&(c==59)&&(day_flag==0))              //日期记时,24整点运行1次
  190.               {
  191.                             date[3]++;                                                        //日加1
  192.                             if(date[3]>day[date[2]-1])              //月末
  193.                             {                                                                     
  194.                                           date[3]=1;                                          //日归1
  195.                                           date[2]++;                                          //月份加1
  196.                                           if(date[2]>12)                                          //年末
  197.                                           {                                         
  198.                                                         date[2]=1;                            //月归1
  199.                                                         date[1]++;                            //年后两位加1
  200.                                                         if(date[1]>99)                            //99年进位
  201.                                                         {
  202.                                                                       date[0]++;              //年前两位加1
  203.                                                                       date[1]=0;              //年后两位归零
  204.                                                                       if(date[0]>99)              //最大9999年
  205.                                                                                     date[0]=0;
  206.                                                         }
  207.                                           }
  208.                             }
  209.                             day_flag=1;                                                        //执行1次标志位置1,防止重复执行
  210.               }            
  211.               if(day_flag){if(c!=59)day_flag=0;}              //24整点过后,整点标志位复位
  212. }

  213. unsigned char keyscan()                                                        //键盘扫描子程序,无符号字符型子程序,返回键号
  214. {
  215.               unsigned int a;
  216.               unsigned char temp;
  217.               temp=P1&0x03;                                                        //读取按键输入,屏蔽无关位
  218.               for(a=0;a<100&&((P1&(0x07))==0x06);a++)
  219.                                                                                                                 //按键延时功能
  220.               {
  221.                             display(p);                                                        //调用显示程序
  222.                             if(a==99)                                                                      //延时阈值
  223.                             {
  224.                                           edit=!edit;
  225. if(screen==3)position=8;//进入编辑模式初始状态设置
  226. else position=1;a=3;temp=0;P0=0xff;delay(1000);
  227.                                                                       //进入编辑模式,则返回值为3不调用任何按键程序
  228.                                           while((P1&(0x07))==0x06)display(p);
  229.                                                                                                                 //按键松开
  230.                             }
  231.               }
  232.               switch(temp)                                                                      //根据按键状态返回键号
  233.               {
  234.                             case 0x06:a=1;break;
  235.                             case 0x05:a=2;break;
  236.                             default:break;
  237.               }
  238.               if(edit)while((P1&0x07)!=0x07)display(p);//按键松开
  239.               return(a);                                                                                    //返回键号
  240. }


  241. void key2_function_d()                                                                      //按键2,显示状态,闹钟启动/停止,闹铃消停
  242. {
  243.               unsigned int a;            
  244.               if(!edit)                                                                                                  //显示模式,执行以下程序
  245.               {
  246.                             for(a=0;(a<100)&&((P1&(0x07))==0x05);a++)
  247.                                                                                                                               //按键延时功能
  248.                             {
  249.                                           display(p);                                                        //调用显示程序
  250.                                           if(a==99)                                                                      //到达延时阈值,执行以下程序
  251.                                           {            
  252.                                                         alarm=!alarm;                                          //开启或取消闹钟
  253.                                                         P_alarm=!alarm;                            //立即输出反馈闹钟状态
  254.                                                         while((P1&(0x07))==0x05)display(p);//按键松开
  255.                                           }
  256.                             }
  257.                             if(alarm)                                                                                    //闹钟开启
  258.                             {
  259.                                           if(alarming)alarming=0;                            //手动取消,显示模式下短按取消闹铃
  260.                             }else alarming=alarm;                                          //闹钟关闭状态                                                                                                                }
  261. }

  262. void key2_function_e()                                                                      //编辑模式,按键2,加1功能
  263. {
  264.               if(edit)
  265.               {
  266.                             if(!disp_num)                                                                      //显示时间,则执行以下程序
  267.                             {
  268.                                           switch(position)                                          //根据位置进行加1
  269.                                           {
  270.                                                         case 1:if(p[2]%10<9)p[2]++;else p[2]-=9 ;break;              //秒个位
  271.                                                         case 2:if(p[2]<50)p[2]+=10;else p[2]-=50;break;              //秒十位
  272.                                                         case 3:if(p[1]%10<9)p[1]++;else p[1]-=9 ;break;              //分钟个位
  273.                                                         case 4:if(p[1]<50)p[1]+=10;else p[1]-=50;break;              //分钟十位
  274.                                                         case 5:if((p[0]%10)<9&&(p[0]<23))p[0]++;else{if(p[0]<20)p[0]-=9;else p[0]-=3;};break;                                                                                                                                                                        //小时个位
  275.                                                         case 6:if((p[0]/10)<2&&(p[0]<14))p[0]+=10;else{if(p[0]<20)p[0]-=10;else p[0]-=20;};break;                                                                                                                                                                        //小时十位
  276.                                                         default: break;
  277.                                           }
  278.                             }
  279.                             else                                                                                                  //显示日期,则执行以下程序
  280.                             {            
  281.                                          
  282.                                           switch(position)                                          //根据位置进行加
  283.                                           {
  284.                                                         case 8:if(p[0]<99)  p[0]+=10;else p[0]%=10;break;                            //年千位
  285.                                                         case 7:if(p[0]%10<9)p[0]++;              else p[0]-=9;break;                            //年百位
  286.                                                         case 6:if(p[1]<99)  p[1]+=10;else p[1]%=10;break;                            //年十位
  287.                                                         case 5:if(p[1]%10<9)p[1]++;  else p[1]-=9;break;                            //年个位
  288. case 4:p[3]=1;if(p[2]==1)p[2]=10;else p[2]=1;break;              //若进行月份的调整,需要重新设置日期,调整月份十位代表10以上和10以下切换
  289.                                                         case 3:p[3]=1;if(p[2]>=10){if(p[2]<12) p[2]++;else p[2]=10; } else if(p[2]<9) p[2]++;else p[2]=1;break;              //调整月份个位
  290.                                                         case 2:               //日期的十位
  291.                                                                                                                 p[3]+=10;                                                                                    //日期的十位加1
  292.                                                                                                                 if(p[3]>day[p[2]-1])                                                        //如果超过月末
  293.                                                                                                                 {
  294.                                                                                                                               p[3]%=10;                                                        //保留日期个位
  295.                                                                                                                               if(p[3]==0)p[3]=10;                                          //十位归零
  296.                                                                                                                 }break;
  297.                                                         case 1:              //日期的个位            
  298. if((p[3]/10)==(day[p[2]-1]/10))              //如果与月末十位相
  299.                                                                                     等则执行以下程序
  300.                                                                                                                 {
  301.                                                                                                                               if(p[3]==day[p[2]-1]){ p[3]=p[3]-(day[p[2]-1]%10);}
  302.                                                                                     //达到月末,减去月末个位数
  303.                                                                                                                               else
  304.                                                                                                                               p[3]++;                                                                      //日期加1
  305.                                                                                                                 }
  306.                                                                                                                 else                                                                                                    //不与月末十位相等
  307.                                                                                                                 {                                                                                   
  308.                                                                                                                               if(p[3]%10<9)p[3]++;                            //最大值为9
  309.                                                                                                                               else
  310.                                                                                                                               {
  311.                                                                                                                                             p[3]-=9;
  312.                                                                                                                                             if(p[3]==0)p[3]=1;                            //日期为0时则为1
  313.                                                                                                                               }
  314.                                                                                                                 }break;
  315.                                                         default: break;
  316.                                           }
  317.                             }
  318.               }
  319. }


  320. void key1_function_d()                                          //按键1,显示状态,显示切换
  321. {
  322.               if(!edit)                                                                      //显示状态下
  323.               {
  324.                             screen++;                                                        //每调用1次,加1,范围“1~3”
  325.                             switch(screen)
  326.                             {
  327.                                           case 1:p=time;disp_num=0;break;                            //显示时间
  328.                                           case 2:p=clock;disp_num=0;break;                            //显示闹钟
  329.                                           case 3:p=date;disp_num=1;break;                            //显示日期
  330.                                           default:p=time;disp_num=0;screen=1;break;              //显示时间
  331.                             }
  332.               }
  333. }

  334. void key1_function_e()                                          //按键1,编辑模式下,移位
  335. {
  336.               if(edit)                                                                      //编辑模式下执行以下程序
  337.               {
  338.                             if(!disp_num)                                          //显示时间状态
  339.                             {
  340.                                           position++;
  341.                                           if(position>6)position=1;              //position范围“1~6”
  342.                             }else                                                                      //显示日期
  343.                             {
  344.                                           position--;                            //从年千位开始修改
  345.                                           if(position==0||position>8)
  346.                                                         position=8;              //范围“1~8”
  347.                             }                                         
  348.               }else position=1;                                          //复位
  349. }
  350. void delay(unsigned int i)                            //延时程序
  351. {
  352.               unsigned int k;
  353.               for(;i>0;i--)
  354.                             for(k=0;k<54;k++);
  355. }
  356. void time_int0() interrupt 1                            //定时中断,中断方式2
  357. {
  358.               EA=0;                                                                      //关闭总中断,防止高级中断影响定时
  359.               if(mcs!=0x28)                                                        //10ms定时
  360.                             mcs++;
  361.               else
  362.               {
  363.                             mcs=0;
  364.                             if(ms_10!=100)ms_10++              ;              //1s定时
  365.                             else
  366.                             {
  367.                                           ms_10=0;            
  368.                                           if(time[2]!=59)time[2]++;              //1s到则秒位加1
  369.                                           else
  370.                                           {
  371.                                                         time[2]=0;                                          //60秒归0
  372.                                                         if(time[1]!=59)time[1]++;//分钟加1
  373.                                                         else
  374.                                                         {
  375.                                                                       time[1]=0;                             //60分钟归0
  376.                                                                       if(time[0]!=23)time[0]++; //小时加1
  377.                                                                       else time[0]=0;              //24小时归0
  378.                                                         }
  379.                                           }
  380.                             }
  381.               }
  382.               EA=1;                                                                                                  //开放总中断
  383. }
复制代码


所有资料51hei提供下载:
时钟.zip (4.86 MB, 下载次数: 208)



评分

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

查看全部评分

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏11 分享淘帖 顶 踩
回复

使用道具 举报

沙发
ID:426296 发表于 2018-11-18 16:39 | 只看该作者
谢谢分享
回复

使用道具 举报

板凳
ID:429043 发表于 2018-11-19 10:52 | 只看该作者
厉害,刚好需要这个
回复

使用道具 举报

地板
ID:447688 发表于 2018-12-17 09:27 来自手机 | 只看该作者
改成8个数码管显示器怎么改 求教
回复

使用道具 举报

5#
ID:457618 发表于 2018-12-28 21:12 | 只看该作者
压缩包的仿真和上面显示的不同,压缩包的仿真文件里P3口都连接了,实际上就连接了两个LED灯,而且按键的功能并不能完全实现,长按K2没反应啊
回复

使用道具 举报

6#
ID:489509 发表于 2019-3-26 19:33 | 只看该作者
谢谢分享
回复

使用道具 举报

7#
ID:278679 发表于 2019-11-7 14:17 | 只看该作者
谢谢分享!!!
回复

使用道具 举报

8#
ID:630916 发表于 2019-11-29 12:33 | 只看该作者
不错  学习下
回复

使用道具 举报

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

本版积分规则

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

Powered by 单片机教程网

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