基于AT89C51单片机电子万年历的设计(含文档、源码与proteus仿真,以及系统详细介绍)

本篇文章论述的是基于AT89C51单片机电子万年历的设计的详情介绍,如果对您有帮助的话,还请关注一下哦,如果有资源方面的需要可以联系我。

目录

原理图

仿真图

元件清单

 代码实现

系统论文

资源下载


摘    要

电子万年历是一种应用非常广泛日常计时工具,对现代社会越来越重要此电子万年历在硬件方面主要采用AT89C51单片机作为主控核心,由DS1302时钟芯片提供时钟、LCD1602液晶显示屏显示。AT89C51单片机是由STC公司推出的,功耗小,电压可选用4~6V电压供电;DS1302时钟芯片是美国DALLAS公司推出的低功耗实时时钟芯片,它可以对年、月、日、星期、时、分、秒进行计时,还具有闰年补偿等多种功能,而且DS1302的使用寿命长,误差小;对于数字电子万年历采用直观的数字显示,数字显示是采用的LCD1602液晶显示屏来显示,可以同时显示年、月、日、星期、时、分、秒等信息。此外,该电子万年历还具有时间校准等功能。在软件方面,主要包括日历程序、时间调整程序,显示程序等。所有程序编写完成后,在Keil软件中进行调试,确定没有问题后,烧写到单片机上进行测试。

此设计主要由时钟芯片DS1302和温度传感器DS18B20采集数据到单片机进行处理再通过LCD1602显示出来,本论文主要研究了液晶显示器LCD1602及时钟芯片DS1302,温度传感器DS18B20与单片机之间的硬件互联及通信,对数种硬件连接方案进行了详尽的比较,在软件方面对日历算法也进行了论述。

原理图


仿真图


元件清单


 代码实现(部分)


#include<reg51.h>
#include"DS18B20.h"
uchar a,b,miao,shi,fen,ri,yue,nian,week,flag,key1n,temp,miao1,shi1=12,fen1=1,miao1=0,clock=0 ;
//flag用于读取头文件中的温度值,和显示温度值
#define yh 0x80 //LCD第一行的初始位置,因为LCD1602字符地址首位D7恒定为1(100000000=80)
#define er 0x80+0x40 //LCD第二行初始位置(因为第二行第一个字符位置地址是0x40)
//液晶屏的与C51之间的引脚连接定义(显示数据线接C51的P0口)
sbit rs=P2^0;
sbit en=P2^2;
sbit rw=P2^1; //如果硬件上rw接地,就不用写这句和后面的rw=0了
sbit led=P2^6; //LCD背光开关
//DS1302时钟芯片与C51之间的引脚连接定义
sbit IO=P1^1;
sbit SCLK=P1^0;
sbit RST=P1^2;
sbit CLO=P1^4;
sbit ACC0=ACC^0;
sbit ACC7=ACC^7;
/************************************************************
ACC累加器=A
ACC.0=E0H 

ACC.0就是ACC的第0位。Acc可以位寻址。

累加器ACC是一个8位的存储单元,是用来放数据的。但是,这个存储单元有其特殊的地位,
是单片机中一个非常关键的单元,很多运算都要通过ACC来进行。以后在学习指令时,
常用A来表示累加器。但有一些地方例外,比如在PUSH指令中,就必须用ACC这样的名字。
一般的说法,A代表了累加器中的内容、而ACC代表的是累加器的地址。 
***************************************************************/
//校时按键与C51的引脚连接定义
sbit key1=P1^5;  //设置键
sbit key2=P1^6;  //加键
sbit key3=P1^7;  //减键
sbit buzzer=P1^3;//蜂鸣器,通过三极管9012驱动,端口低电平响
/**************************************************************/
uchar code tab1[]={"20  -  -   "}; //年显示的固定字符
uchar code tab2[]={"  :  :  "};//时间显示的固定字符
uchar code tab3[]={"    HELLO WELCOME"};//开机动画
//延时函数,后面经常调用
void delay(uint xms)//延时函数,有参函数
{
	uint x,y;
	for(x=xms;x>0;x--)
		for(y=110;y>0;y--);
}
/********液晶写入指令函数与写入数据函数,以后可调用**************/

/*在这个程序中,液晶写入有关函数会在DS1302的函数中调用,所以液晶程序要放在前面*/

