51单片机LCD1602显示电子时钟 带按键设置和星期显示


当时看书看到lcd那章,就想利用定时器做一个电子时钟,当时也不知道有时钟模块的,以为都是定时器定时产生的时间,也是一个main文件从头到尾的版本,注释倒不少,不要说什么模块化编程和代码优化,想到什么就写什么,业余的就是可以随便写,所以思路肯定比别人的清楚明白,随便谁都看得懂,当然后来就把自己写的例程都自己做了个c51的库,傻瓜型的库,以后有空想到就慢慢记录下来

#include <reg52.h>
#include <string.h>

#define uchar unsigned char
#define uint unsigned int

//sbit u1la=P2^6;        		//U1锁存器  为1则打开锁存器 为0则关闭锁存器
//sbit u2la=P2^7;     	 		//U2锁存器
sbit lcdrs=P2^5;          //lcd数据命令选择端  rs为L(ledrs=0)则写命令 H(ledrs=1)则写数据
sbit lcdrw=P2^6;
sbit lcden=P2^7;          //lcd使能   lcden=1 则写入 

//sbit key1=P3^0;         //矩阵键盘中第4列中从上到下  第一个按键轮流3种状态 按一下小时闪烁 分钟闪烁 不闪烁则继续加秒数
//sbit key2=P3^1;          //         								第二个按键增加当前选中的秒 小时或者 分钟 等
//sbit key3=P3^2;           //         								第三个按键减少当前选中的数字
//sbit rd=P3^7;							//置低电平后分离出第4列四个独立按键
sbit key1=P3^6;      //独立 按键  由于P34 P35端口已经被lcd占用  所以只有两个独立按键可以用
sbit key2=P3^7;
sbit sbeep=P2^3;        //蜂鸣器

uchar code table1[]="   2019-08-05";
uchar code table2[]="    00:00:00";
int code month[12]={31,28,31,30,31,30,31,31,30,31,30,31};   //每个月天数  第二月有两种情况 运用时闰年判断下后处理
char code *week[7]={"SUN","MON","TUE","WED","THU","FRI","SAT"};       //每周一天英文缩写  元素从0 星期天开始
void delayms(uint ms) ; 												 //延迟子函数
void display(uchar *table,uchar line);           //显示字符串子函数  table为字符串,最大16字符  line为显示的行数,1为第一行 2为第二行  函数目前不容错
void write_com(uchar cmd);                       //lcd写命令子函数  一次写入一个命令 例如0x80
void write_data1(uchar);										 //lcd写数据子函数 一次写入一个字符
void write_data(uchar ,uchar , uchar );        //写数据 第几行 第几个开始  数据  第三个参数必须是1-2位数
void init();                                     //初始化函数
void day_add();																	//增加一天后处理函数
void calweek();																		//计算某一天星期几并刷新显示
void mybeep();																		//蜂鸣器
void keyscan();																	//检测 处理按键
void settime_add();                                  //按键修改时间后刷新时间
void settime_minus();																//修改时间
void cursor_blink();                                  //判断blink状态 更新光标闪烁
uchar num0;																						//num0为定时器0中断次数 
char months,weeks, days, hours,minutes,seconds,comparemonth,m;    //月 周几 日 小时 分钟 秒 每月天数 m用来公式计算周几
char blink;                                      //第一个设置按键状态 循环7中状态 秒闪烁  分钟闪烁 小时闪烁 月 日 年 不闪烁
unsigned int years;                                      //年份超过255数字
                                                                                                                                        
void main()
{
	init();
	display(table1,1);
	display(table2,2);
	while(1)
	{
		keyscan();
		delayms(5);
		cursor_blink();
	}
}

