51单片机应用篇-- --电子数字钟(LCD1602显示)

开篇先说一句废话····
本旺名字叫萨摩耶,,Please 叫我旺财,,,哈哈,招财进宝嘛!

开篇

这篇文章算是对之前所学的内容的综合应用,很多人都说能实现一个数字钟的时候算是正式入门单片机,或者是晋级成单片机的孙子等级。

项目分析

《电子数字钟》这篇是基于定时器,所以首先就有了定时器的应用,本想着在数码管上显示,但是既然学了LCD1602,刚好拿来用用。还有对独立键盘的使用。(除完整代码外,其余代码都是拼接在一起的,单独使用要注意

  1. 第一步就是让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]);
	}
}
  1. 接着就是让定时器开始工作,声明一个标志位,作为一个一秒的标志。
//定时器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;
	}
}
  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;
  1. 时间可以正常走了,那接着就是对时间的调整,就是对按键的处理,首先就是按键检测
//按键处理
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);
	}
}
  1. 按键检测完之后就是进入每个按键的功能,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);
	}
}
  1. 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);
	}
}
  1. 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;
			}
		}
	}
}
  1. 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;
}

  1. 对之前功能的调用(主函数),首先就是初始化,分为初始化参数和初始化定时器,初始化完就是不断地检测按键和时间的处理。
void main(){
	Init();
	while(1){
		KeyDown();
		TimePross();
	}
}

总结

大概的思路以及步骤就在上面了,如果阅读起来有问题,我建议单个看,也就是先调试时间,在调试日期,这样你会发现,时间和日期就是用一个标志位加各自的标志位(就三个标志位)区分的,这个就比较容易阅读了,若想自己写,可以观看 郭天祥《新概念51单片机C语言教程》视频。

完整代码贴在这不方便,所以我打包上传了,链接在下面,为了避免掏钱,我设置了粉丝可下载,如果你觉得我写的还不错,还看得过去,就点个赞关注一下,就可以下载了;如果不想这样,也可以先关注然后下载后取关就好,都是为了学习知识。谢谢各位。

实验:数字钟–基于定时器

  • 51
    点赞
  • 106
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
下面是一个简单的示例代码,可以在51单片机上驱动LCD1602显示时间,并具有闹钟功能。你可以根据需要进行修改和优化。 ``` #include <reg52.h> #include <intrins.h> #define LCD_DATA P0 sbit LCD_RS = P2^0; sbit LCD_RW = P2^1; sbit LCD_EN = P2^2; unsigned char hour = 0, minute = 0, second = 0; unsigned char alarm_hour = 0, alarm_minute = 0; bit alarm_flag = 0; void delay(unsigned int i) { while(i--); } void write_command(unsigned char command) { LCD_RS = 0; LCD_RW = 0; LCD_DATA = command; LCD_EN = 1; _nop_(); _nop_(); LCD_EN = 0; delay(5); } void write_data(unsigned char data) { LCD_RS = 1; LCD_RW = 0; LCD_DATA = data; LCD_EN = 1; _nop_(); _nop_(); LCD_EN = 0; delay(5); } void init_lcd() { write_command(0x38); // 8位数据接口,2行显示,5x7点阵字符 write_command(0x0c); // 显示开,光标不显示 write_command(0x06); // 光标右移,字符不移动 write_command(0x01); // 显示清屏 } void display_time() { write_command(0x80); // 第一行第1个字符 write_data(hour/10+'0'); write_data(hour%10+'0'); write_data(':'); write_data(minute/10+'0'); write_data(minute%10+'0'); write_data(':'); write_data(second/10+'0'); write_data(second%10+'0'); write_command(0xc0); // 第二行第1个字符 write_data('A'); write_data('l'); write_data('a'); write_data('r'); write_data('m'); write_data(':'); write_data(alarm_hour/10+'0'); write_data(alarm_hour%10+'0'); write_data(':'); write_data(alarm_minute/10+'0'); write_data(alarm_minute%10+'0'); } void timer0_init() { TMOD &= 0xf0; TMOD |= 0x01; // 16位定时器 TH0 = 0xfc; // 1ms TL0 = 0x18; ET0 = 1; TR0 = 1; } void interrupt_timer0() interrupt 1 { TH0 = 0xfc; // 1ms TL0 = 0x18; second++; if (second >= 60) { second = 0; minute++; } if (minute >= 60) { minute = 0; hour++; } if (hour >= 24) { hour = 0; } if (hour == alarm_hour && minute == alarm_minute) { alarm_flag = 1; } display_time(); } void main() { init_lcd(); timer0_init(); while (1) { if (alarm_flag) { // 响铃操作 alarm_flag = 0; } } } ``` 以上代码使用定时器0中断来实现时间的更新和显示,每1ms中断一次,计算秒、分、时并在LCD上显示。同时,将闹钟设定的时间与当前时间进行比较,如果相等,则设置闹钟标志位,等待响铃操作。 在实际应用中,你可能需要加入更多的功能,如按键设置时间、闹钟等,这需要根据具体情况进行修改和扩展。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值