⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 av.c

📁 多功能数字钟的设计。要求:使用单片机实现智能数字钟
💻 C
字号:
#include <reg51.h>
#include <intrins.h>
#define Key_Port P1
#define LCD1602_DataPort P2
unsigned char code dis_week[]={"SUN,MON,TUE,WED,THU,FRI,SAT"};
unsigned char code para_month[13]={0,0,3,3,6,1,4,6,2,5,0,3,5};	//星期月参变数
unsigned char code DefineChar[14]={	0x00,0x0e,0x0a,0x0e,0x00,0x15,0x00,//第一个自定义字符									
						     			0x1e,0x04,0x02,0x06,0x02,0x04,0x1e,
										}; //第二个自定义字符
unsigned char data dis_buf1[16];		//lcd上排显示缓冲区
unsigned char data dis_buf2[16];		//lcd下排显示缓冲区	
unsigned char data year,month,date,week;//年、月、日、星期
unsigned char data armhour,armmin,armsec;//闹钟时、分、秒
unsigned char data hour,min,sec,sec100;	//时、分、秒、百分之一秒
unsigned char data flag,vkey,skey;//设置状态计数标志、按键先前值、按键当前值
unsigned char const Dis_Char0=0xf2,Dis_Char1=0x5c;
bit	alarm;	//标识是否启用闹钟,1--启用,0--关闭
//av

sbit	 rs = P3^7;				//LCD数据/命令选择端(H/L)
sbit	 rw = P3^6;				//LCD读/写选择端(H/L)
sbit	 ep = P3^5;				//LCD使能控制
sbit	PRE = P1^4;				//调整键(k3)
sbit	SET = P1^5;				//调整键(k4)
sbit	SPK = P3^1;	
/*	
sbit	 rs = P2^0;				//LCD数据/命令选择端(H/L)
sbit	 rw = P2^1;				//LCD读/写选择端(H/L)
sbit	 ep = P2^2;				//LCD使能控制
sbit	SPK = P1^6;	
*/		
void delayms(unsigned char ms);	//延时程序
bit  lcd_busy();				//测试LCD忙碌状态程序
void lcd_wcmd(char cmd);		//写入指令到LCD程序
void lcd_wdat(char dat);		//写入数据到LCD程序
void lcd_pos(char pos);			//LCD数据指针位置程序
void lcd_init();				//LCD初始化设定程序
void pro_timedate();			//时间日期处理程序
void pro_display();				//显示处理程序
void pro_key();					//按键处理程序
void time_alarm();				//定时报警功能(闹钟)
unsigned char scan_key();		//按键扫描程序
unsigned char week_proc();		//星期自动计算与显示函数
bit leap_year();				//判断是否为闰年
void lcd_sef_chr();				//LCD自定义字符程序
void update_disbuf(unsigned char t1,unsigned char t2[],unsigned char dis_h,unsigned char dis_m,unsigned char dis_s);			
								//更新显示缓冲区函数
 

// 延时程序
void delayms(unsigned char ms)
{	while(ms--)
	{	unsigned char i;
		for(i = 0; i< 250; i++)     
		{
			_nop_();			   //执行一条_nop_()指令为一个机器周期
			_nop_();
			_nop_();
			_nop_();
		}
	}
}		
	

//测试LCD忙碌状态
bit lcd_busy()
{	
	bit result;
	rs = 0;
	rw = 1;
	ep = 1;
	_nop_();
	_nop_();
	_nop_();
	_nop_();
	result =(bit)(LCD1602_DataPort&0x80);	//LCD的D0--D7中,D7=1为忙碌,D7=0为空闲
	ep = 0;
	return result;	
}

//写入指令到LCD
void lcd_wcmd(char cmd)
{							
	while(lcd_busy());	//当lcd_busy为1时,再次检测LCD忙碌状态,lcd-busy为0时,开始写指令
	rs = 0;
	rw = 0;
	ep = 0;
	_nop_();
	_nop_();	
	LCD1602_DataPort = cmd;
	_nop_();
	_nop_();
	_nop_();
	_nop_();
	ep = 1;
	_nop_();
	_nop_();
	_nop_();
	_nop_();
	ep = 0;		
}

//写入数据到LCD
void lcd_wdat(char dat)	
{							
	while(lcd_busy());	//当lcd_busy为1时,再次检测LCD忙碌状态,lcd-busy为0时,开始写数据
	rs = 1;
	rw = 0;
	ep = 0;
	LCD1602_DataPort = dat;
	_nop_();
	_nop_();
	_nop_();
	_nop_();
	ep = 1;
	_nop_();
	_nop_();
	_nop_();
	_nop_();
	ep = 0;	
}

