摘 要
随着社会的进步和科学技术水平的不断发展,人们对于资源的需求量越来越大,在资源开发利用的同时也可能
会造成资源的浪费,尤其是在建筑物中对于电能的利用方面。教室中空无一人但灯火通明的情况时常发生,所以设
计一款基于单片机的智能教室照明系统,从而实现教室中人走灯灭,节能减排的这种需求。
本文最终确定了AT89C51单片机为核心,结合时钟模块,温度检测模块,显示模块,按键模块,红外检测模块,
光线检测模块,LED灯控制电路等硬件设备来构成整个控制系统。其中显示模块采用LCD1602,用来显示时间和温度
信息并通过按键进行定时调节的显示;温度检测模块采用DS18B20来进行环境温度的检测;时钟模块采用DS1302对时
间的设定进行控制;红外检测模块采用红外对管来对进出教室的人员进行处理;光线检测模块采用光敏电阻对周围
环境光进行检测。本文将对硬件模块,进行外围应用电路的深入分析和设计,并且针对其软件开发进行概述。同时
通过制作实物来验证整个设计,通过实物的制作和调试,证明了本次设计的有效性和可靠性。
关键词:51单片机,节能减排,智能照明
第1章 绪 论
1.1 选题的目的及意义
在现实生活中,随着经济的高速发展,人们面临着能源短缺的重大问题,这成为了每一个国家经济快速发展的
阻碍。电能的利用对于人们日常生活和工商业更是不可或缺的。现如今低碳生活成为更多地区的代名词,人们对于
节能减排愈发重视,甚至对于资源的节约利用已经成为全球共识,而作为培养国家人才的高校更应该起到模范带头的作用。但是目前全国各大高校不断进行扩招,院校和教室不断扩建,导致教室照明的需求量也在不断上升。与此
同时,教室内空无一人却灯火通明的现象时常发生,这不仅对公共资源造成了严重的浪费,而且对高校的形象造成
了很坏的影响。
目前国内外对于教室内部照明的智能控制十分重视,很多国家和地区采用了智能教室照明系统,从而实现了对
电能的节约利用。然而在国内,对于教室中灯光的智能化控制尤其欠缺,仍停留在传统的手工管理模式中。由于学
生的普遍增多,各大高校新增了很多教学用楼,导致教室用电额度普遍增加。与此同时,学生们对于节约用电的意
识尚未成熟,上完课后少有关灯节能的习惯,导致空无一人的教室中照明设施依旧工作。而且学校对于节能的计划
很不完善,导致在学生和老师都离校后,还需要安排额外的人力去对教室进行检查,教室数量众多,导致人力资源
利用不合理,这样就造成额外的电能浪费和经济损失。这种资源的浪费与当今的节约能源理念十分违背,所以在智
能化和自动化的今天,众多的半导体企业纷纷开发专用功率开关器件和控制集成电路,使各种照明控制系统兼具高
品质和高可靠的特性。与此同时,随着互联网技术的深入,各种控制模式和控制方法的互联互通,实现了资源共
享、远程监控、设定、控制以及对各种异常数据的统一调度。为了解决我国教学楼中的电能浪费的重大问题,本文
所研究的智能教室照明控制系统就可以很好地实现节约能源的作用。
1.2 国内外现状
在八十年代的美国,照明行业兴起并迅速发展起来,到了九十年代后期,计算机技术、自动控制技术、现代通
信技术等等在全世界得到广泛应用,且技术趋向成熟,对各行各业都带来了巨大的影响,照明行业也发生巨大变
革。进入21世纪后,大量的新产品、新设备、新技术的出现,使智能照明技术向着系统一体化集成的方向发展。
欧洲的智能照明控制系统已经很普遍了,占据了50%以上的市场份额,但国内在这方面起步较晚,由于产品的技
术和生产成本太高,市场份额远远低于欧美,甚至连1%都不到。现如今智能照明市场总线种类繁多,比较典型的有
西门子的Philips的DALI总线、EIB/KNX总线、奇胜的C-BUS总线以及CAN总线,在市场中有很多总线技术。目前,在
照明行业中,还没有一个统一的总线协议规范,存在着各种不同的协议,这些协定相互不相容,不利于产品替换。
如果想替代传统照明需要重新更改布线。随着国际、国内的照明总线逐渐形成一个统一的规范,各公司的照明设备
相互兼容,这将是一个巨大的变革,将会整合整个行业。照明系统的使用会更加便利,节约了费用。
大量的外资企业进驻中国,对我国的智能化照明产业起到了促进作用。索博、瑞朗、九州等都是最先进入到智
能照明领域的公司。索博是国际型智能家居产品生产企业,拥有全亚洲最大的产品研发中心和生产基地,以高端智
能安防系统和智能微控模块在国际上取得较好成绩。天津瑞朗主打智能照明培训,从而培养了很多这方面的国内人
才。上海奇胜推出智能化系统,通过对C-Bus智能控制系统、安防控制系统、无线网络技术等的结合,实现了灯光控
制、家电控制等。但是对于学校领域,教学楼中的智能照明系统少之又少,在我国很少地区能够实现人走灯灭的智
能控制,都是通过人力资源来对教室中的照明进行控制的,导致了资源的浪费和经济的损失。本设计能够很好的满
足资源的合理利用,并且在资金方面和人力方面的投入也能大大减少。
第2章 系统硬件方案
2.1 系统总体方案
本设计选择AT89C51作为主控芯片,使用DS1302时钟芯片实现日期时间计时,同时通过LCD1602液晶显示屏实时
显示信息,利用光敏电阻检测教室的光线强弱,采用两个红外对管进一步来对教室内部人员数量的信息进行收集。
教室内部人员数量的信息数据最大值为99人并且人员数量信息的数据将反映到LCD1602显示器上,使用者可以根据此
清楚而准确地了解教室内的人数情况。本设计分为手动模式和自动模式两种,在手动模式下,LED灯开启的数量是要
通过人工进行按键的切换。在自动模式下,首先需要人工进行当前时间的设置以及整个系统能够开启的时间段的设
置,在设定好的时间段内,如果教室中有人进入且周围环境光很暗,则系统自动打开LED灯来进行照明。教室中有人
和环境光暗两者必须同时具备,缺一不可,才能灯亮,灯亮的个数需要根据教室内部的人员数量信息来决定,如下
图2.1为本设计的总体方案。
图2.1 系统总体方案图
2.2 硬件方案的选择
2.2.1 主控芯片方案的选择
方案一:选择AT89C51单片机。该型号的单片机是51系列单片机中的一种,其引脚分布、程序编写等知识是学校
中老师所教学的内容,我对此单片机十分熟悉,所以选择该型号就比较容易上手进行操作。整个单片机一共有40个
引脚,除去电源引脚外,还包括复位引脚,晶振配置引脚和IO输入输出管脚。在程序编写时除了可以直接对单个引
脚进行操作外,还可以直接对一组单片机的引脚进行操作,非常方便。
方案二:选择STM32单片机。此单片机有着数量很庞大的用户,这是一种高端的主控芯片,一般用来作为无人
机、机械手的主控芯片。它能够对程序中的不同步骤进行同时加工,处理数据的速度很快,但是此单片机在学校中
很少用来做为实验设计的主控芯片,对其的熟悉程度没有AT89C51的高。如果本设计采用此芯片,可能对于该芯片的
学习和投入将花费不少的精力和财力。
综上所述,经过反复比较,从费用、单片机的优缺点、操作经验等,选择51系列单片机对本课题性价比最高,
所以最终主控芯片选择AT89C51单片机。
2.2.2 显示器件方案的选择
方案一:选择LCD液晶显示器。在学校的学习和设计实验的过程当中,多次选择LCD来作为大学实验中的显示器
件,所以对LCD显示器十分了解。它是采用两块玻璃板来作为液晶盒的外部保护设备,对于该器件的应用和维修都是
十分了解的,而且价格上能够让人接受。
方案二:选择LED显示器。在当今社会的诸多领域中,LED显示器已经是大众十分熟悉的显示器件了,此显示器
广泛应用在医疗、照明等领域,设计十分巧妙,通过电子和空穴复合释放能量发光。但是在大学实验中很少接触,
对于此器件的维修更是十分繁琐。
综上所述,在LCD和LED的优缺点的比较中以及费用上的预算等方面综合考虑,最终选择LCD液晶显示器,在本设计中选择LCD1602。
2.2.3 时钟芯片方案的选择
方案一:选择DS1302。在本科的学习过程中,多次采用DS1302作为实验设计的时钟芯片,不仅对芯片的程序输
入,还是引脚分布都有着充足的了解,能够用此来实现各种时间上的设计,以达到实验目的。此芯片操作起来十分
得心应手,而且其价格容易让人接受,性价比很高。
方案二:选择DS3231。此芯片有着极高的时间分辨率,是一款高精度的时钟芯片能够做到记录一年时间的误差
仅仅小于一分钟,属于高端产品。如果需要做与时间紧密相关的实验设计可以考虑用此芯片,该芯片价格高昂,由
于接触很少,操作起来会费时费力,精力和财力会有过多的投入。
综上所述,对于时钟芯片的最终选择是DS1302,该芯片适合本课题,能够为本实验设计提供可靠的时间模块。
2.2.4 电源方案的选择
方案一: 选择现实生活中常见的220V市电来作为系统输入,这种方式需要设计的电压转换电路,因为单片机等
芯片基本都是5V甚至更低的电压,所以电源转换电路较为复杂,成本较高,且设计难度较大。
方案二:选择DC电源接口,用适配器供电,再经过两个电容滤波,从而得到更稳更干净的电源。通过进行降
压,输出5V,给这个单片机以及其他原件进行供电。
综上所述,在本次设计中对于电源供电的方案进行比较,最终决定选择方案二,因为方案二的方式大大增加了
信度和效度,从而更好地投入到现实生活使用中。
2.2.5 温度检测方案的选择
方案一:选择温度敏感元件来作为温度检测元件,如PT铂电阻。这是一种随着温度的改变,其自身阻值也会改
变的材料,经过加工后的铂电阻其准确度非常高,可检测的温度范围也非常高,通常其高温可以达到几千摄氏度,
低温则可以到达绝对零度。但是这种方式还需要结合模数转换器,将其检测到的温度数据转换成数字量给控制器,
控制器才能正确识别,因此硬件和软件设计都较为复杂。
方案二:利用集成的数字温度传感器,如DS18B20。这种芯片检测范围并不宽,只有零下55到正125摄氏度,但
是其工作方式简单。控制器只需要通过一根数据线与其管脚连接,就可以直接将温度数据读出来,比较适合在一些
要求低的地方,并且该方案成本也低,可靠性也经过了市场验证。
综上所述,由于本次设计中应用的场合对温度检测范围要求不高,因此方案二更加贴合本次设计需求。
2.2.6 人数检测方案的选择
方案一:选择红外对管来作为人数检测装置。红外对管由红外发射管和光敏接收管组成。采用两组红外对管,
并排放置,一前一后组成。人们正常进入教室,首先经过第一个传感器,这时第二个传感器未被阻挡。然后因为我
们的红外传感器的宽度要大于人体的厚度,所以人继续往前走,第一个传感器不被阻挡,第二个传感器处于阻挡状
态,这样操作属于成功进入教室,人数加一,相反则人数减一。
方案二:选择压力传感器来作为人数检测装置。在压力探测人数的电路中,包括至少一个用于感应学生进入教
室的第一压力传感器和至少一个用于感应学生离开教室的第二压力传感器,第一压力传感器和第二压力传感器分别
通过模数转换电路与我们所选择的单片机相连。采用压力传感器来检测人数,能够多人同时通过门口立柱,但是费
用高昂,设计难度加大。
综上所述,在考虑到两种方案不同的优缺点和设计所需的费用问题,本设计采用方案一来作为人数检测装置。
2.2.7 光线检测方案的选择
方案一:选择光敏电阻来作为光线检测装置。首先通过光敏电阻来检测周围环境光,周围环境光的检测会有一
个中间值,超过这个值,被评定为周围环境光亮,从而阻值变小,分配到的电压也就会随着降低;相反低于这个
值,则评定为周围环境光暗,从而阻值也就会变大,分配到的电压也就会随着增加,从而控制照明。
方案二:采用光电二极管来作为光线检测装置。利用其产生的电流,电流的大小决定最终的照明控制,随着周
围环境光的不同变化,产生的电流会在通路中产生模拟信号,进一步将模拟信号转换为数字信号,再将数字信号通
过通信模块输送到单片机,进行比较得出最大值,从而控制照明。
综上所述,方案二的设计过于繁琐,用到的元器件较多,需要编程比较复杂,费用较高,所以本设计选择方案一来
对光线进行检测,更加清晰明了。
2.3 本章小结
在本章中,我们首先对硬件设计提供了一个整体的方案,其次考虑到硬件的不同型号以及适用范围,对主控芯片、
显示器件、时钟芯片、电源供电、人数检测、红外检测以及温度检测的不同方案进行了选择。
第3章 系统硬件电路设计
3.1 AT89C51单片机及其最小系统
本次基于单片机的智能照明系统设计选择的控制器型号为AT89C51,该型号的单片机是51系列单片机中的一种,
其引脚分布,程序编写等与学校课程教学的基本一致,所以选择该型号就比较容易上手进行操作。整个单片机一共
有40个引脚,除去电源引脚外,还包括复位引脚,晶振配置引脚和IO输入输出管脚。而在程序编写时,主要就是对
IO管脚进行操作,整个IO管脚一共有4组,分为P0-P3,单片机虽然是8位的,但是在程序编写时除了可以直接对单个
引脚进行操作外,还可以直接对一组单片机的引脚进行操作,非常方便。在指令集上完全采用MCS-51指令集,所以
其库文件的使用可以直接使用它的。并且为了方便客户使用,其内部集成了4K的FLASH,来保证用户可以将程序下载
到单片机里面,并且可以反复插写不会丢失。另外其还自带有多个定时器、中断、串口通信功能,方便与外部芯片
进行配合使用,达到功能设计目标,如下图3.1所示。
图3.1 AT89C51单片机图
单片机中的最小系统晶振在电路中的作用很大,在单片机的程序工作中,是依靠着相关的准则来执行命令的,
而这些准则就是通过单片机中的晶振来决定的。简单来说,输入一条赋值的指令是需要周期计算的,这个周期具体
多长时间就是通过晶振的频率来决定的。比如说,选择的是12MHZ晶振,将单片机的XTAL1和XTAL2引脚与此晶振相
连,接收晶振所产生的震荡波形并对其分频,从而来决定单片机的运行基准频率。同时还需要对其增加两个电容,
也就是图中所示的C3和C4,以增加其运行时的稳定性,其晶振电路如下图3.2所示。
图3.2 晶振电路
在介绍完晶振电路后,复位电路在单片机最小系统中也是十分重要的。复位电路的作用是将单片机的程序复位
到起始点并运行程序。设计复位电路的意义是因为在单片机接通电源后的一段时间,电的上升状态很不稳定,这个
时候单片机中的程序是很容易跑乱的。在单片机的引脚中设计了RST复位引脚,只有当该引脚接收到高电平时程序才
正常工作,否则程序会一直停留在初始状态。在上电的瞬间,由于电容的作用,复位引脚与GND连接变低,当上电完
成后,电容充满电,该引脚变高,程序重新开始运行。与此同时还加入了按键,其作用是可以进行人工复位,按下
按键后复位引脚变低,系统复位,松开按键后则重新开始运行程序,
3.2 温度检测部分电路设计
本设计中的温度检测部分,我们采取DS18B20作为温度检测的主控芯片。DS18B20发送信息传递到单片机,单片
机对此信息进行处理,进行温度检测,得出温度值。DS18B20一共有三个引脚,引脚1连接电阻R19,其作为上拉电
阻,与引脚2相连。引脚3接地,保护本电路。本电路中的上拉电阻具有能够增加本设计中温度检测部分的抗干扰能
力从而使设计精度更高,电路设计如图3.4。
图3.4 温度检测电路设计图
3.3 时钟电路设计
本设计中的时钟芯片,我们选择DS1302来为本系统进行时间上的工作运算,也就是图中的U1。此芯片一共有八
个引脚,引脚8的位置连接了一个纽扣电池,来为时钟芯片供电,为了保证时钟芯片的时间能够与正常生活中的时间
相一致,防止时间数据发生异常或者时间停止等事件的发生。纽扣电池也就是图中的J1,也可以被称为备用电池。
引脚1为正常的电源输入,这是当整个系统有电源供电时的电能输入。引脚5、引脚6、引脚7与本设计中的单片机相
连接,为单片机传递时间信息,从而让单片机运算并判断,然后传送到显示器件显示时间。引脚5、引脚6、引脚7分
别并联R5、R6、R7三个电阻来作为本电路中的上拉电阻,在时钟电路设计中这三个上拉电阻的主要作用是增强本部
分设计的抗干扰能力从而使精度增加。引脚2和引脚3所连接的晶振,也就是图中所示的Y1,此部分设计中的晶振作
用是给本时钟芯片提供震荡信号,从而芯片才能正常运行。引脚4接地,从而保护电路,
3.4 红外对管电路设计
本设计中的人数检测部分,我们选择红外对管和电压比较器相互配合完成,电压比较器选择LM339,也就是图中
所示的U3,共有14个引脚。此电压比较器在本部分设计中的作用是来比较输入电压中的正电压与负电压的大小,也
就是图中所示的引脚5和引脚4的电压,对此进行判断,从而输出高电平还是低电平。引脚9和引脚11连接着本部分设
计中的红外对管,D5和D6为一组红外对管,分别负责接收信号和传送信号,在有人经过的时候,红外线被人所遮挡
导致接收信息的红外对管导通,此时通过电压比较器比较,输出低电平。相反,当没有人经过时,则输出高电平。
D10和D3也是一组红外对管,功能同上。在两组红外对管的基础上分别串联一个有着限流保护作用的发射管为R10和
R3。在两条线路接收端并联接入R14和R9两个10K欧的电阻来作为本设计中的上拉电阻,本部分设计中的上拉电阻的
目的是得到高电平
3.5 光线检测部分电路设计
本设计中的光线检测部分,选择光敏电阻和电压比较器相互配合完成,电压比较器选择LM339,也就是图中所示的U3,共有14个引脚。此电压比较器在本部分设计中的作用是来比较输入电压中的正电压与负电压的大小,也就是
图中所示的引脚5和引脚4所接收的电压,对此进行判断,从而输出高电平还是低电平。在本部分设计中,引脚4所连
接的是光敏电阻,也就是图中所示的LIGHT1,引脚5所连接的是一个10K欧的电阻R17,当周围环境光的亮度强时,光
敏电阻的阻值就会变小,从而分到更少的电压。然后通过比较器比较,进一步输出高电平,相反,周围环境光的亮
度弱时,则输出低电平,导致引脚2所连接的LED灯D4被点亮,
3.6 显示模块电路设计
本设计中的显示模块,我们采用LCD1602,也就是图中所示的LCD1,共分为16的引脚。引脚1、引脚2、引脚3所连接
的R13在本部分设计中的作用是对LCD1602液晶屏进行调节。引脚16接地,保护本电路,
3.7 独立按键操作电路设计
本设计中的按键电路,一共有6个按键,按键按下接地,单片机通过判断端口为低电平,从而知道按键按下,
3.8 LED灯控制电路设计
本设计中对于LED灯的电路设计,一共包括6个,可以看到图中最上方两个小灯分别负责自动模式和手动模式的
指示灯,也就是D8和D9。图中下面的四个小灯的作用是负责教室内部的照明,也就是D14、D13、D12、D11,
3.9 电源电路设计
本设计中的电源电路部分,首先将电源接通电池盒,也就是图中所示的P1部分。然后经过开关控制,进一步电
流经过两个电容,也就是图中的C5和C6,滤波之后得到更加稳定的电源,从而给整个系统供电,系统正常运行。图
中所示的LED灯D7为电源指示灯,方便使用者判断本系统是否处于运行状态,
3.10 本章小结
在本章中,主要介绍了本智能教室照明系统的各部分的电路设计,对各个组成部分的模块做了详细的介绍。在
单一部分的电路设计中,涉及到的各个元件,也对其进行充足的补充说明,本章中所介绍的电路设计均是智能教室
照明系统中最重要的组成成分。
:PCB图
第4章 系统软件部分设计
4.1 软件开发环境的介绍
此次系统的设计过程中,采用单片机作为系统的主控模块,在软件程序的编写过程中,也要相对应的选用相匹
配的软件程序开发平台,来进行软件设计工作。一般来说,与单片机相匹配的软件开发平台有KEIL、IAR等。此次设
计选用的是KEIL软件开发设计平台。KEIL软件内部集成多种库文件,例如51单片机、stm32单片机库文件等。软件编
写过程中,可以直接进行编写然后进行相对应的编译,随后即可将生成的文件下载至单片机中去,确保单片机可以
进行正常的运行工作。KEIL软件支持在线调试操作,可运用专门的在线调试工具对单片机内部的数据进行实时的检
测操作,同时可以设置断电来逐步进行程序的运行工作,便于用户在操作过程中对程序进行调试工作,KEIL软件的
页面如图4.1所示。
图4.1 KEIL开发平台
4.2 系统重要部分程序的设计
4.2.1 主程序的设计
接通电源后,各个元器件和端口进行初始化,首先向时钟芯片DS1302发送信号请求,经回复的数据确认,数据
正确则更新时间数据,数据错误则返回上一步操作。在更新数据后,选择按键,按键正确则进入实时时间设置环
节,按键错误则返回上一步。在设置实时时间环节可以根据按键选择设置时间或者设置定时。如果没有选择设置定
时,则判断系统处于自动模式或者手动模式两种模式,在手动模式中,LED灯通过按键控制。在自动模式中,系统先
对时间进行判断,再对光线强弱进行判断,最后对人数进行判断,三个判断同时成立,LED灯才会亮起并根据人数控
制LED灯的数量,具体流程图如下图4.2所示。
图4.2 主程序流程图
4.2.2 LCD1602显示程序的设计
在接通电源后,首先设置光标,设置输入方式,然后进行清屏环节,对显示位置和内容进行初始化,进一步进行判断是否调用显示的子程序。程序判断是,则进行数据的处理,判断否,则跳转到上一步初始化显示位置和内
容。数据处理后进入下一步处理参数显示的地址,然后保留显示处理后的一位数据,最后结束,显示程序流程图如
图4.3所示。
图4.3 显示函数流程图
4.2.3 红外检测程序的设计
在接通电源后,程序首先进行扫描,内管检测到有人则进入下一步,外管进行检测。判断是,则记录从外入
内,人数加一;判断否,则让内管标记当前位置然后程序跳转到初始程序扫描环节。外管检测到有人则进入下一步
,内管进行检测,判断是,则记录从内出外,人数减一,然后对标记位置进行;判断否,则外管标记当前位置然后
跳转到初始程序扫描环节,具体流程图如下图4.4所示。
图4.4 红外检测程序流程图
4.2.4 时钟程序的设计
在接通电源后,时钟芯片开始工作。首先在显示器件显示时间,然后可以通过按键设置时间,程序内部转换BCD
码,去除写保护和加写保护,从而最终读取数据,具体流程图如下图4.5所示。
图4.5 时钟程序流程图
4.2.5 按键程序的设计
在接通电源后,按下选择键进入调节模式,这时可以设置实时时间和开关锁时间,设置好时间后结束。按下切
换键可以切换自动模式和手动模式,在手动模式下,亮灯的个数等于按键的次数,本设计的小灯一共有四个,按键
第五下会使小灯全灭。在自动模式下,首先判断时间是否处于开关锁时间段之内。判断结果为否,则灯不亮;判断
结果为是,则判断光线是否充足,结果为是,则灯不亮;判断为否,则自动亮灯,根据进入教室人数,自动调节亮
灯个数。本设计按键较多,如下图4.6和4.7所示为按键流程图。
图4.6 按键程序流程图1
图4.7 按键程序流程图2
4.2.6 定时器程序的设计
在接通电源后,定时器开始工作,分为两种模式,人数增加和人数减少。人数最大值为99,最小值为0,在三秒
内无触发另一个则自动清零,具体流程图如下图4.8所示。
图4.8 定时器程序流程图
4.2.7 温度检测程序的设计
接通电源后,首先初始化函数,然后启动温度检测,判断温度是否大于等于0,判断否,则在显示器件相应位置
添加一个负号;判断是,则在对应位置不写内容,接下来显示温度,然后程序结束,具体流程图如下图4.9所示。
图4.9 温度检测程序流程图
4.3 本章小结
在本章中,详细的介绍了本系统中重要部分的程序设计,本设计采用Keil软件进行对单片机的编程设计与开发
等操作。在本系统中,红外检测、显示模块、温度检测、时钟程序、定时器程序、按键程序为本设计重要组成部
分,对其进行了详尽的介绍和充足的补充说明,与此同时也对本设计的主程序的流程进行了详细的说明并配图加以
说明
附 录
附录1:程序
#include <reg51.h>
#include <intrins.h>
#define uchar unsigned char
#define uint unsigned int
uchar time_data[7];
uchar code write_add[7]={0x8c,0x8a,0x88,0x86,0x84,0x82,0x80}; //数据的地址
uchar code read_add[7]={0x8d,0x8b,0x89,0x87,0x85,0x83,0x81};
uchar code table1[]="C: 00|2000/00/00";
uchar code table2[]="T: 00|00:00:00 0";
uchar code table3[]=" "; //清屏内容
uchar code table4[]=" Set Real Time ";
uchar code table5[]=" Set Open Time ";
uchar code table6[]=" Start Time: ";
uchar code table7[]=" 00:00:00 ";
uchar code table8[]=" End Time: ";
uchar code table9[]="Date: 2000/00/00";
uchar code table0[]="Time: 00:00:00 0";
bit Adjust; //调节标志位,=1表示进入调节模式,=0是正常模式
bit Auto_flg; //自动模式标志位,=1表示手动模式,=0是自动模式
sbit rs=P1^0; //LCD1602
sbit rw=P1^1; //LCD1602
sbit e=P1^2; //LCD1602
sbit sck=P2^5; //时钟端口
sbit io=P2^6; //时钟端口
sbit rst=P2^7; //时钟端口
sbit SELT =P1^6; //选择键
sbit ENTER=P1^7; //确认键
sbit UP =P3^0; //加键
sbit DOWN =P3^1; //减键
sbit MODE =P3^2; //切换按键接口
sbit SWITCH=P3^3; //手动开关
sbit AUTO_LED=P1^3; //自动模式灯接口
22
sbit HAND_LED=P1^4; //手动灯接口
sbit LAMP1=P3^4; //灯接口
sbit LAMP2=P3^5; //灯接口
sbit LAMP3=P3^6; //灯接口
sbit LAMP4=P3^7; //灯接口
sbit LIGHT1=P2^0; //光线检测端口
sbit IN1=P2^2;
sbit OUT1=P2^1;
sbit DQ=P1^5; //温度数据口
uchar Select_num; //选择按键按下次数
uchar Enter_num; //确认按键按下次数
uchar Switch_num; //手动开关按键按下次数
float Year,Month,Day,Hour,Minute,Second; //时间设置值
uchar Week;
float Hour_H,Minute_H,Second_H; //设置开始时间
float Hour_L,Minute_L,Second_L; //设置结束时间
int Value1; //人数
uchar IN1_flag=0;
uchar OUT1_flag=0;
uint Count; //T0定时器中断计数次数
uchar Miao;
int temperature; //全局变量 温度
void read_rtc();
/**************************************************LCD1602显示
*******************************************************************/
void delay1(uint z) //延时函数
{
uint x,y;
for(x=z;x>0;x--)
for(y=10;y>0;y--);
}
void write_com(uchar com) //写指令函数
{
rw=0;
delay1(5);
rs=0;
delay1(5);
e=1;
delay1(5);
P0=com;
delay1(5);
e=0;
delay1(5);
}
void write_date(uchar date) //写数据函数
{
rw=0;
23
delay1(5);
rs=1;
delay1(5);
e=1;
delay1(5);
P0=date;
delay1(5);
e=0;
delay1(5);
}
void init() //初始化函数
{
uchar num;
Year=17;
Month=1;
Day=1;
Hour=15;
Minute=30;
Second=50;
Week=1;
Hour_H=22;
Minute_H=0;
Second_H=0;
Hour_L=18;
Minute_L=0;
Second_L=0;
e=0; // 时序表e初始为0
write_com(0x38); //设置16*2显示,5*7点阵,8位数据接口
write_com(0x0c); //设置光标
write_com(0x06); //光标自动加1,光标输入方式
write_com(0x01); //清屏
write_com(0x80); //设置初始显示位置
for(num=0;num<16;num++)
{
write_date(table1[num]);
delay1(5);
}
write_com(0x80+0x40);
for(num=0;num<16;num++)
{
write_date(table2[num]);
delay1(5);
}
}
void SetTime_dispaly(uchar add,uchar dat) //第一个:参数的地址,第二个:参数的内容
{
uchar shi,ge;
24
shi=dat/10; //把十位提取出来
ge=dat%10; //把个位提取出来
write_com(add); //要写的地址
write_date(0x30+shi); //十位的内容 1602字符库
write_date(0x30+ge); //个位的内容 1602字符库
}
void Week_dispaly(uchar add,uchar dat) //第一个:参数的地址,第二个:参数的内容
{
write_com(add); //要写的地址
write_date(0x30+dat); //十位的内容 1602字符库
}
void Date_dispaly(uchar add,uchar dat) //第一个:参数的地址,第二个:参数的内容
{
uchar shi,ge;
shi=dat/16; //把十位提取出来
ge=dat%16; //把个位提取出来
write_com(add+3); //要写的地址
write_date(0x30+shi); //十位的内容 1602字符库
write_date(0x30+ge); //个位的内容 1602字符库
}
void LCD_Clean(uchar i) //液晶屏清除显示
{
uchar num;
if(i==1)
{
write_com(0x80); //设置初始显示位置
for(num=0;num<16;num++) //显示第一行内容
{
write_date(table3[num]);
delay1(1);
}
}
if(i==2)
{
write_com(0x80+0x40);
for(num=0;num<16;num++) //显示第二行内容
{
write_date(table3[num]);
delay1(1);
}
}
}
void LCD_Display_String(unsigned char line,unsigned char *string)
{ //液晶屏显示内容,把要显示的内容写到对应的位置
unsigned char i;
unsigned char address=0;
if(line==1)
25
{
address=0x80; //0X80是第1行的第1个位置 ,0x81第2位
}
else if(line==2)
{
address=0x80+0x40; //0X80+0x40是第2行的第1个位置 ,0X80+0x40+1是第2行第2位
}
for(i=0;i<16;i++)
{
write_com(address);
write_date(string[i]);
address++;
}
}
/**********************************DS1302时钟***************************************/
void Time_Display(void)
{
read_rtc();
Date_dispaly(0x80+0x40+9,time_data[6]); //显示秒
Date_dispaly(0x80+0x40+6,time_data[5]); //显示分
Date_dispaly(0x80+0x40+3,time_data[4]); //显示时
Date_dispaly(0x80+11,time_data[3]); //显示日
Date_dispaly(0x80+8,time_data[2]); //显示月
Week_dispaly(0x80+0x40+15,time_data[1]); //显示周
Date_dispaly(0x80+5,time_data[0]); //显示年
// Year/10*16+Year%10
Hour=time_data[4]/16*10+time_data[4]%16;;
Minute=time_data[5]/16*10+time_data[5]%16;;
Second=time_data[6]/16*10+time_data[6]%16;;
}
void write_ds1302_byte(uchar dat)
{
uchar i;
for(i=0;i<8;i++)
{
sck=0;
io=dat&0x01; //准备数据,从最低位开始
dat=dat>>1;
sck=1;
}
}
void write_ds1302(uchar add,uchar dat)
{
rst=0;
_nop_(); //CPU原地踏步
sck=0;
_nop_();
26
rst=1;
_nop_();
write_ds1302_byte(add); //传地址
write_ds1302_byte(dat); //传数据
rst=0; //不受其他影响
_nop_();
io=1; //释放
sck=1;
}
uchar read_ds1302(uchar add)
{
uchar i,value;
rst=0;
_nop_(); //CPU原地踏步
sck=0;
_nop_();
rst=1;
_nop_();
write_ds1302_byte(add);
for(i=0;i<8;i++)
{
value=value>>1;
sck=0;
if(io)
value=value|0x80;
sck=1;
}
rst=0;
_nop_();
sck=0;
_nop_();
sck=1;
io=1;
return value;
}
void set_rtc() //设置时间
{
uchar i,j;
for(i=0;i<7;i++) //转换BCD码
{
j=time_data[i]/10;
time_data[i]=time_data[i]%10;
time_data[i]=time_data[i]+j*16;
}
write_ds1302(0x8e,0x00); //去除写保护
for(i=0;i<7;i++)
{
27
write_ds1302(write_add[i],time_data[i]);
}
write_ds1302(0x8e,0x80); //加写保护
}
void read_rtc()
{
uchar i;
for(i=0;i<7;i++)
{
time_data[i]=read_ds1302(read_add[i]); //最终读出来的数 16进制
}
}
/*************************************************按键
****************************************************************/
void Keyscan(void)
{
if(SELT==0)
{
delay1(2);
if(SELT==0)
{
while(!SELT);
Select_num++; //选择键按下一次
Adjust=1; //进入调节模式
}
if(Select_num==1)
{
LCD_Clean(1);
LCD_Clean(2);
LCD_Display_String(1,table4);
LCD_Display_String(2,table5);
write_com(0x80+0); //写 >>
write_date(0x3e);
write_com(0x80+1); //写 >>
write_date(0x3e);
Enter_num=0;
}
if(Select_num==2)
{
LCD_Clean(1);
LCD_Clean(2);
LCD_Display_String(1,table4);
LCD_Display_String(2,table5);
write_com(0x80+0x40+0); //写 >>
write_date(0x3e);
write_com(0x80+0x40+1); //写 >>
write_date(0x3e);
28
Enter_num=0;
}
if(Select_num==3)
{
LCD_Clean(1);
LCD_Clean(2);
LCD_Display_String(1,table1);
LCD_Display_String(2,table2);
Select_num=0;
Enter_num=0;
Adjust=0;
}
write_com(0x0c); //光标不再闪烁
Enter_num=0;
}
if(ENTER==0)
{
delay1(2);
if(ENTER==0)
{
while(!ENTER);
Enter_num++;
}
if(Select_num==1) //设置实时时间
{
if(Enter_num==1)
{
LCD_Clean(1);
LCD_Clean(2);
LCD_Display_String(1,table9);
LCD_Display_String(2,table0);
SetTime_dispaly(0x80+8,Year);
SetTime_dispaly(0x80+11,Month);
SetTime_dispaly(0x80+14,Day);
SetTime_dispaly(0x80+0x40+6,Hour);
SetTime_dispaly(0x80+0x40+9,Minute);
SetTime_dispaly(0x80+0x40+12,Second);
Week_dispaly(0x80+0x40+15,Week);
write_com(0x80+8); //光标闪烁地址,停留在年的位置上
write_com(0x0f); //光标闪烁
}
if(Enter_num==2)
{
write_com(0x80+11); //光标闪烁地址,停留在月的位置上
write_com(0x0f); //光标闪烁
}
if(Enter_num==3)
29
{
write_com(0x80+14); //光标闪烁地址,停留在日的位置上
write_com(0x0f); //光标闪烁
}
if(Enter_num==4)
{
write_com(0x80+0x40+6); //光标闪烁地址,停留在时的位置上
write_com(0x0f); //光标闪烁
}
if(Enter_num==5)
{
write_com(0x80+0x40+9); //光标闪烁地址,停留在分的位置上
write_com(0x0f); //光标闪烁
}
if(Enter_num==6)
{
write_com(0x80+0x40+12); //光标闪烁地址,停留在秒的位置上
write_com(0x0f); //光标闪烁
}
if(Enter_num==7)
{
write_com(0x80+0x40+15); //光标闪烁地址,停留在星期的位置上
write_com(0x0f); //光标闪烁
}
if(Enter_num==8)
{
Enter_num=0;
write_com(0x0c); //光标不再闪烁
LCD_Clean(1);
LCD_Clean(2);
LCD_Display_String(1,table1);
LCD_Display_String(2,table2);
time_data[0]=Year;///10*16+Year%10;
time_data[1]=Week;///10*16+Week%10;
time_data[2]=Month;///10*16+Month%10;
time_data[3]=Day;///10*16+Day%10;
time_data[4]=Hour;///10*16+Hour%10;
time_data[5]=Minute;///10*16+Minute%10;
time_data[6]=Second;///10*16+Second%10;
set_rtc(); //设置时间
Select_num=0;
Adjust=0;
}
}
if(Select_num==2) //设置开关锁时间
{
if(Enter_num==1)
30
{
LCD_Clean(1);
LCD_Clean(2);
LCD_Display_String(1,table6);
LCD_Display_String(2,table7);
SetTime_dispaly(0x80+0x40+4,Hour_L);
SetTime_dispaly(0x80+0x40+7,Minute_L);
SetTime_dispaly(0x80+0x40+10,Second_L);
write_com(0x80+0x40+4); //光标闪烁地址,停留在时的位置上
write_com(0x0f); //光标闪烁
}
if(Enter_num==2)
{
write_com(0x80+0x40+7); //光标闪烁地址,停留在分的位置上
write_com(0x0f); //光标闪烁
}
if(Enter_num==3)
{
write_com(0x80+0x40+10); //光标闪烁地址,停留在秒的位置上
write_com(0x0f); //光标闪烁
}
if(Enter_num==4)
{
write_com(0x0c); //光标不再闪烁
LCD_Clean(1);
LCD_Clean(2);
LCD_Display_String(1,table8);
LCD_Display_String(2,table7);
SetTime_dispaly(0x80+0x40+4,Hour_H);
SetTime_dispaly(0x80+0x40+7,Minute_H);
SetTime_dispaly(0x80+0x40+10,Second_H);
write_com(0x80+0x40+4); //光标闪烁地址,停留在时的位置上
write_com(0x0f); //光标闪烁
}
if(Enter_num==5)
{
write_com(0x80+0x40+7); //光标闪烁地址,停留在分的位置上
write_com(0x0f); //光标闪烁
}
if(Enter_num==6)
{
write_com(0x80+0x40+10); //光标闪烁地址,停留在秒的位置上
write_com(0x0f); //光标闪烁
}
if(Enter_num==7)
{
Enter_num=0;
31
write_com(0x0c); //光标不再闪烁
LCD_Clean(1);
LCD_Clean(2);
LCD_Display_String(1,table1);
LCD_Display_String(2,table2);
Select_num=0;
Adjust=0;
}
}
}
if(UP==0)
{
delay1(2);
if(UP==0)
{
while(!UP);
if(Select_num==1)
{
if(Enter_num==1)
{
Year++;
if(Year>99)
Year=0;
SetTime_dispaly(0x80+8,Year);
write_com(0x80+8);
write_com(0x0f);
}
if(Enter_num==2)
{
Month++;
if(Month>12)
Month=1;
SetTime_dispaly(0x80+11,Month);
write_com(0x80+11);
write_com(0x0f);
}
if(Enter_num==3)
{
Day++;
if(Day>31)
Day=1;
SetTime_dispaly(0x80+14,Day);
write_com(0x80+14);
write_com(0x0f);
}
if(Enter_num==4)
{
32
Hour++;
if(Hour>23)
Hour=0;
SetTime_dispaly(0x80+0x40+6,Hour);
write_com(0x80+0x40+6);
write_com(0x0f);
}
if(Enter_num==5)
{
Minute++;
if(Minute>59)
Minute=0;
SetTime_dispaly(0x80+0x40+9,Minute);
write_com(0x80+0x40+9);
write_com(0x0f);
}
if(Enter_num==6)
{
Second++;
if(Second>59)
Second=0;
SetTime_dispaly(0x80+0x40+12,Second);
write_com(0x80+0x40+12);
write_com(0x0f);
}
if(Enter_num==7)
{
Week++;
if(Week>7)
Week=1;
Week_dispaly(0x80+0x40+15,Week);
write_com(0x80+0x40+15);
write_com(0x0f);
}
}
if(Select_num==2)
{
if(Enter_num==1)
{
Hour_L++;
if(Hour_L>23)
Hour_L=0;
SetTime_dispaly(0x80+0x40+4,Hour_L);
write_com(0x80+0x40+4);
write_com(0x0f);
}
if(Enter_num==2)
33
{
Minute_L++;
if(Minute_L>59)
Minute_L=0;
SetTime_dispaly(0x80+0x40+7,Minute_L);
write_com(0x80+0x40+7);
write_com(0x0f);
}
if(Enter_num==3)
{
Second_L++;
if(Second_L>59)
Second_L=0;
SetTime_dispaly(0x80+0x40+10,Second_L);
write_com(0x80+0x40+10);
write_com(0x0f);
}
if(Enter_num==4)
{
Hour_H++;
if(Hour_H>23)
Hour_H=0;
SetTime_dispaly(0x80+0x40+4,Hour_H);
write_com(0x80+0x40+4);
write_com(0x0f);
}
if(Enter_num==5)
{
Minute_H++;
if(Minute_H>59)
Minute_H=0;
SetTime_dispaly(0x80+0x40+7,Minute_H);
write_com(0x80+0x40+7);
write_com(0x0f);
}
if(Enter_num==6)
{
Second_H++;
if(Second_H>59)
Second_H=0;
SetTime_dispaly(0x80+0x40+10,Second_H);
write_com(0x80+0x40+10);
write_com(0x0f);
}
}
}
}
34
if(DOWN==0)
{
delay1(2);
if(DOWN==0)
{
while(!DOWN);
if(Select_num==1)
{
if(Enter_num==1)
{
Year--;
if(Year<0)
Year=99;
SetTime_dispaly(0x80+8,Year);
write_com(0x80+8);
write_com(0x0f);
}
if(Enter_num==2)
{
Month--;
if(Month<1)
Month=12;
SetTime_dispaly(0x80+11,Month);
write_com(0x80+11);
write_com(0x0f);
}
if(Enter_num==3)
{
Day--;
if(Day<1)
Day=31;
SetTime_dispaly(0x80+14,Day);
write_com(0x80+14);
write_com(0x0f);
}
if(Enter_num==4)
{
Hour--;
if(Hour<0)
Hour=23;
SetTime_dispaly(0x80+0x40+6,Hour);
write_com(0x80+0x40+6);
write_com(0x0f);
}
if(Enter_num==5)
{
Minute--;
35
if(Minute<0)
Minute=59;
SetTime_dispaly(0x80+0x40+9,Minute);
write_com(0x80+0x40+9);
write_com(0x0f);
}
if(Enter_num==6)
{
Second--;
if(Second<0)
Second=59;
SetTime_dispaly(0x80+0x40+12,Second);
write_com(0x80+0x40+12);
write_com(0x0f);
}
if(Enter_num==7)
{
Week--;
if(Week<1)
Week=7;
Week_dispaly(0x80+0x40+15,Week);
write_com(0x80+0x40+15);
write_com(0x0f);
}
}
if(Select_num==2)
{
if(Enter_num==1)
{
Hour_L--;
if(Hour_L<0)
Hour_L=23;
SetTime_dispaly(0x80+0x40+4,Hour_L);
write_com(0x80+0x40+4);
write_com(0x0f);
}
if(Enter_num==2)
{
Minute_L--;
if(Minute_L<0)
Minute_L=59;
SetTime_dispaly(0x80+0x40+7,Minute_L);
write_com(0x80+0x40+7);
write_com(0x0f);
}
if(Enter_num==3)
{
36
Second_L--;
if(Second_L<0)
Second_L=59;
SetTime_dispaly(0x80+0x40+10,Second_L);
write_com(0x80+0x40+10);
write_com(0x0f);
}
if(Enter_num==4)
{
Hour_H--;
if(Hour_H<0)
Hour_H=23;
SetTime_dispaly(0x80+0x40+4,Hour_H);
write_com(0x80+0x40+4);
write_com(0x0f);
}
if(Enter_num==5)
{
Minute_H--;
if(Minute_H<0)
Minute_H=59;
SetTime_dispaly(0x80+0x40+7,Minute_H);
write_com(0x80+0x40+7);
write_com(0x0f);
}
if(Enter_num==6)
{
Second_H--;
if(Second_H<0)
Second_H=59;
SetTime_dispaly(0x80+0x40+10,Second_H);
write_com(0x80+0x40+10);
write_com(0x0f);
}
}
}
}
if(MODE==0) //切换按键按下
{
delay1(2); //消抖
if(MODE==0)
{
while(!MODE); //按键释放
Auto_flg=~Auto_flg; //模式切换
if(Auto_flg==0) //自动模式
{
AUTO_LED=0; //自动灯亮
37
HAND_LED=1;
}
if(Auto_flg==1) //手动模式
{
AUTO_LED=1;
HAND_LED=0; //手动灯亮
LAMP1=1; //关灯
LAMP2=1;
LAMP3=1;
LAMP4=1;
}
}
}
if(Auto_flg==1) //手动模式
{
if(SWITCH==0) //灯开关按键按下
{
delay1(2); //消抖
if(SWITCH==0)
{
while(!SWITCH); //按键释放
Switch_num++; //按下次数增加
if(Switch_num==1) //按下一次,开一盏灯
{
LAMP1=0;
LAMP2=1;
LAMP3=1;
LAMP4=1;
}
if(Switch_num==2) //按下二次,开二盏灯
{
LAMP1=0;
LAMP2=0;
LAMP3=1;
LAMP4=1;
}
if(Switch_num==3) //按下3次,开3盏灯
{
LAMP1=0;
LAMP2=0;
LAMP3=0;
LAMP4=1;
}
if(Switch_num==4) //按下4次,开4盏灯
{
LAMP1=0;
LAMP2=0;
38
LAMP3=0;
LAMP4=0;
}
if(Switch_num==5) //按下5次,开0盏灯
{
LAMP1=1;
LAMP2=1;
LAMP3=1;
LAMP4=1;
Switch_num=0; //次数清除
}
}
}
}
}
void Open(void)
{
float Now;
float Set_L,Set_H;
Now=Hour+Minute/60+Second/3600; //把实时时间化成小时为单位
Set_L=Hour_L+Minute_L/60+Second_L/3600; //把设置下限时间化成小时为单位
Set_H=Hour_H+Minute_H/60+Second_H/3600; //把设置上限时间化成小时为单位
if(Auto_flg==0) //自动模式
{
if((Now>=Set_L)&(Now<=Set_H)) //实时时间在上下限时间范围内
{
if(LIGHT1==0) //光线不足
{
if(Value1==0)
{
LAMP1=1; //全灭
LAMP2=1;
LAMP3=1;
LAMP4=1;
}
if((Value1>0)&(Value1<10))
{
LAMP1=0; //亮一个灯
LAMP2=1;
LAMP3=1;
LAMP4=1;
}
if((Value1>=10)&(Value1<20))
{
LAMP1=0; //亮二个灯
LAMP2=0;
LAMP3=1;
39
LAMP4=1;
}
if((Value1>=20)&(Value1<30))
{
LAMP1=0; //亮三个灯
LAMP2=0;
LAMP3=0;
LAMP4=1;
}
if(Value1>=30)
{
LAMP1=0; //亮四个灯
LAMP2=0;
LAMP3=0;
LAMP4=0;
}
}
if(LIGHT1==1) //光线强烈
{
LAMP1=1; //全灭
LAMP2=1;
LAMP3=1;
LAMP4=1;
}
}
if((Now<Set_L)|(Now>Set_H)) //实时时间不在范围内
{
LAMP1=1; //全灭
LAMP2=1;
LAMP3=1;
LAMP4=1;
}
}
}
/*******************************************************************************************************/
void Count_Value(void)
{
SetTime_dispaly(0x80+3,Value1);
if(IN1==0)
{
delay1(1);
if(IN1==0)
{
while(!IN1);
if(OUT1_flag==0)
40
{
IN1_flag=1;
TR0=1; //开启定时器 防止误触发
}
if(OUT1_flag==1)
{
Value1++; //人数增加
if(Value1>=99) //如果人数计数到99,变为最大99
Value1=99;
OUT1_flag=0; //清零
IN1_flag=0;
TR0=0; //关闭定时器
Count=0;
Miao=0;
}
}
}
if(OUT1==0)
{
delay1(1);
if(OUT1==0)
{
while(!OUT1);
if(IN1_flag==0)
{
OUT1_flag=1;
TR0=1; //开启定时器 防止误触发
}
if(IN1_flag==1)
{
Value1--; //人数减少
if(Value1<0) //如果人数计数小于0,变为最小0
Value1=0;
IN1_flag=0; //清零
OUT1_flag=0;
TR0=0; //关闭定时器
Count=0;
Miao=0;
}
}
}
}
/****************************************温度
************************************************************/
/*void delay(uint y) //DS18B20延时函数(仿真用)
{
while(y--);
41
}
*/
void delay(uint z) //DS18B20延时函数(实物用)
{
uint x,y;
for(x=z;x>0;x--)
for(y=1;y>0;y--);
}
void write_byte(uint dat) //写一个字节
{
uchar i;
for(i=0;i<8;i++) //循环八次 共有八位
{
DQ=0; //写零
DQ=dat&0x01; //向总线写位数据,从最低位写起
delay(4);
DQ=1; //写一
dat>>=1; //下次写作准备,移位数据
}
delay(4);
}
uchar read_byte(void) //读一个字节,返回值
{
uchar i;
uint value;
for(i=0;i<8;i++) //循环八次 共有八位
{
DQ=0;
value>>=1;
DQ=1; //释放总线
if(DQ)
value|=0x80; //DQ=1,value取1
delay(4);
}
return value;
}
void ds18b20_init() //初始化函数
{
uint n;
DQ=1;
delay(8);
DQ=0;
delay(80); //低电平480——960us
DQ=1; //总线释放
delay(8); //等待50——100us
n=DQ; //读取复位状态
delay(4);
42
}
int readtemperature() //读整数部分
{
int a,b; //高八位,低八位
ds18b20_init();
write_byte(0xcc); //跳过rom匹配,跳过读序列号的操作,可节省操作时间
write_byte(0x44); //启动温度检测
delay(300); //测温度
ds18b20_init(); //开始操作前需要复位
write_byte(0xcc); //跳过rom匹配
write_byte(0xbe); //写读寄存器中温度值的命令
a=read_byte(); //low 低位开始读取
b=read_byte(); //high 高位开始读取
b<<=8; //把高位左移八位
b=b|a; //高八位与第八位进行或运算,得到总和
if(b>=0) //如果温度大于等于0
{
b=b*0.0625; //直接乘以0.0625
write_com(0x80+0x40+2); //在LCD1602对应的位置不写内容
write_date(0x20);
}
else //温度小于0
{
b=~b+1; //取反码再加一
b=b*0.0625; //再运算
write_com(0x80+0x40+2); //在LCD1602对应的位置写一个负号
write_date(0x2d);
}
return b; //返回b,此时以表示温度
}
void temperature_dispaly(char add,char dat) //温度显示函数:第一个:参数的地址,第二个:参数的内
容
{
uchar shi,ge;
shi=dat/10; //把温度的十位提取出来
ge=dat%10; //把温度的个位提取出来
write_com(0x80+0x40+add); //要写的地址
write_date(0x30+shi); //十位的内容 1602字符库
write_date(0x30+ge); //个位的内容 1602字符库
}
/*****************************************定时器初始化和主函数
***********************************************************/
void T0_init(void)
{
TMOD=0x11; //设置定时器0为工作方式1
TH0=(65536-50000)/256; //50MS定时
TL0=(65536-50000)%256;
43
EA=1; //开定时器T0的中断 总中断
ET0=1; //允许T0中断
// TR0=1; //开启定时器
}
void main()
{
AUTO_LED=0; //默认自动模式
temperature=readtemperature();
init(); //液晶初始化
T0_init(); //定时器初始化
delay1(8000);
while(1)
{
if(Adjust==0) //非调节模式下显示时间人数和温度
{
temperature=readtemperature(); //读取温度
temperature_dispaly(3,temperature); //显示温度
Time_Display(); //显示时间
Open(); //动作判断,根据光线和声响开关
Count_Value(); //计算人数
}
Keyscan(); //扫描键盘
}
}
void Timer0() interrupt 1 //定时器T0中断函数
{
TH1=0x3c; //赋初值=50ms
TL1=0xb0; //TH0=3C,TL0=B0
Count++; //每中断一次,Count加一,计数20次,表示1s的时间到
if(Count>=20) //1s到
{
Count=0;
Miao++;
if(Miao>=3) //3秒内无触发另一个,全部清零
{
Miao=0;
OUT1_flag=0; //清零
IN1_flag=0;
TR1=0; //关闭定时器
}
}
}
第5章 系统调试
5.1 硬件调试
在对本系统中的各部分电路设计和程序设计的逐步完善之后,我们所需要做的就是对已经购买好的硬件设备和
各元器件进行焊接。在焊接的过程中,首先需要准备恒温电烙铁、焊锡丝、松香、镊子等等。在准备好焊接所需的
器件后,开始进行对本系统进行焊接,在焊接的同时我们要先对可能发生的一些不良状况进行注意,比如虚焊、漏
焊、焊接短路等问题,进而对这些问题的解决方案做到心中有数。在对本系统整体焊接结束后,我们要进行调试环
节,在调试的过程中,需要使用到示波器、流量计、标准温度计、万用表等器件。需要对这些调试器件的使用方法
十分熟悉并牢牢掌握实际操作,以防因为自己的知识疏漏导致差误。与此同时,要对检测后可能出现的各种情况加
以分析,能够清楚地了解焊接好的系统发生问题后该如何修复,以下为本设计焊接好后的实物图5.1和图5.2。
图5.1 实物图正面
图5.2 实物图背面
5.2 软件调试
本次采用的是Keil 来进行软件程序的开发,首先新建工程,然后进入的CPU型号选择窗口,从中选择CPU生产厂
家及芯片型号。我们所选择的主控芯片是AT89C51芯片,选好芯片后,对此进行编程,运用到在学校所学的编程知
识,对本设计一步一步进行程序的编写。在编程的过程中,多次遇到错误,通过不断的尝试新语句,不断更改错误
语句,在结合老师的指导,最终完成了程序的编写,选择芯片界面设置如图5.3所示。
图5.3 选择芯片界面设置
程序代码编写完后需要编译链接生成目标代码,然后进行软件调试或模拟仿真,编译代码可以点击
或键盘的快捷键F7,编译后的结果如图5.4所示。
图5.4 软件编译结果界面
编译软件后,要对代码进行下载,下载到AT89C51单片机中。在经过调试,系统设计准确无误后,首先将Keil软
件把写好的程序转变为HEX代码,然后通过对已购买好的AT89C51单片机的专用烧录器,配合Easy 51Pro烧写软件对
程序进行烧录。最后按照设计指标进行测试,经测试后,本系统达到了设计目标。
5.3 本章小结
在本章中,介绍了本系统硬件和软件的调试,在硬件调试部分,详细介绍了有关硬件焊接的操作部分,在焊接
过后,对硬件进行基础性测试同时对其进行安全性测试。在软件调试部分,借助计算机的软件对单片机的程序进行
烧录,在烧录程序过后,对整个系统进行测试,经过一系列的调试后,本系统设计能够达到设计目标。
第6章 结 论
本次基于单片机的智能教室照明系统设计,最终确定了AT89C51单片机为核心,结合时钟模块、温度检测模块、
显示模块、按键模块、红外检测模块、光线检测模块、LED灯控制电路等硬件设备来构成整个控制系统。其中显示模
块采用LCD1602,用来显示时间和温度信息并通过按键进行定时调节的显示;温度检测模块采用DS18B20来进行环境
温度的检测;时钟模块采用DS1302对时间的设定进行控制;红外检测模块采用红外对管来对进出教室的人员进行信
息收集;光线检测模块采用光敏电阻对周围环境光进行检测。系统工作时整体稳定性强,基本可以实现预期的基于
单片机的智能教室照明系统功能,满足了教室中合理用电,智能控制,人走灯灭的要求。虽然本次分别从硬件和软
件的角度进行了设计,但是由于时间有限,还存在很多可以进一步改进的地方,如加入蓝牙连接、网络控制等模块
对本系统进行更加人性化的设计。
致 谢
白驹过隙,时光荏苒。现如今,我完成了我的大学毕业论文,也将离开学习四年的大学校园。本以为毕业遥遥无期,转眼就要各奔东西。我的内心充满了不舍,回首过往的点点滴滴,百般思绪涌上心头。面对四年间学习生活
的老师以及同学,我要向你们说一声谢谢。
首先感谢的是我的论文指导老师,赵丽娜老师,深深受益于老师得教导以及关心,我才能够在论文选题初的迷
茫阶段,逐渐找到写作的方向,从各渠道搜集论文的相关知识信息,逐渐清晰写作思路,同时对我的论文提出修改
意见,完善写作结构,提升写作知识层面内涵,让我受益良多。我需要学习赵老师身上一丝不苟的工作作风,严谨
踏实的工作态度。
其次需要感谢我的同学,大学生活有了你们的陪伴才有了生活的色彩,是你们让我学习到人与人之间的相处之
道,我们一起面对生活中的难题,正是因为你们,让我感受到了最简单的友谊。在日后的工作中,我们依然是最真
挚的朋友。
大学四年,我能够专心学习,不必为生活苦恼,正是因为父母作为最坚实的后盾支持着我,感谢父母的细心呵
护,在困难时期的鼓励,在成功时期的付出,我的每一步都有你们无私的爱与付出。即将踏上社会,我会努力工
作,用自己的实际行动感恩你们的付出,成为你们的骄傲。
最后,向所有关心我的师长、亲人、朋友表示我最真挚的谢意。