void init()
{
		TMOD = 0x01;								//定时器1工作方式1 定时器0工作方式1   定时器1在高四位
		TH0=(65536-45872)/256;			//定时器0  16位计数器高8位初始计数
		TL0=(65536-45872)%256;			//定时器0  16位计数器低8位初始计数
		EA=1;												//中端使能  中端总开关
		ET0=1;											//定时器0 中断允许位 
		TR0=1;											//初始中断开始启动				
//		rd=0;										//初始化rd******此处用来分离矩阵键盘独立按键第四列*******
//		u1la=0;             //关闭U1 
//		u2la=0;             //关闭U2
		lcden=0;            //lcd关闭写入
		lcdrw=0;
		years=2019;         //初始化显示  2019/08/05 程序开始测试的日子
		months=8;
		days=5;
		weeks=1;
		blink=0;
		
		write_com(0x38);    //设置16x2显示 5x7点阵 8位数据接口
		write_com(0x0c);		//开显示 不显示光标
		write_com(0x06);		//写一个字符后地址指针加1
		write_com(0x01);		//显示清零 数据指针清零  要写入数据先写入命令 write_cmd(0x80) 第一行第一个字符地址  0x80+0x40 第二行开头地址写入
		write_com(0x80+0x40+6);
		write_data1(':');
		write_com(0x80+0x40+9);
		write_data1(':');	
		calweek();
}
void T0_time() interrupt 1        //*******定时器0的中断序号是1*********
{    
		TH0=(65536-45872)/256;			//进入中断再次初始化计数 TRO=1 所以计数会不停 下次中断仍然是50ms后
		TL0=(65536-45872)%256;			//16位计数器低8位初始计数
		num0++;									
		if(num0 == 20)								//中断20次后 50ms20次为1s
		{	
			num0=0;
			seconds++;
			if(seconds==60)                //60秒加1分钟
			{
				seconds=0;
				minutes++;
				if(minutes==60)              //60分钟加1小时
				{
					minutes=0;
					hours++;
					if(hours==24)            //超过24小时增加一天
					{
						hours=0;						
						day_add();
					}
					write_data(2,4,hours);
				}
				write_data(2,7,minutes);
			}
			write_data(2,10,seconds);
		}
}
void keyscan()
{
	if(key1==0)            //第一个按键轮流7种状态 按一下秒闪烁  分钟闪烁 小时闪烁 月 日 年 不闪烁则继续加秒数  0 1 2 3 4 5
	{
		delayms(10);
		if(key1==0)
		{
			mybeep();
			blink++;         //循环blink状态
			if(blink ==7) 
			{
				blink=0;          
				write_com(0x0c);     //取消光标闪烁
				TR0=1;								//继续计时
			}
			if(blink != 0)
			{
				TR0=0;        //暂停计时
//				cursor_blink();
			}
		}
		while(!key1);     //等待按键弹起
	}
	if(key2==0)             //第二个按键在闪烁时增加 小时、分钟等数字  各数字不联动
	{
		delayms(10);
		if(key2==0)           //确认按下按键
		{
			mybeep();
			switch (blink)
					{
						case 1: seconds++;					//秒
										settime_add();										
										break;
						case 2: minutes++;        			 //分
										settime_add();
										break;
						case 3: hours++;               			//时
										settime_add();
										break;
						case 4: days++;          					 //天
										settime_add();
										break;
						case 5: months++;        						 //月
										settime_add();
										break;				
						case 6: years++;       							 //年
										settime_add();
										break;				
						default :      ;      //光标不闪烁的时候按增加键  不处理												
					}
		}
		while(!key2);            //按键弹起后继续
	}
//	if(key3==0)
//	{
//		delayms(10);
//		if(key3==0)             //按键3  减小数字
//		{
//						mybeep();
//			switch (blink)
//					{
//						case 1: seconds--;						//秒		
//										settime_minus();
//										break;
//						case 2: minutes--;        		//分
//										settime_minus();
//										break;
//						case 3: hours--;               //时
//										settime_minus();
//										break;
//						case 4: days--;          					 //天
//										settime_minus();
//										break;
//						case 5: months--;        						 //月
//										settime_minus();
//										break;				
//						case 6: years--;       							 //年
//										settime_minus();
//										break;				
//						default :      ;      //光标不闪烁的时候按减小键  不处理												
//					}
//		}
//		while(!key3);
//	}
}