//LCD数据指针位置程序
void lcd_pos(char pos)
{						
	lcd_wcmd(pos|0x80);	//数据指针=80+地址码(00H~27H,40H~67H)
}


//设定二个自定义字符,(注意:LCD1602中自定义字符的地址为0x00--0x07,即可定义8个字符)
//这里我们设定把一个自定义字符放在0x00位置(000),另一个放在0x01位子(001)
void lcd_sef_chr()
{	lcd_wcmd(0x40);	
  	lcd_wdat(0x1f);	
	lcd_wcmd(0x41);	
  	lcd_wdat(0x11);	
	lcd_wcmd(0x42);	
  	lcd_wdat(0x15);	
	lcd_wcmd(0x43);	
  	lcd_wdat(0x11);	
	lcd_wcmd(0x44);	
  	lcd_wdat(0x1f);	
	lcd_wcmd(0x45);
  	lcd_wdat(0x0a);
	lcd_wcmd(0x46);	
  	lcd_wdat(0x1f);	


	lcd_wcmd(0x48);	 
  	lcd_wdat(0x01);	 
	lcd_wcmd(0x49);	
  	lcd_wdat(0x1b);	
	lcd_wcmd(0x4a);	
  	lcd_wdat(0x1d);	
	lcd_wcmd(0x4b);	
  	lcd_wdat(0x19);	
	lcd_wcmd(0x4c);	
  	lcd_wdat(0x1d);
	lcd_wcmd(0x4d);	
  	lcd_wdat(0x1b);	
	lcd_wcmd(0x4e);	
  	lcd_wdat(0x01);	
}

//LCD初始化设定
void lcd_init()
{						
	lcd_wcmd(0x38);		//设置LCD为16X2显示,5X7点阵,八位数据借口
	delayms(1);
	lcd_wcmd(0x0c);		//LCD开显示及光标设置(光标不闪烁,不显示"-")
	delayms(1);
	lcd_wcmd(0x06);		//LCD显示光标移动设置(光标地址指针加1,整屏显示不移动)
	delayms(1);
	lcd_wcmd(0x01);		//清除LCD的显示内容
	delayms(1);
}

//闰年的计算
bit leap_year()
{
	bit leap;
	if((year%4==0&&year%100!=0)||year%400==0)//闰年的条件
		leap=1;
	else
		leap=0;
	return leap;
}

//星期的自动运算和处理
unsigned char week_proc()
{	unsigned char num_leap;	
	unsigned char c;
	num_leap=year/4-year/100+year/400;//自00年起到year所经历的闰年数
	if( leap_year()&& month<=2 )	  //既是闰年且是1月和2月	
		c=5;
	else 
		c=6;
	week=(year+para_month[month]+date+num_leap+c)%7;//计算对应的星期
	return week;
}	

//更新显示缓冲区
void update_disbuf(unsigned char t1,unsigned char t2[],unsigned char dis_h,unsigned char dis_m,unsigned char dis_s)	
{	dis_buf1[0]=t1; 			//
	dis_buf1[1]=0x20; 			//空格
	dis_buf1[2]=50; 			//'2' 
	dis_buf1[3]=48;             //'0'
	dis_buf1[4]=year/10+48; 
	dis_buf1[5]=year%10+48; 
	dis_buf1[6]=0x2d;
	dis_buf1[7]=month/10+48;
	dis_buf1[8]=month%10+48; 
	dis_buf1[9]=0x2d; 			//'-'
	dis_buf1[10]=date/10+48;
	dis_buf1[11]=date%10+48;
	dis_buf1[12]=0x20;
	dis_buf1[13]=dis_week[4*week];
	dis_buf1[14]=dis_week[4*week+1];
	dis_buf1[15]=dis_week[4*week+2];

	dis_buf2[0]=t2[0]; 
	dis_buf2[1]=t2[1]; 
	dis_buf2[2]=t2[2]; 
	dis_buf2[3]=t2[3];
	dis_buf2[4]=t2[4]; 
	dis_buf2[5]=t2[5];
	dis_buf2[6]=0x20;			//空格
 	if (alarm)
		dis_buf2[7]=0x01;		//alarm=1,显示闹钟启用标致(第二个自定义字符)
	else
		dis_buf2[7]=0x20;		//alarm=0,不显示闹钟启用标致
	dis_buf2[8]=dis_h/10+48; 
	dis_buf2[9]=dis_h%10+48; 
	dis_buf2[10]=0x3a;			//':'
	dis_buf2[11]=dis_m/10+48;
	dis_buf2[12]=dis_m%10+48;
	dis_buf2[13]=0x3a;
	dis_buf2[14]=dis_s/10+48;
	dis_buf2[15]=dis_s%10+48;
}
void Define_CharSelf(unsigned char code *p,unsigned char index,unsigned char vec)
{
	unsigned char temp,i,tp;
	tp = 7*index;
	temp =vec<<3;
	temp|=0x40;
	for(i=0;i<7;i++)
    {
		lcd_wcmd(temp+i);
		lcd_wdat(*(p+tp+i));
	}
	lcd_wcmd(temp+i);
	lcd_wdat(0x1f);
}


