- 1. 前言
- 1.1. 设计要求
- 2. 硬件原理
- 2.1. 时钟信号(晶振)
- 2.2. 按键开关
- 2.3. 数码管显示
- 3. 原理图
- 3.1. 仿真原理图
- 3.2. AD原理图
- 3.3. PCB图
- 4. 软件设计
- 4.1. 初版代码(无年月日)
- 4.2. 终版代码
- 5. 元器件清单
- 5.1. 仿真软件
- 5.2. 实物
在此之前我们已经学习了单片机的定时器、中断、数码管。这篇文章主要讲述如何用上述的知识自己制作一个基于51单片机的数字时钟。
1.1. 设计要求(1)主电路由秒信号发生器、“时、分、秒”计数器、译码器及显示器、校准电路等构成。
(2)秒信号发生器一般用石英晶体振荡器加分频器实现。
(3)译码电路将时、分、秒计数器的输出状态送七段译码器译码,经过八位LED七段共阴显示器显示出来。
(4)校时电路用来对时、分、秒显示数字进行校对。
单片机晶振部位电路,详情请参考《51单片机入门——单片机最小系统》,在此项目中我们选择 11.0592 MHz的晶振。
按键部分我们选择使用单片机的P3.0、P3.1、P3.2 三个 I/O 口 使用。
初版原理:按下开关K2“时”加一,按下开关K3“分”加一,按下开关K4“秒”加一。
终版原理:按下开关K3加一,按下开关K2控制按钮2的位移(例如:刚开始按K3无反应,按一下K2后秒加、再按分加),按下开关K4控制“时分秒”与“年月日”的切换。
2.3. 数码管显示在仿真软件中我们选择使用两个4位共阴极数码管(红色),实际电路中选择两个4位0.36英寸的红色共阴极数码管。
//该程序使用两个四位共阴数码管,10.0592Mhz晶振,STC89C52RC单片机,三个轻触开关 #include4.2. 终版代码#define uchar unsigned char #define uint unsigned int //单独控制某个引脚的电平不能直接写P3^0=1,必须先定义引脚 sbit key0 = P3^0 ; sbit key1 = P3^2 ; sbit key2 = P3^3 ; uchar s[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F}; //共阴数码管0~9 uchar a = 0 ,hour = 0 , min = 0 , sec = 0 ; uchar b = 0 , c = 0 , d = 0; void time0_server_() interrupt 1 // interrupt 0表示外部中断0 , 1表示定时器中断0 , 2表示外部中断1 , 3表示定时器中断1 , 4表示串口中断 { TH0 = 0x4c ; TL0 = 0x00 ; a++; if(a == 20) //20 * 50 = 1000ms = 1s 即每隔1s“秒”加1 { a = 0 ; sec = sec + 1 ; } if(sec == 60) //sec=60时清空,“分”加1 { min = min + 1 ; sec = 0 ; } if(min == 60) //min=60时清空,“时”加1 { hour = hour + 1 ; min = 0 ; } if(hour == 24) //hour=24时清空 { hour = 0 ; } } void init_t0() { TMOD = 0x01 ; //采用16位定时器 TH0 = 0x4C ; //50ms (65536-50000)/256 TL0 = 0x00 ; // (65536-50000)%256 EA = 1 ; //开启定时器0的中断 ET0 = 1 ; //开启总中断 TR0 = 1 ; //启动定时器0 } void delay(uint n) //延时函数(运行空项目以达到延迟时间的效果) { uint i,j; for(i = 0 ; i < n ; i ++) { for(j = 0 ; j < 120 ; j ++); } } void display1() //在数码管上显示 { uchar t0 = 0 ,t1 = 0 , t2 = 0 , t3 = 0 , t4 = 0 , t5 = 0 ; t0 = hour / 10 ; //t0表示时针的十位 t1 = hour % 10 ; //t1表示时针的个位 t2 = min / 10 ; //t2表示分针的十位 t3 = min % 10 ; //t3表示分针的个位 t4 = sec / 10 ; //t4表示秒针的十位 t5 = sec % 10 ; //t5表示秒针的个位 P2 = 0x7f ; //P2^7低电平 0111 1111 控制第一个数码管 P0 = s[t0] ; //用来显示“时”的十位 delay(1) ; //延时大约1ms P2 = 0xbf ; //P2^6低电平 1011 1111 控制第二个数码管 P0 = s[t1] ; //用来显示“时”的个位 delay(1) ; P2 = 0xdf ; //P2^5低电平 1101 1111 控制第三个数码管 P0 = 0x40 ; //显示“—”来分割“时”与“分” delay(1) ; P2 = 0xef ; //P2^4低电平 1110 1111 控制第四个数码管 P0 = s[t2] ; //用来显示“分”的十位 delay(1) ; P2 = 0xf7 ; //P2^3低电平 1111 0111 控制第五个数码管 P0 = s[t3] ; //用来显示“分”的个位 delay(1) ; P2 = 0xfb ; //P2^2低电平 1111 1011 控制第六个数码管 P0 = 0x40 ; //显示“—”来分割“分”与“秒” delay(1) ; P2 = 0xfd ; //P2^1低电平 1111 1101 控制第七个数码管 P0 = s[t4] ; //用来显示“秒”的十位 delay(1) ; P2 = 0xfe ; //P2^0低电平 1111 1110 控制第八个数码管 P0 = s[t5] ; //用来显示“秒”的个位 delay(1) ; } void KEY0() //开关0控制“时”加1 { if(key0 == 0 && c == 0) { c = 1 ; } if(key0 == 1 && c == 1) { c = 0 ; hour = hour + 1 ; } } void KEY1() //开关1控制“分”加1 { if(key1 == 0 && d == 0) { d = 1 ; } if(key1 == 1 && d == 1) { d = 0 ; min = min + 1 ; } } void KEY2() //开关2控制“秒”加1 { if(key2 == 0 && b == 0) { b = 1 ; } if(key2 == 1 && b == 1) { b = 0 ; sec = sec + 1 ; } } void main() //主函数 { init_t0(); while(1) { KEY0(); KEY1(); KEY2(); display1(); } }
#include5. 元器件清单 5.1. 仿真软件#define uchar unsigned char #define uint unsigned int sbit key1 = P3^0 ; sbit key0 = P3^1 ; sbit key2 = P3^2 ; sbit key3 = P3^3 ; uchar s[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F}; //共阴数码管0~9 uchar hour = 10 , min = 10 , sec = 0 ,day = 25 , month = 1 , year = 20 ; uchar a = 0 ,b = 0 , c = 0 , d = 0 , e = 0 ,f = 0 ; uchar b_1 = 0 ,c_1 = 0 ; void time0_server_() interrupt 1 { TH0 = 0x4c ; TL0 = 0x00 ; a++; e = 2000 + year ; if(a == 20) { a = 0 ; sec = sec + 1 ; } if(sec == 60) { min = min + 1 ; sec = 0 ; } if(min == 60) { hour = hour + 1 ; min = 0 ; } if(hour == 24) { day = day + 1 ; hour = 0 ; } switch(month) //根据月份来控制天数 { case 1: case 3: case 5: case 7: case 8: case 10: case 12:if(day == 32) { month = month + 1 ; day = 1 ; } break ; case 4: case 6: case 9: case 11:if(day == 31) { month =month + 1; day = 1 ; } break ; case 2:if(e%400==0||e%4==0&&e%100!=0) { if(day == 30) { month = month + 1 ; day = 1 ; } } else if(day == 29) { month = month + 1 ; day = 1 ; } break ; } if(month == 13) { year = year + 1 ; month = 1 ; } } void init_t0() { TMOD = 0x01 ; TH0 = 0x4C ; TL0 = 0x00 ; EA = 1 ; ET0 = 1 ; TR0 = 1 ; } void delay(uint n) //延时函数 大约1ms { uint i,j; for(i = 0 ; i < n ; i ++) { for(j = 0 ; j < 120 ; j ++); } } void display1() //显示 “时、分、秒” { uchar t0 = 0 ,t1 = 0 , t2 = 0 , t3 = 0 , t4 = 0 , t5 = 0 ; t0 = hour / 10 ; t1 = hour % 10 ; t2 = min / 10 ; t3 = min % 10 ; t4 = sec / 10 ; t5 = sec % 10 ; // P2 = 0x1c ; // P0 = s[t0] ; // delay(2) ; // P2 = 0x18 ; // P0 = s[t1] ; // delay(2) ; // P2 = 0x14 ; // P0 = 0x40 ; // delay(2) ; // P2 = 0x10 ; // P0 = s[t2] ; // delay(2) ; // P2 = 0x0c ; // P0 = s[t3] ; // delay(2) ; // P2 = 0x08 ; // P0 = 0x40 ; // delay(2) ; // P2 = 0x04 ; // P0 = s[t4] ; // delay(2) ; // P2 = 0x00 ; // P0 = s[t5] ; delay(2) ; P2 = 0x7f ; P0 = s[t0] ; delay(1) ; P2 = 0xbf ; P0 = s[t1] ; delay(1) ; P2 = 0xdf ; P0 = 0x40 ; delay(1) ; P2 = 0xef ; P0 = s[t2] ; delay(1) ; P2 = 0xf7 ; P0 = s[t3] ; delay(1) ; P2 = 0xfb ; P0 = 0x40 ; delay(1) ; P2 = 0xfd ; P0 = s[t4] ; delay(1) ; P2 = 0xfe ; P0 = s[t5] ; delay(1) ; } void display2() //显示 “年、月、日” { uchar t0 = 0 ,t1 = 0 , t2 = 0 , t3 = 0 , t4 = 0 , t5 = 0 ; t0 = year / 10 ; t1 = year % 10 ; t2 = month / 10 ; t3 = month % 10 ; t4 = day / 10 ; t5 = day % 10 ; // P2 = 0x1c ; // P0 = s[t0] ; // delay(2) ; // P2 = 0x18 ; // P0 = s[t1] ; // delay(2) ; // P2 = 0x14 ; // P0 = 0x40 ; // delay(2) ; // P2 = 0x10 ; // P0 = s[t2] ; // delay(2) ; // P2 = 0x0c ; // P0 = s[t3] ; // delay(5) ; // P2 = 0x08 ; // P0 = 0x40 ; // delay(2) ; // P2 = 0x04 ; // P0 = s[t4] ; // delay(2) ; // P2 = 0x00 ; // P0 = s[t5] ; // delay(2) ; P2 = 0x7f ; P0 = s[t0] ; delay(1) ; P2 = 0xbf ; P0 = s[t1] ; delay(1) ; P2 = 0xdf ; P0 = 0x40 ; delay(1) ; P2 = 0xef ; P0 = s[t2] ; delay(1) ; P2 = 0xf7 ; P0 = s[t3] ; delay(1) ; P2 = 0xfb ; P0 = 0x40 ; delay(1) ; P2 = 0xfd ; P0 = s[t4] ; delay(1) ; P2 = 0xfe ; P0 = s[t5] ; delay(1) ; } void KEY0() //控制按钮2的位移 { if(key0 == 0 && c == 0) { c = 1 ; } if(key0 == 1 && c == 1) { c = 0 ; c_1 = c_1 + 1 ; } if(c_1 == 4) { c_1 = 0 ; } } void KEY1() //控制 "秒"、“分”、“时”、“天”、“月”、“年” 的加1 { switch(c_1) { case 1: if(b_1 == 0) { if(key1 == 0 && d == 0) { d = 1 ; } if(key1 == 1 && d == 1) { d = 0 ; sec = sec + 1 ; } } else if(b_1 == 1) { if(key1 == 0 && d == 0) { d = 1 ; } if(key1 == 1 && d == 1) { d = 0 ; day = day + 1 ; } } break ; case 2: if(b_1 == 0) { if(key1 == 0 && d == 0) { d = 1 ; } if(key1 == 1 && d == 1) { d = 0 ; min = min + 1 ; } } else if(b_1 == 1) { if(key1 == 0 && d == 0) { d = 1 ; } if(key1 == 1 && d == 1) { d = 0 ; month = month + 1 ; } } break ; case 3: if(b_1 == 0) { if(key1 == 0 && d == 0) { d = 1 ; } if(key1 == 1 && d == 1) { d = 0 ; hour = hour + 1 ; } } else if(b_1 == 1) { if(key1 == 0 && d == 0) { d = 1 ; } if(key1 == 1 && d == 1) { d = 0 ; year = year + 1 ; } } break ; } } void KEY2() //控制 "秒"、“分”、“时”、“天”、“月”、“年” 的减1 { switch(c_1) { case 1: if(b_1 == 0) { if(key2 == 0 && f == 0) { f = 1 ; } if(key2 == 1 && f == 1) { f = 0 ; sec = sec - 1 ; if(sec == 255) { sec = 59 ; } } } else if(b_1 == 1) { if(key2 == 0 && f == 0) { f = 1 ; } if(key2 == 1 && f == 1) { f = 0 ; day = day - 1 ; if(day <= 1) { day = 1 ; } } } break ; case 2: if(b_1 == 0) { if(key2 == 0 && f == 0) { f = 1 ; } if(key2 == 1 && f == 1) { f = 0 ; min = min - 1 ; if(min == 255) { min = 0 ; } } } else if(b_1 == 1) { if(key2 == 0 && f == 0) { f = 1 ; } if(key2 == 1 && f == 1) { f = 0 ; month = month - 1 ; if(month <= 1) { month = 1 ; } } } break ; case 3: if(b_1 == 0) { if(key2 == 0 && f == 0) { f = 1 ; } if(key2 == 1 && f == 1) { f = 0 ; hour = hour - 1 ; if(hour == 255) { hour = 0 ; } } } else if(b_1 == 1) { if(key2 == 0 && f == 0) { f = 1 ; } if(key2 == 1 && f == 1) { f = 0 ; year = year - 1 ; if(year == 255) { year = 0 ; } } } break ; } } void KEY3() //控制“时、分、秒”与“年、月、日”的转换 { if(key3 == 0 && b == 0) { b = 1 ; } if(key3 == 1 && b == 1) { b = 0 ; b_1 = b_1 + 1; } if(b_1 == 2) { b_1 = 0 ; } switch(b_1) { case 0: display1() ; break ; case 1: display2() ; break ; } } void main() { init_t0(); while(1) { KEY0(); KEY1(); KEY2(); KEY3(); } }
数码管:7SEG-MPX4-CC
单片机:AT89C52
按键:BUTTON
排阻:RESPACK-8
| 名称 | 封装 | 数量 |
|---|---|---|
| STC89C52 | PDIP-40 | 1 |
| 排阻 4.7K | A472J | 1 |
| 10K色环电阻 | AXIAL0.5 | 1 |
| 0.36寸共阴数码管 | CL3631AH | 2 |
| 轻触开关 | DIP-6x6x6 | 4 |
| 30pF电容 | RAD-0.2 | 2 |
| 10uF电解电容 | DIP-EC2.0X5X11 | 1 |
| 11.0592MHz晶振 | OSC HC-49S | 1 |



