开篇先说一句废话····
本旺名字叫萨摩耶,,Please 叫我旺财,,,哈哈,招财进宝嘛!
开篇
这篇文章算是对之前所学的内容的综合应用,很多人都说能实现一个数字钟的时候算是正式入门单片机,或者是晋级成单片机的孙子等级。
项目分析
《电子数字钟》这篇是基于定时器,所以首先就有了定时器的应用,本想着在数码管上显示,但是既然学了LCD1602,刚好拿来用用。还有对独立键盘的使用。(除完整代码外,其余代码都是拼接在一起的,单独使用要注意)
- 第一步就是让LCD1602上显示一个初始化的日期+时间,这里就要用到LCD1602的初始化。
u8 Date[]=" 2020-12-18 5";
u8 Time[]=" 16:00:00";
void LcdInit(){
u8 i;
WriteCom(0x38);
WriteCom(0x0c);
WriteCom(0x06);
WriteCom(0x01);
WriteCom(0x80);
for(i=0;i<16;i++){
WriteData(Date[i]);
}
WriteCom(0x80+0x40);
for(i=0;i<12;i++){
WriteData(Time[i]);
}
}
- 接着就是让定时器开始工作,声明一个标志位,作为一个一秒的标志。
//定时器0初始化
TMOD=0x01;
TH0=(65536-9174)/256; //10ms
TL0=(65536-9174)%256;
EA=1;
ET0=1;
TR0=1;
void Time0() interrupt 1{
TH0=(65536-9174)/256;
TL0=(65536-9174)%256;
tt++;
if(tt==100){ //10ms*100=1s
tt=0;
SecondFlag=1;
}
}
- 一秒时间是出来了,接着就是一秒发生后对时间的处理了。秒不断自加,如果秒到了60要向分进位;而分到了60就要想时进位了;时到了24就要想天进位,天满的情况有四种(28(不闰年2月),29(闰年2月),30(4,6,8,11),31(1,3,5,7,8,10,12)),先对年进行判断,来决定2月有多少天。每次进完位都在在月的位置写入(月份变化了)新的数据
//闰年判断
u8 LeapYear(u16 year){
if(((year%4==0) && (year%100!=0)) || (year%400))
return 1; //闰年
else
return 0;
}
if(mouth==2){
YearFlag=LeapYear(year);
if((YearFlag==1) && (day==30)){
mouth++;
day=1;
WriteLcd(7,mouth);
}
if((YearFlag==0) && (day==29)){
mouth++;
day=1;
WriteLcd(7,mouth);
}
}
2月完了,接着是对31和30天的月份判断处理,如果月份刚好和天数对应那表示当月结束,月份加一,天数归1.要注意的是31天的月份里如果是 12月的话,要向年进位1. 并且写入年的位置新数据。
if((mouth==1) || (mouth==3)|| (mouth==5)|| (mouth==7)|| (mouth==8)|| (mouth==10)|| (mouth==12)){
if(day==32){
mouth++;
day=1;
WriteLcd(7,mouth);
if(mouth==13){
year++;
WriteLcd(2,year/100);
WriteLcd(4,year%100);
mouth=1;
WriteLcd(7,mouth);
day=1;
WriteLcd(10,day);
}
}
}
if((mouth==4) || (mouth==6)|| (mouth==9)|| (mouth==11)){
if(day==31){
mouth++;
day=1;
WriteLcd(7,mouth);
}
}
还要注意每次秒,分,时变化的时候分别对各自的位置使用 WriteLcd() 写入新的数据。而且初始化秒分时的值要和第一次在LCD1602上显示的一致。
if(SecondFlag==1){
SecondFlag=0;
second++;
if(second==60){
second=0;
minute++;
if(minute==60){
minute=0;
hour++;
if(hour==24){
hour=0;
day++;
week++;
if(week==8)
week=1;
WriteCom(0x80+15);
WriteData(0x30+week);
WriteLcd(10,day);
}
WriteLcd(0x40+4,hour);
}
WriteLcd(0x40+7,minute);
}
WriteLcd(0x40+10,second);
}
//时分秒天月年周的初始化
hour=16;
minute=00;
second=00;
year=2020;
mouth=12;
week=5;
day=17;
- 时间可以正常走了,那接着就是对时间的调整,就是对按键的处理,首先就是按键检测
//按键处理
void KeyDown(){
if(k1==0){
Delayms(50);
if(k1==0){
//k1功能
K1Pross();
}while(!k1);
}
if(k2==0){
Delayms(50);
if(k2==0){
//k2功能
K2Pross();
}while(!k2);
}
if(k3==0){
Delayms(50);
if(k3==0){
//k3功能
K3Pross();
}while(!k3);
}
if(k4==0){
Delayms(50);
if(k4==0){
//k4功能
K4Pross();
}while(!k4);
}
}
- 按键检测完之后就是进入每个按键的功能,K1的功能就是对修改时间或者修改日期的设置,要注意的就是设置之后还要注意光标闪烁初始位置的设置,(也是对某位的设置)
//K1功能处理
void K1Pross(){
TR0=0;
flag++;
if(flag>2)
flag=1;
if(flag==1){ //flag=1 表示对时间的设定
SetFlag=1;
SetYearFlag=0;
SiteFlag=0;
WriteCom(0x80+0x44+SiteFlag);
WriteCom(0x0f);
}
else{ //flag=2 表示对日期的设定
SetYearFlag=1;
SetFlag=0;
SiteYearFlag=0;
WriteCom(0x80+0x08+SiteYearFlag);
WriteCom(0x0f);
}
}
- K2功能的设置,K2用来切换设置位置,(是要设置时呀还是分啥的),因为有个变量来控制这个位置,只需要注意跳过用来分割每个数值的“-”或者“:”,做法就是判断如果到达分割位自加一或者二跳过即可。
//K2功能处理
void K2Pross(){
if(SetFlag==1){
SiteFlag++;
if(SiteFlag==8)
SiteFlag=0;
if((SiteFlag==2) || (SiteFlag==5))
SiteFlag++;
WriteCom(0x80+0x44+SiteFlag);
}
if(SetYearFlag==1){
SiteYearFlag++;
if(SiteYearFlag==4)
SiteYearFlag=0;
if(SiteYearFlag==1)
SiteYearFlag+=2;
WriteCom(0x80+0x08+SiteYearFlag);
}
}
- K3功能的设置,K3则是在当前位置的数值自加一,虽然是自加一,但是要判断那位自加1,判断是否要进位,判断方法和时间进位的那些方法差不多,主需要相对应的修改一些即可。
//K3功能处理
void K3Pross(){
if(SetFlag==1){
switch(SiteFlag){
case 0:{hour+=10;
WriteLcd(0x44+SiteFlag,hour);
WriteCom(0x10);
WriteCom(0x10);
break;}
case 1:{hour+=1;
WriteLcd(0x43+SiteFlag,hour);
WriteCom(0x10);
break;}
case 3:{minute+=10;
WriteLcd(0x44+SiteFlag,minute);
WriteCom(0x10);
WriteCom(0x10);
break;}
case 4:{minute+=1;
WriteLcd(0x43+SiteFlag,minute);
WriteCom(0x10);
break;}
case 6:{second+=10;
WriteLcd(0x44+SiteFlag,second);
WriteCom(0x10);
WriteCom(0x10);
break;}
case 7:{second+=1;
WriteLcd(0x43+SiteFlag,second);
WriteCom(0x10);
break;}
}
}
if(SetYearFlag==1){
switch(SiteYearFlag){
case 0:{
mouth++;
if(mouth==13){
mouth=1;
year++;
WriteLcd(2,year/100);
WriteLcd(4,year%100);
}
WriteLcd(7,mouth);
if(mouth==3){ //从2月加过去
YearFlag=LeapYear(year);
if(YearFlag==1){
week+=(29%7);
if(week>7)
week=week-7;
WriteCom(0x80+15);
WriteData(0x30+week);
}
if(YearFlag==0){
week+=(28%7);
if(week>7)
week=week-7;
WriteCom(0x80+15);
WriteData(0x30+week);
}
}
if((mouth==2) || (mouth==4)|| (mouth==6)|| (mouth==8)|| (mouth==9)|| (mouth==11)|| (mouth==1)){
week+=(31%7);
if(week>7)
week=week-7;
WriteCom(0x80+15);
WriteData(0x30+week);
}
if((mouth==5) || (mouth==7)|| (mouth==10)|| (mouth==12)){
week+=(30%7);
if(week>7)
week=week-7;
WriteCom(0x80+15);
WriteData(0x30+week);
}
break;
}
case 3:{
day++;
week++;
if(week==8)
week=1;
WriteCom(0x80+15);
WriteData(0x30+week);
if(mouth==2){
YearFlag=LeapYear(year);
if((YearFlag==1) && (day==30)){
mouth++;
day=1;
WriteLcd(7,mouth);
}
if((YearFlag==0) && (day==29)){
mouth++;
day=1;
WriteLcd(7,mouth);
}
}
if((mouth==1) || (mouth==3)|| (mouth==5)|| (mouth==7)|| (mouth==8)|| (mouth==10)|| (mouth==12)){
if(day==32){
mouth++;
day=1;
WriteLcd(7,mouth);
if(mouth==13){
year++;
WriteLcd(2,year/100);
WriteLcd(4,year%100);
mouth=1;
WriteLcd(7,mouth);
day=1;
WriteLcd(10,day);
}
}
}
if((mouth==4) || (mouth==6)|| (mouth==9)|| (mouth==11)){
if(day==31){
mouth++;
day=1;
WriteLcd(7,mouth);
}
}
WriteLcd(10,day);
WriteCom(0x10);
break;
}
}
}
}
- K4功能的设定,K4功能比较简单,就是开启定时器开始工作,还需要注意的就是判断每个位置的数值是否符合要求,如果不符合强制归为初值,避免发生错误。
//K4功能处理
void K4Pross(){
if(hour>23)
hour=0;
WriteLcd(0x40+4,hour);
if(minute>59)
minute=0;
WriteLcd(0x40+7,minute);
if(second>59)
second=0;
WriteLcd(0x40+10,second);
SetFlag=0;
SiteFlag=0;
SetYearFlag=0;
SiteYearFlag=0;
WriteCom(0x0c);
TR0=1;
}
- 对之前功能的调用(主函数),首先就是初始化,分为初始化参数和初始化定时器,初始化完就是不断地检测按键和时间的处理。
void main(){
Init();
while(1){
KeyDown();
TimePross();
}
}
总结
大概的思路以及步骤就在上面了,如果阅读起来有问题,我建议单个看,也就是先调试时间,在调试日期,这样你会发现,时间和日期就是用一个标志位加各自的标志位(就三个标志位)区分的,这个就比较容易阅读了,若想自己写,可以观看 郭天祥《新概念51单片机C语言教程》视频。
完整代码贴在这不方便,所以我打包上传了,链接在下面,为了避免掏钱,我设置了粉丝可下载,如果你觉得我写的还不错,还看得过去,就点个赞关注一下,就可以下载了;如果不想这样,也可以先关注然后下载后取关就好,都是为了学习知识。谢谢各位。