精华内容
下载资源
问答
  • 随着生活节奏的日益加快,人们的时间观也越来越重,同时对电子钟表、日历的需求也随之提高。因此,研究实用电子时钟及其扩展应用,有着非常现实的意义,具有很大的实用价值。
  • 要1 概论1.1 万年历发展背景1.2 国内外现状、发展2 系统基本方案选择和...功能分析及模块电路设计3.2.1 时钟模块3.2.2 温度模块3.2.3 显示模块3.2.4 独立键盘模块3.2.5 蜂鸣器模块4 系统软件设计4.1 万年历软件系统的...

    随着电子技术的发展,人类不断研究,不断创新纪录。万年历目前已经不再局限于以书本形式出现。以电脑软件或者电子产品形式出现的万年历被称为电子万年历。与传统书本形式的万年历相比,电子万年历得到了越来越广泛的应用,采用电子时钟作为时间显示已经成为一种时尚。目前市场上各式各样的电子时钟数不胜数,但多数是只针对时间显示,功能单一不能满足人们日常生活需求。

    本设计将制作一种基于单片机控制的带实时温度显示、具有定时功能的电子万年历。传统的电子日历大都体积大,功耗大,显示不准确等特点。为了缩小体积,减小功耗,使其变得小巧灵敏,本设计加入了时钟芯片DS1302,可对时间进行准确记时,同时可设置定时时间,实现定时功能。另外本设计具有显示实时温度的功能。传统的温度传感器系统大都采用放大、调理、A/D转换,转换后的数字信号送入计算机处理,处理电路复杂、可靠性相对较差,占用计算机的资源比较多。本设计将采用DS18B20一线制数字温度传感器,可将温度信号直接转换成数字信号送给微处理器,电路简单,成本低,实现了时间温度同时显示的效果。最后,温度和时间都将通过12864液晶显示器进行显示。测试表明系统达到了设计要求的各项功能,各部分工作正常。


    # 摘 要

    本设计将制作一种基于单片机控制的带实时温度显示、具有定时功能的电子万年历。传统的电子日历大都体积大,功耗大,显示不准确等特点。为了缩小体积,减小功耗,使其变得小巧灵敏,本设计加入了时钟芯片DS1302,可对时间进行准确记时,同时可设置定时时间,实现定时功能。另外本设计具有显示实时温度的功能。传统的温度传感器系统大都采用放大、调理、A/D转换,转换后的数字信号送入计算机处理,处理电路复杂、可靠性相对较差,占用计算机的资源比较多。本设计将采用DS18B20一线制数字温度传感器,可将温度信号直接转换成数字信号送给微处理器,电路简单,成本低,实现了时间温度同时显示的效果。最后,温度和时间都将通过12864液晶显示器进行显示。测试表明系统达到了设计要求的各项功能,各部分工作正常。
    关键词 : 时钟 温度检测 单片机 温度 LCD12864 DS1302

    1 概论

    1.1 万年历发展背景

    随着电子技术的发展,人类不断研究,不断创新纪录。万年历目前已经不再局限于以书本形式出现。以电脑软件或者电子产品形式出现的万年历被称为电子万年历。与传统书本形式的万年历相比,电子万年历得到了越来越广泛的应用,采用电子时钟作为时间显示已经成为一种时尚。目前市场上各式各样的电子时钟数不胜数,但多数是只针对时间显示,功能单一不能满足人们日常生活需求。

    1.2 国内外现状、发展

    随着电子技术的迅速发展,特别是随大规模集成电路出现,给人类生活带来了根本性的改变。尤其是单片机技术的应用产品已经走进了千家万户。电子万年历的出现给人们的生活带来的诸多方便。
    万年历中使用的LCD的应用很广泛,如手表上的液晶显示屏,仪表仪器上的液晶显示器或者是电脑笔记本上的液晶显示器,都使用了LCD。在一般的办公设备上也很常见,如传真机,复印机,以及一些娱乐器材玩具等也常常见到LCD的足迹。字符型液晶显示模块是一种专门用于显示字母,数字,符号等的点阵式液晶显示模块。在显示器件上的设计,它是由若干个5×7或5×11等点阵符位组成。每一个点阵字符位都可以显示一个字符。点阵字符位之间有一空点距的间隔起到了字符间距和行距的作用。目前市面上常用的有16字×1行,16字×2行,20字×2行和40字×2行等的字符模块组。这些LCD虽然显示字数各不相同,但是都具有相同的输入输出界面。
    随着单片机的发展,电子万年历呈现了微型化 ,功能丰富化的趋势,而且价格在不断下降,考虑到资源问题,现在的设计设计的万年历都采用了节能设计方案,万年历对人们的生活有着十分重要的作用,所以电子万年历还是有很大的发展前景的。

    2 系统基本方案选择和论证

    2.1 单片机芯片的选择方案和论证

    方案一:
    采用89C51芯片作为硬件核心,89C51是一种带4K字节闪烁可编程可擦除只读存储器,采用Flash ROM,内部具有4KB ROM 存储空间,能于3V的超低压工作,而且与MCS-51系列单片机完全兼容,与工业标准的MCS-51指令集和输出管脚相兼容。由于将多功能8位CPU和闪烁存储器组合在单个芯片中,89C51是一种高效微控制器,51单片机为很多嵌入式控制系统提供了一种灵活性高且价廉的方案但是运用于电路设计中时由于不具备在线编程(ISP)技术,当在对电路进行调试时,由于程序的错误修改或对程序的新增功能需要烧入程序时,对芯片的多次拔插可能对芯片造成一定的损坏,目前该型号芯片已经停产。
    方案二:
    采用AT89S52单片机,AT89S52单片机是ATMEL生产的单片机,是新一代8051单片机,指令代码完全兼容传统8051。内部集成看门狗电路。AT89S52单片机内部有8KB的程序Flash存储器。由于我们设计的万年历烧写文件大概在7KB左右 ,而AT89S52单片机的程序Flash为8KB,不用再外接程序存储器了。

    经过综合比较最终选择方案二,即选择AT89S52作为主控制器。

    2.2 显示模块的选择方案和论证

    方案一:
    LCD12864液晶是一种具有8位并行接口方式的点阵图形液晶显示模块;其显示分辨率为128×64。利用该模块灵活的接口方式和简单、方便的操作指令,可构成全中文人机交互图形界面。可以显示16×16点阵的汉字,也可完成图形显示,低电压低功耗是其又一显著特点。由该模块构成的液晶显示方案与同类型的图形点阵液晶显示模块相比,不论硬件电路结构或显示程序都要简洁得多。万年历要求显示年月日、时分秒、星期、和农历。LCD12864液晶可以完成设计的要求 。
    方案二:
    系统采用LED显示。LED应用可分为两大类:一是LED单管应用,包括背光源LED,红外线LED等;另外就是LED显示屏,目前,中国在LED基础材料制造方面与国际还存在着一定的差距,但就LED显示屏而言,中国的设计和生产技术水平基本与国际同步。LED显示屏是由发光二极管排列组成的显示器件。它采用低电压扫描驱动,具有:耗电少、使用寿命长、成本低、亮度高、故障少、视角大、可视距离远等特点。采用LED数码管动态扫描.价格上比较经济实惠,但不能显示文字,性价比不是很高,操作起来比较液晶显示来说略显繁琐,所以也不用此种作为显示。
    经过综合比较最终选择方案一,即选择LCD12864液晶显示屏。

    2.3 时钟芯片的选择方案和论证

    方案一:
    采用单片机定时。单片机集成度高、功能强、可靠性高、体积小、功耗低、使用方便、价格低廉等一系列优点,单片机的应用领域已从面向工业控制、通讯、交通、智能仪表等迅速发展到家用消费产品、办公自动化、汽车电子、PC机外围以及网络通讯等广大领域。
    直接采用单片机定时计数器提供秒信号,计数的脉冲由外部提供,定时的脉冲由外部晶振提供,定时加1的周期为一个机器周期;定时时间与初值和晶振频率有关。使用程序实现年、月、日、星期、时、分、秒计数。采用此种方案减少芯片的使用,节约成本,但程序复杂度较高。
    方案二:
    采用DS1302时钟芯片。DS1302 是美国DALLAS公司推出的一种高性能、低功耗、带RAM的实时时钟电路,它可以对年、月、日、星期、时、分、秒进行计时,具有闰年补偿功能,工作电压为2.5V~5.5V。采用双电源供电(主电源和备用电源),可设置备用电源充电方式,提供了对后背电源进行涓细电流充电的能力。DS1302用于数据记录,特别是对某些具有特殊意义的数据点的记录上,能实现数据与出现该数据的时间同时记录,因此广泛应用于测量系统中。采用三线接口与CPU进行同步通信,并可采用突发方式一次传送多个字节的时钟信号或RAM数据。DS1302内部有一个31×8的用于临时性存放数据的RAM寄存器。采用DS1302只需要写出驱动程序,调用程序读出寄存器内数据经过简单的变换就可以输出万年历的数据。
    经过综合比较最终选择方案二,即采用DS1302时钟芯片。

    2.4 温度传感器的选择方案和论证

    方案一:
    采用热敏电阻作为温度传感器。热敏电阻是开发早、种类多、发展较成熟的敏感元器件。热敏电阻由半导体陶瓷材料组成,利用的原理是温度引起电阻变化。热敏电阻的主要特点是:灵敏度较高,其电阻温度系数要比金属大10~100倍以上;工作温度范围宽,常温器件适用于-55℃~315℃,高温器件适用温度高于315℃(目前最高可达到2000℃)低温器件适用于-273℃~55℃;体积小,能够测量其他温度计无法测量的空隙、腔体及生物体内血管的温度;使用方便,电阻值可在0.1~100kΩ间任意选择;易加工成复杂的形状,可大批量生产;稳定性好、过载能力强。由于半导体热敏电阻有独特的性能,所以在应用方面它不仅可以作为测量元件(如测量温度、流量、液位等),还可以作为控制元件(如热敏开关、限流器)和电路补偿元件。热敏电阻广泛用于家用电器、电力工业、通讯、军事科学、宇航等各个领域,发展前景极其广阔。
    使用热敏电阻作为传感器,用热敏电阻与一个相应阻值电阻相串联分压,利用热敏电阻阻值随温度变化而变化的特性,采集这两个电阻变化的分压值,并进行A/D转换。此设计方案需用A/D转换电路,增加硬件成本而且热敏电阻的感温特性曲线并不是严格线性的,会产生较大的测量误差。
    方案二:
    采用DS18B20温度传感器。在应用与高精度、高可靠性的场合时DALLAS(达拉斯)公司生产的DS18B20温度传感器当仁不让。超小的体积,超低的硬件开消,抗干扰能力强,精度高,附加功能强,使得DS18B20更受欢迎。对于我们普通的电子爱好者来说,DS18B20的优势更是我们学习单片机技术和开发温度相关的小产品的不二选择。这是世界上第一片支持“一线总线”接口的温度传感器。DS18B20数字温度计提供9位(二进制)温度读数,指示器件的温度。信息经过单线接口送入DS18B20或从DS18B20送出,因此从单片机到DS18B20仅需一条线连接即可。它可在1秒钟(典型值)内把温度变换成数字
    经过综合比较最终选择方案二,即采用采用DS18B20温度传感器。

    2.5 电路设计最终方案确定

    最终选择单片机AT89S52作为主控制器;选择LCD12864型液晶作为显示模块,此模块可以显示字母、数字符号、中文字型及图形,具有绘图及文字画面混合显示功能,可实现设计对“年”、“月”、“日”、“室内温度”、“度”五个词语的显示要求;选择采用DS1302时钟芯片,使程序实现年、月、日、星期、时、分、秒的显示。采用DS18B20温度传感器,可以对温度做出比较精确的测量,而且和单片机通讯只要一个IO,连接方便。

    3 系统硬件电路设计

    3.1 系统功能模块划分及整体电路图

    根据系统功能要求,可大致画出系统所需硬件结构框图如图3-1-1所示:
    在这里插入图片描述

    图3-1-1 系统功能模块图

    主控模块采用性价比较高的AT89S52单片机芯片,在其内部烧写好程序,可通过程序的运行控制测温模块进行测温;测温模块主要是由DS18B20构成,将其与所测对象进行接触即可获取被测对象的温度数据,而所测得的温度和时钟芯片测得的实时日历将通过显示模块的液晶显示器以数字形式显示;单片机调用程序,读取DS1302内寄存器,可以得到万年历的时间数据,经过程序处理就可以输出在LCD上;键盘电路可对实时日历进行调整;蜂鸣器可以在闹钟定时中,作为声音提醒。
    作品整体电路图如图3-1-2所示
    在这里插入图片描述

    图3-1-2 整体电路图

    3.2 各单元模块功能分析及模块电路设计

    3.2.1 时钟模块

    DS1302的工作原理和单片机的接口:
    DS1302为美国DALLAS公司的一种实时时钟芯片,主要特点是采用串行数据传输,可为掉电保护电源提供可编程的充电功能,并且可以关闭充电功能。采用32.768Hz晶振。它可以对年、月、日、星期、时、分、秒进行计时,且具有闰年补偿等多种功能。DS1302 用于数据记录,特别是对某些具有特殊意义的数据点的记录上,能实现数据与出现该数据的时间同时记录。这种记录对长时间的连续测控系统结果的分析以及对异常数据出现的原因的查找有重要意义。在本设计中,它的实际电路图如图3-2所示:
    在这里插入图片描述

    图3-2 DS1302与单片机的连接
    DS1302需要外接32.768K的晶振,1号引脚接主电源VCC(5V)电源,8号引脚接备用电池(3V),当主电源掉电后,备用电源为DS1302提供电源,维持DS1302内数据不丢失,这正是时钟芯片所必须的特性。

    3.2.2 温度模块

    传统的温度传感器系统大都采用放大、调理、A/D转换,转换后的数字信号送入计算机处理,处理电路复杂、可靠性相对较差,占用计算机的资源比较多,本设计测温模块采用一线制总线数字温度传感器DS18B20,可将温度信号直接转换成数字信号送给微处理器,电路简单,成本低,其电路原理图如图3-3所示:
    在这里插入图片描述

    图3-3 DS18B20温度模块
    从图中可看出,将温度传感器的一线制总线通过端口2与本设计主控芯片STC12C5A6S2的端口标号为DS18B20的相连即可实现相互之间的通信。设计中的测温元件采用的是DS18B20测温元件,DS18B20是由DALLAS(达拉斯)公司生产的一种温度传感器。超小的体积,超低的硬件开消,抗干扰能力强,精度高,附加功能强,使得DS18B20很受欢迎。这是世界上第一片支持“一线总线”接口的温度传感器。DS18B20数字温度计提供9位(二进制)温度读数,指示器件的温度。信息经过单线接口送入DS18B20或从DS18B20送出,因此从单片机到DS18B20仅需一条线连接即可。它可在1秒钟(典型值)内把温度变换成数字。

    3.2.3 显示模块

    本设计显示模块主要采用LCD12864液晶显示器,其电路原理图如下:
    在这里插入图片描述

    图3-5 LCD12864模块
    LCD12864液晶显示器通过数据端口也即端口7~14与主控芯片AT89S52的I/O端口P3相连接实现数据与指令的传输,再通过控制端口RS、RW、EN也即端口4~6与主控芯片P1.5,P1.6,P1.7端口相接实现对数据和指令传输的控制 。显示模块采用12864液晶显示器可实现对温度和时间的直接显示,清晰明了。

    3.2.4 独立键盘模块

    键盘是人与万年历实现信息交互的接口,本设计中,我们采用3个独立键盘,电路原理如下图3-9:
    在这里插入图片描述

    图3-9 独立键盘
    当按键按下,与主控芯片连接的端口被降为低电平,按键松开则也升为高电平。按键采用的是Tack Switch按钮开关,它具有自动恢复(弹回)的功能。当我们按下按钮时,其中的接点接通(或切断),放开按钮后,接点恢复为切断(或接通)。按照尺寸区分,电子电路或微型计算机所使用的Tack Swith可分为8mm、10mm、12mm等。虽然Tack Switch有4个引脚,但实际上,其内部只有一对a接点,即其中两个引脚是内部相连通的,而另外两个引脚内部也是相连通的。3个按键实现了开机模式选择,日期调节等功能,独立按键的引入使得体现了本设计的人性化,智能化,功能的强大。

    3.2.5 蜂鸣器模块

    蜂鸣器模块是本设计中体现人机交互的又一大设计亮点,其电路原理图如下
    在这里插入图片描述

    图3-10 蜂鸣器模块
    本设计里,我们采用有源蜂鸣器,由于蜂鸣器的工作电流一般比较大,以至于单片机的I/O 口是无法直接驱动的,所以要利用放大电路来驱动,我们使用三极管来放大电流,驱动蜂鸣器,此模块只要通过BELL(连接到到单片机P2.7)输入的PWM波既可以使蜂鸣器分出声音,我们设计的这款万年历可以在闹钟定时中作为声音提醒信号。

    4 系统软件设计

    4.1 万年历软件系统的流程图

    在这里插入图片描述

    图4-1 系统软件流程图
    当接通电源开始工作后,单片机中的程序开始运行,将对DS18B20进行初始化,以便和单片机芯片达成通信协议。完成初始化后,由于本系统只有一个测温元件,单片机会向其发出跳过RAM指令,接下来便可向其发送操作指令,启动测温程序,测温过程完成后,发出温度转换指令,从而便可将温度转化成数字模式进行显示读取;同时DS1302将读取时分秒星期以及年月日寄存器然后通过液晶显示实时时间、星期及日期;键盘电路中按键可对实时日历时钟进行调整。

    4.2 万年历软件系统的程序部分

    DS1302程序代码

    /*
    * DS1302 时钟芯片
    */
    #include "DS1302.h"		//包含头文件
    /*
     * 写一个字节
    */
    void write_ds1302_byte(uint8 dat) 
    {
    	uint8 i;	
    	for (i=0;i<8;i++) //循环8次
    	{ 	SDA = dat & 0x01;   
    		SCK = 1;     // SCK端口置1
    		dat >>= 1;
    		SCK = 0;	// SCK端口置0
    	}
    }
    
    /*
     * 读一个字节
    */
    uint8 read_ds1302_byte(void) 
    {
    	uint8 i, dat=0;
    	
    	for (i=0;i<8;i++)//循环8次
    	{	
    		dat >>= 1;
    		if (SDA)
    			dat |= 0x80;
    		SCK = 1;// SCK端口置1
    		SCK = 0;// SCK端口置0
    	} 
    
    	return dat;
    }
    
    void reset_ds1302(void)
    {
    	RST = 0;// RST端口置0
    	SCK = 0;// SCK端口置0
    	RST = 1;// RST端口置1
    }
    
    /*
     * 清除写保护
    */
    void clear_ds1302_WP(void) 
    {
    	reset_ds1302();
    	RST = 1;// RST端口置1
    	write_ds1302_byte(0x8E); 
    	write_ds1302_byte(0);
    	SDA = 0;// SDA端口置0
    	RST = 0;// RST端口置0
    }
    
    /*
     * 设置写保护
    */
    void set_ds1302_WP(void) 
    {
    	reset_ds1302();//复位1302
    	RST = 1;// RST端口置1
    	write_ds1302_byte(0x8E);
    	write_ds1302_byte(0x80);
    	SDA = 0;// SDA端口置0
    	RST = 0;// RST端口置0
    }
    
    
    /*
     * 设定时钟数据 (秒分时日月周年)
    */
    void set_time(uint8 *timedata)
    {
    	uint8 i, tmp, tmps[7];
    
    	for (i=0; i<7; i++)  // 转化为BCD格式
    	{
    		tmp = timedata[i] / 10;
    		tmps[i] = timedata[i] % 10;
    		tmps[i] = tmps[i] + tmp*16;
    	}
    
    	clear_ds1302_WP();//取消写保护
    
    	reset_ds1302();//复位芯片
    	RST = 1;// RST端口置1
    	write_ds1302_byte(DS1302_W_ADDR);
    	for (i=0; i<7; i++)//循环7次
    	{
    		write_ds1302_byte(tmps[i]);
    		delay(10);//延时
    	}
    	write_ds1302_byte(0);
    	SDA = 0;// SDA端口置0
    	RST = 0;// RST端口置0
    
    	set_ds1302_WP();//设置写保护
    }
    
    /*
     * 读时钟数据(秒分时日月周年)
    */
    void read_time(uint8 *timedata)
    {
    	uint8 i, tmp;
    
    	clear_ds1302_WP();//取消写保护
    
    	reset_ds1302();	//复位芯片
    	RST = 1;		//ST端口置1
    	write_ds1302_byte(DS1302_R_ADDR);
    	for (i=0; i<7; i++)//循环7次
    	{
    		timedata[i] = read_ds1302_byte();
    		delay(10);	//延时
    	}
    	SDA = 0;// SDA端口置0
    	RST = 0;// RST端口置0
    
    	set_ds1302_WP();//设置写保护
    
    	for (i=0; i<7; i++)//循环7次
    	{
    		tmp = timedata[i];
    		timedata[i] = (tmp/16%10)*10;
    		timedata[i] += (tmp%16);
    	}
    }
    
    

    LCD12864程序代码

    /*
     * LCD128*64
    */
    
    #include "LCD.h"
    #include "word.h"
    #define Page_Add   0xb8
    #define Col_Add    0x40
    #define Disp_On    0x3f
    #define Disp_Off   0x3e
    #define Start_Line 0xc0
    /*
     * 12864判忙
    */
    void chekbusy12864(void)
    {
    	uint8 dat;
    
    	RS = 0;       	//指令模式 
    	RW = 1;       	//读数据
    	do{
    		P3 = 0;
    		E = 1;		//E端口置1
    	    _nop_();	//延时1us
    		dat = P3 & 0x80;
    		E = 0;		//E端口置0
    	}while (dat != 0);
    }
    
    
    /*
     * 12864片选
     * i:0是左屏,1是右屏,2是双屏
    */
    void choose12864(uint8 i)
    {
    	switch (i)
    	{
    		case 0: CS1 = 0; CS2 = 1; break;//片选左屏
    		case 1: CS1 = 1; CS2 = 0; break;//片选右屏
    //		case 2: CS1 = 0; CS2 = 0; break;
    		default: break;//退出
    	}
    }
    
    /*
     * 写命令
    */
    void cmd_w12864(uint8 cmd)
    {
    	chekbusy12864();
    	
    	RS = 0;//RS端口置0
    	RW = 0;//RW端口置0
    	_nop_();//延时1us
    	E = 1;//E端口置1
    	_nop_();//延时1us
    	P3 = cmd;
    	_nop_();//延时1us
    	E = 0;//E端口置0
    }
    
    /*
     * 写数据
    */
    void dat_w12864(uint8 dat)
    {
    	chekbusy12864();
    	
    	RS = 1;//RS端口置1
    	RW = 0;//RW端口置0
       _nop_();//延时1us
    	E = 1;//E端口置1
       _nop_();//延时1us
    	P3 = dat;
       _nop_();//延时1us
    	E = 0;//E端口置0
    }
    
    /*
     * 清屏
    */
    void clear12864(void)
    {
    	uint8 page,row;
    	
    	choose12864(0);
    
    	for(page=0; page<8; page++)
    	{
    		cmd_w12864(0xb8+page);
    		cmd_w12864(0x40);
    		for(row=0; row<64; row++)
    		{  
    		    
    			dat_w12864(0x00);//写数据0
    		}
    	}
    		choose12864(1);
    
    	for(page=0; page<8; page++)
    	{
    		cmd_w12864(0xb8+page);
    		cmd_w12864(0x40);
    		for(row=0; row<64; row++)
    		{  		    
    			dat_w12864(0x00);//写数据0
    		}
    	}
    }
    
    
    /*
     * LCD初始化
    */
    void LCD_init(void)
    {
    	chekbusy12864();
    	cmd_w12864(0xc0);	  //从第0行开始显示
    	cmd_w12864(0x3f);	  //LCD显示RAM中的内容
    	clear12864();
    }
    
    /*
     * 8x16字符的显示
    */
    void play8(uint8 x, uint8 y, uint8 *addr)
    {
    	uint8 i;
    
    	if (x > 63)
    	{
    		choose12864(1);
    		x = x-64;
    	}
    	else
    	{
    		choose12864(0);
    	}
    	cmd_w12864(0x40|x);
    	cmd_w12864(0xb8|(y++));
    	if ((y & 0x80) == 0)
    		for (i=0;i<8;i++)
    			dat_w12864(*addr++);
    	else
    		for (i=0;i<8;i++)
    			dat_w12864(0xFF - *addr++);
    
    	cmd_w12864(0x40|x);
    	cmd_w12864(0xb8|y);
    	if ((y & 0x80) == 0)
    		for (i=0;i<8;i++)
    			dat_w12864(*addr++);
    	else
    		for (i=0;i<8;i++)
    			dat_w12864(0xFF - *addr++);
    }
    
    /*
     * 16x16显示
    */
    void play16(uint8 x, uint8 y, uint8 *addr)
    {
    	uint8 i;
    
    	if (x > 63)
    	{
    		choose12864(1);
    		x = x-64;
    	}
    	else
    	{
    		choose12864(0);
    	}
    	cmd_w12864(0x40|x);
    	cmd_w12864(0xb8|(y++));
    	if ((y & 0x80) == 0)
    		for (i=0; i<16; i++)
    			dat_w12864(*addr++);
    	else
    		for (i=0; i<16; i++)
    			dat_w12864(0xFF - *addr++);
    
    	cmd_w12864(0x40|x);
    	cmd_w12864(0xb8|y);
    	if ((y & 0x80) == 0)
    		for (i=0; i<16; i++)
    			dat_w12864(*addr++);
    	else
    		for (i=0; i<16; i++)
    			dat_w12864(0xFF - *addr++);
    }
    
    /*
     * 16*32 字符显示
    */
    void play32(uint8 x, uint8 y, uint8 num)
    {
    	uint8 i, j, *addr;
    
    	addr = Num + num*64;
    	if (x > 63)
    	{
    		choose12864(1);
    		x = x-64;
    	}
    	else
    	{
    		choose12864(0);
    	}
    
    	
    	for (j=0; j<4; j++)
    	{
    		cmd_w12864(0x40|x);
    		cmd_w12864(0xb8|(y++));
    		if ((y & 0x80) == 0)
    			for (i=0; i<16; i++)
    				dat_w12864(*addr++);
    		else
    			for (i=0; i<16; i++)
    				dat_w12864(0xFF - *addr++);
    	}
    }
    
    /*
     * 8x16数字的显示
    */
    void play8_num(uint8 x, uint8 y, uint8 num)
    {
    	play8(x, y, &S_num[16*(num/10%10)]);
    	play8(x+8, y, &S_num[16*(num%10)]);
    }
    
    /*
     * 16x32BCD数字的显示
    */
    void play32_num(uint8 x, uint8 y, uint8 num)
    {
    	play32(x, y, num/10%10);
    	play32(x+16, y, num%10);
    }
    
    void play_week(uint8 x, uint8 y, uint8 num)
    {
    	switch (num)
    	{
    		case 1: play16(x+32, y, zhou_yi); break;
    		case 2: play16(x+32, y, zhou_er); break;
    		case 3: play16(x+32, y, zhou_san); break;
    		case 4: play16(x+32, y, zhou_si); break;
    		case 5: play16(x+32, y, zhou_wu); break;
    		case 6: play16(x+32, y, zhou_liu); break;
    		case 7: play16(x+32, y, zhou_qi); break;
    		default : break;
    	}
    }
    //************************************************************************/
    // 函数: LCD_Delay()
    // 描述: 延时t ms函数
    // 参数: t 
    // 返回: 无
    // 备注: 11.0592MHZ       t=0延时时间约13us
    // 版本:  2011/01/01      First version
    //************************************************************************/
    void LCD_Delay_us(unsigned int t)
    {
    	while(t--); 
    }
    //************************************************************************/
    // 函数: LCD_Delay()
    // 描述: 延时t ms函数
    // 参数: t 
    // 返回: 无
    // 备注: 11.0592MHZ       t=1延时时间约1ms
    // 版本:  2011/01/01      First version
    //************************************************************************/
    void LCD_Delay_ms(unsigned int t)
    {
    	unsigned int i,j;
    	for(i=0;i<t;i++)
    	for(j=0;j<113;j++)
    	;
    }
    
    

    主界面框架显示及设置页面

    /*
     * 主界面框架
    */
    void main_frame(void)
    {
    	play32(80, 2, 10); //显示数
    	play32(32, 2, 10); //显示数
    	play16(17, 0, hanzi_nian);//显示斜线
    	play16(49, 0, hanzi_yue);//显示斜线
    	play16(80, 0, hanzi_ri);//显示斜线
      
    	play16(0, 6 ,hanzi_si);
    	play16(16, 6 ,hanzi_nei);
    	play16(32, 6 ,hanzi_wen);
    	play16(48, 6 ,hanzi_du);
    	play16(112, 6,hanzi_du);//显示度
    }
    
    /*
     * 主界面
    */
    void main_show(bit refresh)
    {
    //    uint8  lunar[2];
    	if (refresh)
    		read_time((uint8 *)&time);// 读时间函数// 时间											   
    	if (refresh || (time.sec != tmp_time.sec)) // 秒更新
    	{
    		tmp_time.sec = time.sec;			  	//读取秒数据
     	    play8_num(96, 6,zhengshu);   			//温度显示
    		play32_num(96, 2, time.sec);			//显示秒
    	}
    	if (refresh)
    		main_frame();//刷新界面
    	if (refresh || (time.min != tmp_time.min)) // 分更新
    	{
    		if (!refresh)
    			flag = 0;
    		tmp_time.min = time.min;//读取分
    		play32_num(48, 2, time.min); //显示分
    	}
    	if (refresh || (time.hour != tmp_time.hour)) // 时更新
    	{
    		if ((!refresh)&&(Clock_flag))
    			alarm_sound();
    		tmp_time.hour = time.hour; 				//读取时
    		play32_num(0, 2, time.hour);			//显示时
    	}
    	if (refresh || (time.day != tmp_time.day))  //日更新
    	{
    		tmp_time.day = time.day;				//读取日
    		play8_num(64, 0, time.day);				//显示日
    		// 农历	
    		turn_lunar_calendar(&time, lunar);
    		play_lunar_calendar(0, 6, lunar[0], lunar[1]);
    	}
    	if (refresh || (time.week != tmp_time.week)) // 周更新
    	{
    		tmp_time.week = time.week;				//读取周
    		play_week(140, 0, time.week);			//显示周
    	}
    	if (refresh || (time.mon != tmp_time.mon))  // 月更新
    	{
    		tmp_time.mon = time.mon;				//读取月
    		play8_num(32, 0, time.mon);				//显示月
    		// 农历	
    		turn_lunar_calendar(&time, lunar);				//转换农历年
    		play_lunar_calendar(0, 6, lunar[0], lunar[1]);	//显示农历年
    	}
    
    	if (refresh || (time.year != tmp_time.year)) // 年更新
    	{
    		tmp_time.year = time.year;					//读取年数据
    		play8_num(0, 0, time.year);					//显示年
    		// 农历	
    		turn_lunar_calendar(&time, lunar);				//转换农历年
    		play_lunar_calendar(0, 6, lunar[0], lunar[1]);	//显示农历年
    	}	
    }
    /*
     * 主机界面设置 
    */
    void main_set(void)
    {	
    	int8 key_val, state=1;			 	//变量
    	play32_num(96, 2|0x80, time.sec);	//显示秒			
    	while (1)
    	{	key_val = scan_key();//键盘扫描
    		if (key_val == 1) // 设置
    		{
    			if (state >= 7)
    				state = 0;
    			else
    				state++;				//位置状态加1
    			set_time((uint8 *)&time);	//设置时间
    			main_show(1);				//显示主界面
    			switch (state)
    			{	case 0:	set_time((uint8 *)&time); break;//设置时间
    				case 1:	play32_num(96, 2|0x80, time.sec); break;//显示秒
    				case 2:	play32_num(48, 2|0x80, time.min); break;//显示分
    				case 3:	play32_num(0, 2|0x80, time.hour); break;//显示时
    				case 4:	play_week(140, 0|0x80, time.week); break;//显示周
    				case 5:	play8_num(64, 0|0x80, time.day); break; //显示日
    				case 6:	play8_num(32, 0|0x80, time.mon); break; //显示月
    				case 7:	play8_num(0, 0|0x80, time.year); break; //显示年
    				default: break;		//退出循环						  
    			}
    		}
    		else if (key_val > 1)//按键值大于1
    		{		
    			if (key_val == 4)
    			{
    				state = 0;
    				clear12864();         //清屏幕
    				main_show(1);		  //主界面
    			}
    			if (state == 1)//位置1设置秒
    			{
    				if (key_val == 3)//加按下?
    					time.sec++;//秒加1
    				else if(key_val == 2)
    					time.sec--; //秒减1
    				if (time.sec >= 60)
    					time.sec = 0;
    				else if (time.sec < 0)
    					time.sec = 59;       
    				play32_num(96, 2|0x80, time.sec);//显示秒	
    			}
    			else if (state == 2)				//位置2设置分
    			{
    				if (key_val == 3)				//加按下?
    					time.min++;					//加1
    				else if(key_val == 2)
    					time.min--;					//减1
    				if (time.min >= 60)
    					time.min = 0;
    				else if (time.min < 0)
    					time.min = 59;
    				play32_num(48, 2|0x80, time.min);//显示分
    			}
    			else if (state == 3)				//位置3设置时
    			{		
    				if (key_val == 3)				//加按下?
    					time.hour++;				//加1
    				else if(key_val == 2)
    					time.hour--;				//减1
    				if (time.hour >= 24)
    					time.hour = 0;
    				else if (time.hour < 0)
    					time.hour = 23;
    				play32_num(0, 2|0x80, time.hour);//显示时
    			}
    			else if (state == 4)				//位置4设置周
    			{		
    				if (key_val == 3)			//加按下?
    					time.week++;				//加1
    				else if(key_val == 2)
    					time.week--;				//减1
    				if (time.week >= 8)
    					time.week = 1;
    				else if (time.week < 1)
    					time.week = 7;
    				play_week(140, 0|0x80, time.week);//显示周
    			}
    			else if (state == 5)//位置5设置日
    			{
    				if (key_val == 3)				//加按下?
    					time.day++;					//加1
    				else if(key_val == 2)
    					time.day--;					//减1
    				if (time.day >= 32)
    					time.day = 1;
    				else if (time.day < 1)
    					time.day = 31;
    				play8_num(64, 0|0x80, time.day);//显示日
    			}
    			else if (state == 6)				//位置6设置月
    			{
    				if (key_val == 3)				//加按下?
    					time.mon++;					//加1
    				else if(key_val == 2)
    					time.mon--;					//减1
    				if (time.mon >= 13)
    					time.mon = 1;
    				else if (time.mon < 1)
    					time.mon = 12;
    				play8_num(32, 0|0x80, time.mon);//显示月
    			}
    			else if (state == 7)				//位置7设置年
    			{
    				if (key_val == 3)				//加按下?
    					time.year++;				//加1
    				else if(key_val == 2)
    					time.year--;				//减1
    				if (time.year >= 100)
    					time.year = 0;				//0年
    				else if (time.year < 0)
    					time.year = 99;				//99年
    				play8_num(0, 0|0x80, time.year);//显示年
    			}
    			else
    			{
    				break;		//退出循环
    			}
    		}
    		if (state == 0)
    			break;		//退出循环
    	}
    }
    

    闹钟界面显示

    /*
     * 闹钟界面显示
    */
    void alarm_show(void)
    {
    	int8 key_val, state=1;
    	uint32 t=0;
    
    	play16(16, 0, nao);			//显示 闹
    	play16(32, 0, zhong);		//钟
    	play16(48, 0, hanzi_she);
    	play16(64, 0, hanzi_ding);
    	play16(80, 0, hanzi_ye);
    	play16(96, 0, hanzi_mian);
    	
    	play16(0, 2, nao);			//显示 闹
    	play16(16, 2, zhong);		//钟
    	play16(32, 2, maohao);		//冒号:
    	if (Alarm_flag)
    		play16(48, 2, kai);		//开
    	else
    		play16(48, 2, guan);//关
    	play8_num(80, 2, alarm.hour);	//时
    	play8(96, 2, maohao1);				//冒号
    	play8_num(104, 2, alarm.min);	//分	
    	play16(0, 4, zheng);			//显示 整
    	play16(16, 4, dian);		//显示 点
    	play16(32, 4, bao);			//显示 报
    	play16(48, 4, shi);			//显示 时
    	play16(64, 4, maohao);			  //显示 冒号
    	play16(0, 6, hanzi_she);			//显示 整
    	play16(16, 6, hanzi_ding);		//显示 点
    	play16(32, 6, hanzi_wen);			//显示 报
    	play16(48, 6, hanzi_du);			//显示 时
    	play16(64, 6, maohao);			  //显示 冒号
    	play8_num(80, 6,shedingwendu);
    	play16(96, 6, hanzi_du);      //显示 度
    	if (Clock_flag)
    		play16(80, 4, kai);			//显示 开
    	else
    		play16(80, 4, guan);		//显示 关	
    	for (t=0; t<30000; t++)
    	{	
    		key_val = scan_key();		//键盘扫描 获取键值
    		if (key_val > 1)			//判断数据
    			break;
    		if (key_val ==4)			//判断数据
    		{
    			clear12864();         //清屏幕
    			main_show(1);		  //主界面
    		}
    		else if (key_val == 1)		//判断数据
    		{
    			if (Alarm_flag)
    				play16(48, 2|0x80, kai);//显示 开
    			else
    				play16(48, 2|0x80, guan);//关			
    			while (1)
    			{				
    				key_val = scan_key();//键盘扫描 获取键值
    				if (key_val == 1) 				// 完成设置
    				{
    					if (state >= 5)				//判断数据
    						state = 0;
    					else
    						state++;
    					if (Alarm_flag)
    						play16(48, 2, kai);			//显示 开
    					else
    						play16(48, 2, guan);		//显示 关
    					play8_num(80, 2, alarm.hour);  //闹钟 时 显示 
    					play8_num(104, 2, alarm.min);	//闹钟 分 显示 
    					if (Clock_flag)
    						play16(80, 4, kai);			//显示 开
    					else
    						play16(80, 4, guan);		//显示 关
    
    					switch (state) //判断数据
    					{
    						case 1: 	
    							if (Alarm_flag)//判断数据
    								play16(48, 2|0x80, kai);	//显示 开
    							else
    								play16(48, 2|0x80, guan); 	//显示 关
    							break;
    						case 2:
    							play8_num(104, 2|0x80, alarm.min);//闹钟 分 显示 
    							break;
    						case 3: 
    							play8_num(80, 2|0x80, alarm.hour);//闹钟 时 显示 
    							break;
    						case 4: 
    							if (Clock_flag)//判断数据
    								play16(80, 4|0x80, kai);//显示 开
    							else
    								play16(80, 4|0x80, guan);//显示 关
    							break;
    						case 5: 
    							play8_num(80, 6|0x80, shedingwendu);
    						default: break;
    					}
    				}
    				else if (key_val > 1)//判断数据
    				{
    					if (state == 1)//判断数据
    					{
    						Alarm_flag = ~Alarm_flag;
    						if (Alarm_flag)
    							play16(48, 2|0x80, kai);//显示 开
    						else 
    							play16(48, 2|0x80, guan);//显示 关
    					}
    					else if (state == 2)//判断数据
    					{
    						if (key_val == 3)//判断数据
    							alarm.min++;//加1
    						else if(key_val == 2)
    							alarm.min--;//减1
    						if (alarm.min >= 60)//判断数据
    							alarm.min = 0;
    						else if (alarm.min < 0)//判断数据
    							alarm.min = 59;
    						play8_num(104, 2|0x80, alarm.min);//闹钟 分 显示 
    					}
    					else if (state == 3)//判断数据
    					{
    						if (key_val == 3)//判断数据
    							alarm.hour++;//加1
    						else if(key_val == 2)
    							alarm.hour--;//减1
    						if (alarm.hour >= 24)//判断数据
    							alarm.hour = 0;
    						else if (alarm.hour < 0)//判断数据
    							alarm.hour = 23;
    						play8_num(80, 2|0x80, alarm.hour);//闹钟 时 显示 
    					}
    					else if (state == 4)			//判断数据
    					{
    						Clock_flag = ~Clock_flag;
    						if (Clock_flag)				//判断数据
    							play16(80, 4|0x80, kai);//显示 开
    						else
    							play16(80, 4|0x80, guan);//显示 关
    					}
    					else if (state == 5)			//判断数据
    					{
    						if (key_val == 3)//判断数据
    							shedingwendu++;//加1
    						else if(key_val == 2)
    							shedingwendu--;//减1
    						if (shedingwendu >= 99)//判断数据
    							shedingwendu = 99;
    						else if (shedingwendu < 0)//判断数据
    							shedingwendu = 99;
    						play8_num(80, 6|0x80, shedingwendu);
    					}
    					else
    					{
    						break;	//退出
    					}
    				}
    				if (state == 0)	//状态为0退出
    				break;			//状态为0退出
    			}
    			if (state == 0)		//状态为0退出
    				break;			//状态为0退出
    		}
    	}
    }
    

    主函数

    void main()
    {
    	uint8 key_val;
    	read_18B20();    //初始DS18B20
    	Delay_nms(1000);//延时1S,等待18B20工作正常
    	
    	LCD_init();           //初始化液晶
    	clear12864();         //清屏幕
    	information();
    	delayms(3000);
    	LCD_init();           //初始化液晶
    	clear12864();         //清屏幕
    	main_frame();         //显示主界面框架
    	main_show(1);         //刷新1次
        read_18B20();    				//读温度
    	play8_num(96, 6,zhengshu);   //显示温度
    
    	while(1)
    	{ 
    		key_val = scan_key();
    		if (key_val == 1)         //K1?
    		{
    			main_set();           //设置主界面
    		}
    
    		else if (key_val == 3)    //K3?
    		{
    			clear12864();         //清屏幕
    			alarm_show();		  //闹钟画面
    			clear12864();         //清屏幕
    			main_show(1);		  //主界面
    		}
    		else
    		{
    			read_time((uint8 *)&time);			//读取时间
    			main_show(0);						//显示主界面
    			if((time.sec%2)==0){read_18B20();}  //每隔2S采集一次
    		}
    		
    		/*********************闹钟*********************/
    		if (Alarm_flag)//如果闹钟标志有 执行下面的
    		{
    			if ((flag == 0) && (alarm.hour == time.hour) && (alarm.min == time.min))//判断条件是否满足
    			{
    				flag = 1;
    				clear12864();     //清屏幕
    				alarm_show();     //闹钟
    		    	PlayMusic();    //播放音乐
    				PlayMusic();      //播放音乐
    				clear12864();     //清屏幕  
    				main_show(1);     //显示主界面
    			}
    		}
    	  if(zhengshu>shedingwendu)
    		{
    			BEEP = ~BEEP;			
    		}
    	}
    }
    

    5 测试结果

    在硬件电路焊接和软件程序设计分别完成的基础之上,进行软硬件的结合与调试。通过下载将在电脑上已完成的程序下载到单片机芯片中。在调试中发现软件中存在的问题,及时解决问题,确保系统能正常工作并达到设计要求。通过反复的调试与实验,可以证明该系统能够较好地完成设计所需的基本要求。即能够正确的显示万年历。
    在完成软件系统时,刚开始我是用的是12M的晶振,所有器件正常,后来我换了11.0592M的晶振,结果温度就不正常了,经过认真排查才发现是由于DS18B20在数据读取时,对时间要求很精确,由于晶振的不同造成了读数据的错误,经过这次调试,让我更清晰的认识到了时序对元器件的重要性。在设计中,因为考虑到闹钟定时功能,我们希望我们设置的闹钟时刻不会因为系统的掉电而丢失,考虑到DS1302是有锂电池作为电源的,不会因为主系统掉电丢失内部数据,所以我们将闹钟的定时时刻放到了DS1302内的空余寄存器里面,像这些灵活的技巧就需要我们认真的阅读元件的数据手册,从中索取对自己有用的信息。
    经测试,本作品完成设计所有要求。经过万年历的设计,让我学到了很多,让我认识到了学习基础知识的重要性,当设计完整的系统时,要考虑到硬件和软件两者的结合,有时硬件的不足,我们可以用软件程序来弥补,从而节约硬件成本,在设计软件程序时要模块化,可以提高程序的可读性。

    致 谢

    在论文即将完成之际,我要特别感谢我的指导老师对我的热情关怀和细心指导。在我做毕业设计的整个过程中,老师都以她最大的可能来帮助我,教导我,跟着老师做毕业设计,我学会了好多东西,这些都对我未来的工作和生活产生重大的影响。她不仅仅是我们学术上的良师,更是生活中的益友。她以一个教育工作者热忱的心胸不厌其烦地指导着我们,教育者我们,使我们不仅学到了扎实的专业知识,更学到了做人的道理。她孜孜不倦悉心细致的教诲和严谨治学一丝不苟的工作作风使我永远都不能忘记。在此,特向她表示真诚的感谢。祝老师身体健康,桃李满天下。
    同时,在我三年的大学生活中,也得到了很多老师、同学、朋友的支持和帮助,在此一并表示感谢,正是由于你们,才使我的大学生活更加丰富多彩,感谢你们。

    参考文献

    [1] 李群芳,肖看.单片机原理、接口及应用.北京:清华大学出版社,2007
    [2] 谭浩强.C语言程序设计.北京:清华大学出版社,2006
    [3] 张义和,王敏男,许宏昌等.例说51单片机.北京:人民邮电出版社,2008
    [4] 刘坤,宋戈,赵红波等.51单片机C语言应用技术开发技术大全.北京:人民邮电出版社,2008
    [5] 白延敏.51单片机典型系统开发实例精讲.北京:电子工业出版社,2009
    [6] 周丽娜.Protel99SE电路设计技术.北京:中国铁道出版社,2009
    [7] 王为青,程国钢.单片机Keil C×51应用开发技术.北京:人民邮电出版社,2007
    [8] 江志红.51单片机技术与应用系统开发案例精选.北京:清华大学出版社,2009
    [9] Muhammad Ali Mazidi,Janice Gillispie,Rolin Mckinlay.The 8051 Microcontroller and Embedded Systems:Using Assembly and C,Second Edition.Pearson Education,2006
    [10] U. Tietze Ch. Schenk. Electronic Circuits. Handbook for Design and Application, Berlin, New York: Springer-Verlag,2005

    创作不易,分享更需勇气! 喜欢的可以给点个 ❤赞❤ 吗?欢迎评论区留言交流~

    展开全文
  • 多功能电子万年历

    2019-01-28 14:43:01
    电子万年历具有读取方便、显示直观、功能多样、电路简洁、成本低廉等诸多优点,符合电子仪器仪表的发展趋势,具有广阔的市场前景[1]。 在此次的设计结果表明,电子万年历具有一定的实用美观价值,相应参数的修改手段...
  • 本文主要介绍了基于单片机的智能电子万年历的研制,该万年历能够实时显示公历年、月、日、时、分、秒,以及对应的农历日期、24节气、天干地支、闹铃功能,同时还能够实时测取环境温度。 本系统的硬件部分主要由AVR...
  • 此源代码完全模块化,包括12864.C、12864.h、ds1302.c、ds1302.h、ds18b20.c、key.c等,里面注释齐全,通俗易懂
  • 基于单片机的电子万年历设计,论文及硬件,论文里附关键程序
  • 本文介绍了基于STC89C52单片机的多功能电子万年历的硬件结构和软硬件设计方法。本设计由数据显示模块、温度采集模块、时间处理模块和调整设置模块四个模块组成。系统以STC89C52单片机为控制器,以串行时钟日历芯片DS...
  • 万年历是STC89系列单片机做主控芯片,很使用,制作成本低,也适合做课程设计用,是一个值得和大家分享。该资料仅供大家作学习参考用。
  • 基于AT89C51单片机的多功能电子万年历设计(附程序)(12000字)AT89C51 SCM-BASED ELCTRONIC DESIGN CALENDAR摘要本文介绍了基于AT89C51单片机的多功能电子万年历的硬件结构和软硬件设计方法。系统以AT89C51单片机为...

    基于AT89C51单片机的多功能电子万年历的设计(附程序)(12000字)

    AT89C51 SCM-BASED ELCTRONIC DESIGN CALENDAR

    摘要

    本文介绍了基于AT89C51单片机的多功能电子万年历的硬件结构和软硬件设计方法。系统以AT89C51单片机为控制器,以串行时钟日历芯片DS1302记录日历和时间,它可以对年、月、日、时、分、秒进行计时,还具有闰年补偿等多种功能。万年历采用直观的数字显示,可以在LED上同时显示年、月、日、周日、时、分、秒,还具有时间校准等功能。此万年历具有读取方便、显示直观、功能多样、电路简洁、成本低廉等诸多优点,具有广阔的市场前景。

    关键字AT89C51;电子万年历; DS1302

    本课题所研究的电子万年历是单片机控制技术的一个具体应用,主要研究内容包括以下几个方面:

    (1)选用电子万年历芯片时,应重点考虑功能实在、使用方便、单片存储、低功耗、抗断电的器件。

    (2)根据选用的电子万年历芯片设计外围电路和单片机的接口电路。

    (3)在硬件设计时,结构要尽量简单实用、易于实现,使系统电路尽量简单。

    (4)根据硬件电路图,在开发板上完成器件的焊接。

    (5)根据设计的硬件电路,编写控制AT89C51芯片的单片机程序。

    (6)通过编程、编译、调试,把程序下载到单片机上运行,并实现本设计的功能。

    (7)在硬件电路和软件程序设计时,主要考虑提高人机界面的友好性,方便用户操作等因素。

    (8)软件设计时必须要有完善的思路,要做到程序简单,调试方便。

    2 系统的总体设计

    单片机电子万年历的制作有多种方法,可供选择的器件和运用的技术也有很多种。所以,系统的总体设计方案应在满足系统功能的前提下,充分考虑系统使用的环境,所选的结构要简单使用、易于实现,器件的选用着眼于合适的参数、稳定的性能、较低的功耗以及低廉的成本。

    2.1系统方案的构想与确定

    系统的功能往往决定了系统采用的结构,经过成本,性能,功耗等多方面的考虑决定用三个8位74LS164串行接口外接LED显示器,RESPACK-8对单片机AT89C51进行供电,时间芯片DS1302连接单片机AT89C51。从而实现电子万年历的功能。

    2.2 器件的选用

    单片机AT89C51

    电容RESPACK-8

    三个74LS164串行接口

    传感器DS1302

    2.2.1单片机的选择

    单片机自70年代问世以来以微处理器(MPU)技术及超大规模集成电路技术的发展为先导,用广泛的应用领域拉动得到蓬勃发展,单片机功能正日渐完善。单片机的应用,使许多领域的技术水平和自动化程度大大提高,可以说当今世界正在经受一场以单片机技术为标志的新技术革命浪潮的冲击。

    目       录

    1 绪论    1

    1.1课题研究的背景    1

    1.2课题的研究目的与意义    1

    1.3课题解决的主要内容    1

    2 系统的总体设计    2

    2.1系统方案的构想与确定    2

    2.2 器件的选用    2

    2.2.1单片机的选择    2

    3 系统硬件的设计    4

    3.1系统硬件电路设计    4

    3.1.1系统硬件框图    4

    3.1.2 AT89C51单片机    4

    3.1.3 8位移位寄存器74LS164(串行输入,并行输出)    8

    3.1.4 ds1302    11

    4 系统的软件设计    14

    4.1 主程序    14

    4.2 从1302读取日期和时间程序    15

    5 PROTEUS使用    16

    5.1编程环境PROTEUS    16

    5.2用PROTEUS ISIS对电子万年历的硬件电路设计    16

    5.3用PROTEUS ISIS进行电子万年历的仿真测试    20

    结论    23

    致谢    24

    参考文献    25

    附录    26

    附录1    26

    展开全文
  • 本设计是一块基于AT89S52的电子万年历设计,单片机选用AT89S52,温湿度传感器采用DHT11,显示模块采用液晶屏LCD1602,时钟芯片采用DS1302,同时采用按键来设置调整时间。本系统可以显示年月日时分秒以及星期,同时...
  • 51单片机多功能万年历时钟系统设计程序
  • 基于单片机的多功能万年历/电子时钟,实现功能主要有:显示年、月、日、星期、时、分、秒,实时温度显示,整点报时,设置闹钟,显示农历时间及平润年,功能完善保证可行。 该资源内含设计电路图、Proteus仿真、keil...
  • 基于单片机的电子万年历设计

    千次阅读 2020-09-24 20:29:36
                           基于51单片机的电子万年历设计                                    摘 要    电子万年历是单片机系统的一个应用,由硬件和软件相配合使用。硬件由主控器...

    文末下载完整资料

                           基于51单片机的电子万年历的设计
                                       摘 要
       电子万年历是单片机系统的一个应用,由硬件和软件相配合使用。硬件由主控器、时钟电路、温度检测电路、显示电路、键盘接口5个模块组成。主控模块用AT89C52、时钟电路用时钟芯片DS1302、显示模块用LED数码管、温度检测采用DS18B20温度传感器、键盘接口电路用普通按键接上拉电阻完成;软件利用C语言编程实现单片机程序控制。单片机通过时钟芯片DS1302获取时间数据,DS18B20采集温度信号送该给单片机处理,单片机再把时间数据和温度数据送给74LS154译码,然后通过三极管C9015放大驱动LED数码管显示阳历年、月、日、时、秒、闹钟、星期、温度。
    关键词 电子万年历;单片机;温度传感器;时钟;数码显示

                                   1 引言
       随着微电子技术和超大规模集成电路技术的不断发展,家用电子产品不但种类日益丰富,而且变得更加经济实用,单片微型计算机体积小、性价比高、功能强、可靠性高等独有的特点,在各个领域得到了广泛的应用。电子万年历是一种应用非常广泛的日常计时工具,数字显示的日历钟已经越来越流行,特别是适合在家庭居室、办公室、大厅、会议室、车站和广场等使用。LED数字显示的日历钟显示清晰直观、走时准确、可以进行夜视,并且还可以扩展出多种功能。功能也越来越齐全,除了公历年月日、时分秒、星期显示及闹铃。但通过我们对各种电子钟表、历的不断观察总结发现目前市场的钟、历都存在一些不足之处,比如:时钟不精确、产品成本太高、无环境温度显示等,这都给人们的使用带来了某些不便。为此设计了一种功能全面、计时准确、成本低廉的基于51单片机的万年历。
                                   2 功能要求
       1. 万年历能用数码管显示阳历年、月、日、星期、[小]时、分、秒并设置指定时间的闹铃。
       2. 数字式温度计要求测温范围-50~100°C, LED数码管直读显示。
       3 方案论证与设计
    3.1 控制部分的方案选择
       1. 用可编程逻辑器件设计。可采用ALTERA公司的FLEX10K系列PLD器件。设计起来结构清晰,各个模块,从硬件上设计起来相对简单,控制与显示的模块间的连接也会比较方便。但是考虑到本设计的特点,EDA在功能扩展上比较受局限,而且EDA占用的资源也相对多一些。从成本上来讲,用可编程逻辑器件来设计也没有什么优势。
       2. 用凌阳16位单片机设计。凌阳16位单片机有丰富的中断源和时基,方便本实验的设计。它的准确度相当高,并且C语言和汇编兼容的编程环境也很方便来实现一些递归调用。I/O口功能也比较强大,方便使用。用凌阳16位单片机做控制器最有特色的就是它的可编程音频处理,可完成语音的录制播放和识别。这些都方便对设计进行扩展,使设计更加完善。成本也相对低一些。但是,在控制与显示的结合上有些复杂,显示模组资源相对有限,而且单片机的稳定性不是很高。
       3. 主控芯片使用51系列AT89C52单片机,时钟芯片用美国DALLAS公司推出的一种高性能、低功耗、带RAM的时钟DS1302。采用DS1302作为主要计时芯片,可以做到计时准确。更重要的是,DS1302可以在很小电流的后备电源(2.5~5V电源,在2.5V时耗电小于300nA)下继续计时,停电后时钟无需重新调整,并可编程选择多种充电电流来对后备电源进行慢速充电,可以保证后备电源基本不耗电,还可自设闹铃,阳历、星期与年月日自动对应。本系统采用了此方案。
    3.2 测温部分的方案选择
       1.在日常生活及工农业生产中经常要乃至温度的检测及控制,传统的测温元件有热电偶和热电阻。而热电偶和热电阻测一般都是电压,再转换成对应的温度,需要比较多的外部硬件支持,硬件电路复杂,软件调试复杂,制作成本高。
       2.与前面相比,采用美国DALLAS半导体公司继DS1820之后推出的一种改进型智能温度传感器DS18B20作为检测元件,测温范围为-55~125°C,最大分辨率可达0.0625°C。DS18B20可以直接读出被测温度值,而且采用3线制与单片机相连,减少了外部的硬件电路,具有低成本和易使用的特点。
    3.3 显示部分的方案选择
       1. 液晶显示方式。液晶显示效果出众,可以运用菜单项来方便操作,但是在显示时,特别是使用秒表功能时扫描速度跟不上,屏幕会有明显的闪烁。而且由于61板的存储空间有限,液晶显示就不能与语音播抱程序同时实现。这些大大影响了电子万年历的性能。
       2. 相比液晶显示,8段数码管虽然操作比液晶显示略显繁琐,但可视范围十分宽,而且经济实惠,也不需要复杂的驱动程序。所以最后选择LED数码管显示方案。
    综上所述,按照系统设计功能的要求,确定硬件系统由主控制器、时钟模块、测温电路、显示模块、键盘接口共5个模块组成,总体系统构成框图如图3.1所示。

    在这里插入图片描述
    4 系统硬件电路设计
       电子万年历电路原理图见附件一,系统由主控制器AT89C52、时钟芯片DS1302、温度传感器DS18B20传感器、显示电路及键盘扫描电路组成。
    4.1 主控器 AT89C52
       ATMEL公司生产的AT89C52单片机采用高性能的静态80C51设计,由先进工艺制造,并带有非易失性Flsah程序存储器。它是一种高性能、低功耗的8位CMOS微处理芯片,市场应用最多。主要性能特点有:
       8KB Flash ROM,可以檫写1000次以上,数据保存10年。
       256字节内部RAM。
        电源控制模式
       时钟可停止和恢复;
       空闲模式;
       掉电模式。
       6个中断源。
       4个中断优先级。
       4个8位I/O口。
       全双工增强型UART。
       3个16位定时/计数器,T0、T1(标准80C51)和增加的T2(捕获和比较)。
       全静态工作方式:0~24MHz。
    4.2 时钟电路 DS1302
    4.2.1. DS1302的性能特性
       实时时钟,可对秒、分、时、日、周、月以及带闰年补偿的年进行计数;
       用于高速数据暂存的31×8位RAM;
       最少引脚的串行I/O;
    2.5~5.5V电压工作范围;
    2.5V时耗电小于300nA;
       用于时钟或RAM数据读/写的单字节或多字节(脉冲方式)数据传送方式;
    简单的3线接口;
       可选的慢速充电(至Vcc1)的能力。
       DS1302时钟芯片包括实时时钟/日历和31字节的静态RAM。它经过一个简单的串行接口与微处理器通信。实时时钟/日历提供秒、分、时、日、周、月和年等信息。对于小于31天的月和月末的日期自动调整,还包括闰年校正的功能。时钟的运行可以采用24h或带AM(上午)/PM(下午)的12h格式。采用三线接口与CPU进行同步通信,并可采用突发方式一次传送多个字节的时钟信号或RAM数据。DS1302有主电源/后备电源双电源引脚:Vcc1在单电源与电池供电的系统中提供低电源,并提供低功率的电池备份;Vcc2在双电源系统中提供主电源,在这种运用方式中,Vcc1连接到备份电,以便在没有主电源的情况下能保存时间信息以及数据。DS1302由Vcc1或Vcc2中较大者供电。当Vcc2大于Vcc1+0.2V时,Vcc2给DS1302供电;当Vcc2小于Vcc时, DS13026由Vcc1供电。
    4.2.2 DS1302数据操作原理
       DS1302在任何数据传送时必须先初始化,把RST脚置为高电平,然后把8位地址和命令字装入移位寄存器,数据在SCLK的上升沿被输入。无论是读周期还是写周期,开始8位指定40个寄存器中哪个将被访问到。在开始8个时钟周期,把命令字节装入移位寄存器之后,另外的时钟周期在读操作时输出数据,在写操作是写入时写入数据。时钟脉冲的个数在单字节方式下为8加8,在多字节方式下为8加字节数,最大可达248字节数。
       如果在传送过程中置RST脚为低电平,则会终止本次数据传送,并且I/O引脚变为高阻态。上电运行时,在Vcc大于等于2.5V之前,RST脚必须保持低电平。只有在SCLK为低电平时,才能将RST置为高电平。DS1302的引脚及内部结构图如图4.1所示,表4.1为各引脚的功能。
    DS1302的控制字如图4.2所示。控制字节的最高位(位7)必须是逻辑1;如果它为0,则不能把数据写入到DS1302中。位6如果为0,则表示存取日历时钟数据;为1表示存取RAM数据。位5~1(A4~A0)指示操作单元的地址。最低有效位(位0)如为0,表示要进行写操作;为1表示进行读操作。控制字节总是从最低位开始输入/输出。
       为了提高对32个地址的寻址能力(地址/命令位1~5=逻辑1),可以把时钟/日历或RAM寄存器规定为多字节(burst)方式。位6规定时钟或RAM,而位0规定读或写。在时钟/日历寄存器中的地址9~31或RAM寄存器中的地址31不能寄存数据。在多字节方式中,读或写从地址0的位0开始。必须按数据传送的次序写最先的8个寄存器。但是,当以多字节方式写RAM时,为了传送数据不必写所有31字节。不管是否写了全部31字节,所写的每一字节都将传送至RAM。
    在这里插入图片描述
       DS1302共有12个寄存器,其中有7个寄存器与日历、时钟相关,存放的数据位为BCD码形式。其日历、时间寄存器及其控制字见表3.2,其中奇数为读操作,偶数为写操作。
    时钟暂停:秒寄存器的位7定义位时钟暂停位。当它为1时,DS1302停止震荡,进入低功耗的备份方式。通常在对DS1302进行写操作时(如进入时钟调整程序),停止震荡。当它为0时,时钟将开始启动。
       AM-PM/12-24[小]时方式:[小]时寄存器的位7定义为12或24[小]时方式选择位。它为高电平时,选择12[小]时方式。在此方式下,位5是AM/PM位,此位是高电平时表示PM低电平表示AM。在24[小]时方式下,位5为第二个10[小]时位(20~23h)。

    在这里插入图片描述
       DS1302的晶震选用32.768kHz,电容推荐值为33pF,因为震荡频率较低,也可以不接电容,对计时精度影响不大。
    4.3 测温电路的设计
       测温电路主要使用温度传感器DS18B20,由于精度要求不高所以采用2位共阳LED数码管以动态扫描法实现温度显示。其设计原理图如附件一所示。
    4.3.1 温度传感器工作原理
       DS18B20温度传感器是美国DALLAS半导体公司最新推出的一种改进型智能温度传感器,与传统的热敏电阻等测温元件相比,它能直接读出被测温度,并且可根据实际要示通过简单的编程    实现9~12位的数字值读数方式。DS18B20的性能特点如下:
       独特的单线接口仅需要一个端口引脚进行通信;
       多个DS18B20可以并联在唯一的三线上,实现多点组网功能;
       无须外部器件;
       可通过数据线供电,电压范围为3.0~3.5V;
       零待机功耗;
       温度以9或12数字量读出;
       用户可定义的非易失性温度报警设置;
       报警搜索命令识别并标志超过程序限定温度(温度报警条件)的器件;
       负电压特性,电源极性接反时,温度计不会因发热而烧毁,但不能正常工作。
       DS18B20采用3脚PR—35封装或8脚SOIC封装,其内部结构框图如图4.3所示。
       64位ROM的位结构如图4.4所示。开始8位是产品类型的编号,接着是每个器件的唯一的序号,共有48位,最后8位是前面56位的CRC检验码,这也是多个DS18B20可以采用一线进行通信的原因。非易失性温度报警触发器TH和TL,可通过软件写入户报警上下限。
       DS18B20温度传感器的内部存储器还包括一个调整暂存RAM和一个易失性的可电擦除的EERAM。高速暂存RAM的结构为8字节存储器,结构如图4.5所示。头2个字节包含测得的温度信息,第3和第4节是TH和TL的拷贝,是易失的,每次上电复位时被刷新。第5个字节为配置寄存器,它的内容用于确定温度值的数字转换分辨率。DS18B20工作时按此寄存器中的分辨率将温度转换为相应
    5 系统程序的设计
    5.1 阳历程序设计
       因为使用了时钟芯片DS1302,阳历程序只需从DS1302各寄存器中读出年、周、月、日、[小]时、分、秒等数据,再处理即可。在首次对DS1302进行操作之前,必须对它进行初始化,然后从DS1302中读出数据,再经过处理后,送给显示缓冲单元。阳历程序流程图见图5.1所示。
    5.2 时间调整程序设计
       调整时间用5个调整按钮,1个作为移位、控制用,2个作为加和减用,还有2个作为闹钟调整使用,分别定义为控制按钮、加按钮、减按钮、闹钟加按纽、闹钟减按纽。在调整时间过程中,要调整的位与别的位应该有区别。所以增加了闪烁功能,即调整的位一直在闪烁,直到调整下一位。闪烁原理就是,让要调整的一位每隔一定时间熄灭一次,比如说50ms。利用定时器计时,当达到50ms溢出时,就送给该位熄灭符,在下一次溢出时,再送正常显示的值,不断交替,直到调整该位结束。此时送正常显示值给该位,再进入下一位调整闪烁程序。时间调整程序流程图如图5.2所示。
    5.3 温度程序设计
       系统程序主要包括主程序、读出温度子程序、温度转换命令子程序、计算温度子程序、显示数据刷新子程序等等。
    5.3.1 主程序
       主程序的主要功能是负责温度的实时显示、读出并处理DS18B20的测量温度值,温度测量每1s进行一次。其程序流程图见5.3。
    5.3.2 读出温度子程序
       主要功能是读出RAM中的9字节,在读出时需进行CRC校验,校验有错时不进行温度数据的改写。其程序流程图如图5.4所示。
    5.3.3 温度转换命令子程序
       温度转换子程序主要是发温度转换开始命令,当采用12位分辨率时转换时间约为750ms,在本程序设计中采用1s显示程序延时法等待转换的完成。温度转换命令子程序流程图如图5.5所示。


    全套资料免费下载:
    关注v-x-公-众-号:【嵌入式基地
    后-台-回-复:【毕业设计】 即可获资料
    回复【编程】即可获取
    包括有:C、C++、C#、JAVA、Python、JavaScript、PHP、数据库、微信小程序、人工智能、嵌入式、Linux、Unix、QT、物联网、算法导论、大数据等资料
    在这里插入图片描述

    展开全文
  • 基于STC89C52单片机的电子万年历设计论文
  • 多功能电子时钟的诞生,很好解决了这一问题,它在显示时间的同时,又显示了温度、日期、时间等诸多功能于一身。 硬件主要由51单片机+最小系统+LCD液晶显示电路+DS1302时钟电路+蜂鸣器提示音电路+DS18B20温度采集电路...

    硬件方案

    随着生活和工作的繁忙,人们在获知时间的同时,对于与人们生活和工作息息相关的信息也不可缺少,比如:温度,日期等;多功能电子时钟的诞生,很好解决了这一问题,它在显示时间的同时,又显示了温度、日期、时间等诸多功能于一身。
    硬件主要由51单片机+最小系统+LCD液晶显示电路+DS1302时钟电路+蜂鸣器提示音电路+DS18B20温度采集电路+按键电路;
    在这里插入图片描述

    二.设计功能

    (1)本采用STC89C51/52RC(与AT89S51/52、AT89C51/52通用)单片机作为主控制器;
    (2)时钟芯片采用DS1302,温度传感器采用DS18B20;
    (3)有一组闹钟和阴历显示功能,有闰年补偿,可以准确及正确的显示时间等信息。
    (4)可以显示年、月、日、时、分、秒、星期、温度、农历和闹钟设定。
    (5)电路上的纽扣电池可在掉电后给时钟芯片供电,重上电后不用重新调时。

    三.设计原理图

    (1)原理图主要采用AD软件进行设计,如图:
    在这里插入图片描述
    (2)Protues仿真图如下:
    在这里插入图片描述

    软件设计

    (1)程序流程图
    在这里插入图片描述
    (2)主程序源码

    ......
    //-------------------------------
    void init(void)   //定时器、计数器设置函数
    {
        TMOD=0x11; 		//指定定时/计数器的工作方式为3
        TH0=0; 			//定时器T0的高四位=0
        TL0=0;  		//定时器T0的低四位=0
        TH1=0x3C;
        TL1=0xB0;
        EA=1;  			//系统允许有开放的中断
        ET0=1; 			//允许T0中断
        ET1=1;
    //	IT1=1;
    //	IT0=0;
        PT1=1;
        PT0=0;
        TR0=1; 			//开启中断,启动定时器
        TR1=0;
    }
    
    
    void InitialTimer2(void)
    {
        T2CON  = 0x00;			//16 Bit Auto-Reload Mode
        TH2 = RCAP2H = 0x3C;  	//重装值,初始值	TL2 = RCAP2L = 0x18;
        TL2 = RCAP2L = 0xB0;
        ET2=1;					//定时器 2 中断允许
        TR2 = 0;				//定时器 2 启动
    }
    //*******************主函数**************************
    //***************************************************
    void main()
    {
        ds1302_init();   //调用DS1302时钟的初始化子函数
        init();          //调用定时计数器的设置子函数
        lcd_init();      //调用液晶屏初始化子函数
        InitialTimer2();
        led=1;           //关闭LCD的背光电源
    
        while(1)  //无限循环下面的语句:
        {
            keyscan();      //调用键盘扫描子函数
            led=led1;
        }
    }
    ......
    

    如需资料请关注公众号“单片机实例设计”,首页回复“万年历”获取资料;
    在这里插入图片描述

    展开全文
  • 对于数字电子万年历采用直观的数字显示,可以同时显示年、月、日、周日、时、分、秒和温度等信息,还具有时间校准等功能。该电路采用AT89S52单片机作为核心,能够显示从2000至2099年的年、月、日、星期、时、分、秒...
  • 参赛作品《多功能遥控电子时钟》 (已完成)-数码万年历_C51.rar
  • 选题一:电子万年历设计 设计要求: 1) 利用51单片机、专用时钟芯片DS1302、DS18B20数字温度采集器及1602液晶显示器件设计一个万年历; 正确显示年月日,时分秒,星期等信息; 显示环境温度; 能够通过按键...
  • 本人用C#自编的万年历,此软件是集有万年阴阳历、闹钟、电子时钟、音乐播放器功能于一身的多功能万年历查询器。欢迎使用。
  • 针对当今社会对时间信息的要求不断丰富,设计了一种具有多功能的数字万年历。系统以Arduino控制板为主控芯片,由时钟芯片DS1302实现时钟计数功能,温度传感器DS18B20实现温度信号的采集功能,最后通过液晶LCDI602对...
  • C语言 万年历 温控 单片机
  • 随着科技的发展,生活中电子万年历越来越普遍,功能也越来越。根据人们的日常基本需求,设计一种电子万年历,可以实现测温、闹钟、秒表和语音报警等功能,为人们的日常生活提供舒适和便捷,具有重要的使用价值。 ...
  • 万年历程序(汇编语言),功能:能显示阳历年、月、日、星期、时、分、秒和阴历月、日,并能标明是否是闰年的电子万年历
  • 单片机万年历系统设计,帮助大家更好的了解单片机设计
  • 单片机电子万年历

    2020-08-22 00:01:56
    1 引言 在日新月异的21世纪里,家用电子产品得到了迅速发展。许多家电设备都趋于人性化、智能化,这些电器设备大部分都含有CPU控制器或者是单片机。单片机以其高可靠性、高性价比、...本文设计电子万年历属于小型智能

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 706
精华内容 282
关键字:

多功能电子万年历的设计