void write_1602com(uchar com)//****液晶写入指令函数****
{
	rs=0;//数据/指令选择置为指令
	rw=0; //读写选择置为写
	P0=com;//送入数据
	delay(1);
	en=1;//拉高使能端,为制造有效的下降沿做准备
	delay(1);
	en=0;//en由高变低,产生下降沿,液晶执行命令
}
void write_1602dat(uchar dat)//***液晶写入数据函数****
{
	rs=1;//数据/指令选择置为数据
	rw=0; //读写选择置为写
	P0=dat;//送入数据
	delay(1);
	en=1; //en置高电平,为制造下降沿做准备
	delay(1);
	en=0; //en由高变低,产生下降沿,液晶执行命令
}
void lcd_init()//***液晶初始化函数****
{
	uchar j;
    write_1602com(0x0f|0x08);
	for(a=0;a<17;a++)
		write_1602dat(tab3[a]);
	j=17;
	while(j--)
	{
		write_1602com(0x1c);//循环左移
		delay(700);
	} 
	write_1602com(0x01);
	delay(10);
	write_1602com(0x38);//设置液晶工作模式,意思:16*2行显示,5*7点阵,8位数据
	write_1602com(0x0c);//开显示不显示光标
	write_1602com(0x06);//整屏不移动,光标自动右移
	write_1602com(0x01);//清显示
	/***开机动画显示hello welcome dianzizhong****/
	write_1602com(yh+1);//日历显示固定符号从第一行第1个位置之后开始显示
	for(a=0;a<14;a++)
	{
		write_1602dat(tab1[a]);//向液晶屏写日历显示的固定符号部分
		//delay(3);
	}
	write_1602com(er+2);//时间显示固定符号写入位置,从第2个位置后开始显示	
	for(a=0;a<8;a++)
	{
		write_1602dat(tab2[a]);//写显示时间固定符号,两个冒号
		//delay(3);
	}
	write_1602com(er+0);
	write_1602dat(0x24);
	write_1602com(er+1);
	write_1602dat(0x20);
}
/***************DS1302有关子函数********************/
void write_byte(uchar dat)//写一个字节
{
	ACC=dat;
	RST=1;
	for(a=8;a>0;a--)
	{
		IO=ACC0;
		SCLK=0;
		SCLK=1;
		ACC=ACC>>1;
	}
}
uchar read_byte()//读一个字节
{
	RST=1;
	for(a=8;a>0;a--)
	{
		ACC7=IO;
		SCLK=1;
		SCLK=0;
		ACC=ACC>>1;
	}
	return(ACC);
}
//----------------------------------------
void write_1302(uchar add,uchar dat)//向1302芯片写函数,指定写入地址,数据
{
	RST=0;
	SCLK=0;
	RST=1;
	write_byte(add);
	write_byte(dat);
	SCLK=1;
	RST=0;
}
uchar read_1302(uchar add)//从1302读数据函数,指定读取数据来源地址
{
	uchar temp;
	RST=0;
	SCLK=0;
	RST=1;
	write_byte(add);
	temp=read_byte();
	SCLK=1;
	RST=0;
	return(temp);
}
uchar BCD_Decimal(uchar bcd)//BCD码转十进制函数,输入BCD,返回十进制
{
 	uchar Decimal;
 	Decimal=bcd>>4;
 	return(Decimal=Decimal*10+(bcd&=0x0F));
}
//--------------------------------------
void ds1302_init() //1302芯片初始化子函数(2010-01-07,12:00:00,week4)
{
	RST=0;
	SCLK=0;
	write_1302(0x8e,0x00); //允许写,禁止写保护 
	write_1302(0x80,0x00); //向DS1302内写秒寄存器80H写入初始秒数据00
	write_1302(0x82,0x00);//向DS1302内写分寄存器82H写入初始分数据00
	write_1302(0x84,0x12);//向DS1302内写小时寄存器84H写入初始小时数据12
	write_1302(0x8a,0x01);//向DS1302内写周寄存器8aH写入初始周数据4
	write_1302(0x86,0x08);//向DS1302内写日期寄存器86H写入初始日期数据07
	write_1302(0x88,0x07);//向DS1302内写月份寄存器88H写入初始月份数据01
	write_1302(0x8c,0x10);//向DS1302内写年份寄存器8cH写入初始年份数据10
	write_1302(0x8e,0x80); //打开写保护
}
//------------------------------------
//温度显示子函数
void write_temp(uchar add,uchar dat)//向LCD写温度数据,并指定显示位置
{
	uchar gw,sw,bw;
	if(dat>=0&&dat<=128)
	{
		gw=dat%10;//取得个位数字
		sw=dat%100/10;//取得十位数字
		bw=dat/100	;//取得百位数字	
	}
	else
	{
		dat=256-dat;
		gw=dat%10;//取得个位数字
		sw=dat%100/10;//取得十位数字
		bw=-3;		 //0x30-3表示为负号
	}
	write_1602com(er+add);//er是头文件规定的值0x80+0x40
	write_1602dat(0x30+bw);//数字+30得到该数字的LCD1602显示码
	write_1602dat(0x30+sw);//数字+30得到该数字的LCD1602显示码
	write_1602dat(0x30+gw);//数字+30得到该数字的LCD1602显示码
    write_1602dat(0xdf);//显示温度的小圆圈符号,0xdf是液晶屏字符库的该符号地址码
  	write_1602dat(0x43);	//显示"C"符号,0x43是液晶屏字符库里大写C的地址码		
}
//------------------------------------
//时分秒显示子函数
void write_sfm(uchar add,uchar dat)//向LCD写时分秒,有显示位置加、现示数据,两个参数
{
	uchar gw,sw;
	gw=dat%10;//取得个位数字
	sw=dat/10;//取得十位数字
	write_1602com(er+add);//er是头文件规定的值0x80+0x40
	write_1602dat(0x30+sw);//数字+30得到该数字的LCD1602显示码
	write_1602dat(0x30+gw);//数字+30得到该数字的LCD1602显示码				
}
//-------------------------------------
//年月日显示子函数
void write_nyr(uchar add,uchar dat)//向LCD写年月日,有显示位置加数、显示数据,两个参数
{
	uchar gw,sw;
	gw=dat%10;//取得个位数字
	sw=dat/10;//取得十位数字
	write_1602com(yh+add);//设定显示位置为第一个位置+add
	write_1602dat(0x30+sw);//数字+30得到该数字的LCD1602显示码
	write_1602dat(0x30+gw);//数字+30得到该数字的LCD1602显示码	
}
//-------------------------------------------
void write_week(uchar week)//写星期函数
{
	write_1602com(yh+0x0c);//星期字符的显示位置
	switch(week)
	{
		case 1:	write_1602dat('M');//星期数为1时,显示
			   	write_1602dat('O');
			   	write_1602dat('N');
			   	break;
	   
		case 2:	write_1602dat('T');//星期数据为2时显示
			   	write_1602dat('U');
			   	write_1602dat('E');
			   	break;
		
		case 3:	write_1602dat('W');//星期数据为3时显示
			   	write_1602dat('E');
			   	write_1602dat('D');
			   	break;
		
		case 4:	write_1602dat('T');//星期数据为4是显示
			   	write_1602dat('H');
			   	write_1602dat('U');
			   	break;
		
		case 5:	write_1602dat('F');//星期数据为5时显示
			   	write_1602dat('R');
			   	write_1602dat('I');
			   	break;
		
		case 6:	write_1602dat('S');//星期数据为6时显示
			   	write_1602dat('T');
			   	write_1602dat('A');
			   	break;
		
		case 7:	write_1602dat('S');//星期数据为7时显示
			   	write_1602dat('U');
			   	write_1602dat('N');
			   	break;
	}
}
//****************键盘扫描有关函数**********************
void keyscan()
{
	if(key1==0)//---------------key1为功能键(设置键)--------------------
	{
		delay(9);//延时,用于消抖动
		if(key1==0)//延时后再次确认按键按下
		{
    		buzzer=0;//蜂鸣器短响一次
    		delay(20);
    		buzzer=1;
			while(!key1);
			key1n++;
			if(key1n==12)
				key1n=1;//设置按键共有秒、分、时、星期、日、月、年、返回,8个功能循环
			switch(key1n)
			{		
				case 1: TR0=0;//关闭定时器
						//TR1=0;
						write_1602com(er+0x09);//设置按键按动一次,秒位置显示光标
					   	write_1602com(0x0f);//设置光标为闪烁
					   	temp=(miao)/10*16+(miao)%10;//秒数据写入DS1302
					   	write_1302(0x8e,0x00);
					   	write_1302(0x80,0x80|temp);//miao
					    write_1302(0x8e,0x80);
					   	break;
				case 2: write_1602com(er+6);//按2次fen位置显示光标						
					    //write_1602com(0x0f);
						break;
				case 3: write_1602com(er+3);//按动3次,shi
					    //write_1602com(0x0f);
						break;
				
				case 4: write_1602com(yh+0x0e);//按动4次,week
					    //write_1602com(0x0f);
						break;
				case 5: write_1602com(yh+0x0a);//按动5次,ri
					    //write_1602com(0x0f);
						break;
				case 6: write_1602com(yh+0x07);//按动6次,yue
					    //write_1602com(0x0f);
						break;
				case 7: write_1602com(yh+0x04);//按动7次,nian
					    //write_1602com(0x0f);
						break;
				case 8: write_1602com(er+1);
				        write_1602dat(0x4d);
						write_1602com(er+1);
				        break;
				case 9: write_1602com(er+1);
				        write_1602dat(0x46);
						write_1602com(er+1);
				        break;
				case 10:write_1602com(er+1);
				        write_1602dat(0x53);
						write_1602com(er+1);
						break;
				case 11:
				        write_1602com(er+1);
			        	write_1602dat(0x20);
						write_1602com(0x0c);//按动到第8次,设置光标不闪烁
						TR0=1;//打开定时器
			         	temp=(miao)/10*16+(miao)%10;
					   	write_1302(0x8e,0x00);
					   	write_1302(0x80,0x00|temp);//miao数据写入DS1302
					   	write_1302(0x8e,0x80);
			            break;																		
			}
		} 
	}
	//------------------------------加键key2----------------------------		
	if(key1n!=0)//当key1按下以下。再按以下键才有效(按键次数不等于零)
	{
		if(key2==0)  //上调键
		{
			delay(10);
			if(key2==0)
			{
    			buzzer=0;//蜂鸣器短响一次
    			delay(20);
    			buzzer=1;
				while(!key2);
				switch(key1n)
				{
					case 1:	miao++;//设置键按动1次,调秒
							if(miao==60)
								miao=0;//秒超过59,再加1,就归零
							write_sfm(0x08,miao);//令LCD在正确位置显示"加"设定好的秒数
							temp=(miao)/10*16+(miao)%10;//十进制转换成DS1302要求的DCB码
						   	write_1302(0x8e,0x00); //允许写,禁止写保护 
						   	write_1302(0x80,temp); //向DS1302内写秒寄存器80H写入调整后的秒数据BCD码
						   	write_1302(0x8e,0x80); //打开写保护
							write_1602com(er+0x09);//因为设置液晶的模式是写入数据后,光标自动右移,所以要指定返回
							//write_1602com(0x0b);
							break;
					case 2:	fen++;
							if(fen==60)
								fen=0;
							write_sfm(0x05,fen);//令LCD在正确位置显示"加"设定好的分数据
							temp=(fen)/10*16+(fen)%10;//十进制转换成DS1302要求的DCB码
						   	write_1302(0x8e,0x00);//允许写,禁止写保护 
						   	write_1302(0x82,temp);//向DS1302内写分寄存器82H写入调整后的分数据BCD码
						   	write_1302(0x8e,0x80);//打开写保护
							write_1602com(er+6);//因为设置液晶的模式是写入数据后,指针自动加一,在这里是写回原来的位置
							break;
					case 3:	shi++;
							if(shi==24)
								shi=0;
							write_sfm(2,shi);//令LCD在正确的位置显示"加"设定好的小时数据
							temp=(shi)/10*16+(shi)%10;//十进制转换成DS1302要求的DCB码
						   	write_1302(0x8e,0x00);//允许写,禁止写保护 
						   	write_1302(0x84,temp);//向DS1302内写小时寄存器84H写入调整后的小时数据BCD码
						   	write_1302(0x8e,0x80);//打开写保护
							write_1602com(er+3);//因为设置液晶的模式是写入数据后,指针自动加一,所以需要光标回位
							break;
					case 4:	week++;
							if(week==8)
								week=1;
				            write_1602com(yh+0x0C);//指定'加'后的周数据显示位置
							write_week(week);//指定周数据显示内容
				            temp=(week)/10*16+(week)%10;//十进制转换成DS1302要求的DCB码
						   	write_1302(0x8e,0x00);//允许写,禁止写保护 
						   	write_1302(0x8a,temp);//向DS1302内写周寄存器8aH写入调整后的周数据BCD码
						   	write_1302(0x8e,0x80);//打开写保护
							write_1602com(yh+0x0e);//因为设置液晶的模式是写入数据后,指针自动加一,所以需要光标回位
							break;
					case 5:	ri++;
							switch(yue)
							{
				            	case 1:
								case 3:
								case 5:
								case 7:
								case 8:
								case 10:
								case 12:
										if(ri>31) 
											ri=1;
						        		break;
				                case 2: 
										if(nian%4==0||nian%400==0)
						  	       		{ 
								     		if(ri>29) 
												ri=1;
								    	}	
						           		else					  	
						         		{					    
								    		if(ri>28) 
												ri=1;
						           		}
										break;
				               case 4:
							   case 6:
							   case 9:
							   case 11:
							      		ri++;
								    	if(ri>30) 
											ri=1;
							         	break;
				            }		 
							write_nyr(9,ri);//令LCD在正确的位置显示"加"设定好的日期数据
							temp=(ri)/10*16+(ri)%10;//十进制转换成DS1302要求的DCB码
						   	write_1302(0x8e,0x00);//允许写,禁止写保护
						   	write_1302(0x86,temp);//向DS1302内写日期寄存器86H写入调整后的日期数据BCD码
						   	write_1302(0x8e,0x80);//打开写保护
							write_1602com(yh+10);//因为设置液晶的模式是写入数据后,指针自动加一,所以需要光标回位
							break;
					case 6:	yue++;
							if(yue==13)
								yue=1;
							write_nyr(6,yue);//令LCD在正确的位置显示"加"设定好的月份数据
							temp=(yue)/10*16+(yue)%10;//十进制转换成DS1302要求的DCB码
						   	write_1302(0x8e,0x00);//允许写,禁止写保护
						   	write_1302(0x88,temp);//向DS1302内写月份寄存器88H写入调整后的月份数据BCD码
						   	write_1302(0x8e,0x80);//打开写保护
							write_1602com(yh+7);//因为设置液晶的模式是写入数据后,指针自动加一,所以需要光标回位
							break;
					case 7:	nian++;
						 	if(nian==100)
								nian=0;
							write_nyr(3,nian);//令LCD在正确的位置显示"加"设定好的年份数据
				            temp=(nian)/10*16+(nian)%10;//十进制转换成DS1302要求的DCB码
						   	write_1302(0x8e,0x00);//允许写,禁止写保护
						   	write_1302(0x8c,temp);//向DS1302内写年份寄存器8cH写入调整后的年份数据BCD码
						   	write_1302(0x8e,0x80);//打开写保护
							write_1602com(yh+4);//因为设置液晶的模式是写入数据后,指针自动加一,所以需要光标回位
							break;
					case 8:	write_1602com(er+9); //设置闹钟的秒定时
					        miao1++;
							if(miao1==60)
							   miao1=0;
							write_sfm(0x08,miao1);//令LCD在正确位置显示"加"设定好秒的数据
							write_1602com(er+9);//因为设置液晶的模式是写入数据后,指针自动加一,在这里是写回原来的位置
							break;
					case 9:	write_1602com(er+6); //设置闹钟的分钟定时
					        fen1++;
					        if(fen1==60)
								fen1=0;
							write_sfm(0x05,fen1);//令LCD在正确位置显示"加"设定好的分数据
							write_1602com(er+6);//因为设置液晶的模式是写入数据后,指针自动加一,在这里是写回原来的位置
							break;
					case 10:write_1602com(er+3); //设置闹钟的小时定时
					        shi1++;
					        if(shi1==24)
								shi1=0;
							write_sfm(0x02,shi1);//令LCD在正确的位置显示"加"设定好的小时数据
							write_1602com(er+3);//因为设置液晶的模式是写入数据后,指针自动加一,所以需要光标回位
							break;
				}
			}
		}
		//------------------减键key3,各句功能参照'加键'注释---------------
		if(key3==0)
		{
			delay(10);//调延时,消抖动
			if(key3==0)
			{
	    		buzzer=0;//蜂鸣器短响一次
	    		delay(20);
			    buzzer=1;
				while(!key3);
				switch(key1n)
				{
					case 1:	miao--;
							if(miao==-1)
								miao=59;//秒数据减到-1时自动变成59
							write_sfm(0x08,miao);//在LCD的正确位置显示改变后新的秒数
				            temp=(miao)/10*16+(miao)%10;//十进制转换成DS1302要求的DCB码
						   	write_1302(0x8e,0x00); //允许写,禁止写保护 
						   	write_1302(0x80,temp); //向DS1302内写秒寄存器80H写入调整后的秒数据BCD码
						   	write_1302(0x8e,0x80); //打开写保护
							write_1602com(er+0x09);//因为设置液晶的模式是写入数据后,指针自动加一,在这里是写回原来的位置
							//write_1602com(0x0b);
							break;
					case 2:	fen--;
							if(fen==-1)
								fen=59;
							write_sfm(5,fen);
							temp=(fen)/10*16+(fen)%10;//十进制转换成DS1302要求的DCB码
						   	write_1302(0x8e,0x00);//允许写,禁止写保护 
						   	write_1302(0x82,temp);//向DS1302内写分寄存器82H写入调整后的分数据BCD码
						   	write_1302(0x8e,0x80);//打开写保护
							write_1602com(er+6);//因为设置液晶的模式是写入数据后,指针自动加一,在这里是写回原来的位置
							break;
			
					case 3:	shi--;
						   	if(shi==-1)
								shi=23;
							write_sfm(2,shi);
							temp=(shi)/10*16+(shi)%10;//十进制转换成DS1302要求的DCB码
						   	write_1302(0x8e,0x00);//允许写,禁止写保护 
						   	write_1302(0x84,temp);//向DS1302内写小时寄存器84H写入调整后的小时数据BCD码
						   	write_1302(0x8e,0x80);//打开写保护
							write_1602com(er+3);//因为设置液晶的模式是写入数据后,指针自动加一,所以需要光标回位
							break;
					case 4:	week--;
							if(week==0)
								week=7;						 
			            	write_1602com(yh+0x0C);//指定'加'后的周数据显示位置
							write_week(week);//指定周数据显示内容
						   	temp=(week)/10*16+(week)%10;//十进制转换成DS1302要求的DCB码
					   		write_1302(0x8e,0x00);//允许写,禁止写保护 
					   		write_1302(0x8a,temp);//向DS1302内写周寄存器8aH写入调整后的周数据BCD码
					   		write_1302(0x8e,0x80);//打开写保护
						   	write_1602com(yh+0x0e);//因为设置液晶的模式是写入数据后,指针自动加一,所以需要光标回位
							break;
					case 5:	ri--;
							switch(yue)
							{ 
			                	case 1:
								case 3:
								case 5:
								case 7:
								case 8:
								case 10:
								case 12: 
							 			if(ri==0) 
											ri=31;
					         			break;
			                  	case 2: if(nian%4==0||nian%400==0)
					  	       			{ 
							    			if(ri==0) 
												ri=29;
							    		}			
					           			else	
						        		{
							        		if(ri==0) 
												ri=28;
					         			}
										break;
			                 	case 4:
								case 6:
								case 9:
								case 11:
							      		if(ri==0) 
											ri=30;
						         		break;
			             	}
							write_nyr(9,ri);
							temp=(ri)/10*16+(ri)%10;//十进制转换成DS1302要求的DCB码
					   		write_1302(0x8e,0x00);//允许写,禁止写保护
					   		write_1302(0x86,temp);//向DS1302内写日期寄存器86H写入调整后的日期数据BCD码
					   		write_1302(0x8e,0x80);//打开写保护
							write_1602com(yh+10);//因为设置液晶的模式是写入数据后,指针自动加一,所以需要光标回位
							break;
					case 6:	yue--;
							if(yue==0)
								yue=12;
							write_nyr(6,yue);
							temp=(yue)/10*16+(yue)%10;//十进制转换成DS1302要求的DCB码
						   	write_1302(0x8e,0x00);//允许写,禁止写保护
						   	write_1302(0x88,temp);//向DS1302内写月份寄存器88H写入调整后的月份数据BCD码
						   	write_1302(0x8e,0x80);//打开写保护
							write_1602com(yh+7);//因为设置液晶的模式是写入数据后,指针自动加一,所以需要光标回位
							break;	
					case 7:	nian--;
					 		if(nian==-1)
								nian=99;
							write_nyr(3,nian);
			         		temp=(nian)/10*16+(nian)%10;//十进制转换成DS1302要求的DCB码
						   	write_1302(0x8e,0x00);//允许写,禁止写保护
						   	write_1302(0x8c,temp);//向DS1302内写年份寄存器8cH写入调整后的年份数据BCD码
						   	write_1302(0x8e,0x80);//打开写保护
							write_1602com(yh+4);//因为设置液晶的模式是写入数据后,指针自动加一,所以需要光标回位
							break;
					case 8:	write_1602com(er+9); //设置闹钟的秒定时
				        	miao1--;
							if(miao1==-1)
						   		miao1=59;
							write_sfm(0x08,miao1);//令LCD在正确位置显示"加"设定好秒的数据
							write_1602com(er+9);//因为设置液晶的模式是写入数据后,指针自动加一,在这里是写回原来的位置
							break;
					case 9:	write_1602com(er+6); //设置闹钟的分钟定时
				        	fen1--;
				        		if(fen1==-1)
							fen1=59;
							write_sfm(0x05,fen1);//令LCD在正确位置显示"加"设定好的分数据
							write_1602com(er+6);//因为设置液晶的模式是写入数据后,指针自动加一,在这里是写回原来的位置
							break;
				    case 10:write_1602com(er+3); //设置闹钟的小时定时
				        	shi1--;
				        	if(shi1==-1)
								shi1=23;
							write_sfm(0x02,shi1);//令LCD在正确的位置显示"加"设定好的小时数据
							write_1602com(er+3);//因为设置液晶的模式是写入数据后,指针自动加一,所以需要光标回位
							break;			
				}
			}
		}
	}
}
//定时器0初始化程序
void init() //定时器、计数器设置函数
{
	TMOD=0x11; //指定定时/计数器的工作方式为1
	TH0=0;  //定时器T0的高四位=0
	TL0=0;  //定时器T0的低四位=0
	EA=1;  //系统允许有开放的中断
	ET0=1; //允许T0中断
	TR0=1; //开启中断,启动定时器
}
//*******************主函数**************************
//***************************************************
void main()
{	
	lcd_init();      //调用液晶屏初始化子函数
	ds1302_init();   //调用DS1302时钟的初始化子函数
	init();          //调用定时计数器的设置子函数
	led=0;           //打开LCD的背光电源
    buzzer=0;//蜂鸣器长响一次
    delay(80);
    buzzer=1; 
	while(1)  //无限循环下面的语句:
	{		
    	keyscan();      //调用键盘扫描子函数
    }
}
/*************通过定时中断实现定是独处并显示数据******************/
void t0() interrupt 1  //取得并显示日历和时间
{	
   	//Init_DS18B20();//温度传感器DS18b2初始化子函数,在头文件中
   	flag=ReadTemperature();//将18b2头文件运行返回的函数结果送到变量FLAG中,用于显示
  	//读取秒时分周日月年七个数据(DS1302的读寄存器与写寄存器不一样):
	miao=BCD_Decimal(read_1302(0x81));
	fen=BCD_Decimal(read_1302(0x83));
	shi=BCD_Decimal(read_1302(0x85));
	ri=BCD_Decimal(read_1302(0x87));
	yue=BCD_Decimal(read_1302(0x89));
	nian=BCD_Decimal(read_1302(0x8d));
	week=BCD_Decimal(read_1302(0x8b));
	//显示温度、秒、时、分数据:
	write_temp(12,flag);//显示温度,从第二行第12个字符后开始显示
	write_sfm(8,miao);//秒,从第二行第8个字后开始显示(调用时分秒显示子函数)
	write_sfm(5,fen);//分,从第二行第5个字符后开始显示
	write_sfm(2,shi);//小时,从第二行第2个字符后开始显示
	//显示日、月、年数据:
	write_nyr(9,ri);//日期,从第二行第9个字符后开始显示
	write_nyr(6,yue);//月份,从第二行第6个字符后开始显示
	write_nyr(3,nian);//年,从第二行第3个字符后开始显示
	write_week(week);
	/***********整点报时程序************/
	if(fen==0&&miao==0)
		if(shi<22&&shi>6 )
		{
			buzzer=0;//蜂鸣器短响一次
			delay(20);
			buzzer=1;
		}
		/**************闹钟程序: 将暂停键按下停止蜂鸣********************/
		if(shi1==shi&&fen1==fen&&miao==0)
		{		
			clock=1; 
		}	
		if(clock==1)
		{
			buzzer=0;//蜂鸣器短响一次
			delay(20);
			buzzer=1;
		}
		if(CLO==0)	/*按下p1.4停止蜂鸣*/
			clock=0;	
}
typedef unsigned char uchar;
typedef unsigned int  uint;
sbit DQ=P3^3;                // 定义DQ引脚为P3.3
/*******************************  延时函数 ********************************
*  功能:在11.059MHz的晶振条件下调用本函数需要24μs ,然后每次计数需16μs 
**************************************************************************/
void DS18_delay(int useconds) 
{
	int s;
	for(s=0;s<useconds;s++);
}
/*******************************  复位函数 *******************************
* 功能:完成单总线的复位操作。
* 复位时间为480μs,因此延时时间为(480-24)/16 = 28.5,取29μs。
* 经过70μs之后检测存在脉冲,因此延时时间为(70-24)/16 = 2.875,取3μs。
**************************************************************************/
uchar ow_reset(void) 
{
	uchar presence;
	DQ=0;  			// 将 DQ 线拉低
	DS18_delay(29); 			// 保持 480μs
	DQ=1; 			// DQ返回高电平
	DS18_delay(3); 			// 等待存在脉冲
	presence=DQ; 		// 获得存在信号
	DS18_delay(25); 			// 等待时间隙结束
	return(presence); 	// 返回存在信号,0 = 器件存在, 1 = 无器件
}
/****************************** 位写入函数 *******************************
* 功能:向单总线写入1位值:bitval
*************************************************************************/
void write_bit(char bitval) 
{
	DQ=0; 				// 将DQ 拉低开始写时间隙
	if(bitval==1) 
		DQ=1; 	// 如果写1,DQ 返回高电平
	DS18_delay(5); 				// 在时间隙内保持电平值,
	DQ=1; 	// DS18_delay函数每次循环延时16μs,因此DS18_delay(5) = 104μs
}	
/**************************** 字节写入函数 *******************************
* 功能:向单总线写入一个字节值:val
*************************************************************************/
void ds18write_byte(char val) 
{
	uchar i;
	uchar temp;
	for(i=0;i<8;i++) 
	{	// 写入字节, 每次写入一位 
		temp=val>>i; 		
		temp&=0x01; 		
		write_bit(temp); 
	}
	DS18_delay(5);
}
/**************************** 位读取函数 ********************************
* 功能:从单总线上读取一位信号,所需延时时间为15μs,因此无法调用前面定义
* 的DS18_delay()函数,而采用一个for()循环来实现延时。
* ***********************************************************************/
uchar read_bit(void) 
{
	uchar i;
	DQ=0;     		 //将DQ 拉低开始读时间隙
	DQ=1; 			// then return high
	for(i=0;i<3;i++); 	// 延时15μs
	return(DQ); 			// 返回 DQ 线上的电平值
}
/**************************** 字节读取函数 *******************************
* 功能:从单总线读取一个字节的值
*************************************************************************/
uchar DSread_byte(void) 
{
	uchar i;
	uchar value=0;
	for(i=0;i<8;i++) 
	{  				// 读取字节,每次读取一个字节
		if(read_bit()) 
			value|=0x01<<i; 	// 然后将其左移
		DS18_delay(6); 					
	}
	return(value);
}
/******************************* 读取温度函数 *****************************
* 功能:如果单总线节点上只有一个器件则可以直接掉用本函数。如果节点上有多个器
*      件,为了避免数据冲突,应使用Match ROM函数来选中特定器件。
* 注: 本函数是根据DS1820的温度数据格式编写的,若用于DS18B20,必须根据
*      DS18B20的温度数据格式作适当修改。
**************************************************************************/unsigned 

