一、硬件电路 六位数字钟仿真原理图如下(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-17 平年2月28日 图6-18 设定当天23:59:49 图6-19 24点后日
单片机源程序如下:
- #include<reg51.h> //包含51单片机头文件
- #include<intrins.h> //包含标准函数库
- sbit P_alarm =P1^6; //闹钟开启与关闭指示灯,低电平有效,灭代表闹钟关闭
- sbit P_alarming=P1^7; //闹铃指示灯,到时则亮,低电平有效
- unsigned char code dis_7[10]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};
- //七段共阴显示数码管“0~9”阴码
- unsigned char data time[3]={23,59,58};
- //时间,从左到右分别为“小时、分钟、秒”
- unsigned char data clock[3]={23,0,0};
- //闹钟,从左到右分别为“小时、分钟、秒”
- unsigned char data date[4]={20,18,02,20};
- //日期, 从左到右分别为“年前两位、年后两位、月、日”
- unsigned char data day[12]={31,28,31,30,31,30,31,30,31,30,31,30};
- //12个月对应的天数
- unsigned char data *p; //指针变量,指向当前显示地址
- unsigned char data mcs,ms_10; //计数器,mcs每250us加1次记录40次,ms_10记录mcs 100次
- unsigned char data position=1; //编辑模式下,记录当前修改位“1~6”分别表示数码管右到左标号
- unsigned char data screen=1; //显示模式下,记录当前显示屏幕功能,如下:“1”代表time,“2”代表clock,“3”代表date
- unsigned int data fliker; //编辑模式下,修改位闪烁功能定时计数器
- unsigned int data flow; //显示模式下,日期流动计数器
-
- bit edit; //“0”代表显示模式,“1”代表编辑模式
- bit day_flag=0; //24点标志位
- bit disp_num; //显示数据位数切换,“0”代表显示6位,即时间,“1”代表显示日期
- bit alarm,alarming; //闹钟状态和闹铃状态标志位
-
-
- void display(unsigned char *p); //显示函数
- void clock_c(); //闹钟功能程序
- void date_c(); //日期程序
- unsigned char keyscan(); //键盘扫描程序,另外,K1长按进行模式切换
- void key2_function_d(); //显示状态,按键2功能程序,闹钟取消\启动,闹铃消停
- void key2_function_e(); //编辑状态,按键2功能程序,加1
- void key1_function_d(); //显示状态,按键2功能程序,显示切换
- void key1_function_e(); //编辑状态,按键2功能程序,修改位切换
- void delay(unsigned int i); //延时函数
-
-
-
- void main()
- {
- unsigned char keyboard,key;
- TMOD=0x02; //设置定时器0工作方式为2
- TL0=0x06; //设置低八位初始值
- TH0=0x06; //设置高八位初始值
- EA=1; //开启总中断
- ET0=1; //开启定时器0中断允许
- TR0=1; //启动定时起0
- p=time; //指向时间地址
- while(1)
- {
- keyboard=P1&0x03; //读取按键的值,并且屏蔽无关位
- display(p); //调用显示函数
- clock_c(); //调用闹钟函数
- date_c(); //调用日期函数
- if((screen==1)&&(edit==1))TR0=0;else TR0=1;
- //修改时间时,关闭定时器
- if(keyboard!=0x03) //若有按键按下,则执行以下程序
- {
- display(p); //显示函数消除按键抖动
- if(keyboard!=0x03) //再次检测,若有,则执行以下程序
- {
- key=keyscan(); //调用按键扫描程序,读取按键值
- if(!edit) //显示模式
- {
- switch(key) //如果KEY1按下,则KEY1为1,若KEY2按下则为2
- {
- case 1:key1_function_d();break;//显示屏幕切换
- case 2:key2_function_d();break;//闹钟功能设置,闹铃取消
- default:break;
- }
- }
- else //编辑模式
- {
- switch(key) //如果KEY1按下,则KEY1为1,若KEY2按下则为2
- {
- case 1:key1_function_e();break;//修改位切换
- case 2:key2_function_e();break;//加1
- default:break;
- }
- } } } } }
- void display(unsigned char *p) //显示子程序,显示参数为无符号字符型指针变量
- {
- unsigned char disp[8]={0,0,0,0,0,0,0,0};
- //显示数据缓存变量
- unsigned char disp_com[6]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf};
- //数码管位控端分别代表从左到右“1~6”号数码管
- unsigned char a,b,d,dot;
- unsigned char c=0;
- if(!disp_num)a=6;else a=8; //显示时间则a=6,否则a=8
- for(b=0;b<a;b++) //将显示数据按位缓存与disp数据中
- {
- if(c==0)disp[b]=p[b/2]/10;
- else disp[b]=p[b/2]%10;
- c++;if(c==2)c=0;
- }
- if(!edit) //显示模式
- {
- if(!disp_num) //显示时间
- {
- for(b=0;b<6;b++)
- {
- if(b==1||b==3)dot=128;else dot=0;
- //1为小时个位,3为分钟个位,其后面加小数点
- P0=0xff; //消影
- P2=~dis_7[disp[b]]+dot;//段码刷新
- P0=disp_com[b]; //位选端有效,显示输出
- delay(5); //延时
- }
- }
- else //显示日期
- {
- b=flow/50; //flow最大为400,每显示50次流动一位
- for(c=0;c<6;c++)
- {
- d=c+b;if(d>7)d-=8; //每次显示6位,但流动显示起始位,第9位时返回第1位显示
- if(d==3||d==5||d==7)dot=128;else dot=0;
- //年份、月份、日期个位加小数点
- P0=0xff; //消影
- P2=~dis_7[disp[d]]+dot;//断码刷新
- P0=disp_com[c]; //输出位选端
- delay(5); //延时
- }
- flow++; //每显示1次屏幕加1;
- if(flow==400)flow=0; //最大399
- }
- }
- else //编辑模式
- {
- fliker++;if(fliker>100)fliker=0;//修改位显示闪烁功能
- if(!disp_num) //显示时间
- {
- for(b=0;b<6;b++)
- {
- if(b==1||b==3)dot=128;else dot=0;
- P0=0xff;
- P2=~dis_7[disp[b]]+dot;
- if(b==(6-position)) //修改位闪烁,position=1代表修改第6位数码管,最右边数码管
- {
- if(fliker<50)P0=disp_com[b]; //亮
- else P0=0xff; //灭
- }
- else P0=disp_com[b];
- delay(5);
- }
- }
- else //显示日期
- {
- if(position>6)b=0;else b=2; //显示日期移动,起始显示状态“年,月”
- for(c=0;c<6;c++)
- {
- d=c+b; //起始位置为disp[0或者2]
- if(d==3||d==5)dot=128;else dot=0;
- P0=0xff;
- P2=~dis_7[disp[d]]+dot;
- if(c==(6-position)||(c==(8-position)&&(position>6)))
- //修改位闪烁,年最大位开始修改(显示“年、月”||显示“年、月、日”)
- {
- if(fliker<50)P0=disp_com[c];
- else P0=0xff;
- }
- else P0=disp_com[c];
- delay(5);
- } } } }
- void clock_c() //闹钟子程序
- {
- if(alarm) //开启闹钟则允许以下程序
- {
- int a,b,c;
- a=time[0]-clock[0];
- b=time[1]-clock[1];
- c=time[2]-clock[2]; //比较闹钟时间和当前时间
- if((a==0)&&(b==0)&&(c==0)) //时间到
- alarming=1; //启动闹铃
- if(alarming) //闹铃10秒自动取消
- {
- if((b*60+c)>=10)alarming=0;
- }
- }
- P_alarm=!alarm; //闹钟状态输出
- P_alarming=!alarming; //闹铃状态输出
- }
-
- void date_c() //日期子程序
- {
- unsigned char a,b,c,d;
- d=date[0]*100+date[1];
- a=23-time[0];
- b=59-time[1];
- c=59-time[2]; //是否到24点
-
- if((d%400==0)||((d%100!=0)&&(d%4==0))) //闰年
- { day[1]=29;}
- else
- { day[1]=28;if(date[3]>28)date[3]=28;} //平年
-
- if((a==23)&&(b==59)&&(c==59)&&(day_flag==0)) //日期记时,24整点运行1次
- {
- date[3]++; //日加1
- if(date[3]>day[date[2]-1]) //月末
- {
- date[3]=1; //日归1
- date[2]++; //月份加1
- if(date[2]>12) //年末
- {
- date[2]=1; //月归1
- date[1]++; //年后两位加1
- if(date[1]>99) //99年进位
- {
- date[0]++; //年前两位加1
- date[1]=0; //年后两位归零
- if(date[0]>99) //最大9999年
- date[0]=0;
- }
- }
- }
- day_flag=1; //执行1次标志位置1,防止重复执行
- }
- if(day_flag){if(c!=59)day_flag=0;} //24整点过后,整点标志位复位
- }
-
- unsigned char keyscan() //键盘扫描子程序,无符号字符型子程序,返回键号
- {
- unsigned int a;
- unsigned char temp;
- temp=P1&0x03; //读取按键输入,屏蔽无关位
- for(a=0;a<100&&((P1&(0x07))==0x06);a++)
- //按键延时功能
- {
- display(p); //调用显示程序
- if(a==99) //延时阈值
- {
- edit=!edit;
- if(screen==3)position=8;//进入编辑模式初始状态设置
- else position=1;a=3;temp=0;P0=0xff;delay(1000);
- //进入编辑模式,则返回值为3不调用任何按键程序
- while((P1&(0x07))==0x06)display(p);
- //按键松开
- }
- }
- switch(temp) //根据按键状态返回键号
- {
- case 0x06:a=1;break;
- case 0x05:a=2;break;
- default:break;
- }
- if(edit)while((P1&0x07)!=0x07)display(p);//按键松开
- return(a); //返回键号
- }
-
- void key2_function_d() //按键2,显示状态,闹钟启动/停止,闹铃消停
- {
- unsigned int a;
- if(!edit) //显示模式,执行以下程序
- {
- for(a=0;(a<100)&&((P1&(0x07))==0x05);a++)
- //按键延时功能
- {
- display(p); //调用显示程序
- if(a==99) //到达延时阈值,执行以下程序
- {
- alarm=!alarm; //开启或取消闹钟
- P_alarm=!alarm; //立即输出反馈闹钟状态
- while((P1&(0x07))==0x05)display(p);//按键松开
- }
- }
- if(alarm) //闹钟开启
- {
- if(alarming)alarming=0; //手动取消,显示模式下短按取消闹铃
- }else alarming=alarm; //闹钟关闭状态 }
- }
-
- void key2_function_e() //编辑模式,按键2,加1功能
- {
- if(edit)
- {
- if(!disp_num) //显示时间,则执行以下程序
- {
- switch(position) //根据位置进行加1
- {
- case 1:if(p[2]%10<9)p[2]++;else p[2]-=9 ;break; //秒个位
- case 2:if(p[2]<50)p[2]+=10;else p[2]-=50;break; //秒十位
- case 3:if(p[1]%10<9)p[1]++;else p[1]-=9 ;break; //分钟个位
- case 4:if(p[1]<50)p[1]+=10;else p[1]-=50;break; //分钟十位
- 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; //小时个位
- 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; //小时十位
- default: break;
- }
- }
- else //显示日期,则执行以下程序
- {
-
- switch(position) //根据位置进行加
- {
- case 8:if(p[0]<99) p[0]+=10;else p[0]%=10;break; //年千位
- case 7:if(p[0]%10<9)p[0]++; else p[0]-=9;break; //年百位
- case 6:if(p[1]<99) p[1]+=10;else p[1]%=10;break; //年十位
- case 5:if(p[1]%10<9)p[1]++; else p[1]-=9;break; //年个位
- case 4:p[3]=1;if(p[2]==1)p[2]=10;else p[2]=1;break; //若进行月份的调整,需要重新设置日期,调整月份十位代表10以上和10以下切换
- 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; //调整月份个位
- case 2: //日期的十位
- p[3]+=10; //日期的十位加1
- if(p[3]>day[p[2]-1]) //如果超过月末
- {
- p[3]%=10; //保留日期个位
- if(p[3]==0)p[3]=10; //十位归零
- }break;
- case 1: //日期的个位
- if((p[3]/10)==(day[p[2]-1]/10)) //如果与月末十位相
- 等则执行以下程序
- {
- if(p[3]==day[p[2]-1]){ p[3]=p[3]-(day[p[2]-1]%10);}
- //达到月末,减去月末个位数
- else
- p[3]++; //日期加1
- }
- else //不与月末十位相等
- {
- if(p[3]%10<9)p[3]++; //最大值为9
- else
- {
- p[3]-=9;
- if(p[3]==0)p[3]=1; //日期为0时则为1
- }
- }break;
- default: break;
- }
- }
- }
- }
-
- void key1_function_d() //按键1,显示状态,显示切换
- {
- if(!edit) //显示状态下
- {
- screen++; //每调用1次,加1,范围“1~3”
- switch(screen)
- {
- case 1:p=time;disp_num=0;break; //显示时间
- case 2:p=clock;disp_num=0;break; //显示闹钟
- case 3:p=date;disp_num=1;break; //显示日期
- default:p=time;disp_num=0;screen=1;break; //显示时间
- }
- }
- }
-
- void key1_function_e() //按键1,编辑模式下,移位
- {
- if(edit) //编辑模式下执行以下程序
- {
- if(!disp_num) //显示时间状态
- {
- position++;
- if(position>6)position=1; //position范围“1~6”
- }else //显示日期
- {
- position--; //从年千位开始修改
- if(position==0||position>8)
- position=8; //范围“1~8”
- }
- }else position=1; //复位
- }
- void delay(unsigned int i) //延时程序
- {
- unsigned int k;
- for(;i>0;i--)
- for(k=0;k<54;k++);
- }
- void time_int0() interrupt 1 //定时中断,中断方式2
- {
- EA=0; //关闭总中断,防止高级中断影响定时
- if(mcs!=0x28) //10ms定时
- mcs++;
- else
- {
- mcs=0;
- if(ms_10!=100)ms_10++ ; //1s定时
- else
- {
- ms_10=0;
- if(time[2]!=59)time[2]++; //1s到则秒位加1
- else
- {
- time[2]=0; //60秒归0
- if(time[1]!=59)time[1]++;//分钟加1
- else
- {
- time[1]=0; //60分钟归0
- if(time[0]!=23)time[0]++; //小时加1
- else time[0]=0; //24小时归0
- }
- }
- }
- }
- EA=1; //开放总中断
- }
复制代码
所有资料51hei提供下载:
时钟.zip
(4.86 MB, 下载次数: 208)
|