void settime_add()         //增大时间时,不进位 只处理溢出情况
{
		if(seconds==60) 	seconds=0;
		if(minutes==60)   minutes=0;
		if(hours==24) 		hours=0;	
		comparemonth=month[months-1];  //comparemonth除了2月不确定,其他每月天数都是数组中的数字
		if(months==2)    	{  	if(years%4==0)	comparemonth=29;	}	 //闰年二月29天
		if(days>comparemonth)  days=1;	
		if(months>12)  		months=1;	
		if(years%100==0)	write_data(1,3,years/100);  //年份后两位进位就增加年份前两位更新
		write_data(1,5,years%100);       //年份后两位没进位,只要更新后两位
		write_data(2,4,hours);
		write_data(2,7,minutes);
		write_data(2,10,seconds);	
		write_data(1,8,months);     //更新月份
		write_data(1,11,days);             //更新天数
		calweek();                       //计算周几并更新		
}
void settime_minus()         //减小时间时,不退位 只处理溢出情况
{
		if(seconds<0) 		seconds=59;
		if(minutes<0)   	minutes=59;
		if(hours<0) 			hours=23;	
		comparemonth=month[months-1];  //comparemonth除了2月不确定,其他每月天数都是数组中的数字
		if(months==2)    	{  	if(years%4==0)	comparemonth=29;	}	 //闰年二月29天
		if(days==0)  			days=comparemonth;	
		if(months==0) 		months=12;	
		if(years%100==0)	write_data(1,3,years/100);  //年份后两位进位就增加年份前两位更新
		write_data(1,5,years%100);       //年份后两位没进位,只要更新后两位
		write_data(2,4,hours);
		write_data(2,7,minutes);
		write_data(2,10,seconds);	
		write_data(1,8,months);     //更新月份
		write_data(1,11,days);             //更新天数
		calweek();                       //计算周几并更新		
}
void calweek()
{
	char numi; //显示字符串for 循环用
	if(months==1 || months==2)      //如果是1 2月份,要看成前一年的13 14月
	{
		m=months+12;
		years--;
	}
	else m=months;
	weeks=(days+2*m+3*(m+1)/5+years+years/4-years/100+years/400+1)%7;       //基姆拉尔森公式算星期几
	write_com(0x80+0x40+13);
	for(numi=0;numi<3;numi++)              //for循环写入字符串每一个字符
		{			
			write_data1(week[weeks][numi]);
			delayms(5);
		}
}
void cursor_blink()
{
		switch (blink)
		{
			case 1: write_com(0x80+0x40+11);              //秒 光标定位到秒
							write_com(0x0f);												//光标开始闪烁
							break;
			case 2: write_com(0x80+0x40+8); 				 				//分
							write_com(0x0f);	
							break;
			case 3: write_com(0x80+0x40+5);     						//时
							write_com(0x0f);	
							break;
			case 4: write_com(0x80+12);          					 //天
							write_com(0x0f);	
							break;
			case 5: write_com(0x80+9);        						 //月
							write_com(0x0f);	
							break;				
			case 6: write_com(0x80+6);     							 //年
							write_com(0x0f);	
							break;				
			default :        ;         //光标不闪烁																					
		}
}

void day_add()
{
	comparemonth=month[months-1];
	if(months==2)           //闰年二月29天
	{
		if(years%4==0)
			comparemonth=29;
	}	
	days++;
	if(days>comparemonth)        //days超过当月天数 回归1日 月份加一
	{
		days=1;
		months++;
		if(months>12)								//加一年
		{
			months=1;
			years++;
			if(years%100==0)								//加一个世纪
			{
				write_data(1,3,years/100);      //年份后两位近位就前两位更新
			}
			write_data(1,5,years%100);       //年份后两位没进位,只要更新后两位
		}
		write_data(1,8,months);     //更新月份
	}
	write_data(1,11,days);             //更新天数
	calweek();                       //计算周几
}

void write_com(uchar cmd)
{
	lcdrs=0;									//lcd数据命令选择端  rs为L(ledrs=0)则写命令 H(ledrs=1)则写数据
	P0=cmd;                   //P0数据与lcd数据口相连
	delayms(1);
	lcden=1;                  //写入命令
	delayms(1); 
	lcden=0;									//关闭写入
}
void write_data1(uchar data2)
{
	lcdrs=1;									//lcd数据命令选择端  rs为L(ledrs=0)则写命令 H(ledrs=1)则写数据
	P0=data2;                 
	delayms(1);
	lcden=1;
	delayms(1);
	lcden=0;
}
void write_data(uchar line,uchar add, uchar data1)   //写数据 第几行 第几个开始  数据  data1必须是1-2位数  此程序中不会溢出
{
	uchar address,shi,ge;
	shi=data1/10;
	ge=data1%10;
	address=0x80+(line-1)*0x40+add;         //根据行数和add参数定位写数据位置
	write_com(address);						
	write_data1(0x30+shi); 		输出十位字符****写完后光标自动下移一个,无需再定位地址****
  write_data1(0x30+ge);     输出个位字符
}
void display(uchar *table,uchar line)       //显示字符串子函数  table为字符串,最大16字符  line为显示的行数,1为第一行 2为第二行  函数目前容错
{
	uchar strlen1;
	uchar num;
	strlen1=strlen(table);                    //strlen函数得到当前字符串长度
	if(strlen1>16) strlen1=16;								//超出16字符后只取前16字符
	if(line==1)      													//第一行写入地址0x80
		write_com(0x80);
	else if(line==2)                                  //第二行写入地址0xc0
		write_com(0x80+0x40);
	if(line==1 || line==2)                    //line参数1或者2才操作,不然不处理
	{
		for(num=0;num<strlen1;num++)              //for循环写入字符串每一个字符
			{
				write_data1(table[num]);
				delayms(1);
			}
	}
	else                                       //行数溢出 bibi两声
	{
		mybeep();
		mybeep();
	}
}
void delayms(uint ms)         							//延迟毫秒函数 参数为需要延迟的毫秒数
{
	uint i,j;
	for(i=ms;i>0;i--)
		for(j=110;j>0;j--);
}
void mybeep()                            //蜂鸣器发声
{
	sbeep=0;
	delayms(100);
	sbeep=1;
}
  • 15
    点赞
  • 174
    收藏
    觉得还不错? 一键收藏
  • 12
    评论
