基于51单片机的数字电子钟C程序源码和Proteus仿真程序

[复制链接]
查看4 | 回复0 | 2021-6-5 16:32:50 | 显示全部楼层 |阅读模式
1.png
1.设计任务和要求
1、数字电子钟(使用定时器做计时,用静态或动态显示时钟)
主要设计内容 :
(1)用定时器实现走时功能
(2)用 4 位或 6 位数码管实现时钟显示(时、分、秒)
(3)用 2 个或多个按键实现校时修改(采用移位键和加 1 键修改时、分、秒)
(4)修改校时过程中,被修改位要求定位闪烁,以表明该位数值正在修改
(5)时钟走时,要求秒点闪烁
(6)时钟走时误差控制在 1 秒/天
(7)扩展增加年月日计时功能,并按键切换分屏显示
2.电路设计方案选择,分析电路工作原理和系统性能
         1. 数据显示模块
考虑到要显示的内容颇多,故运用1602A显示实时数据,第一行显示状态以及年月日星期(如S 2009—01—01 THU),第二行显示温度和实时时间(  22.0C12:00:00),在处理按键设置时,第二行暂时屏蔽温度的显示而显示设置的内容。这样虽然在程序方面多了1602A的一些初始化和读写子函数的定义,但程序的模块化却更加的清楚。而且采用1602A LCM的液晶显示模块后不仅满足了大量数据的显示,系统的硬件电路变的十分简单清晰明
2.温度采集模块
采用常用的温度采集芯片DS18B20单线数字温度传感器进行温度的采集DS18B20的外部电源供电方式 在外部电源供电方式下,DS18B20工作电源由VDD引脚接入,此时I/O线不需要强上拉,不存在电源电流不足的问题,可以保证转换精度,外部电源供电方式是DS18B20最佳的工作方式,工作稳定可靠,抗干扰能力强,而且电路也比较简单。在外接电源方式下,可以充分发挥DS18B20宽电源电压范围的优点,即使电源电压VCC降到3V时,依然能够保证温度量精度。外接电源解决了电压方面的苛刻要求,虽多接了一根线,但对于本设计的近距离测试来说还是很适合的
     3.时间处理模块
利用51内部的定时器和中生成秒,再利用程序累加清零等基本时间,尔后再调用C中一些经典的算法生成年月日以及星期等信息。但是该方案必须保证系统一直处于供电的情况,不能断电,否则时间由初始化了,所以该方案中又添加了三个设置按钮,也就是增加了一个设置调整模块,在再次供电后可以调整时间以同步,完美的解决了时间的连续性问题。这虽使得电路和方案一复杂度相当,但是系统的设计对资源的利用更充分。
        4.调整设置模块
这个模块完全是第三个模块的一个衍生品,它就是为了调整时间以便断电后正常使用。该模块包括四个按键(两个中断,两个设置),一个中断就是简单要停止闹钟;另一个中断时是停止时钟,进入设置状态,包括调整时间以同步和设置闹钟的值,而另外两个设置建一个就是为了此时增加或减少时间值,另一个的作用是在调整完了当前量后进入下一个量的调整或退出调整。

3.主控制器AT89C52介绍
1.AT89C52功能介绍
AT89C52是8字节FLASH闪速存储器,256字竹内部RAM , 32个I/O口线,3个16 位定时/计数器,一个6向量两级中断结构,一个全双工串行通信口,片内振荡器及时钟电路。同时,AT89c52可降至OHz的静态逻辑操作,并支持两种软件可选的节电上作模式。空闲方式停止CPU 的工作,但允许RAM,定时/计数器.串行通信口及中断系统继续工作。掉电方式保存RAM 中的内容,但振荡器停止工作并禁止其它所有部件工作直到下一个硬件复位.
2.功能引脚说明:
Vcc:电源电压
GND:地
P0:P0口是一组8位漏极开路型双向1/O 口,也即地址/数据总线复用口。作为输出口用时.每位能吸收电流的方式驱动8个TTL 逻辑门电路,对端口P0 写“1”时,可作为高阻抗输入端用。
在访问外部数据存储器或程序存储器时,这组口线分时转换地址(低8位)和数据总线复用,在访问期间激活内部 上拉电阻。
在FLASH由编程时,P0口接收指令字节,而在程序校验时,输出指令字 节,校验时,要求外接上拉电阻。
P1口:PI 是一个带内部上拉电阻的8位双向I/O口,Pl的输出缓冲级可驱动(吸收或输出电流4个TTL逻辑门电路。对端口写“1”,通过内部的上拉电阻把端口拉到高电平,此时可作输入口。作 输入口使用时,因为内部存在上拉电阻某个引脚被外部信号拉低时会输出一个电流IIL
与AT89C51不同之处是,Pl.0 和P1.1还可分别作为定时/计数器2 的外部计数输入(Pl.0/T2 )和输入(P1.1/T2EX) ,
FLASH编程和程序校验期间,Pl接收低8位地址。
PI.O 和PI.l 的第二功能:
P2 是一个带有内部上拉电阻的8位双向I/O口,P2的输出缓冲级可驱动(吸收或输出电流)4个TTL逻辑电路。对端口P2写“l",通过内部的上拉电阻把端口拉到高电平,此时可作输入口,           作输入口使用时,因为内部存在上拉电阻,某个引脚被外部信号拉低时会输出一个电流(llt )。
在访问外部程序存储器或16位地址的外部数据存储器,P2送出高8 位地址数据。在访问8位地址的外部数据存储器、如执行MOVX@RI指令)时,P2口输出P2锁存器的内容。
FLASH编程或校验时,P2亦接收高位地址和一些控制信号。
     P3口:P3口是一组带有内部上拉电阻的8位双向I/O口。P3口输出缓冲级可驱动(吸收或输出电流)4个TTL逻辑门电路。对P3口写入“1”时,它们被内部上拉电阻拉高并可作为输入端口。此时,被外部拉低的P3口将用上拉电阻输出电流(IIL) .
