设为首页收藏本站淘宝杂货铺

从F到0 - From F to 0

 找回密码
 注册已关闭
搜索
查看: 2256|回复: 4
收起左侧

基于C51系列的共阴数码管实现的数字时钟系统74595+74154+15个数码管

[复制链接]
发表于 2018-2-12 17:31:56 | 显示全部楼层 |阅读模式
仿真截图.png
  1. /*
  2. 基于C51系列的共阴数码管实现的数字时钟系统74595+74154+15个数码管
  3.         Proteus 的仿真结果未必和实体运行结果一致,没有部分特殊功能寄存器而无法实现仿真。
  4.         仿真文件由Proteus 7.7 SP2所生成,若无法打开,请升级到更高的版本。
  5.         请一定要点击复制代码再粘贴到Keil4中编译,否则编译时可能会出现其他问题。
  6.         本源码需要头文件 stc12c5a60s2.h 若不存在 请访问STC官网或打开STC烧录软件获取即可。
  7. 版本修订历史:
  8. (2018-02-12) V1.1
  9.         新增蜂鸣器整点报时功能,7:00~21:00报时,其余时间不报。
  10.         上电初始年由2000年改为2018年,可在源码中自行修改
  11.         可调年月日时分秒星期 (不含自动计算星期的函数)
  12.         修正了日期调到2月31日等无效日期仍然可以正常走时的bug。
  13.         修正了年月日时分秒星期的调整顺序。
  14.         单片机型号:STC12C5A**S2 其中**是容量的意思,没有过多要求。
  15.         晶振频率:24Mhz By:LscmunaixMG 2018-02-12
  16. */
  17. #include "stc12c5a60s2.h"
  18. #include "intrins.h"
  19. #define ui unsigned int
  20. #define uc unsigned char
  21. uc code nbr[16]={ //共阴数码管段码数据,严禁修改否则会显示乱码。
  22. 0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71
  23. };


  24. sbit SRCLK = P2^0;  //74595 串行数据时钟输入
  25. sbit SER   = P2^1;  //74595 串行数据输入
  26. sbit LATCH = P2^2;  //74595 锁存信号输入
  27. sbit A3=P2^3; //74154译码输入3
  28. sbit A2=P2^4; //74154译码输入2
  29. sbit A1=P2^5; //74154译码输入1
  30. sbit A0=P2^6; //74154译码输入0
  31. sbit EN=P2^7; //74154 低电平使能
  32. sbit K0=P3^3; //按键0,按下后进入时间调节模式,调节完毕后恢复正常走时功能。
  33. sbit K1=P3^4; //按键1,在时间调节模式下对调节的日期时间加1,长按连续加。
  34. sbit LED=P3^2; //秒闪烁LED,可并联两个LED作为冒号。
  35. sbit beep=P3^5; //有源蜂鸣器
  36. unsigned char bd=0; //蜂鸣器时间计数
  37. ui y; //定义变量年
  38. uc m,d,h,i,s,w; //定义变量月、日、时、分、秒、周
  39. uc ms5; //5毫秒定时器中断计数 中断200次计数即为1秒
  40. uc adt; //调时标志位 0走时 1调年 2调月 3调日 4调星期 5调时 6调分 7调秒
  41. bit lj; //调时闪烁标志,0点亮1熄灭部分日期时间的显示,每隔数毫秒进行切换。
  42. uc ld; //调时按键按下时间计数,按键放开或叠加日期时间则清零
  43. uc sld; //调试按键按下时间计数超过此值连续叠加日期时间,按键放开清零



  44. void InitTimer0(void){ //初始化5毫秒定时器,根据晶振频率修改TH0和TL0的值。
  45.     TMOD = 0x01;
  46.     TH0 = 0xEC;
  47.     TL0 = 0x78;
  48.     EA = 1;
  49.     ET0 = 1;
  50.     TR0 = 1;
  51. }


  52. bit if_leap_year(ui y){ //平闰年检测,闰年返回1,平年返回0
  53.          //闰年是4的倍数且不是100的倍数或者是400的倍数,否则是平年。
  54.         if((y%4 == 0 && y%100 != 0) || y%400 == 0){
  55.         return 1; //返回1表示闰年
  56.         } else { //否则就是平年
  57.         return 0; //返回0表示平年
  58.         }
  59. }

  60. uc get_mon_day_nbr(ui y,uc m){ //通过年月获取该月有多少天
  61.         if(m == 1 || m == 3 || m == 5 || m == 7 || m == 8 || m == 10 || m == 12) { //大月则31天
  62.                 return 31;
  63.         }

  64.         if(m == 4 || m == 6 || m == 9 || m == 11){ //小月则30天
  65.                 return 30;
  66.         }
  67.                 if(m == 2){ //2月
  68.                         if(if_leap_year(y) == 1){ //闰年29天
  69.                         return 29;
  70.                         } else { //平年28天
  71.                         return 28;
  72.                         }
  73.                 }
  74.         return 0;
  75. }


  76. bit dt_is_ok(ui y,uc m,uc d,uc w,uc h,uc i,uc s){ //检测设置的日期时间格式是否有效 1有效0无效
  77.         if(w > 7 || w < 1) return 0; //星期大于7或小于1 返回0
  78.         if(y > 9999 || m > 12 || m < 1 || d < 1) return 0; //年大于9999 或 月大于12 或 月小于1 或日小于1 返回0
  79.         if(d > get_mon_day_nbr(y,m)) return 0; //通过年月获取当月天数 大于该值 返回0
  80.         if(h > 23 || i > 59 || s > 59) return 0; //时大于23 或 分大于59 或秒大于59 返回0
  81.         return 1; //检测有效 返回1
  82. }

  83. bit set_time(ui yy,uc mm,uc dd,uc ww,uc hh,uc ii,uc ss){ //设置日期时间
  84.                 if(dt_is_ok(yy,mm,dd,ww,hh,ii,ss) == 1){  //检查日期时间格式有效
  85.                 y=yy;m=mm;d=dd;w=ww; //设置年月日周
  86.                 h=hh;i=ii;s=ss; //设置时分秒
  87.                 return 1;  //设置成功 返回1
  88.                 } else { //格式无效 返回0
  89.                 return 0;
  90.         }
  91. }




  92. void add_time(){ //时间步进1秒
  93. s++; //秒加1
  94.         if(s > 59){ //秒超过59
  95.         s=0; //秒清零
  96.         i++; //分加1
  97.                 if(i > 59){ //分超过59
  98.                 i=0; //分清零
  99.                 h++; //时加1

  100.                         if(h > 23){ //时超过23
  101.                         h=0; //时清零
  102.                         d++; //日加1
  103.                         w++; //星期加1
  104.                         if(w > 7){ //星期超过7
  105.                         w=1; //星期等于1
  106.                         }
  107.                                 if(d > get_mon_day_nbr(y,m)){  //日超过当前年月的天数
  108.                                 d=1; //日清1
  109.                                 m++; //月加1
  110.                                         if(m > 12){ //月加到12以上
  111.                                         m=1; //月清1
  112.                                         y++; //年加1
  113.                                                 if(y > 9999){ //年超过9999
  114.                                                 y=0; //年清零
  115.                                                 }
  116.                                         }
  117.                                 }
  118.                         }
  119.                 }
  120.         }
  121. }




  122. void add_time2(){ //时间步进1天
  123.         if(w > 7){ //星期超过7
  124.         w=1; //星期等于1
  125.         }
  126.                        
  127.         if(d > get_mon_day_nbr(y,m)){  //日超过当前年月的天数
  128.                 d=1; //日清1
  129.                 m++; //月加1
  130.                 }
  131.         if(m > 12){ //月加到12以上
  132.                 m=1; //月清1
  133.                 y++; //年加1
  134.                 }
  135.         if(y > 9999){ //年超过9999
  136.                 y=0; //年清零
  137.                 }
  138.                                        
  139.         }



  140. void Delay1ms()                //@12MHz
  141. {
  142.         unsigned char i, j;

  143.         _nop_();
  144.         _nop_();
  145.         i = 12;
  146.         j = 168;
  147.         do
  148.         {
  149.                 while (--j);
  150.         } while (--i);
  151. }



  152. void SendByte(uc dat)
  153. {   
  154.         uc i;
  155.         
  156.         for(i=0;i<8;i++)
  157.     {
  158.                SRCLK=0;        //先将串行时钟输入端SRCLK置成低电平
  159.                SER=dat&0x80;        //每次只取一位
  160.                dat<<=1;        //每次只取一位进行左移
  161.                SRCLK=1;        //数据在串行时钟输入端SRCLK的上升沿输入到移位寄存器中
  162.     }
  163.          
  164. }


  165. void Out595(void)
  166. {
  167.         LATCH=0;//锁存
  168.         _nop_();//空指令
  169.         LATCH=1;//LATCH上升沿并行数据输出
  170. }

  171. void out_y(bit o){
  172.         A0=0;A1=0;A2=0;A3=0;SendByte(nbr[y/1000%10]);Out595();EN=!o;Delay1ms();EN=1;  //年的千位
  173.         A0=0;A1=0;A2=0;A3=1;SendByte(nbr[y/100%10]);Out595();EN=!o;Delay1ms();EN=1;  //年的百位
  174.         A0=0;A1=0;A2=1;A3=0;SendByte(nbr[y/10%10]);Out595();EN=!o;Delay1ms();EN=1;  //年的十位
  175.         A0=0;A1=0;A2=1;A3=1;SendByte(nbr[y%10]);Out595();EN=!o;Delay1ms();EN=1;  //年的个位
  176. }


  177. void out_m(bit o){
  178.         A0=0;A1=1;A2=0;A3=0;SendByte(nbr[m/10]);Out595();EN=!o;Delay1ms();EN=1;  //月的十位
  179.         A0=0;A1=1;A2=0;A3=1;SendByte(nbr[m%10]);Out595();EN=!o;Delay1ms();EN=1;  //月的个位
  180. }

  181. void out_d(bit o){
  182.         A0=0;A1=1;A2=1;A3=0;SendByte(nbr[d/10]);Out595();EN=!o;Delay1ms();EN=1;  //日的十位
  183.         A0=0;A1=1;A2=1;A3=1;SendByte(nbr[d%10]);Out595();EN=!o;Delay1ms();EN=1;  //日的个位
  184. }

  185. void out_w(bit o){
  186.         A0=1;A1=0;A2=0;A3=0;SendByte(nbr[w%10]);Out595();EN=!o;Delay1ms();EN=1;  //星期
  187. }

  188. void out_h(bit o){
  189.         A0=1;A1=0;A2=0;A3=1;SendByte(nbr[h/10]);Out595();EN=!o;Delay1ms();EN=1;  //时的十位
  190.         A0=1;A1=0;A2=1;A3=0;SendByte(nbr[h%10]);Out595();EN=!o;Delay1ms();EN=1;  //时的个位
  191. }
  192. void out_i(bit o){
  193.         A0=1;A1=0;A2=1;A3=1;SendByte(nbr[i/10]);Out595();EN=!o;Delay1ms();EN=1;  //分的十位
  194.         A0=1;A1=1;A2=0;A3=0;SendByte(nbr[i%10]);Out595();EN=!o;Delay1ms();EN=1;  //分的个位
  195. }

  196. void out_s(bit o){
  197.         A0=1;A1=1;A2=0;A3=1;SendByte(nbr[s/10]);Out595();EN=!o;Delay1ms();EN=1;  //秒的十位
  198.         A0=1;A1=1;A2=1;A3=0;SendByte(nbr[s%10]);Out595();EN=!o;Delay1ms();EN=1;  //秒的个位

  199. }




  200. void out_nbr(bit o_y,bit o_m,bit o_d,bit o_w,bit o_h,bit o_i,bit o_s){ //输出时间到数码管


  201.         out_y(o_y); //输出年
  202.         out_m(o_m);  //输出月
  203.         out_d(o_d); //输出日
  204.         out_w(o_w); //输出星期
  205.         out_h(o_h); //输出时
  206.         out_i(o_i); //输出分
  207.         out_s(o_s); //输出秒
  208.         Delay1ms(); //延时1毫秒



  209. }

  210. void out_nbrs(){ //输出时间到数码管 包含调时闪烁
  211.                         if(adt == 0){ //正常走时
  212.                         out_nbr(1,1,1,1,1,1,1); //输出所有时间
  213.                         } else { //调时模式
  214.                                 switch(adt){
  215.                                 case 1:out_nbr(lj,1,1,1,1,1,1);break; //年闪烁
  216.                                 case 2:out_nbr(1,lj,1,1,1,1,1);break; //月闪烁
  217.                                 case 3:out_nbr(1,1,lj,1,1,1,1);break; //日闪烁


  218.                                 case 4:out_nbr(1,1,1,1,lj,1,1);break; //时闪烁
  219.                                 case 5:out_nbr(1,1,1,1,1,lj,1);break; //分闪烁
  220.                                 case 6:out_nbr(1,1,1,1,1,1,lj);break; //秒闪烁
  221.                                 case 7:out_nbr(1,1,1,lj,1,1,1);break; //周闪烁
  222.                                 default:
  223.                                 adt=0;


  224.                                 while(!dt_is_ok(y,m,d,w,h,i,s)){ //无效时间一直循环,有效为止
  225.                                         add_time2(); //时间步进1天
  226.                                         out_nbr(1,1,1,1,1,1,1);
  227.                                         }
  228.                                 EA=1;

  229.                                 break;        //退出调时模式

  230.                                 }
  231.                         }

  232. }

  233. void set_time_key(){
  234.                         switch(adt){
  235.                         case 1: //调年
  236.                         y++; //年加1
  237.                         if(y > 9999)y=0; //年超过9999则清0
  238.                         break;

  239.                         case 2: //调月
  240.                         m++; //月加1
  241.                         if(m > 12)m=1; //月超过12则清1
  242.                         break;

  243.                         case 3: //调日
  244.                         d++; //日加1
  245.                         if(d > get_mon_day_nbr(y,m))d=1; //日超过当月天数则清1
  246.                         break;

  247.                         case 4: //调时
  248.                         h++; //时加1
  249.                         if(h > 23) h=0; //时超过23则清0
  250.                         break;

  251.                         case 5: //调分
  252.                         i++;//分加1
  253.                         if(i > 59) i=0; //分超过59则清0
  254.                         break;

  255.                         case 6: //调秒
  256.                         s++;//秒加1
  257.                         if(s > 59) s=0; //秒超过59则清0
  258.                         break;

  259.                         case 7: //调星期

  260.                         w++; //星期加1
  261.                         if(w > 7) w=1; //星期超过7则清1
  262.                         break;
  263.                


  264.                         default:break;

  265.                         }
  266. }


  267.         void main(){ //硬件上电首先进入此函数
  268. CLK_DIV = 0x01;
  269.         adt=0;
  270.         set_time(2018,1,1,1,0,0,0); //设置日期时间
  271.           //  AUXR = AUXR|0x80;
  272.         InitTimer0();
  273.        
  274.                 while(1){ //进入死循环
  275.                 out_nbrs();
  276.                         if(K0 == 0){

  277.                         out_nbrs();

  278.                         if(K0 == 0){
  279.                         adt++;
  280.                         bd=0;
  281.                         while(K0 == 0){
  282.                         if(bd<5) {bd++;beep=0;} else {beep=1;}
  283.                         out_nbrs();
  284.                         } //按键放开
  285.                         beep=1;bd=0;
  286.                         }

  287.                         }
  288.                         if(K1 == 0 && adt != 0){
  289.                         out_nbrs();
  290.                         if(K1 == 0){
  291.                         set_time_key();
  292.                         sld=50;
  293.                         bd=0;
  294.                         while(K1 == 0){
  295.                         if(bd<5) {bd++;beep=0;} else {beep=1;}
  296.                         ld++;
  297.                         out_nbrs();
  298.                                 if(ld > sld){
  299.                                         sld=25;
  300.                                         ld=0;
  301.                                         beep=0;
  302.                                         set_time_key();
  303.                                         out_nbrs();out_nbrs();
  304.                                         beep=1;
  305.                                              } //if
  306.                                        } //while
  307.                         ld=0;
  308.                         bd=0;beep=1; //解除蜂鸣器鸣叫
  309.                         }

  310.                 }

  311.                        
  312.         }
  313. }



  314.         void Timer0Interrupt(void) interrupt 1{ //5毫秒定时器中断
  315.         //重置TH0与TL0的值,根据晶振频率修改TH0和TL0的值。
  316.     TH0 = 0xEC;
  317.     TL0 = 0x78;
  318.                         ms5++; //5毫秒中断计数加1
  319.                                 if(ms5%40 == 0 && adt != 0)lj=~lj;//每200毫秒切换一次数码管亮灭状态(调时模式有效)
  320.                                  //5毫秒中断计数加到200 200*5=1000毫秒 1秒触发1次
  321.                                 if(ms5%100 == 0 && adt == 0) { //正常走时模式下,每半秒切换一次闪灯亮灭状态
  322.                                         LED=~LED;
  323.                                         } else {
  324.                                                 if(adt != 0) LED = 1; //调时模式始终熄灭LED
  325.                                         }
  326.                                 if(ms5 >= 200){
  327.                                
  328.                                 ms5=0; //5毫秒计数清零
  329.                                 if(adt ==0) {        //只有正常走时状态就会加秒。
  330.                                         add_time(); //时间步进1秒
  331.                                                 if(i == 0 && s == 0){ //分秒等于0 整点报时
  332.                                                         switch(h){ //7点到21点整报时 蜂鸣器鸣叫1秒 其余时间不报
  333.                                                         case 7:beep=0;break;
  334.                                                         case 8:beep=0;break;
  335.                                                         case 9:beep=0;break;
  336.                                                         case 10:beep=0;break;
  337.                                                         case 11:beep=0;break;
  338.                                                         case 12:beep=0;break;
  339.                                                         case 13:beep=0;break;
  340.                                                         case 14:beep=0;break;
  341.                                                         case 15:beep=0;break;
  342.                                                         case 16:beep=0;break;
  343.                                                         case 17:beep=0;break;
  344.                                                         case 18:beep=0;break;
  345.                                                         case 19:beep=0;break;
  346.                                                         case 20:beep=0;break;
  347.                                                         case 21:beep=0;break;
  348.                                                         default:beep=1;break;
  349.                                                         }

  350.                                                 } else {
  351.                                                 beep=1;
  352.                                                 }
  353.                         }
  354.                                 }
  355.                        


  356.         }
复制代码


数字钟.dsn

107.89 KB, 下载次数: 1361

发表于 2018-2-12 17:51:48 | 显示全部楼层
不但楼主发的3级精华帖很难百度到,而且用户名也一样。
发表于 2018-2-12 18:08:15 | 显示全部楼层
本帖最后由 欣欣 于 2018-2-12 18:09 编辑
全世界只有1个我 发表于 2018-2-12 17:51
不但楼主发的3级精华帖很难百度到,而且用户名也一样。

我也没明白是什么鬼词了,只知道有一段时间没来了。

发表于 2018-2-12 19:39:17 | 显示全部楼层
看不懂,路过帮顶好了
发表于 2018-4-22 10:36:38 来自手机 | 显示全部楼层
这个绝品,值得拥有!
您需要登录后才可以回帖 登录 | 注册已关闭

本版积分规则

QQ|手机版|Archiver|从F到0 ( 蒙ICP备17002595号-1 )
蒙公网安备15010402000325号

腾讯云安全认证

GMT+8, 2024-4-20 04:51 , Processed in 0.413024 second(s), 18 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2021, Tencent Cloud.

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