矿石收音机论坛

 找回密码
 加入会员

QQ登录

只需一步,快速开始

搜索
查看: 30318|回复: 47

再贴一个2051的数字钟

[复制链接]
     
发表于 2007-11-26 23:23:56 | 显示全部楼层 |阅读模式
网上常见的一款电路极简的单片机电子钟设计详解

电路特点
这里介绍的电子钟,电路可称得上极简,它仅使用单片的20引脚单片机完成电子钟的全部功能,而笔者见到的其它设计方案均采用二片以上的多片IC实现。
电路见图1。
51clock2.jpg
一片20引脚的单片机AT89C2051为电子钟主体,其显示数据从P1口分时输出,P3.0~3.3则输出对应的位选通信号。由于LED数码管点亮时耗电较大,故使用了四只PNP型晶体管VT1~VT4进行放大。本来笔者还有一种更简的设计方案(见图2),可省去VT1~VT4及R1~R4八个元件,但这种设计由于テ??涑隹诘墓嗳氲缌饔邢蓿ㄔ?0mA),数码管亮度较暗而不向读者介绍,除非你采用了高亮度的发光数码管。
51clock3.jpg
P3.4、P3.5、3.7外接了三个轻触式按键,这里我们分别命名为:模式设定键set(P3.4)、时调整键hour(P3.5)、分调整键min(P3.7)。C1、R13组成上电复位电路。VT5及蜂鸣器Bz为闹时讯响电路。三端稳压器7805输出的5V电压供整个系统工作。此电子钟可与任何9~20V/100mA的交直流电源适配器配合工作,适应性强。

电子钟功能
1.走时:通过模式设定键set选择为走时,U1、U2显示小时,U3、U4显示分。U2的小数点为秒点,每秒闪烁一次。
2.走时调整:通过模式设定键set选择为走时调整,按下hour键对U1、U2的走时“时”显示进行调整(每0.2秒递加1)。按下min键对U3、U4的走时“分”显示进行调整(每0.2秒递加1)。
3.闹时调整:通过模式设定键set选择为闹时调整,按下hour键对U1、U2的闹时“时”显示进行调整(每0.2秒递加1)。按下min键对U3、U4的闹时“分”显示进行调整(每0.2秒递加1)。
4.闹时启/停设定:通过模式设定键set选择为闹时启/停设定,按下min键U3的小数点点亮,闹时功能启动;按下hour键U3的小数点熄灭,闹时功能关停。
由于电路设计得极其简单,因此丰富的功能只能由软件完成,这里软件设计成为了关键。下面介绍软件设计要点。
图3为主程序状态流程。
51clock1.gif
运行时建立的主要状态标志如下:
flag—掉电标志。掉电后,flag内为一随机数;重新设定时间后flag内写入标志数55H。
set—工作模式设定标志。
hour—走时“时”单元。
min—走时“分”单元。
sec—走时“秒”单元。
deda—走时5mS计数单元
t_hour—闹时“时”单元。
t_min—闹时“分”单元。
d_05s—0.5秒位标志。每秒钟的前0.5秒置1,后0.5秒置0,以使秒点闪烁。
o_f—闹时启/停位标志。闹时启动置1,闹时关停置0。
另外将定时器T0设定为5mS的定时中断。这里晶振频率为12MHz,因此5mS的初值为-5000,但实际上程序还要作其它运算,使得时间偏长,经调整为-4800后试验刚好。计时单元deda每次中断均加1。走时函数判断deda>=200时即令秒单元sec加1。同理秒单元sec满60后令分单元min加1。分单元min满60后令时单元hour加1。时单元hour满24后清0。
上电后,首先进行初始化,对各状态标志、输入输出口及定时器T0进行初始化工作,以适应后面程序的要求。
随后程序判断有无set键按下,如按下,则set键值从0起加1。set键值只能从0加到3,然后又回到0。
接下来,根据键值进行散转。若set=0,运行走时程序;若set=1,进入调整闹时时间程序;若set=2,进入调整走时时间程序;若set=3,显示已调好的闹时时间,同时进入启/停闹时程序。
散转完后,进行判断掉电标志flag是否等于55H。若不等,说明刚开机上电或运行过程中掉过电,这时四个数码管以1Hz的频率闪烁四个8字,提示时间不准;若相等,说明未掉电,RAM区内容未变化,时间准确。
再下来程序又转回到初始化之后进行循环运行。
由于使用了四只数码管密集排列,因此只能采用双面印刷板设计,图4、5分别为正面(元件面)、反面的印板图。尺寸3000milx4000mil(7.62cmx10.16cm)。图6为计算机输出的三维仿真印制板。
51clock1.jpg 51clock2.gif 51clock3.gif