系统论文(由于论文字数太多,在这里只介绍部分)


第一章  工作原理

1.1设计目标

1.1.1基本功能

此万年历主要具有:年、月、日、星期、时、分、秒、温度显示。

主要功能:

1、万年历

2、时间

3、星期

4、温度显示

(以上四项内容同时显示)

5、四键调时(设置键、上调键、下调键、关音按键)

第二章  硬件设计与原理

以AT89C51单片机为核心,起着控制作用。系统包括LCD1602液晶显示电路、复位电路、时钟电路、按键电路、温度传感器电路、时钟芯片电路。设计思路分为七个模块:复位电路、晶振电路模块、AT89C51、LCD1602液晶显示电路、按键电路、温度传感器电路、时钟芯片电路这七个模块。

2.1 总设计框图

2.2 硬件设计分析

2.2.1 电源的设计

系统电源使用直流5伏。

由电脑USB接口提供电源。

USB是通用串行总线(Universal Serial Bus)接口的简称。它是目前使用比较广泛的电脑接口之一,主要版本有1.0、1.1和最新的2.0三种版本。根据USB总线的工业标准,它可以提供额定功率为5V/500mA的电源供USB设备使用。

2.2.2 单片机最小系统

51单片机是对目前所有兼容intel 8031指令系统的单片机的统称。该系列单片机的始祖是intel的8031单片机,后来随着技术的发展,成为目前广泛应用的8为单片机之一。单片机是在一块芯片内集成了CPU、RAM、ROM、定时器/计数器和多功能I/O口等计算机所需要的基本功能部件的大规模集成电路,又称为MCU。51系列单片机内包含以下几个部件:

一个8位CPU;一个片内振荡器及时钟电路;

4KB的ROM程序存储器;

一个128B的RAM数据存储器;

寻址64KB外部数据存储器和64KB外部程序存储空间的控制电路;

32条可编程的I/O口线;

两个16位定时/计数器;

一个可编程全双工串行口;

5个中断源、两个优先级嵌套中断结构。

如图2-2-1所示为AT89C51单片机基本构造,其基本性能介绍如下:

图2-2-1 AT89C51单片机

AT89C51本身内含40个引脚,32个外部双向输入/输出(I/O)端口,同时内含2个外中端口,3个16位可编程定时计数器,2个全双工串行通信口,AT89C51可以按照常规方法进行编程,但不可以在线编程。其将通用的微处理器和Flash存储器结合在一起,特别是可反复擦写的Flash存储器可有效地降低开发成本。

AT89C51的主要特性如下表所示:

兼容MCS—51指令系统

32个可编程I/O线

4k字节可编程闪烁存储器

可编程UARL通道

三个16位可编程定时/计数器中断

时钟频率0-24MHz

2个外部中断源,共8个中断源

256×8bit内部RAM

2个读写中断口线

可直接驱动LED

软件设置睡眠和唤醒功能

低功耗空闲和掉电模式

表2-2-1  AT89C51主要功能描述