//时间和日期处理程序
void pro_timedate()
{	
	sec++;
	if(sec > 59)
		{sec = 0;
		 min++;
		 if(min>59)
			{min=0;
			 hour++;
			 if(hour>23)
				{hour=0;
				 date++;
				 if (month==1||month==3||month==5||month==7||month==8||month==10||month==12)
					 if (date>31) {date=1;month++;}					
				 if (month==4||month==6||month==9||month==11)		
				 	 if (date>30) {date=1;month++;}					
				 if (month==2)			
					 {if( leap_year())						
						{if (date>29) {date=1;month++;}}			
					 else
						{if (date>28) {date=1;month++;}}			
					 }				
				 if (month>12) {month=1;year++;}
				 if (year>99) year=0;
				}
			}
		}
	week_proc();
	if (sec==armsec && min==armmin && hour==armhour)				
		{if (alarm)
			TR1=1;					//闹钟启用时,报警时间到,启动Timer1
		}

}

//显示处理程序
void pro_display()
{	unsigned char i;
		lcd_pos(0x00);
		for (i=0;i<=15;i++)
			{lcd_wdat(dis_buf1[i]);}

		lcd_pos(0x40);
		for (i=0;i<=15;i++)
			{lcd_wdat(dis_buf2[i]);}
}

//Timer0中断处理程序,秒的产生
void timer0() interrupt 1 
{
	TH0=0x3c;			//Timer0置10ms定时初值dc00H(2^16=65536D,dc00H=56320D)
	TL0=0xd0;			//定时时间=(65536-56320)*(1/11.0592)*12=10ms (f0=11.0592Mhz)
	sec100++;
	if(sec100 >= 9)	//1秒时间 (100*10ms=1000ms=1s)
		{sec100 = 0;
		 pro_timedate();//调用时间和日期处理程序
		}
    if (sec&0x01)										
		update_disbuf(0x00,"willed",hour,min,sec);	  		
	else 
		update_disbuf(0x00,"willed ",hour,min,sec);	
	
	pro_display();		//调用显示处理函数


}	
	
//按键扫描程序
unsigned char  scan_key()
{	
	unsigned char Key,Temp; 
	Key = Key_Port;
	Key = ~Key;
	Key = Key&0x3f;
	if(Key!=0)
	{
		delayms(1);
		Temp = Key_Port;
		Temp = ~Temp;
		Temp = Temp&0x3f;
		if(Temp==Key)
			return Key;
		else
			return 0;
	}
	else
		return 0;
}

//外部中断INT0中断处理程序
void Key_Process()
{	
	TR0=0;									//禁止Timer0
	IE=0;									//禁止中断
	lcd_wcmd(0x0e);							//显示光标"_",整个光标不闪烁
	alarm=1;
	Define_CharSelf(DefineChar,0,0);
	Define_CharSelf(DefineChar,1,1);
	update_disbuf(0x00,"alarm:",armhour,armmin,armsec);	//更新显示数据,0x50表示要显示"P"	
	pro_display();							//调用显示处理程序
	lcd_pos(0x47);	    			//使光标位于第一个调整项下
		switch(skey)	
		{						
		case 0x01:		//PRE键按下
		{	 
			flag++;	//调整标志位加1 
			switch (flag)	//将光标置于相应调整位置
			{	
										  		
				case 1: {	
							lcd_pos(0x49);
						 }break;		                              //光标置小时报警设置位置						 
				case 2:{
							lcd_pos(0x4c);
						}break;		                              //光标置分钟报警设置位置	   	
				case 3:	lcd_pos(0x4f);break;		                             //光标置秒时报警设置位置						
				case 4:	
						update_disbuf(0x00,"time: ",hour,min,sec);
						pro_display();
					lcd_pos(0x05);break;		//光标置年调整位置	
				case 5:	lcd_pos(0x08);break;		//光标置月调整位置
				case 6:	lcd_pos(0x0b);break;		//光标置日调整位置
				case 7: lcd_pos(0x49);break;		//光标置时调整位置
				case 8:	lcd_pos(0x4c);break;		//光标置分调整位置
				case 9:	lcd_pos(0x4f);break;		//光标置秒调整位置
				default:flag = 0;break;
			}
			} break;
			case 0x02:		    //SET键按下
			{
				pro_key();		//转设置按键处理程序
			}break;
			case 0x04:
			{
					lcd_sef_chr();//写入自定义字符号
					lcd_wcmd(0x0c);							//设置LCD开显示及光标不闪烁,不显示"-"
					lcd_wcmd(0x01);						    //清除LCD的显示内容
					IE=0x8f;      							//CPU开中断,INT0,INT1,开中断
					TR0=1;								//Timer0启动
			}
			case 0x08:
			{
				if(TR1)
				TR1=0;	
			}break;
			default:break;	
		}

}