元件选用
为了走时准确,晶振X最好选用温漂小的。四个数码管U1~U4可选用发绿光的,这样光泽较柔和。蜂鸣器Bz要购买绕线型的电动式蜂鸣器(市场上有一种加电压即工作的蜂鸣器这里不适用),因驱动信号为脉冲信号。其它元件一般无特殊要求。

使用方法
上电后,四个LED数码管闪烁四个8字。
按一下set键,闪烁现象消失(此时set=1),U1的小数点亮,说明此刻可以调整闹时时间。按下hour键,U1、U2作加法;按下min键,U3、U4作加法。这些调整好的数据被同时送入RAM区的闹时记忆“时”单元t_hour和闹时记忆“分”单元t_min。
再按一下set键,set=2,U2的小数点亮,此刻可以调整走时时间。按下hour键,U1、U2作加法;按下min键,U3、U4作加法。这些调整好的数据也被同步送入RAM区的“时”记忆单元hour和“分”记忆单元min。
再按一下set键,set=3,显示刚才调整的闹时时间,此刻可以选择启动/关停闹时。按下min键,o_f位标志置1,U3的小数点亮,闹时启动;按下hour键,o_f位标志置0,U3的小数点灭,闹时关闭。
若再按一下set键,set=0,电路进入走时状态,U1、U2显示小时,U3、U4显示分。U2的小数点作秒点闪烁。
在闹时启动的情况下,走时到达设定闹时,则蜂鸣器Bz鸣响一分钟进行提醒。
附:用C51编写的源程序清单(已由实验板运行通过)
;***这个程序是C的,我看不懂***
  1. #include                        /*包含器件配置文件*/
  2. #define uchar unsigned char
  3. #define uint unsigned int
  4. char DATA_7SEG[10]={0xC0,0xF9,0xA4,0xB0,0x99,
  5.                     0x92,0x82,0xF8,0x80,0x90,};/*0~9的数码管段码*/
  6. uchar hour=0,min=0,sec=0;                /*时、分、秒单元清零*/
  7. uchar deda=0;                                  /*5mS计数单元清零*/
  8. uchar t_hour=0,t_min=0;                   /*闹时时、分单元清零*/
  9. bit d_05s=0;                             /*0.5秒标志*/
  10. bit o_f=0;                                         /*闹时启/停标志*/
  11. uchar set=0;                                      /*模式设定标志*/
  12. uchar m=0;
  13. uchar flag=0;                                     /*RAM掉电标志*/
  14. void delay(uint k);                              /*延时子函数*/
  15. void conv();                                       /*走时单元转换*/
  16. void p_out();                                      /*判别闹时到否子函数*/
  17. void dirve();                                       /*走时时间输出驱动子函数*/
  18. void t_dirve();                                    /*闹时时间输出驱动子函数*/
  19. /*闹时启/停子函数*/
  20. void time1_of()                                 
  21. {uchar m;
  22.        if(P3_7==0)delay(1);
  23.        if(P3_7==0)o_f=1;
  24.        for(m=0;m<30;m++)
  25.               {
  26.               t_dirve();
  27.               P1=DATA_7SEG[t_min/10];P3=0xfd;delay(1);
  28.               if(P3_1==0){if(o_f==1)P1_7=0;}else P1_7=1;
  29.               delay(1);
  30.               }

  31.        if(P3_5==0)delay(1);
  32.        if(P3_5==0)    o_f=0;
  33.        for(m=0;m<30;m++)
  34.               {
  35.               t_dirve();
  36.               P1=DATA_7SEG[t_min/10];P3=0xfd;delay(1);
  37.               if(P3_1==0){if(o_f==1)P1_7=0;else P1_7=1;}
  38.               delay(1);
  39.               }
  40. }

  41. /*走时函数*/
  42. void time()
  43. {
  44. conv();                                              /*走时单元转换*/
  45. dirve();                                              /*走时时间输出驱动子函数*/
  46. p_out();                                             /*判别闹时到否子函数*/
  47. }
  48. /*定时器T0  5mS初始化*/
  49. void init_timer()
  50. {
  51. TMOD=0x01;
  52. TH0=-(4800/256);
  53. TL0=-(4800%256);
  54. IE=0x82;
  55. TR0=1;
  56. }

  57. /*扫描按键子函数*/
  58. void scan_key()
  59. {
  60. delay(1);
  61. if(P3_4==0)set++;
  62. if(set>=4)set=0;
  63. if(set==1)flag=0x55;
  64. F0:if(P3_4==0)goto F0;                      /*按键未释放,在此等候*/
  65. }

  66. /*延时子函数*/
  67. void delay(uint k)
  68. {
  69. uint i,j;
  70. for(i=0;i<K;I++){< DIV>
  71. for(j=0;j<121;j++)
  72. {;}}
  73. }

  74. /*5mS定时中断服务子函数*/
  75. void zd(void) interrupt 1
  76. {
  77. TH0=-(4800/256);
  78. TL0=-(4800%256);
  79. deda++;
  80. }

  81. /*调整走时时间*/
  82. void time_adj()
  83. {uchar m;
  84.        if(P3_5==0)delay(1);
  85.        if(P3_5==0)hour++;
  86.        if(hour==24)hour=0;
  87.        for(m=0;m<30;m++)
  88.               {
  89.               dirve();
  90.               if(P3_2==0)P1_7=0;
  91.               else P1_7=1;
  92.               delay(1);
  93.               }
  94.        if(P3_7==0)delay(1);
  95.        if(P3_7==0)min++;
  96.        if(min==60)min=0;
  97.        for(m=0;m<30;m++)
  98.               {
  99.               dirve();
  100.                      if(P3_2==0)P1_7=0;
  101.               else P1_7=1;
  102.               delay(1);
  103.               }
  104. }

  105. /*调整闹时时间*/
  106. void time1_adj()
  107. {uchar m;
  108.        if(P3_5==0)delay(1);
  109.        if(P3_5==0)t_hour++;
  110.        if(t_hour==24)t_hour=0;
  111.        for(m=0;m<30;m++)
  112.               {
  113.               t_dirve();
  114.               }   

  115.        if(P3_7==0)delay(1);
  116.        if(P3_7==0)t_min++;
  117.        if(t_min==60)t_min=0;
  118.        for(m=0;m<30;m++)
  119.               {
  120.               t_dirve();
  121.               }
  122. }     

  123. /*时、分、秒单元及走时单元转换*/
  124. void conv()
  125. {
  126. if(deda<=100)d_05s=0;
  127.        else d_05s=1;
  128. if(deda>=200){sec++;deda=0;}
  129. if(sec==60){min++;sec=0;}
  130. if(min==60){hour++;min=0;}
  131. if(hour==24){hour=0;}
  132. }

  133. /*走时时间输出驱动子函数*/
  134. void dirve()
  135. {
  136. P1=DATA_7SEG[hour/10];P3=0xf7;delay(1);
  137. P1=DATA_7SEG[hour%10];P3=0xfb;delay(1);
  138. if(d_05s==1){if(P3_2==0)P1_7=0;else P1_7=1;}
  139. delay(1);
  140. P1=DATA_7SEG[min/10];P3=0xfd;delay(1);
  141. if(o_f==1){if(P3_1==0)P1_7=0;else P1_7=1;delay(1);}
  142. P1=DATA_7SEG[min%10];P3=0xfe;delay(1);
  143. }

  144. /*闹时时间输出驱动子函数*/
  145. void t_dirve()
  146. {
  147. P1=DATA_7SEG[t_hour/10];P3=0xf7;delay(1);
  148. if(P3_3==0)P1_7=0;else P1_7=1;
  149. delay(1);
  150. P1=DATA_7SEG[t_hour%10];P3=0xfb;delay(1);
  151. P1=DATA_7SEG[t_min/10];P3=0xfd;delay(1);
  152. P1=DATA_7SEG[t_min%10];P3=0xfe;delay(1);
  153. }

  154. /*判别闹时到否子函数*/
  155. void p_out()
  156. {
  157. if(o_f==1){
  158.        if(t_hour==hour){if(t_min==min)
  159.               if(P3_0==0){P1_7=0;delay(1);}
  160.               else P1_7=1;
  161.                                    }
  162.               }
  163. }

  164. /*主函数*/
  165. void main()
  166. {
  167.        init_timer();                       /*定时器T0初始化*/
  168.        while(1)                                     /*无限循环*/
  169.        {
  170.        if(P3_4==0)scan_key();               /*有按键,调用按键扫描子函数*/
  171. switch(set)                                        /*根据set键值散转*/
  172. {
  173. case 0:time();break;                                   /*走时时间程序*/
  174. case 1:time1_adj();break;                     /*闹时时间调整*/
  175. case 2:time_adj();break;                      /*走时时间调整*/
  176. case 3:time1_of();break;                      /*启/停闹时*/
  177. default:break;                                     /*其它退出*/
  178. }
  179.        if(flag!=0x55)                             /*判断掉电标志*/
  180.        {for(m=0;m<100;m++)               /*点亮四个8字400mS*/
  181.               {
  182.               P1=0x80;P3=0xf7;delay(1);
  183.               P1=0x80;P3=0xfb;delay(1);
  184.               P1=0x80;P3=0xfd;delay(1);
  185.               P1=0x80;P3=0xfe;delay(1);
  186.               }           
  187.               P1=0xff;P3=0xff;delay(400); /*熄灭四个8字400mS*/
  188.        }
  189.        }
  190. }