AT89C51为40脚双列直插封装的8位通用微处理器,采用工业标准的C51内核,在内部功能及管脚排布上与通用的8xc52相同,其主要用于会聚调整时的功能控制。功能包括对会聚主IC内部寄存器、数据RAM及外部接口等功能部件的初始化,会聚调整控制,会聚测试图控制,红外遥控信号IR的接收解码及与主板CPU通信等。主要管脚有:XTAL1(19脚)和XTAL2(18脚)为振荡器输入输出端口,外接12MHz 晶振。RST/Vpd(9脚)为复位输入端口,外接电阻电容组成的复位电路。VCC(40脚)和VSS(20脚)为供电端口,分别接+5V电源的正负端。P0~P3 为可编程通用I/O脚,其功能用途由软件定义,在本设计中,P0端口(32~39脚)被定义为N1功能控制端口,分别与N1的相应功能管脚相连接,13脚定义为IR输入端,10脚和11脚定义为I2C总线控制端口,分别连接N1的SDAS(18脚)和SCLS(19脚)端口,12脚、27脚及28脚定义为握手信号功能端口,连接主板CPU的相应功能端,用于当前制式的检测及会聚调整状态进入的控制功能。

P0口:P0口是一组8位漏极开路型双向I/O 口,也即地址/数据总线复用口。作为输出口用时,每位能吸收电流的方式驱动8个TTL逻辑门电路,对端口P0写“1”时,可作为高阻抗输入端用。在访问外部数据存储器或程序存储器时,这组口线分时转换地址(低8位)和数据总线复用,在访问期间激活内部上拉电阻。在Flash 编程时,P0口接收指令字节,而在程序校验时,输出指令字节,校验时,要求外接上拉电阻。