### 回答1: 您好,对于51单片机lcd1602电子时钟按键的问题,我可以给您一些建议。 首先,您需要确定您的电子时钟需要哪些按键功能,例如设置时间、调整亮度等。然后,您可以在电路设计中添加相应的按键电路,通常使用矩阵按键电路可以节省IO口资源。 接着,您需要编写相应的程序来实现按键功能。可以使用中断或轮询方式来检测按键状态,并根据不同的按键状态执行相应的操作。 最后,您需要将程序烧录到51单片机中,并将LCD1602模块连接到单片机的IO口上,以显示时间和其他信息。 希望这些建议能对您有所帮助。如果您有其他问题,欢迎继续咨询。 ### 回答2: 51单片机lcd1602电子时钟按键是指利用51单片机LCD1602液晶屏幕组成的一款电子时钟,可以通过按键进行设置和调整时间的功能。 该电子时钟采用51单片机作为主控芯片,实现时间的计算、显示以及按键的检测等功能。同时,还采用了LCD1602液晶屏幕进行时间显示,具有低功耗、清晰度高等优点。 在电子时钟中,按键的作用是进行时间的设置和调整。通过不同的按键,可以设置小时、分钟等时间信息,并且通过调整按键可以实现时间的调整和修改。同时,电子时钟还具有报时、闹钟等功能,通过按键可以进行设置和调整。 在51单片机lcd1602电子时钟中,按键的检测采用了软件检测方式。即在程序中通过对按键引脚进行扫描,检测按键状态,然后进行相应的处理和响应。 总之,51单片机lcd1602电子时钟按键是一款小巧、实用的电子时钟,具有多种功能和特点,可应用于家庭、办公室等多种场合,方便实用。 ### 回答3: 51单片机是一种非常流行的微控制器,能够被广泛应用在各种电子设备中,包括电子时钟。在使用51单片机制作电子时钟时,我们可以使用LCD1602液晶显示器来展示时间和日期,并设置按键来方便地对时间进行调整。 首先,在电路设计时,需要将51单片机LCD1602液晶显示器连接,并写出代码来使其能够显示当前的时间和日期。这个过程需要对51单片机的编程有一定的了解,可以使用C语言或汇编语言来编写。 接着,需要设置按键来方便地对时间进行调整。在硬件设计时,可以使用一个光电开关或机械按键,并将其连接到51单片机的输入引脚。在程序中,我们需要设置按键的中断处理程序,当按键按下时,响应中断并调整时间。 为了实现电子时钟的功能,还需要根据时钟的要求来设置时间和日期。在程序中,我们要对时钟进行初始化,并使用定时器来保持时间的准确性。同时,在调整时间和日期时,需要对当前的秒、分、时、日、月、年等数据进行修改,并将修改后的数据存储到51单片机的EEPROM中。 最后,为了节约能量,我们可以设置一个睡眠模式,在用户长时间不操作时,自动关闭LCD1602液晶显示器和以太网模块,降低功耗。 总之,通过对51单片机LCD1602液晶显示器和按键的设计和程序编写,可以制作出功能完备、灵活易用的电子时钟。这不仅能够展示当前的时间和日期,还能够具有一定的调整功能,方便用户使用。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值