复制代码
     
 楼主| 发表于 2007-11-27 12:42:22 | 显示全部楼层
没发现啊,第二个数码管下面的小数点是秒点。
     
发表于 2007-12-5 11:39:02 | 显示全部楼层

程序作了简单处理,直接编译肯定过不去的,不过只要编译一下就知道错误在哪了。

/*延时子函数*/
void delay(uint k)
{
uint i,j;
for(i=0;i<K;I++){< DIV>
for(j=0;j<121;j++)
{;}}
}
把这段里面的“<DIV>”去掉,楼主显然是直接从网页上粘过来的,所以才有这个符号——典型的“超(抄)文本”代码。
for(i=0;i<K;I++)这里面明显作了处理,KEIL是区分大小写的,所以编不过去。把K和I都改成小写就过去了。
楼主改回去重新编译一下就好了。
     
发表于 2007-12-5 14:03:27 | 显示全部楼层
晕死,我的都编过去了,你那怎么还有错?是不是把网页上的格式带过去了?还是你INCLUDE后面没加<REG2051.h>?

[ 本帖最后由 jyyang 于 2007-12-5 14:07 编辑 ]
     
发表于 2008-1-15 07:57:21 | 显示全部楼层

好贴
发表于 2008-2-5 17:31:17 | 显示全部楼层
我也奇怪,好多朋友提供的程序,我在编译的时候都过不了,却不知道哪的问题.楼主何不直接给个HEX,让我们这实用派也省点事
发表于 2008-2-5 18:12:33 | 显示全部楼层