P3口除了作为一般的I/0口线外,更重要的用途是它的第二功能,如下表所示:
P3.0 RXD(串行输入口〕
P3.1 TXD(串行输出口〕
P3.2 INTO(外中断0〕
P3.3 INTO(外中断l)
P3.4 TO (定时/计数器0 )
P3.5 Tl (定时/计数器l )
P3.6 WR(外部数据存储器写选通)
P3.7 RD(外部数据存储器读选通)
此外,P3口还接收一些用于FLASH闪速存储器编程和程序校验的控制信号。
RST:复位输入。当振荡器工作时,RST引脚出现两个机器周期以上高电平将使单片机复位。
ALE/PROG:当访问外部程序存储器或数据存储器时,ALE(地址锁存允许)输出脉冲用于锁存地址的低8位字节.一般情况下,ALE仍以时钟振荡频率的1/6输出固定的脉冲信号,因此它可对外输出时钟或用于定时目的。要注意的是:每当访问外部数据存储器时将跳过一个ALE脉冲。对Flash存储器编程期间,该引脚还用于输入编程脉冲(PROG)。如有必要,可通过对特殊功能寄存器(SFR)区中的8EH单元的D0位置位.可禁止ALE操作。该位置位后,只有一条MOVX和MOVC指令才能将ALE激活,此外,该引脚会被微弱拉高,单片机执行外部程序时,应设置ALE禁止位无效。
PSEN:程序储存允许PSEN输出是外部程序存储器的读选通信号,当AT89C52由外部程序存储器取指令(或数据)时,每个机器周期两次PSEN有效,即输出两个脉冲。在此期间,当访问外部数据存储器,将跳过两次PSEN信号。
EA/VPP:外部访问允许。欲使CPU 仅访问外部程序存储器(地址为0000H-FFFFH ) , EA端必须保持低电平(接地).需注怠的是:如果加密位LBI被编程,复位时内部会锁存EA端状态。
3.最小系统
单片机最小系统包括时钟电路、电源和复位电路等,是单片机工作的基本要求。单片机控制整个系统的工作,一方面读取日历时钟芯片中的日期等数据,检测是否需要设置,并处理相应的按键。另一方面,控制显示器的工作,将各种数据送到液晶显示器去显 80C52型单片机内有一增益反相放大器,振荡频率取决于石英晶体的振荡频率。范围可取1。2—12MHZ,C01、C02主要起频率微调和稳定作用。
(1)复位电路
单片机在开关机时都需要复位,以便中央处理器CPU及其他功能部件都RC构成处于一个确定的初始状态,并从这个状态开始工作。80C51的RST引脚是复位信号的输入端。复位信号高电平有效,持续时间需要24个时钟周期以上。
RC构成微分电路,在接电瞬间,产生一个微分脉冲,其宽度若大于2个机器周期,80C51型单片机将复位。为保证微分脉冲宽度足够大,RC时间常数应大于2个机器周期。一般取10uF电容、8.2K欧姆电阻。
4.振荡器特性:
XTAL1和XTAL2分别为反向放大器的输入和输出。该反向放大器可以配置为片内振荡器。石晶振荡和陶瓷振荡均可采用。如采用外部时钟源驱动器件,XTAL2应不接。有余输入至内部时钟信号要通过一个二分频触发器,因此对外部时钟信号的脉宽无任何要求,但必须保证脉冲的高低电平要求的宽度。
基于51单片机的数字电子钟源码:(完整代码和电路图在附件中)
  1. #include "reg52.h"
  2. //#include "delay.h"
  3. #include "ds1302.h"        
  4. #include "lcd1602.h"
  5. #include "ds18b20.h"
  6. //#include "stdio.h"

  7. #define uint unsigned int
  8. #define uchar unsigned char

  9. sbit K1=P3^3;                                                //选择
  10. sbit K2=P3^4;                  //加
  11. sbit K3=P3^5;                  //减
  12. sbit K4=P3^6;                  //确定

  13. sbit BUZZER=P1^0;                                //蜂鸣器

  14. uchar RTC_flag=0;
  15. char RTC_NYRSFM[14]={0};
  16. code char RTC_set_state[6]={'N','Y','R','S','F','M'};
  17. code char Ping_Run_Year[12]={31,28,31,30,31,30,31,31,30,31,30,31};
  18. code char Tab[]={0x08,0x0F,0x12,0x0F,0x0A,0x1F,0x02,0x02,//年
  19.                                                                                 0x0F,0x09,0x0F,0x09,0x0F,0x09,0x0B,0x11,//月
  20.                                                                                 0x1F,0x11,0x11,0x1F,0x11,0x11,0x1F,0x00};//日
  21. /*
  22. 函数作用:将时间进行加一或减一后返回(DA指令转换)
  23. 函数形参:
  24.                                         DateTime:要转化的时间
  25.                                         n                                :时间的进制
  26.                                         x                                :1表示加1,0表示减1
  27.                                         y                                :0表示起始数值是0,1表示起始数值是1
  28. 返回值        :转化好的时间
  29. */
  30. uchar        DA_DateTime(uchar DateTime,uchar n,uchar x,uchar y)
  31. {
  32.         uchar a;
  33.         a=(DateTime>>4)*10+(DateTime&0x0f);
  34.         if(x)//加
  35.         {
  36.                 if(y)        a=(a%n)+1;//起始为0
  37.                 else        a=(++a)%n;
  38.         }
  39.         else//减
  40.         {
  41.                 if(y)        a=(a+n-1)%n;//起始为0
  42.                 else        a=((a+n-2)%n)+1;
  43.         }
  44.         return ((a/10)*16+a%10);
  45. }
  46. /*
  47. 函数作用:刷新星期(2000年~2099年星期算法)
  48. 函数形参:
  49.                                         y:年
  50.                                         m:月
  51.                                         d:日
  52. 返回值        :
  53. */
  54. void RefreshWeekDay(uchar y,uchar m,uchar d)
  55. {
  56.   if(m==1||m==2)
  57.   {
  58.                 m+=12;
  59.                 y--;
  60.   }
  61.   TIME[5]=((d+2*m+3*(m+1)/5+y+y/4-y/100+y/400+1)%7);
  62. }
  63. /*
  64. 函数作用:显示屏显示从DS1302读出的时钟
  65. 函数形参:
  66. 返回值        :
  67. */
  68. void Lcd_1602_Display(void)
  69. {
  70.                 RefreshWeekDay((TIME[6]>>4)*10+(TIME[6]&0x0f),(TIME[4]>>4)*10+(TIME[4]&0x0f),(TIME[3]>>4)*10+(TIME[3]&0x0f));
  71.         
  72.                 L1602_char(1,1,TIME[6]/16+'0');
  73.                 L1602_char(1,2,(TIME[6]&0x0f)+'0');//年
  74.                 L1602_char(1,3,0x00);//自定义汉字年
  75.                 L1602_char(1,4,TIME[4]/16+'0');
  76.                 L1602_char(1,5,(TIME[4]&0x0f)+'0');//月
  77.                 L1602_char(1,6,0x01);//自定义汉字月
  78.                 L1602_char(1,7,TIME[3]/16+'0');
  79.                 L1602_char(1,8,(TIME[3]&0x0f)+'0');//日
  80.                 L1602_char(1,9,0x02);//自定义汉字日
  81.                 L1602_char(1,11,((TIME[5]&0x0f)+6)%7+1+'0');//周
  82.                 L1602_char(2,1,TIME[2]/16+'0');
  83.                 L1602_char(2,2,(TIME[2]&0x0f)+'0');//时
  84.                 L1602_char(2,3,':');
  85.                 L1602_char(2,4,TIME[1]/16+'0');
  86.                 L1602_char(2,5,(TIME[1]&0x0f)+'0');//分
  87.                 L1602_char(2,6,':');
  88.                 L1602_char(2,7,TIME[0]/16+'0');
  89.                 L1602_char(2,8,(TIME[0]&0x0f)+'0');//秒
  90. }
  91. /*
  92. 函数作用:天数加减一
  93. 函数形参:x为1加,0减
  94. 返回值        :
  95. */
  96. void RCT_DA_R(uchar x)
  97. {
  98.         if(((((TIME[6]>>4)*10+(TIME[6]&0x0f))%4==0 && ((TIME[6]>>4)*10+(TIME[6]&0x0f))%100!=0) || (((TIME[6]>>4)*10+(TIME[6]&0x0f))%400==0)) && ((TIME[4]>>4)*10+(TIME[4]&0x0f))==2)//闰年二月
  99.         {
  100.                 TIME[3]=DA_DateTime(TIME[3],29,x,1);
  101.         }
  102.         else
  103.         {
  104.                 TIME[3]=DA_DateTime(TIME[3],Ping_Run_Year[(TIME[4]>>4)*10+(TIME[4]&0x0f)-1],x,1);
  105.         }
  106. }
  107. /*
  108. 函数作用:写入自定义图形(年月日)
  109. 函数形参:
  110. 返回值        :
  111. */
  112. void LCD1602_CustomData()
  113. {
  114.         uchar i;
  115.         wcmd(0x40);//定义第一个自定义字符
  116.         for(i=0;i<8*3;i++)
  117.         {
  118.                 wdata(Tab[i]);
  119.         }
  120. }

  121. void Int1_Init()//设置INT1
  122. {
  123.         IT1=1;//跳变沿出发方式(下降沿)
  124.         EX1=1;//打开INT1的中断允许。
  125.         EA=1;//打开总中断
  126. }


  127. void main()
  128. {
  129.         uchar i;
  130.         BUZZER=0;
  131.         L1602_init();
  132.         Ds1302_Init();
  133.         Int1_Init();
  134.         LCD1602_CustomData();
  135.         while(1)
  136.         {
  137.                 if(RTC_flag==0)//正常显示模式
  138.                 {
  139.                         Ds1302ReadTime();//DS1302获取时间
  140.                         if((TIME[1]==0)&&(TIME[0]==0))//分秒为0,整点报时
  141.                         {
  142.                                 BUZZER=1;
  143.                         }
  144.                         else
  145.                         {
  146.                                 BUZZER=0;
  147.                         }
  148.                         Lcd_1602_Display();//显示时间
  149.                         L1602_string(1,14,"RTC");
  150.                         datapros();
  151.                 }
  152.                 else//设置模式
  153.                 {
  154.                         BUZZER=0;
  155.                         if(K1==0)
  156.                         {
  157.                                 while(K1==0);
  158.                                 L1602_string(1,14,"[ ]");
  159.                                 for(i=0;i<7;i++)
  160.                                 {
  161.                                         if((i+1)==RTC_flag)//显示设置的参数状态
  162.                                         {
  163.                                                 L1602_char(1,15,RTC_set_state[i]);
  164.                                         }
  165.                                 }
  166.                         }
  167.                         else if(K2==0)//加
  168.                         {
  169.                                 while(K2==0);
  170.                                 switch(RTC_flag)
  171.                                 {
  172.                                         case 1:TIME[6]=DA_DateTime(TIME[6],100,1,0);break;
  173.                                         case 2:TIME[4]=DA_DateTime(TIME[4],12,1,1);break;
  174.                                         case 3:RCT_DA_R(1);break;
  175.                                         case 4:TIME[2]=DA_DateTime(TIME[2],24,1,0);break;
  176.                                         case 5:TIME[1]=DA_DateTime(TIME[1],60,1,0);break;
  177.                                         case 6:TIME[0]=DA_DateTime(TIME[0],60,1,0);break;
  178.                                 }
  179.                                 Lcd_1602_Display();
  180.                         }
  181.                         else if(K3==0)//减
  182.                         {
  183.                                 while(K3==0);
  184.                                 switch(RTC_flag)
  185.                                 {
  186.                                         case 1:TIME[6]=DA_DateTime(TIME[6],100,0,0);break;
  187.                                         case 2:TIME[4]=DA_DateTime(TIME[4],12,0,1);break;
  188.                                         case 3:RCT_DA_R(0);break;
  189.                                         case 4:TIME[2]=DA_DateTime(TIME[2],24,0,0);break;
  190.                                         case 5:TIME[1]=DA_DateTime(TIME[1],60,0,0);break;
  191.                                         case 6:TIME[0]=DA_DateTime(TIME[0],60,0,0);break;
  192.                                 }
  193.                                 Lcd_1602_Display();
  194.                         }
  195.                         else if(K4==0)//确定
  196.                         {
  197.                                 while(K4==0);
  198.                                 L1602_string(1,14,"RTC");
  199.                                 Ds1302_Init();//写进时间
  200.                                 RTC_flag=0;
  201.                         }
  202.                 }
  203.         }               
  204. }

  205. void EX_INT1() interrupt 2
  206. {
  207.         RTC_flag=(RTC_flag++)%6+1;//N Y R S F M
  208. }
复制代码

完整单片机C源码和仿真文件: 数字时钟.zip (176.62 KB, 售价: 3 工控币)
回复

使用道具 举报

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

本版积分规则