P1口:P1是一个带内部上拉电阻的8位双向I/O口,P1的输出缓冲级可驱动(吸收或输出电流)4个TTL逻辑门电路。对端口写“1”,通过内部的上拉电阻把端口拉到高电平,此时可作输入口。作输入口使用时,因为内部存在上拉电阻,某个引脚被外部信号拉低时会输出一个电流(IIL)。与AT89C51不同之处是,P1.0和P1.1还可分别作为定时/计数器2 的外部计数输入(P1.0/T2)和输入(P1.1/T2EX)。Flash编程和程序校验期间,P1接收低8位地址。

P2口:P2是一个带有内部上拉电阻的8 位双向I/O口,P2的输出缓冲级可驱动(吸收或输出电流)4个TTL逻辑门电路。对端口P2写“1”,通过内部的上拉电阻把端口拉到高电平,此时可作输入口,作输入口使用时,因为内部存在上拉电阻,某个引脚被外部信号拉低时会输出一个电流(IIL)。在访问外部程序存储器或16位地址的外部数据存储器(例如执行MOVX @DPTR指令)时,P2口送出高8位地址数据。在访问8位地址的外部数据存储器(如执行MOVX @RI指令)时,P2口输出P2锁存器的内容。Flash编程或校验时,P2亦接收高位地址和一些控制信号。

P3口:P3口是一组带有内部上拉电阻的8位双向I/O口。P3口输出缓冲级可驱动(吸收或输出电流)4个TTL逻辑门电路。对P3口写入“1”时,它们被内部上拉电阻拉高并可作为输入端口。此时,被外部拉低的P3口将用上拉电阻输出电流(IIL)。P3口除了作为一般的I/O口线外,更重要的用途是它的第二功能P3口还接收一些用于Flash 闪速存储器编程和程序校验的控制信号。

RST:复位输入。当振荡器工作时,RST引脚出现两个机器周期以上高电平将使单片机复位。

ALE/PROG:当访问外部程序存储器或数据存储器时,ALE(地址锁存允许)输出脉冲用于锁存地址的低8位字节。一般情况下,ALE仍以时钟振荡频率的1/6输出固定的脉冲信号,因此它可对外输出时钟或用于定时目的。要注意的是:每当访问外部数据存储器时将跳过一个AL脉冲。对Flash存储器编程期间,该引脚还用于输入编程脉冲(PROG)。如有必要,可通过对特殊功能寄存器(SFR)区中的8EH单元的D0位置位,可禁止ALE操作。该位置位后,只有一条 MOVX 和MOVC指令才能将ALE激活。此外,该引脚会被微弱拉高,单片机执行外部程序时,应设置ALE禁止位无效。

PSEN:程序储存允许(PSEN)输出是外部程序存储器的读选通信号,当AT89C51由外部程序存储器取指令(或数据)时,每个机器周期两次PSEN有效,即输出两个脉冲。在此期间,当访问外部数据存储器,将跳过两次PSEN信号。

EA/VPP:外部访问允许。欲使CPU仅访问外部程序存储器(地址为0000H—FFFFH),EA端必须保持低电平(接地)。需注意的是:如果加密位LB1被编程,复位时内部会锁存EA端状态。如EA端为高电平(接Vcc端),CPU则执行内部程序存储器中的指令。Flash存储器编程时,该引脚加上+12V的编程允许电源Vpp,当然这必须是该器件是使用12V编程电压Vpp。

XTAL1:振荡器反相放大器的及内部时钟发生器的输入端。

XTAL2:振荡器反相放大器的输出端。

单片机最小原理图如图2-2-2所示。

图2-2-2 单片机最小系统

单片机最小系统说明:

时钟信号的产生:在MCS-51芯片内部有一个高增益反相放大器,其输入端为芯片引脚XTAL1,其输出端为引脚XTAL2。而在芯片的外部,XTAL1和XTAL2之间跨接晶体振荡器和微调电容,从而构成一个稳定的自激振荡器,这就是单片机的时钟振荡电路。

时钟电路产生的振荡脉冲经过触发器进行二分频之后,才成为单片机的时钟脉冲信号。

一般地,电容C2和C3取30pF左右,晶体的振荡频率范围是1.2-12MHz。如果晶体振荡频率高,则系统的时钟频率也高,单片机的运行速度也就快。

单片机复位使CPU和系统中的其他功能部件都处在一个确定的初始状态下,并从这个状态开始工作。单片机复位条件:必须使9脚加上持续两个机器周期(即24个振荡周期)的高电平。


资源下载

如果有需要这个系统的源码、仿真、论文等资源的可以私信我。感谢你的阅读~

一、 功能分析 硬件的设计采用89ATC51单片机为核心器件。并辅助复位电路,驱动电路,数码管及晶体管显示部分。通过中断扩展实现交通灯系统特殊情况的转换。 软件设计部分分为一个主程序和两个中断子程序,一个用于有紧急车辆通过时,系统要能禁止普通车辆通行,实行中断可使A(东西道)、B(南北道)两道均亮红灯;另一个用于一道有车而另一道无车时,通过控制交通灯系统能立即让有车道放行,假如A道有车B道无车,长按K0可以控制交通灯系统能立即让东西道放行;假如南北道有车东西道无车,长按K1可以控制交通灯系统能立即南北道放行。 十字路口的交通灯在工作时应具有如下特点:红灯表示该条道路禁止通行;黄灯表示该条道路上未过停车线的车辆禁止通行,已过停车线的车辆继续通行;绿灯亮表示该条道路允许通行。 本设计利用单片机控制可以实现以下功能: (1)A道和B道上均有车辆要求通过时,A,B道轮流放行。A道放行5分钟(调试时改为5秒钟),B道放行4分钟(调试时改为4秒钟)。 (2)一道有车而另一道无车(实验时用开关K0和K1控制),交通灯控制系统能立即让有车道放行。 (3)有紧急车辆要求通过时,系统要能禁止普通车辆通行,A,B道均为红灯,紧急车由K2开关模拟。 (4)绿灯转换为红灯时黄灯亮1秒钟
微机原理及接口技术课程设计交通灯 序言 十字道口的红绿灯是交通法规的无声命令,是司机和行人的行为准则。十字道口的交通红绿灯控制是保证交通安全和道路畅通的关键。当前,国内大多数城市正在采用“自动”红绿交通灯,它具有固定的“红灯—绿灯”转换间隔,并自动切换。它们一般由“通行与禁止时间控制显示、红黄绿三色信号灯和方向指示灯”三部分组成。在交通灯的通行与禁止时间控制显示中,通常要么东西、南北两方向各50秒;要么根据交通规律,东西方向60秒,南北方向40秒,时间控制都是固定的。交通灯的时间控制显示,以固定时间值预先“固化”在单片机中,每次只是以一定周期交替变化。但是,实际上不同时刻的车辆流通状况是十分复杂的,是高度非线性的、随机的,还经常受认为因素的影响。采用定时控制经常造成道路有效应用时间的浪费,出现绿灯方向车辆较少,红灯方向车辆积压。它不顾当前道路上交通车辆数的实际情况变化,其最大的缺陷就在于当路况发生变化时,不能满足司机与路人的实际需要,轻者造成时间上的浪费,重者直接导致交通堵塞,导致城市交通效率的下降。 目前,有一种使用“模糊控制”技术控制交通灯的方法。能够根据十字路口两个方向上车辆动态状况,自动判断红绿灯时间间隔,以保证最大车流量,减少道口的交通堵塞。但是却不像定时控制,能用数字显示器显示当前灯色剩余时间,以便于驾驶员随时掌握自己的驾驶动作,及
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱喝奶茶的喵喵

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值