回复 10# 的帖子

如果给你一个现成的可以简洁不少可是让你自己编译程序对你今后的单片机学习会有很大的帮助!
发表于 2008-2-11 16:00:13 | 显示全部楼层
关键是很多网页上提供的程序,经过编译后总是通不过,不知道差哪
     
发表于 2008-5-16 18:52:23 | 显示全部楼层

在#include 后面加 <AT89X051.H> 再按六楼的改正后程序可以通过。

但时间偏快,一个钟快5分钟左右。是不是要吧12M晶振改成11.0592M的啊。我正在实验。
发表于 2008-6-3 13:38:08 | 显示全部楼层
我试着在Keil C51 ver 8.90上完成编译和调试。但不知怎么,在用Protus进行模拟时发现四个LED根本看不清,是不是Protus对于分时扫描的动作显示不够速度啊?
发表于 2008-6-29 23:32:08 | 显示全部楼层
学无止境
     
发表于 2008-6-30 00:40:37 | 显示全部楼层
怎么KEIL出来只有69字节?
也太少了吧?
发表于 2008-6-30 12:04:12 | 显示全部楼层
原帖由 化身为海 于 2008-6-30 00:40 发表
怎么KEIL出来只有69字节?
也太少了吧?

我的HEX文件为2.8K,您的是怎么回事?不可能的。
     
发表于 2008-6-30 21:03:35 | 显示全部楼层
原帖由 绿洲 于 2008-6-30 12:04 发表

我的HEX文件为2.8K,您的是怎么回事?不可能的。  

搞错了,后来重新搞了,2.78K
     
发表于 2008-6-30 21:31:31 | 显示全部楼层
对这些不感兴趣!
只想问你那3D仿真用什么软件上出来的?
好像不是AD6!
也不是99
您需要登录后才可以回帖 登录 | 加入会员

本版积分规则

小黑屋|手机版|矿石收音机 ( 蒙ICP备05000029号-1 )

蒙公网安备 15040402000005号

GMT+8, 2024-5-2 14:45

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

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