//主程序,初始化及初值设定
void main()
{	
	lcd_init();	//初始化LCD
	lcd_sef_chr();//写入自定义字符号
	hour=3;min=4;sec=0;	
	SPK = 0;
//	OutDisplay =0;
 //开机时的时,分,秒显示
	armhour=0;armmin=0;armsec=0;		//开机时的时,分,秒报警初值
	year= 1; month=9;date=4;     		//开机时的年,月,日,星期显示
	week_proc();
	alarm=1;	
	//初始开机,启用闹钟
							//CPU开中断,INT0,INT1,Timer0,Timer1开中断
//	IP = 0x04;							//设置INT0为中断最高优先级
//	IT0=0;IT1=0;						//外部INT0,INT1设置为电平触发方式(注意,触发不要选边沿方式,易误动)
	TMOD = 0x11;						//Timer0,Timer1工作于模式1, 16位定时方式
	TH0 = 0x3c;TL0 = 0xd0;				//Timer0置10ms定时初值 	
	TH1 = 0xff;TL1 = 0xff;				//Timer1置初值 	
	TR0 = 1;							//Timer0启动
	TR1 = 0;
	ET0 = 1;
	ET1 = 1;
	EA =1;
	while(1)
	{
		skey = scan_key();
		if(skey!=0)	
		{
			Key_Process();
			while(skey == scan_key());	
		}
	}
}

//设置按键处理程序
void pro_key()
{
	switch (flag)
		{ 
		case 0:alarm=!alarm;			//启用或关闭闹钟(alarm=1:启用,alarm=0:关闭)
				update_disbuf(Dis_Char0,"alarm:",armhour,armmin,armsec); //更新显示数据
				pro_display();										//调用显示处理
				lcd_pos(0x47);break;								//光标回到原调整位置 
		case 1:armhour++;
				if (armhour>23) armhour=0;
				update_disbuf(Dis_Char0,"alarm:",armhour,armmin,armsec); //更新显示数据
				pro_display();							//调用显示处理
			    ;break;								//光标回到原调整位置
		case 2:armmin++;
				if (armmin>59) armmin=0;
				update_disbuf(Dis_Char0,"alarm:",armhour,armmin,armsec);
				pro_display();
				break;
		case 3:armsec++;
				if (armsec>59) armsec=0;
				update_disbuf(Dis_Char0,"alarm:",armhour,armmin,armsec);
				pro_display();
				lcd_pos(0x4f);break;

		case 4:year++;
				if	(year> 99) year= 0;
				week_proc();                        //星期自动运算
				update_disbuf(Dis_Char0,"time: ",hour,min,sec);
				pro_display();
				lcd_pos(0x05);break;
	 	case 5:month++;
				if (month>12) month=1;
				week_proc();						//星期自动运算
				update_disbuf(Dis_Char0,"time: ",hour,min,sec);
				pro_display();
				lcd_pos(0x08);break;
		case 6:date++;
				if (month==1||month==3||month==5||month==7||month==8||month==10||month==12)
					if (date>31) date=1;			//大月31天
				if (month==4||month==6||month==9||month==11)		
				 	if (date>30) date=1;			//小月30天
				if (month==2)			
					{if(leap_year())				//闰年的条件
						{if (date>29) date=1;}		//闰年2月为29天
					 else
						{if (date>28) date=1;}}		//平年2月为28天	
				week_proc();					    //星期自动运算
				update_disbuf(Dis_Char0,"time: ",hour,min,sec);
				pro_display();
				lcd_pos(0x0b);break;
		
		case 7:hour++;
				if (hour>23) hour=0;
				update_disbuf(Dis_Char0,"time: ",hour,min,sec);
				pro_display();
				lcd_pos(0x49);break;	
		case 8:min++;
				if (min>59) min=0;
				update_disbuf(Dis_Char0,"time: ",hour,min,sec);
				pro_display();
				lcd_pos(0x4c);break;
		case 9:sec++;
				if (sec>59) sec=0;
				update_disbuf(Dis_Char0,"time: ",hour,min,sec);
				pro_display();
				lcd_pos(0x4f);break;
		default: break ;												 
		}
}	

//Timer1中断处理程序,产生报警的声音
void timer1() interrupt 3 
{
	TH1=0xff;				
	TL1=0xff;
	SPK=~SPK;
	 				
}

//外部中断INT1中断处理程序,停止报警声音

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -