这个设计很全ppt 还有源程序和仿真都有
下载地址:
基于单片机的智能交通灯系统的设计.zip
(1.74 MB, 下载次数: 135)
下面是文档的部分内容预览:
ppt内容预览:
2 设计要求和设计方案2.1 设计要求 2.1.1项目概述 道路交通信号灯是道路交通安全的产品,它可以加强道路交通管理,减少交通事故的发生,提高道路使用效率,改善交通状况的一种重要工具。它适用于各种十字、丁字等交叉路口,由道路交通信号灯控制,指导车辆和行人安全而有序地通行。 2.1.2 设计任务 设计一种交通信号灯的控制系统。要求直观,简单。能够满足十字路口交通等的使用需求。 2.1.3 设计要求 1. 交通路口为十字路口。 2.马路为双向六车道,即要有左转灯,直行灯,右转灯。 3.要考虑人行横道的信号灯。 4.有显示设备可以显示通信时间。 5.要求通行时间可调。
2.2 设计方案 2.2.1 供电方案 系统要在正常而稳定的状态下工作,必须要有可靠的电源。而本次基于单片机的设计需要显示的芯片较多,电源供应量大,因此本次设计了如下三种方案。 方案一:利用独立的稳压电源供电。优点是可提供稳定而可靠电源,而且可以利用220V电压转化,不受各种因素的限制;缺点是各模块都采用独立电源,会增加本次设计的难度,而且对其他电路还会造成一定的干扰。 方案二:采用USB转接口5V电压供电,这样简单明了,但是单单一个5V电源供电可能会显得电源不足,而无法满足实际的需要。 方案三:采用USB转接口5V双电源电压供电,这样把本次设计分为两大模块,一是交通信号灯控制系统,二是万年历系统,采用双电源供电,一个为交通信号灯控制系统提供电源,另一个为万年历系统提供电源。这样即简单而又可提供稳定的电源。 考虑到实际情况和电路的简洁,本设计采用了第三套方案,此方案在电路的设计上可以把系统分为两大模块,使系统设计简单化,从而又可以提供稳定而可靠电源。而且在单电源断电的情况下不影响另一模块的工作。
2.2.2 输入方案 该系统要求能手动及智能控制改变交通信号灯的通行时间和万年历时间的调整。 方案一:采用8155扩展I/O 口及键盘、显示等。该方案的优点是:使用灵活可编程,并且有RAM,及计数器。若用该方案,可提供较多I/O 口,但操作起来稍显复杂。 方案二: 直接在I/O口线上接上按键开关。该方案优点是:编程更加简洁,使用更加简单,且成本更低。缺点就是功能有限。 综合考虑本设计的实际需要,在使用输入的时候不需要过于复杂的功能,因此直接在I/O口线上接上按键开关足以满足本次系统设计的需要,故采用方案二。
2.2.3 显示界面方案 本设计涉及到倒计时、状态灯、时间、温度等显示功能。基于功能需求,本设计考虑如下四种方案: 方案一:完全采用数码管显示。这种方案只显示有限的符号和数码字符,无法胜任功能需求。 方案二:完全采用点阵式LED 显示。这种方案实现复杂,且须完成大量的软件工作;但功能强大,可方便的显示各种英文字符,汉字,图形等。 方案三:采用LCD1602液晶显示,这种显示比较乐观,但是工作量大,而且设计复杂,再加上需要到的LCD1602较多,从经济上也不划算。 方案四:采用数码管、LED与LCD1602相结合的方法因为设计既要求倒计时数字输出,又要求有状态灯输出,同时还要显示出年、月、日、时、分、秒、星期和温度。为了方便观看并考虑到实际情况,用数码管显示交通信号灯的倒计时,用LED显示交通灯的状态灯、用LCD1602显示年、月、日、时、分、秒、星期和温度。这种设计方案既满足系统功能要求,又减少了系统实现的复杂度。
2.2.4交通灯方案 2.2.4.1交通灯规则方案 本设计的交通灯以十字路口为模型,在实现基本的功能前提下增加了时间及温度的液晶显示。从而还增加了路口高峰期的智能化人工管理机制。 实际生活中交通信号灯的规则千变万化,在不同的路口上看到的交通信号灯的规则不一样,但是总体还是相差不大,也有一些根据实际的需要而设置不同的交通规则,本次系统交通规则的设置是根据现实生活中的交通规则和多加考虑各种现实因素结合起来而制定了以下交通规则。 下图所示为一种红绿灯规则的状态图:
共四种状态,分别设定为S1、S2、S3、S4,交通灯以这四种状态为一个周期,循环执行如下图所示:
依据上述车辆行驶的状态图,可以列出正常模式下各个路口交通信号灯的逻辑表如下表所示(其中逻辑值“1”表示直行通行,逻辑值“0”表示禁止通行,逻辑值“L”表示左转通行):
表2.1 正常模式下工作表
程序就是在上述四种状态下循环转化的,而每种状态下又包括绿灯通行和黄灯闪烁警告状态。系统在正常模式下循环一个周期所需要的时间为120S,数码管显示的工作模式为半周期60.
2.2.4.2各种模式下通行时间 本系统结合实际的需要,而在上述四种状态的每种状态下再细分为绿灯通行、黄灯闪烁两种状态,总共八种工作状态。系统在正常工作模式下又可分为三种工作模式,分别为:正常模式、繁忙模式和特殊模式。各种模式下的时间分配如下表2.2: 表2.2 各模式下时间分配表
2.2.5 万年历设计方案 电路设计框图 本系统的电路系统框图如图2.6所示。AT89C51单片机对DS1302和DS18B20写入控制字并读取相应的数据,继而控制LCD1602作出对应的显示,同时也向交通信号灯外部发出定时中断,从而改变交通信号灯的工作模式。
3 芯片的介绍及应用3.1 AT89C51单片机介绍及应用 单片微型计算机简称单片机,同时又称为微控制器、嵌入式微控制器等,它属于第四代电子计算机。它把各种芯片电路集成在一块芯片上,因此它具有体积小、抗干扰能力强、功耗低、可靠性高以及低廉的价格。它广泛应用于工业过程控制、测控系统及各种智能仪器仪表等。因此国际上逐渐采用微控制器(MCU)代替单片微型计算机(SCM)这一名称。微控制器可以更准确的反应出单片机的本质,但单片机这一名称以为大多数人所接受,所以单片微型计算机即是单片机,同时又称为微控制器。
3.1.1单片机的引脚及功能 AT89C51单片机的 引脚图如图3.1 所示
图3.1 AT89C51单片机引脚图
VCC:电源电压 GND:地 P0口:P0口是一个漏极开路型8位双向I/0口,即数据/地址总线的复用口。当它作为输出端口使用时,每位能驱动8个TTL逻辑门电路。 P1口:Pl 口是一个带内部上拉电阻的8位双向I/O口,Pl口的输出缓冲级能驱动4个TTL逻辑门电路。当我们对端口写入“1”时,通过内部上拉电阻来把端口拉到高电平,此时P1口可做为输入端口。当P1口做为输入口时,由于内部存在上拉电阻,当被外部引脚拉低时会输出数据。P1口除了以上功能外,最重要的是它的第二功能如表3.1所示。
表3.1 P1口引脚的第二功能
P2口:P2口也是一个带有内部上拉电阻的8 位双向I/O 口。P2 的输出缓冲级也能驱动4 个TTL逻辑门电路。当我们对端口写入“1”时,通过内部上拉电阻来把端口拉到高电平,此时P2口可做为输入端口。当P2口做为输入口时,由于内部存在上拉电阻,当被外部引脚拉低时会输出数据。当访问外部程序存储器或16位地址外部数据的存储器时,P2口输出高8位数据地址。当访问8 位外部数据存储器地址时,P2口的内容在整个访问过程将不会改变。 P3 口:P3 口也是一个带有内部上拉电阻的8 位双向I/0 口。P3 口输出缓冲级也能驱动4 个TTL逻辑门电路。当我们对端口写入“1”时,通过内部上拉电阻来把端口拉到高电平,此时P3口可做为输入端口。当P3口做为输入口时,由于内部存在上拉电阻,当被外部引脚拉低时会输出数据。P3口除了以上用途外,最重要的是它的第二功能如表3.2所示。 表3.2 P3口引脚的第二功能
P3口还可用于接收一些Flash闪速存储器的编程及程序的校验控制等。 RST:复位输入引脚。振荡器在工作时,RST引脚将会出现两个或两个以上的机器周期高电平来使单片机进行复位。WDT 溢出将使该引脚输出高电平,设置SFR AUXR的DISRT0 位(地址8EH)可打开或关闭该功能。DISRT0位缺省为RESET输出高电平打开状态。 ALE/:在访问外部数据存储器或程序存储器时,ALE(地址锁存)主要是利用输出脉冲来锁存地址低8位字节。ALE还可以用于对外输出时钟信号或定时功能,因为ALE时刻以时钟振荡器的1/6频率输出固定的脉冲信号。 值得注意的是:当我们在访问外部数据存储器时将会跳过一个ALE脉冲。在F1ash存储器编程期间,该引脚还可用于输入编程脉冲。还可以通过特殊的功能寄存器中的8EH单元的D0位置来禁止ALE工作。执行该操作后,需要通过MOVX和MOVC指令ALE才能被激活,从而恢复工作状态。在执行单片机外部程序时,ALE应设置为无效。 程序的储存是在()允许输出外部程序存储器时进行,当单片机需要进行读写数据时,此时该引脚在每个周期下就会两次有效,从而发出两次脉冲,当访问外部的数据存储器时,则是发出一次脉冲信号。 /VPP:外部访问允许。当EA端设置为低电平时,CPU可单独访问外部程序存储器(即:0000H-FFFFH地址)。 XTAL1:振荡器的反相放大器和内部时钟发生器的输入端。
3.1.2单片机最小系统的设置 本次基于单片机交通信号灯控制系统的设计的单片机最小系统设置的如下图所示:
图3.2 AT89C51单片机最小系统图
3.2 LCD1602芯片介绍及应用 3.2.1 液晶显示器的介绍 液晶显示器有多种分类方法,通常按显示的方式分可分为字符式、段式、点阵式等。液晶显示器即可显示黑白又可显示多彩色灰度,因此可分为黑白显示器和彩色显示器。如果按驱动方式分,它又可以分为单纯矩阵驱动(Simple Matrix)、静态驱动(Static)和主动矩阵驱动(Active Matrix)三种。液晶显示器它具有厚度薄、体积小,适用于大规模集成电路的驱动等特点,目前已广泛应用在数字摄像机、便携式电脑、MP3、MP4、PDA移动通信工具等领域。
3.2.2 液晶显示器各种图形的显示原理 液晶显示器是利用液晶的物理特性,通过高低电压来控制显示的区域,高电平则亮,低电平则灭,这样就可以显示出图形。液晶显示器图形的显示是通过N*M个液晶显示单元构成,通过控制各个显示单元的亮暗而达到显示图形的目的。例如一个液晶显示器有64行显示屏,而每行有128列,每字节又由8列组成,因此每行可显示16个字节,即整个显示器可显示1024个字节。最后通过控制液晶显示相应位置的亮暗从而显示出每字节上的内容。
3.2.3 LCD1602的基本参数及引脚功能 LCD1602采用标准的14脚(无背光)或16脚(带背光)接口,本次设计采用16脚(带背光)来显示年、月、日、时、分、秒、星期和温度。各引脚接口说明如表3.3所示: 第1脚:VSS为地电源。 第2脚:VDD接5V正电源。 第3脚:VL是显示器对比度调整端,当接地信号时液晶对比度最高,当接正电源时对比度最弱,对比度的高低将会影响液晶显示器的显示,对比度过高时会产生“鬼影”。因此我们通常接一个10K的电位器来调节。 第4脚:RS是寄存器的选择,高电平表示数据寄存器、低电平表示指令寄存器。 第5脚:R/W是信号线的读写,当高电平时表示正在进行读操作,当低电平时表示正在进行写操作,如果RS和R/W同时为低电平时可进行显示地址或写入指令,如果RS为低电平且R/W为高电平时可进行读取信号,如果RS为高电平R/W为低电平时可进行写入数据。 第6脚:E端是使能信号端,当E端由高电平变化为低电平时,液晶模块命令开始执行。 第7~14脚:D0~D7是8位双向数据线。 第15脚:背光源正极。 第16脚:背光源负极。
表3.3引脚接口说明表 编号 | 符号 | 引脚说明 | 编号 | 符号 | 引脚说明 | 1 | VSS | 电源地 | 9 | D2 | 数据 | 2 | VDD | 电源正极 | 10 | D3 | 数据 | 3 | VL | 液晶显示偏压 | 11 | D4 | 数据 | 4 | RS | 数据/命令选择 | 12 | D5 | 数据 | 5 | R/W | 读/写选择 | 13 | D6 | 数据 | 6 | E | 使能信号 | 14 | D7 | 数据 | 7 | D0 | 数据 | 15 | BLA | 背光源正极 | 8 | D1 | 数据 | 16 | BLK | 背光源负极 |
3.2.4 LCD1602的指令说明及时序 LCD1602内部液晶显示模块控制器指令共11条,如表3.4所示: LCD1602的读写操作、光标操作以及屏幕显示等都是通过各种指令程序而实现的,具体说明如下:(注:1为高电平,0为低电平) 指令1:清显示,指令码由01H复位到00H位置。 指令2:光标复位,执行时光标将返回到00H位置。 指令3:光标以及显示模式的设置。光标的移动方向为:高电平表示右移,低电平则表示左移。S:屏幕文字是否进行右移或者进行左移。高电平表示有效,低电平表示无效。 指令4:显示控制开关。B:光标闪烁的控制,高电平表示闪烁,低电平则表示不闪烁。 C:光标开关的控制,高电平表示有光标,低电平则表示无光标 。D:整体显示的开关控制,高电平表示打开显示,低电平则表示关闭显示。 指令5:光标或者显示移位 S/C的控制。高电平表示文字移动的显示,低电平表示光标的移动。 指令6:功能命令的设置。DL为高电平表示4位总线,低电平则表示8位总线。F: 低电平表示显示5x7的点阵字符,高电平表示显示5x10的点阵字符。 N:低电平表示单行显示,高电平表示双行显示 指令7:字符发生器RAM地址设置。 指令8:DDRAM地址的设置。 指令9:光标地址和读忙信号。 BF:表示忙标志位,高电平时表示忙,此时模块不能进行命令或者数据的接受,低电平则表示不忙。 指令10:写数据。 指令11:读数据
表3.4 控制命令表 序号 | 指令 | RS | R/W | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 | 1 | 清显示 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 2 | 光标返回 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | * | 3 | 置输入模式 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | I/D | S | 4 | 显示开/关控制 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | D | C | B | 5 | 光标或字符移位 | 0 | 0 | 0 | 0 | 0 | 1 | S/C | R/L | * | * | 6 | 置功能 | 0 | 0 | 0 | 0 | 1 | DL | N | F | * | * | 7 | 置字符发生存贮器地址 | 0 | 0 | 0 | 1 | 字符发生存贮器地址 | 8 | 置数据存贮器地址 | 0 | 0 | 1 | 显示数据存贮器地址 | 9 | 读忙标志或地址 | 0 | 1 | BF | 计数器地址 | 10 | 写数据到CGRAM或DDRAM) | 1 | 0 | 写入的数据内容 | 11 | 从CGRAM或DDRAM读数 | 1 | 1 | 读出的数据内容 |
LCD1602液晶显示器有多种,其中市场上最常见的是HD44780。HD44780内置了DDRA(80字节),CGRAM(64字节,用来存放用户自定义字符,每个字符需要用到8个字节,所以以一共可以存放8个用户自定义字符),CGROM(内存了160个字符,包括了标准的ASCII码表上的字符等)。与HD44780相兼容的芯片时序表如表3.5所示:
表3.5 基本操作时序表 读状态 | 输入 | RS=L,R/W=H,E=H | 输出 | D0—D7=状态字 | 写指令 | 输入 | RS=L,R/W=L,D0—D7=指令码,E=高脉冲 | 输出 | 无 | 读数据 | 输入 | RS=H,R/W=H,E=H | 输出 | D0—D7=数据 | 写数据 | 输入 | RS=H,R/W=L,D0—D7=数据,E=高脉冲 | 输出 | 无 |
读写操作时序如图3.3和3.4所示。
图3.3 读操作时序
图3.4写操作时序 LCD1602液晶显示是一个慢显示器件,所以每次在执行指令时一定要先进行确认模块的忙标志位是否为低电平,否而指令将会失效。要进行字符的显示,首先要进行输入显示字符的地址,实际就是输入显示的内容。LCD1602内部显示地址如图3.5所示。
对液晶模块的初始化中要先设置其显示模式,在液晶模块显示字符时光标将会自动右移。液晶模块在繁忙状态下将不能输入指令。 3.2.5 LCD1602液晶显示模块原理图 LCD1602显示模块电路,首先VSS接地信号,VDD接5V高电平,VEE接10K的变阻器,变阻器两端分别接5V电压和大地。接着RS、RW和E是数据信号线分别接单片机的P2.5、P2.6和P2.7引脚。最后D0到D7接单片机的P0.0到P0.7引脚,要接排阻。而实际的LCD1602还有两个背光调节引脚,分别是BLA和BLK接5V电压和大地。
图3.6 LCD1602显示电路 3.3 DS1302芯片介绍及应用 DS1302是美国 DALLAS 公司推出的一种高性能、低功耗的时钟芯片,它采用 SPI 三线接口与 CPU 进行同步通信,并可采用突发方式一次传送一个或多个字节的时钟信号和 RAM数据。它可提供年、月、日、星期、时、分、秒的计时显示,各月天数它可进行自动的调整,同时还具有闰年的补偿功能。工作电压范围为2.5~5.5V。DS1302芯片采用双电源供电,即有主电源和备用电源。同时还可设置备用电源的充电方式,提供了对备用电源进行电流充电的能力。DS1302也可用于数据的记录, 特别是对于某些特殊意义的数据点的记录上,它能实现数据和数据出现的时间同时记录,因此在各种测量系统中得到广泛的应用。
3.3.1 DS1302引脚功能 DS1302芯片的外部引脚分配如图3.7所示。
图3.7 DS1302的外部引脚分配
Vcc1:表示主电源;Vcc2:表示备份电源。如果 Vcc2>Vcc1+0.2V, 则 Vcc2向 DS1302供电,如果 Vcc2< Vcc1,由则Vcc1向 DS1302供电。 SCLK:串行时钟,输入,控制数据的输入与输出。 I/O:三线接口时的双向数据线。 CE:输入信号。DS1302在进行读写数据时,此位必须为高。该引脚有两个功能:首先是CE 开始控制字进行访问移位寄存器的逻辑控制;其次是CE 提供结束单字节或者多字节数据的传输方法。
3.3.2 DS1302读写说明 DS1302是 SPI 总线方式来驱动。它不仅需要向寄存器写入控制字, 同时还需要读取相应寄存器的数据。
1:DS1302的读写寄存器 DS1302的时间寄存器总共有12个,其中读寄存器(81H-8DH)和写寄存器(81H-8DH)是以BCD码格式进行数据的存取。 表3.6 DS1302的读写寄存器 DS1302小时寄存器(84H、85H)的位7应用于DS1302是运行的模式。即12小时制还是24小时制。当位7为1时,表示12小时模式,此时相应的寄存器位5为高时表示AM,为低时表示PM。当位7为0时, 表示24小时模式。 DS1302秒寄存器(80H、81H)的位7应用于时钟暂停标志(CH)位。当该位为1时,时钟振荡器停止,DS1302处于低功耗状态;当该位为0时,时钟开始运行。 DS1302 控制寄存器(8EH、8FH)的位7是写入保护位,其它7位均为0。当WP位为0时可以开始进行对时钟和RAW进行写入,当WP位为1时,禁止对寄存器的写入操作。
2:DS1302中静态RAM地址 DS1302在静态工作模式下的RAW地址如表3.7所示。 表3.7 静态 RAM 的地址表
3:DS1302的工作模式寄存器 DS1302的工作模式寄存器又分为时钟突发模式寄存器、RAW突发模式寄存器等。突发模式是指一次传送多个字节的时钟信号和 RAM 数据。 突发模式寄存器如表3.8所示。 表3.8突发模式寄存器
4:DS1302控制字符表 DS1302的控制字最高位(位7)必须是1,才能进行数据的写入,否则不能写入数据;次高位(位6)表示RAW数据或者时钟日历的存取,当为1时表示RAW数据存取,为0则表示时钟日历的存取;接着是数据操作地址(位5-位1);最后是位0表示数据的读写操作,当为1时表示读操作,为0表示写操作。如表3.9所示。 表3.9 控制字(即地址及命令字节)
5:DS1302的读写时序 DS1302数据的读写规则是在控制字的基础上从低位到高位的输出,每当控制字指令输入后下一个SCLK脉冲上升沿到来时,数据就会被写入到DS1302中,数据从最低位开始写入,下一个SCLK脉冲的下降沿到来时,就会读出DS1302里面的数据,读出的规则也是从低位到高位。
图3.8 数据读写时序
3.3.3 DS1302时钟采集模块原理图
图3.9 DS1302原理图 此图为DS1302时钟芯片模块采集原理图,X1和X2接的是时钟晶振,,大小为32.768MHz,VCC1和VCC2分别接3V和5V电压,I/O、SCLK和ST分别接单片机的P1.6、P1.7和P1.5引脚。
3.4 DS18B20芯片介绍及应用 DS18B20数字温度计是DALLAS公司生产的One-Wire,即单总线器件,它具有体积小而且线路简单的优点。它只有三个引脚,分别是接高电源、地信号和一条数据信号线,设计简单而又方便。因此它广泛应用于温度的测温系统。 DS18B20除了体积小、线路设计简单优点外,它在实际应用中不需要其它外部任何器件即可实现温度的测温功能,测量温度范围为-55—+125 °C 之间,如超出检测的温度,DS18B20将会发出报警信号。数字温度计的读取可以从9位到12位选择。可用数据线供电,供电范围+3.0-5.5V电压。
3.4.1 DS18B20引脚功能 DS18B20器件的封装引脚排列及引脚功能的描述如图3.10和表3.10。
图3.10(底视图)
表3.10 DS18B20引脚功能 | | | | | | | | 数据输入/输出引脚。开漏单总线接口引脚。当被用在寄生电源下,也可以向器件提供电源。 | | | 可选择的VDD引脚。当工作于寄生电源时,此引脚必须接地。 |
3.4.2 DS18B20的应用及原理图 DS18B20采用的是one-Wire总线协议方式连接,实际就是在一根数据线进行数据的双向传输,,然而对于51单片机在硬件上并不支持单总线协议方式的连接,因此我们采用软件的方法来模拟单总线的协议方式来完成对DS18B20温度芯片的访问。 DS18B20的读写是在一根I/O线上进行,因此,对数据的读写有着严格的要求。DS18B20的通信协议也有着严格的要求来确保数据传输的正确性和完整性。 DS18B20有严格的通信协议来保证各位数据传输的正确性和完整性。该通信协议又细分了几种信号的时序,即:初始化时序、读时序、写时序。然而在这几种时序的工作下都是以主机作为主设备,单总线器件作为从设备。数据的传输是在主机先启动读入时序,读出数据后进入写时序命令,从而启动读写时序功能,完成数据的传输。数据和命令的传输都是低位在先。下面为DS18B20在本次设计的温度采集模块原理图3.11所示。
图3.11 DS18B20温度采集原理图
温度采集模块图主要是由DS18B20芯片进行,通过AT89C51单片机进行数据的传输和接受,从而再送到LCD1602,从LCD1602显示出来。DS18B20只有三个引脚,分别是VCC为正电源接5V电压,GND地信号接大地,最后是DQ数据输入/输出引脚连接AT89C51单片机的P1.0引脚。
4 程序的设计流程4.1 交通灯程序设计的流程 4.1.1倒计时显示的理论分析 利用定时器中断,设置 TH0= (65536-50000)/256,TH1=(65536-50000)%256,即每0.05秒中断一次。每到第20次中断即过了20*0.05秒=1秒时,使时间的计数值减1,便实现了倒计时的功能。
4.1.2状态灯显示的理论分析 黄灯闪烁同样可以利用定时器中断。每到第10次中断即过了10*0.05秒=0.5秒时,使黄灯标志位反置,即可让黄灯1秒闪烁一次。
4.1.3 交通灯主程序设计流程 交通灯系统在正常模式下工作的 - 首先,正常进入系统开始运行,交通灯在默认正常模式下工作,数码管显示时间为60。
- 交通信号灯开始正常工作,右转弯为常亮灯(绿色)。南北直行方向绿灯亮,其它方向亮红灯,数码管开始倒计时,南北方向从40倒计,东西方向从60倒计。
- 35S后,南北直行方向灭绿灯,闪烁黄灯,闪烁时间为5S。
- 40S后,南北直行方向亮红灯,左转方向亮绿灯,亮时15S,同时数码管从20倒计,其它不变。
- 55S后,南北左转方向灭绿灯,闪烁黄灯,闪烁时间为5S。
- 60S后,南北左转方向黄灯灭,亮红灯,数码管从60倒计,东西直行方向亮绿灯,数码管从40倒计,其它不变。
- 95S后,东西直行方向灭绿灯,闪烁黄灯,闪烁时间为5S。
- 100S后,东西直行方向灭黄灯,亮红灯,左转方向亮绿灯,亮时为15S,同时数码管从20倒计,其它不变。
- 115S后,东西左转方向灭绿灯,闪烁黄灯,闪烁时间为5S。
- 120S后,东西左转方向灭黄灯,亮红灯,数码管从60倒计。南北直行亮绿灯,数码管从40倒计。
图4.1 程序设计流程图
4.2万年历的设计流程 万年历的设置一方面可以更好的显示时间和温度,其次可以对交通信号灯系统的控制,定时向交通信号灯控制系统发出外部中断,从而该变当前工作模式,以致在实际中更具有实用性。
图4.2 万年历主程序流程图
此次万年历的设计功能主要是两个,一个是通过LCD1602显示时间和温度,另一个则是为交通信号灯提供外部定时中断,从而改变当前的工作模式。而万年历的设计流程和普通设计的一样,开始先是对各种芯片的初始化,接着是从DS1302、DS18B20读取时间和温度信息,从而传送到AT89C51单片机进行时间、温度的分离转化,再传送到LCD1602显示出来,其次是时间的调整和校正,通过调整按键可以对秒、分、时、星期、日、月和年进行加减1的修改。最近是定时发出外部中断,利用IF语句进行判断,适合条件则输出低电平到交通信号灯,否则为高电平。从而达到了实验的目的。
4.3万年历时间的调整流程 本次万年历系统的设计只是采用DS1302进行时间的扫描和读取,而没用进行掉电保存电路的设计,因此在每次硬件的开启,系统都是进入初始化设置,以致要进行时间的调整和校正。
图4.3 时间调整控制图 时间的调整程序设计。首先,系统在正常工作下,由DS1302时钟芯片进行时间的读取,传递到AT89C51单片机进行对时间进行分离和转化,最后由LCD1602显示出来。在进行时间的调整和校正时,先进入调整模式,即按下第一个按键,执行调整程序,先是进入秒程序调整,按加减按键相应的进行加1或减1,再按调整切换键进入分程序调整,按加减减进行相应的加1减1,再接着是时、星期、日、月、年的调整。按加减键进行相应的加减1,在进行调整途中,如果按退出时间调整按键都将会退出时间调整程序。如图4.3时间调整控制图所示。 4.4交通信号系统工作模式的工作流程
图4.4 工作模式切换图 开始进入系统时,默认模式为正常模式60S,到了早上7:00时上班高峰期,万年历发出中断,从而使系统进入繁忙模式45S,从而可以减少路口上交通的拥堵。时间到了早上9:00,车辆逐渐减少,万年历又发出定时中断使系统进入正常模式,到了11:00,万年历再次发出定时中断,重新使系统进入繁忙模式,减少车辆的拥堵。到了下午14:00,万年历发出定时中断,使系统进入正常模式,在正常模式下运行到下午17:00下班时间,为了减少交通的拥堵,万年历发出定时中断,使系统进入繁忙模式,到了晚上19:00,道口上车辆减少,万年历最后发出定时中断,使系统在正常模式下正常运行。系统在正常模式、繁忙模式和特殊模式下运行时,还可以进行外部手动进行中断来进行通行时间的调整。如:在正常模式下按下繁忙模式,系统将会进入繁忙模式,当下次中断再次到来时,系统会根据中断的要求而进入相应的模式,不会受上次中断或下次中断的影响。
5 软硬件的仿真调试5.1 PROTEUS仿真图 整个交通灯控制系统在PROTEUS仿真图中的仿真如下所示,双向通行,倒计时是2位时间提醒。每个路口分别有左转、直行和右转,分别有红灯、绿灯和黄灯提示。每个路口还相应配有行人的红绿灯指示 。图右上角数码管显示的工作模式,60代表是在正常模式下工作,45和75分别代表是在繁忙模式和特殊模式,右下角是开关调节按键,左边三个开关从上到下是交通灯各种模式转换开关,右边四个是时间调节开关,从而更具有实用性。 图5.1 PROTEUS仿真图
5.2 硬件实物图 此次硬件实物的设计分为两部分,一部分是电子器件的设计,另一部分是显示界面的设计。电子器件的设计主要是单片机的最小系统设置、DS1302时钟模块的设置、DS18B20温度模块的设置以及非门74HC04的设置等;显示界面的设置主要是LCD1602液晶显示模块、各个方向数码管倒计时、数码管显示系统工作模式、各个道口的交通灯、人行道的交通灯以及控制开关调节按键的设置。两部分的分开设计可以使实物显得更美观,同时对于实物的器件的修改和调整也提供了更好的条件。也更联系了实际生活,显得简单、整洁和美观。 显示界面如图5.2所示。 图5.2 硬件实物图
结 论本次基于单片机的交通信号灯控制系统的设计是在一般交通灯的基础上加上时间的智能控制,从而改变各方向通行的时间。因此本次智能交通灯的设计可以更好的解决了车流量较大的十字路口车辆的通行,与一般交通灯相比,它更具有实用性、简单化和智能化。从而也更好的应用到实际生活中,因此具有一定的实用价值。 此次基于单片机的交通信号灯控制系统的设计可以说得到了成功,但是还是存在很多的不足和缺陷。如:工作模式的时间只有三种选择,而不能通过按键进行随意更改,工作模式时间的智能切换也不能进行手动调节,以及对通过道口车流量的统计、车辆速度的检测和拍摄功能等都还没实现,而且此次设计需要用到的电子元器件较多,从经济上不划算,因此本次基于单片机的交通信号灯控制系统的设计也还存在着很大的不足,还有待改善。 致 谢历时三个月的设计已经告一段落。经过自己不断的搜索努力以及大家的耐心指导和热情帮助,本设计已经基本完成。同时系里兴趣小组实验室的开放和大部分兴趣小组同学也为我的设计提供指导,在他们的帮助和指导下得到了完成。在此对于他们的帮助给与深刻的致谢。 在本次基于单片机的交通信号灯的控制系统设计中一路遇到了很多的困难,首先从开始电路原理图的设计、画PCB图和制板都遇到了困难。先是对芯片的不理解,专业知识的不够从而导致在程序的编写中经常出错。接着是在最后的调试中,由于本次设计分为两块电路板,线路繁多因此也很容易混乱等问题。但是在大家的帮助和鼓励下一一得到了解决。 通过这次设计,使我对自己有了一个深刻的认识。认识到了自己专业知识的不足和专业知识的重要性,同时也理解了理论要联系实际含义。通过这次的设计也检验了自己的学习成果,认识到了自己的不足和缺陷,在今后的工作和学习中将会继续努力,不断完善,从而为今后的发展打下良好的基础。 由于自身水平有限,设计中一定还存在很多不足之处,敬请各位批评指正。
- 附录单片机程序:
- 1: 十字路口交通灯控制C程序1:
- /***********************************************************
- 十字路口交通灯控制 C 程序
- ***********************************************************/
- #include <reg51.h>
- #define uchar unsigned char
- #define uint unsigned int
- /*****定义控制位*******************************************/
- sbit Time_Show_LED1=P2^4; //Time_Show_LED1(直行时间显示)控制位
- sbit Time_Show_LED2=P2^5; //Time_Show_LED2(直行时间显示)控制位
- sbit SN_LED1=P2^0; //SN_LED1控制位
- sbit SN_LED2=P2^1; //SN_LED2控制位
- sbit EW_LED1=P2^2; //EW_LED1控制位
- sbit EW_LED2=P2^3; //EW_LED2控制位
- sbit SN_Yellow=P1^6; //SN黄灯
- sbit EW_Yellow=P1^2; //EW黄灯
- sbit EW_ManGreen=P3^0; //EW人行道绿灯
- sbit SN_ManGreen=P3^1; //SN人行道绿灯
- sbit Special_LED=P2^6; //交通特殊指示灯
- sbit Busy_LED=P2^7; //交通繁忙指示灯
- sbit Nomor_Button=P3^5; //交通正常按键
- sbit Busy_Btton=P3^6; //交通繁忙按键
- sbit Special_Btton=P3^7; //交通特殊按键
- sbit EW_ManRed=P3^3; //EW人行道红灯
- sbit SN_ManRed=P3^4; //SN人行道红灯
- sbit x=P1^7;
- sbit y=P1^3;
- bit Flag_SN_Yellow; //SN黄灯标志位
- bit Flag_EW_Yellow; //EW黄灯标志位
- char Time_EW; //东西方向倒计时单元
- char Time_SN; //南北方向倒计时单元
- uchar EW=60,SN=40,EWL=19,SNL=19; //程序初始化赋值,正常模式
- uchar EW1=60,SN1=40,EWL1=19,SNL1=19; //用于存放修改值的变量
- uchar code table[10]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F}; //0-9段选码
- uchar code S[8]={0x28,0x48,0x98,0x88,0x82,0x84,0x89,0x88};
- //交通信号灯控制代码
- /**********************延时子程序**************************/
- void Delay(uchar a)
- {
- uchar i;
- i=a;
- while(i--){;}
- }
- /*****************显示子函数******************************/
- void Display(void)
- {
- uchar h,l;
- h=Time_EW/10;
- l=Time_EW%10;
- P0=table[l];
- EW_LED2=1; //点亮EW_LED2
- Delay(2);
- EW_LED2=0; //熄灭EW_LED2
- P0=table[h];
- EW_LED1=1; //点亮EW_LED1
- Delay(2);
- EW_LED1=0;
- h=Time_SN/10;
- l=Time_SN%10;
- P0=table[l];
- SN_LED2=1; //点亮SN_LED2
- Delay(2);
- SN_LED2=0;
- P0=table[h];
- SN_LED1=1; //点亮SN_LED1
- Delay(2);
- SN_LED1=0;
-
- h= EW1/10;
- l= EW1%10;
- P0=table[l];
- Time_Show_LED1=1; //点亮Time_Show_LED1
- Delay(2);
- Time_Show_LED1=0;
-
- P0=table[h];
- Time_Show_LED2=1; //点亮Time_Show_LED2
- Delay(2);
- Time_Show_LED2=0;
- }
- /**********************外部0中断服务程序******************/
- void INT0_srv(void)interrupt 0 using 1
- {
- EX0=0; //关中断
- if(Nomor_Button==0) //测试按键是否按下,按下为正常状态
- {
- EW1=60;
- SN1=40;
- EWL1=19;
- SNL1=19;
- Busy_LED=0; //关繁忙信号灯
- Special_LED =0; //关特殊信号灯
- }
- if(Busy_Btton==0) //测试按键是否按下,按下为繁忙状态
- {
- EW1=45;
- SN1=30;
- EWL1=14;
- SNL1=14;
- Special_LED=0; //关特殊信号灯
- Busy_LED=1; //开繁忙信号灯
-
- }
- if(Special_Btton==0)//测试按键是否按下,按下为特殊状态
- {
- EW1=75;
- SN1=55;
- EWL1=19;
- SNL1=19;
- Busy_LED=0; //关繁忙信号灯
- Special_LED =1;//开特殊信号灯
- }
- EX0=1; //开中断
- }
- /**********************T0中断服务程序*******************/
- void timer0(void)interrupt 1 using 1
- {
- static uchar count;
- TH0=(65536-50000)/256;
- TL0=(65536-50000)%256;
- count++;
- if(count==10)
- {
- if(Flag_SN_Yellow==1) //测试南北黄灯标志位
- {SN_Yellow=~SN_Yellow;}
- if(Flag_EW_Yellow==1) //测试东西黄灯标志位
- {EW_Yellow=~EW_Yellow;}
- }
- if(count==20)
- {
- Time_EW--;
- Time_SN--;
- if(Flag_SN_Yellow==1) //测试南北黄灯标志位
- {SN_Yellow=~SN_Yellow;}
- if(Flag_EW_Yellow==1) //测试东西黄灯标志位
- {EW_Yellow=~EW_Yellow;}
- count=0;
- }
- }
- /*********************主程序开始***********************/
- void main(void)
- {
- Busy_LED=0;
- Special_LED=0;
- IT0=1; //INT0负跳变触发
- TMOD=0x01; //定时器工作于方式1
- TH0=(65536-50000)/256; //定时器赋初值
- TL0=(65536-50000)%256;
- EA=1; //CPU开中断总允许
- ET0=1; //开定时中断
- EX0=1; //开外部INTO中断
- TR0=1; //启动定
- while(1)
- {
- /*******S0状态**********/
- SN_ManRed=0;
- SN_ManGreen=1; //SN人行道通行
- EW_ManRed=1; //EW人行道禁止
- EW_ManGreen=0;
- Flag_EW_Yellow=0; //EW关黄灯显示信号
- Time_EW=EW;
- Time_SN=SN;
- while(Time_SN>=5)
- {
- P1=S[0]; //SN绿灯,EW红灯
- Display();
- }
- /*******S1状态**********/
- P1=0x00;
- while(Time_SN>=0)
- {
- Flag_SN_Yellow=1; //SN开黄灯信号位
- P1=P1|0x08; //保持EW红灯
- Display();
- }
- /*******S2状态**********/
- SN_ManRed=1; //SN人行道禁止
- SN_ManGreen=0;
- EW_ManRed=1; //EW人行道禁止
- EW_ManGreen=0;
- Flag_SN_Yellow=0; //SN关黄灯显示信号
- Time_SN=SNL;
- while(Time_SN>=5)
- {
- P1=S[2]; //SN左拐绿灯亮,EW红灯
- Display();
- }
- /*******S3状态**********/
- P1=0x00;
- while(Time_SN>=0)
- {
- //Flag_SN_Yellow=0; //SN开黄灯信号位
- x=1;
- P1=P1|0x08; //保持EW红灯
- Display();
- }
- /***********赋值*********/
- EW=EW1;
- SN=SN1;
- EWL=EWL1;
- SNL=SNL1;
- /*******S4状态**********/
- SN_ManRed=1; //SN人行道禁止
- SN_ManGreen=0;
- EW_ManRed=0;
- EW_ManGreen=1; //EW人行道通行
- Flag_SN_Yellow=0; //SN关黄灯显示信号
- Time_EW=SN;
- Time_SN=EW;
- while(Time_EW>=5)
- {
- P1=S[4]; //EW通行,SN红灯
- Display();
- }
- /*******S5状态**********/
- P1=0X00;
- while(Time_EW>=0)
- {
- Flag_EW_Yellow=1;//EW开黄灯信号位
- P1=P1|0x80; //保持SN红灯
- Display();
- }
- /*******S6状态**********/
- SN_ManRed=1; //SN人行道禁止
- SN_ManGreen=0;
- EW_ManRed=1; //EW人行道禁止
- EW_ManGreen=0;
- Flag_EW_Yellow=0;//EW关黄灯显示信号
- Time_EW=EWL;
- while(Time_EW>=5)
- {
- P1=S[6]; //EW左拐绿灯亮,SN红灯
- Display();
- }
- /*******S7状态**********/
- P1=0X00;
- while(Time_EW>=0)
- {
- //Flag_EW_Yellow=0; //EN开黄灯信号位
- y=1;
- P1=P1|0x80; //保持SN红灯
- Display();
- }
- /***********赋值********/
- EW=EW1;
- SN=SN1;
- EWL=EWL1;
- SNL=SNL1;
- }
- }
- 2:十字路口交通灯控制C程序2
- /***********************************************************
- 十字路口交通灯控制 C 程序
- ***********************************************************/
- #include <reg51.h>
- #define uchar unsigned char
- #define uint unsigned int
- /*****定义控制位*******************************************/
- sbit EW_Green1 =P1^0; //EW直行绿灯
- sbit EW_Green12 =P1^1; //EW左转绿灯
- sbit EW_Green13= P1^2; //EW右转绿灯
- sbit EW_Yellow1 =P1^3; //EW直行黄灯
- sbit EW_Yellow2 =P1^4; //EW左转黄灯
- sbit EW_Yellow3 =P1^5; //EW右转黄灯
- sbit EW_Red1=P1^6; //RE直行红灯
- sbit EW_Red2 =P1^7; //RE左转红灯
- bit Flag_SN_Yellow; //SN黄灯标志位
- bit Flag_EW_Yellow; //EW黄灯标志位
- uchar EW=60,SN=40,EWL=19,SNL=19; //程序初始化赋值,正常模式
- uchar EW1=60,SN1=40,EWL1=19,SNL1=19; //用于存放修改值的变量
- uchar code table[10]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F}; //0-9段选码
- uchar code S[8]={0xA0,0xA0,0x80,0xC0,0xA0,0xA0,0x20,0x24};
- //交通信号灯控制代码
- /**********************延时子程序**************************/
- void Delay(uchar a)
- {
- uchar i;
- i=a;
- while(i--){;}
- }
- /*****************显示子函数******************************/
- void Display(void)
- {
- uchar h,l;
- h=Time_EW/10;
- l=Time_EW%10;
- P0=table[l];
- EW_LED2=1; //点亮EW_LED2
- Delay(2);
- EW_LED2=0; //熄灭EW_LED2
- P0=table[h];
- EW_LED1=1; //点亮EW_LED1
- Delay(2);
- EW_LED1=0;
- h=Time_SN/10;
- l=Time_SN%10;
- P0=table[l];
- SN_LED2=1; //点亮SN_LED2
- Delay(2);
- SN_LED2=0;
- P0=table[h];
- SN_LED1=1; //点亮SN_LED1
- Delay(2);
- SN_LED1=0;
-
- h= EW1/10;
- l= EW1%10;
- P0=table[l];
- Time_Show_LED1=1; //点亮Time_Show_LED1
- Delay(2);
- Time_Show_LED1=0;
- P0=table[h];
- Time_Show_LED2=1; //点亮Time_Show_LED2
- Delay(2);
- Time_Show_LED2=0;
- }
- /**********************外部0中断服务程序******************/
- void INT0_srv(void)interrupt 0 using 1
- {
- EX0=0; //关中断
- if(Nomor_Button==0) //测试按键是否按下,按下为正常状态
- {
- EW1=60;
- SN1=40;
- EWL1=19;
- SNL1=19;
- Busy_LED=0; //关繁忙信号灯
- Special_LED =0; //关特殊信号灯
- }
- if(Busy_Btton==0) //测试按键是否按下,按下为繁忙状态
- {
- EW1=45;
- SN1=30;
- EWL1=14;
- SNL1=14;
- Special_LED=0; //关特殊信号灯
- Busy_LED=1; //开繁忙信号灯
- }
- if(Special_Btton==0)//测试按键是否按下,按下为特殊状态
- {
- EW1=75;
- SN1=55;
- EWL1=19;
- SNL1=19;
- Busy_LED=0; //关繁忙信号灯
- Special_LED =1;//开特殊信号灯
- }
- EX0=1; //开中断
- }
- /**********************T0中断服务程序*******************/
- void timer0(void)interrupt 1 using 1
- {
- static uchar count;
- TH0=(65536-50000)/256;
- TL0=(65536-50000)%256;
- count++;
- if(count==10)
- {
- if(Flag_SN_Yellow==1) //测试南北黄灯标志位
- {SN_Yellow=~SN_Yellow;}
- if(Flag_EW_Yellow==1) //测试东西黄灯标志位
- {EW_Yellow=~EW_Yellow;}
- }
- if(count==20)
- {
- Time_EW--;
- Time_SN--;
- if(Flag_SN_Yellow==1) //测试南北黄灯标志位
- {SN_Yellow=~SN_Yellow;}
- if(Flag_EW_Yellow==1) //测试东西黄灯标志位
- {EW_Yellow=~EW_Yellow;}
- count=0;
- }
- }
- /*********************主程序开始***********************/
- void main(void)
- {
- Busy_LED=0;
- Special_LED=0;
- IT0=1; //INT0负跳变触发
- TMOD=0x01; //定时器工作于方式1
- TH0=(65536-50000)/256; //定时器赋初值
- TL0=(65536-50000)%256;
- EA=1; //CPU开中断总允许
- ET0=1; //开定时中断
- EX0=1; //开外部INTO中断
- TR0=1; //启动定时
- while(1)
- {
- /*******S0状态**********/
- SN_ManGreen=1; //SN人行道通行
- EW_ManGreen=0;
- Flag_EW_Yellow=0; //EW关黄灯显示信号
- Time_EW=EW;
- Time_SN=SN;
- while(Time_SN>=5)
- {
- P1=S[0]; //SN绿灯,EW红灯
- Display();
- }
- /*******S1状态**********/
- P1=0x00;
- while(Time_SN>=0)
- {
- x=1;
- y=1;
- P1=P1|0xA0; //保持EW红灯
- Display();
- }
- /*******S2状态**********/
- EW_ManGreen=0;
- Flag_SN_Yellow=0; //SN关黄灯显示信号
- Time_SN=SNL;
- while(Time_SN>=5)
- {
- P1=S[2]; //SN左拐绿灯亮,EW红灯
- Display();
- }
- /*******S3状态**********/
- P1=0x00;
- while(Time_SN>=0)
- {
- Flag_SN_Yellow=1; //SN开黄灯信号位
- y=1;
- c=0;
- Display();
- }
- /***********赋值*********/
- EW=EW1;
- SN=SN1;
- EWL=EWL1;
- SNL=SNL1;
- /*******S4状态**********/
- SN_ManGreen=0;
- EW_ManGreen=1; //EW人行道通行
- Flag_SN_Yellow=0; //SN关黄灯显示信号
- x=1;
- d=0;
- Time_EW=SN;
- Time_SN=EW;
- while(Time_EW>=5)
- {
- P1=S[4]; //EW通行,SN红灯
- Display();
- }
- /*******S5状态**********/
- P1=0X00;
- while(Time_EW>=0)
- {
- x=1;
- P1=P1|0x80; //保持SN红灯
- Display();
- }
- /*******S6状态**********/
- SN_ManGreen=0;
- EW_ManGreen=0;
- Flag_EW_Yellow=0;//EW关黄灯显示信号
- Time_EW=EWL;
- while(Time_EW>=5)
- {
- P1=S[6]; //EW左拐绿灯亮,SN红灯
- Display();
- }
- /*******S7状态**********/
- P1=0X00;
- while(Time_EW>=0)
- {
- Flag_EW_Yellow=1; //EN开黄灯信号位
- x=1;
- P1=P1|0x20; //保持SN红灯
- Display();
- }
- /***********赋值********/
- EW=EW1;
- SN=SN1;
- EWL=EWL1;
- SNL=SNL1;
- }
- }
- 3:万年历程序:
- #include <REG51.H>
- #include <intrins.h>
- //#include "LCD1602.h"
- //#include "DS1302.h"
- #define uint unsigned int
- #define uchar unsigned char
- sbit DS1302_CLK = P1^7; //实时时钟时钟线引脚
- sbit DS1302_IO = P1^6; //实时时钟数据线引脚
- sbit DS1302_RST = P1^5; //实时时钟复位线引脚
- sbit wireless_1 = P3^0;
- sbit wireless_2 = P3^1;
- sbit wireless_3 = P3^2;
- sbit wireless_4 = P3^3;
- sbit zhengchang = P3^5; //正常模式切换
- sbit fanmang = P3^6; //繁忙模式切换
- sbit ACC0 = ACC^0;
- sbit ACC7 = ACC^7;
- char hide_sec,hide_min,hide_hour,hide_day,hide_week,hide_month,hide_year; //秒,分,时到日,月,年位闪的计数
- void show_time(); //液晶显示程序
- /***********1602液晶显示部分子程序****************/
- //Port Definitions**********************************************************
- sbit LcdRs = P2^5;
- sbit LcdRw = P2^6;
- sbit LcdEn = P2^7;
- sfr DBPort = 0x80; //P0=0x80,P1=0x90,P2=0xA0,P3=0xB0.数据端口
- //内部等待函数**************************************************************************
- unsigned char LCD_Wait(void)
- {
- LcdRs=0;
- LcdRw=1; _nop_();
- LcdEn=1; _nop_();
-
- LcdEn=0;
- return DBPort;
- }
- //向LCD写入命令或数据************************************************************
- #define LCD_COMMAND 0 // Command
- #define LCD_DATA 1 // Data
- #define LCD_CLEAR_SCREEN 0x01 // 清屏
- #define LCD_HOMING 0x02 // 光标返回原点
- void LCD_Write(bit style, unsigned char input)
- {
- LcdEn=0;
- LcdRs=style;
- LcdRw=0; _nop_();
- DBPort=input; _nop_();//注意顺序
- LcdEn=1; _nop_();//注意顺序
- LcdEn=0; _nop_();
- LCD_Wait();
- }
- //设置显示模式************************************************************
- #define LCD_SHOW 0x04 //显示开
- #define LCD_HIDE 0x00 //显示关
- #define LCD_CURSOR 0x02 //显示光标
- #define LCD_NO_CURSOR 0x00 //无光标
- #define LCD_FLASH 0x01 //光标闪动
- #define LCD_NO_FLASH 0x00 //光标不闪动
- void LCD_SetDisplay(unsigned char DisplayMode)
- {
- LCD_Write(LCD_COMMAND, 0x08|DisplayMode);
- }
- //设置输入模式************************************************************
- #define LCD_AC_UP 0x02
- #define LCD_AC_DOWN 0x00 // default
- #define LCD_MOVE 0x01 // 画面可平移
- #define LCD_NO_MOVE 0x00 //default
- void LCD_SetInput(unsigned char InputMode)
- {
- LCD_Write(LCD_COMMAND, 0x04|InputMode);
- }
- //初始化LCD************************************************************
- void LCD_Initial()
- {
- LcdEn=0;
- LCD_Write(LCD_COMMAND,0x38); //8位数据端口,2行显示,5*7点阵
- LCD_Write(LCD_COMMAND,0x38);
- LCD_SetDisplay(LCD_SHOW|LCD_NO_CURSOR); //开启显示, 无光标
- LCD_Write(LCD_COMMAND,LCD_CLEAR_SCREEN); //清屏
- LCD_SetInput(LCD_AC_UP|LCD_NO_MOVE); //AC递增, 画面不动
- }
- //液晶字符输入的位置************************
- void GotoXY(unsigned char x, unsigned char y)
- {
- if(y==0)
- LCD_Write(LCD_COMMAND,0x80|x);
- if(y==1)
- LCD_Write(LCD_COMMAND,0x80|(x-0x40));
- }
- //将字符输出到液晶显示
- void Print(unsigned char *str)
- {
- while(*str!='\0')
- {
- LCD_Write(LCD_DATA,*str);
- str++;
- }
- }
- /***********DS1302时钟部分子程序******************/
- typedef struct __SYSTEMTIME__
- {
- unsigned char Second;
- unsigned char Minute;
- unsigned char Hour;
- unsigned char Week;
- unsigned char Day;
- unsigned char Month;
- unsigned char Year;
- unsigned char DateString[11];
- unsigned char TimeString[9];
- }SYSTEMTIME; //定义的时间类型
- SYSTEMTIME CurrentTime;
- #define AM(X) X
- #define PM(X) (X+12) // 转成24小时制
- #define DS1302_SECOND 0x80 //时钟芯片的寄存器位置,存放时间
- #define DS1302_MINUTE 0x82
- #define DS1302_HOUR 0x84
- #define DS1302_WEEK 0x8A
- #define DS1302_DAY 0x86
- #define DS1302_MONTH 0x88
- #define DS1302_YEAR 0x8C
- void DS1302InputByte(unsigned char d) //实时时钟写入一字节(内部函数)
- {
- unsigned char i;
- ACC = d;
- for(i=8; i>0; i--)
- {
- DS1302_IO = ACC0; //相当于汇编中的 RRC
- DS1302_CLK = 1;
- DS1302_CLK = 0;
- ACC = ACC >> 1;
- }
- }
- unsigned char DS1302OutputByte(void) //实时时钟读取一字节(内部函数)
- {
- unsigned char i;
- for(i=8; i>0; i--)
- {
- ACC = ACC >>1; //相当于汇编中的 RRC
- ACC7 = DS1302_IO;
- DS1302_CLK = 1;
- DS1302_CLK = 0;
- }
- return(ACC);
- }
- void Write1302(unsigned char ucAddr, unsigned char ucDa) //ucAddr: DS1302地址, ucData: 要写的数据
- {
- DS1302_RST = 0;
- DS1302_CLK = 0;
- DS1302_RST = 1;
- DS1302InputByte(ucAddr); // 地址,命令
- DS1302InputByte(ucDa); // 写1Byte数据
- DS1302_CLK = 1;
- DS1302_RST = 0;
- }
- unsigned char Read1302(unsigned char ucAddr) //读取DS1302某地址的数据
- {
- unsigned char ucData;
- DS1302_RST = 0;
- DS1302_CLK = 0;
- DS1302_RST = 1;
- DS1302InputByte(ucAddr|0x01); // 地址,命令
- ucData = DS1302OutputByte(); // 读1Byte数据
- DS1302_CLK = 1;
- DS1302_RST = 0;
- return(ucData);
- }
- void DS1302_GetTime(SYSTEMTIME *Time) //获取时钟芯片的时钟数据到自定义的结构型数组
- {
- unsigned char ReadValue;
- ReadValue = Read1302(DS1302_SECOND);
- Time->Second = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);
- ReadValue = Read1302(DS1302_MINUTE);
- Time->Minute = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);
- ReadValue = Read1302(DS1302_HOUR);
- Time->Hour = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);
- ReadValue = Read1302(DS1302_DAY);
- Time->Day = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);
- ReadValue = Read1302(DS1302_WEEK);
- Time->Week = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);
- ReadValue = Read1302(DS1302_MONTH);
- Time->Month = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);
- ReadValue = Read1302(DS1302_YEAR);
- Time->Year = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);
- }
- void DateToStr(SYSTEMTIME *Time) //将时间年,月,日,星期数据转换成液晶显示字符串,放到数组里DateString[]
- { if(hide_year<2) //这里的if,else语句都是判断位闪烁,<2显示数据,>2就不显示,输出字符串为 2012/05/25
- {
- Time->DateString[0] = '2';
- Time->DateString[1] = '0';
- Time->DateString[2] = Time->Year/10 + '0';
- Time->DateString[3] = Time->Year%10 + '0';
- }
- else
- {
- Time->DateString[0] = ' ';
- Time->DateString[1] = ' ';
- Time->DateString[2] = ' ';
- Time->DateString[3] = ' ';
- }
- Time->DateString[4] = '/';
- if(hide_month<2)
- {
- Time->DateString[5] = Time->Month/10 + '0';
- Time->DateString[6] = Time->Month%10 + '0';
- }
- else
- {
- Time->DateString[5] = ' ';
- Time->DateString[6] = ' ';
- }
- Time->DateString[7] = '/';
- if(hide_day<2)
- {
- Time->DateString[8] = Time->Day/10 + '0';
- Time->DateString[9] = Time->Day%10 + '0';
- }
- else
- {
- Time->DateString[8] = ' ';
- Time->DateString[9] = ' ';
- }
- if(hide_week<2)
- {
- week_value[0] = Time->Week%10 + '0'; //星期的数据另外放到 week_value[]数组里,跟年,月,日的分开存放,因为等一下要在最后显示
- }
- else
- {
- week_value[0] = ' ';
- }
- week_value[1] = '\0';
- Time->DateString[10] = '\0'; //字符串末尾加 '\0' ,判断结束字符
- }
- void TimeToStr(SYSTEMTIME *Time) //将时,分,秒数据转换成液晶显示字符放到数组 TimeString[];
- { if(hide_hour<2)
- {
- Time->TimeString[0] = Time->Hour/10 + '0';
- Time->TimeString[1] = Time->Hour%10 + '0';
- }
- else
- {
- Time->TimeString[0] = ' ';
- Time->TimeString[1] = ' ';
- }
- Time->TimeString[2] = ':';
- if(hide_min<2)
- {
- Time->TimeString[3] = Time->Minute/10 + '0';
- Time->TimeString[4] = Time->Minute%10 + '0';
- }
- else
- {
- Time->TimeString[3] = ' ';
- Time->TimeString[4] = ' ';
- }
- Time->TimeString[5] = ':';
- if(hide_sec<2)
- {
- Time->TimeString[6] = Time->Second/10 + '0';
- Time->TimeString[7] = Time->Second%10 + '0';
- }
- else
- {
- Time->TimeString[6] = ' ';
- Time->TimeString[7] = ' ';
- }
- Time->DateString[8] = '\0';
- }
- void Initial_DS1302(void) //时钟芯片初始化
- {
- unsigned char Second=Read1302(DS1302_SECOND);
- if(Second&0x80) //判断时钟芯片是否关闭
- {
- Write1302(0x8e,0x00); //写入允许
- Write1302(0x8c,0x12); //以下写入初始化时间 日期:12/05/25.星期: 5. 时间: 10:59:55
- Write1302(0x88,0x05);
- Write1302(0x86,0x25);
- Write1302(0x8a,0x05);
- Write1302(0x84,0x10);
- Write1302(0x82,0x59);
- Write1302(0x80,0x55);
- Write1302(0x8e,0x80); //禁止写入
- }
- }
- /***********ds18b20子程序*************************/
- /***********ds18b20延迟子函数(晶振12MHz )*******/
- void delay_18B20(unsigned int i)
- {
- while(i--);
- }
- /**********ds18b20初始化函数**********************/
- void Init_DS18B20(void)
- {
- unsigned char x=0;
- DQ = 1; //DQ复位
- delay_18B20(8); //稍做延时
- DQ = 0; //单片机将DQ拉低
- delay_18B20(80); //精确延时 大于 480us
- DQ = 1; //拉高总线
- delay_18B20(14);
- x=DQ; //稍做延时后 如果x=0则初始化成功 x=1则初始化失败
- delay_18B20(20);
- }
- /***********ds18b20读一个字节**************/
- unsigned char ReadOneChar(void)
- {
- uchar i=0;
- uchar dat = 0;
- for (i=8;i>0;i--)
- {
- DQ = 0; // 给脉冲信号
- dat>>=1;
- DQ = 1; // 给脉冲信号
- if(DQ)
- dat|=0x80;
- delay_18B20(4);
- }
- return(dat);
- }
- /*************ds18b20写一个字节****************/
- void WriteOneChar(uchar dat)
- {
- unsigned char i=0;
- for (i=8; i>0; i--)
- {
- DQ = 0;
- DQ = dat&0x01;
- delay_18B20(5);
- DQ = 1;
- dat>>=1;
- }
- }
- /**************读取ds18b20当前温度************/
- void ReadTemp(void)
- {
- unsigned char a=0;
- unsigned char b=0;
- unsigned char t=0;
- Init_DS18B20();
- WriteOneChar(0xCC); // 跳过读序号列号的操作
- WriteOneChar(0x44); // 启动温度转换
- delay_18B20(100); // this message is wery important
- Init_DS18B20();
- WriteOneChar(0xCC); //跳过读序号列号的操作
- WriteOneChar(0xBE); //读取温度寄存器等前两个就是温度
- delay_18B20(100);
- a=ReadOneChar(); //读取温度值低位
- b=ReadOneChar(); //读取温度值高位
- temp_value=b<<4;
- temp_value+=(a&0xf0)>>4;
- }
- void temp_to_str() //温度数据转换成液晶字符显示
- {
- TempBuffer[0]=temp_value/10+'0'; //十位
- TempBuffer[1]=temp_value%10+'0'; //个位
- TempBuffer[2]=0xdf; //温度符号
- TempBuffer[3]='C';
- TempBuffer[4]='\0';
- }
- void Delay1ms(unsigned int count)
- {
- unsigned int i,j;
- for(i=0;i<count;i++)
- for(j=0;j<120;j++);
- }
- /*延时子程序*/
- void mdelay(uint delay)
- { uint i;
- for(;delay>0;delay--)
- {for(i=0;i<62;i++) //1ms延时.
- {;}
- }
- }
- void outkey() //跳出调整模式,返回默认显示
- { uchar Second;
- if(out==0||wireless_1==1)
- { mdelay(8);
- count=0;
- hide_sec=0,hide_min=0,hide_hour=0,hide_day=0,hide_week=0,hide_month=0,hide_year=0;
- Second=Read1302(DS1302_SECOND);
- Write1302(0x8e,0x00); //写入允许
- Write1302(0x80,Second&0x7f);
- Write1302(0x8E,0x80); //禁止写入
- done=0;
- while(out==0);
- while(wireless_1==1);
- }
- }
- ///////////////////////////////////////////////////////////////////////
- void Upkey()//升序按键
- {
- Up=1;
- if(Up==0||wireless_2==1)
- {
- mdelay(8);
- switch(count)
- {case 1:
- temp=Read1302(DS1302_SECOND); //读取秒数
- temp=temp+1; //秒数加1
- up_flag=1; //数据调整后更新标志
- if((temp&0x7f)>0x59) //超过59秒,清零
- temp=0;
- break;
- case 2:
- temp=Read1302(DS1302_MINUTE); //读取分数
- temp=temp+1; //分数加1
- up_flag=1;
- if(temp>0x59) //超过59分,清零
- temp=0;
- break;
- case 3:
- temp=Read1302(DS1302_HOUR); //读取小时数
- temp=temp+1; //小时数加1
- up_flag=1;
- if(temp>0x23) //超过23小时,清零
- temp=0;
- break;
- case 4:
- temp=Read1302(DS1302_WEEK); //读取星期数
- temp=temp+1; //星期数加1
- up_flag=1;
- if(temp>0x7)
- temp=1;
- break;
- case 5:
- temp=Read1302(DS1302_DAY); //读取日数
- temp=temp+1; //日数加1
- up_flag=1;
- if(temp>0x31)
- temp=1;
- break;
- case 6:
- temp=Read1302(DS1302_MONTH); //读取月数
- temp=temp+1; //月数加1
- up_flag=1;
- if(temp>0x12)
- temp=1;
- break;
- case 7:
- temp=Read1302(DS1302_YEAR); //读取年数
- temp=temp+1; //年数加1
- up_flag=1;
- if(temp>0x85)
- temp=0;
- break;
- default:break;
- }
-
- while(Up==0);
- while(wireless_2==1);
- }
- }
- ///////////////////////////////////////////////////////////////////////void Downkey()//降序按键
- {
- Down=1;
- if(Down==0||wireless_3==1)
- {
- mdelay(8);
- switch(count)
- {case 1:
- temp=Read1302(DS1302_SECOND); //读取秒数
- temp=temp-1; //秒数减1
- down_flag=1; //数据调整后更新标志
- if(temp==0x7f) //小于0秒,返回59秒
- temp=0x59;
- break;
- case 2:
- temp=Read1302(DS1302_MINUTE); //读取分数
- temp=temp-1; //分数减1
- down_flag=1;
- if(temp==-1)
- temp=0x59; //小于0分,返回59分
- break;
- case 3:
- temp=Read1302(DS1302_HOUR); //读取小时数
- temp=temp-1; //小时数减1
- down_flag=1;
- if(temp==-1)
- temp=0x23;
- break;
- case 4:
- temp=Read1302(DS1302_WEEK); //读取星期数
- temp=temp-1; //星期数减1
- down_flag=1;
- if(temp==0)
- temp=0x7;;
- break;
- case 5:
- temp=Read1302(DS1302_DAY); //读取日数
- temp=temp-1; //日数减1
- down_flag=1;
- if(temp==0)
- temp=31;
- break;
- case 6:
- temp=Read1302(DS1302_MONTH); //读取月数
- temp=temp-1; //月数减1
- down_flag=1;
- if(temp==0)
- temp=12;
- break;
- case 7:
- temp=Read1302(DS1302_YEAR); //读取年数
- temp=temp-1; //年数减1
- down_flag=1;
- if(temp==-1)
- temp=0x85;
- break;
- default:break;
- }
-
- while(Down==0);
- while(wireless_3==1);
- }
- }
- void Setkey()//模式选择按键
- {
- Set=1;
- if(Set==0||wireless_4==1)
- {
- mdelay(8);
- count=count+1; //Setkey按一次,count就加1
- done=1; //进入调整模式
- while(Set==0);
- while(wireless_4==1);
- }
- }
- void keydone()//按键功能执行
- { uchar Second;
- if(flag==0) //关闭时钟,停止计时
- { Write1302(0x8e,0x00); //写入允许
- temp=Read1302(0x80);
- Write1302(0x80,temp|0x80);
- Write1302(0x8e,0x80); //禁止写入
- flag=1;
- }
- Setkey(); //扫描模式切换按键
- switch(count)
- {case 1:do //count=1,调整秒
- {
- outkey(); //扫描跳出按钮
- Upkey(); //扫描加按钮
- Downkey(); //扫描减按钮
- if(up_flag==1||down_flag==1) //数据更新,重新写入新的数据
- {
- Write1302(0x8e,0x00); //写入允许
- Write1302(0x80,temp|0x80); //写入新的秒数
- Write1302(0x8e,0x80); //禁止写入
- up_flag=0;
- down_flag=0;
- }
- hide_sec++; //位闪计数
- if(hide_sec>3)
- hide_sec=0;
- show_time(); //液晶显示数据
- }while(count==2);break;
- case 2:do //count=2,调整分
- {
- hide_sec=0;
- outkey();
- Upkey();
- Downkey();
- if(temp>0x60)
- temp=0;
- if(up_flag==1||down_flag==1)
- {
- Write1302(0x8e,0x00); //写入允许
- Write1302(0x82,temp); //写入新的分数
- Write1302(0x8e,0x80); //禁止写入
- up_flag=0;
- down_flag=0;
- }
- hide_min++;
- if(hide_min>3)
- hide_min=0;
- show_time();
- }while(count==3);break;
- case 3:do //count=3,调整小时
- {
- hide_min=0;
- outkey();
- Upkey();
- Downkey();
- if(up_flag==1||down_flag==1)
- {
- Write1302(0x8e,0x00); //写入允许
- Write1302(0x84,temp); //写入新的小时数
- Write1302(0x8e,0x80); //禁止写入
- up_flag=0;
- down_flag=0;
- }
- hide_hour++;
- if(hide_hour>3)
- hide_hour=0;
- show_time();
- }while(count==4);break;
- case 4:do //count=4,调整星期
- {
- hide_hour=0;
- outkey();
- Upkey();
- Downkey();
- if(up_flag==1||down_flag==1)
- {
- Write1302(0x8e,0x00); //写入允许
- Write1302(0x8a,temp); //写入新的星期数
- Write1302(0x8e,0x80); //禁止写入
- up_flag=0;
- down_flag=0;
- }
- hide_week++;
- if(hide_week>3)
- hide_week=0;
- show_time();
- }while(count==5);break;
- case 5:do //count=5,调整日
- {
- hide_week=0;
- outkey();
- Upkey();
- Downkey();
- if(up_flag==1||down_flag==1)
- {
- Write1302(0x8e,0x00); //写入允许
- Write1302(0x86,temp); //写入新的日数
- Write1302(0x8e,0x80); //禁止写入
- up_flag=0;
- down_flag=0;
- }
- hide_day++;
- if(hide_day>3)
- hide_day=0;
- show_time();
- }while(count==6);break;
- case 6:do //count=6,调整月
- {
- hide_day=0;
- outkey();
- Upkey();
- Downkey();
- if(up_flag==1||down_flag==1)
- {
- Write1302(0x8e,0x00); //写入允许
- Write1302(0x88,temp); //写入新的月数
- Write1302(0x8e,0x80); //禁止写入
- up_flag=0;
- down_flag=0;
- }
- hide_month++;
- if(hide_month>3)
- hide_month=0;
- show_time();
- }while(count==7);break;
- case 7:do //count=7,调整年
- {
- hide_month=0;
- outkey();
- Upkey();
- Downkey();
- if(up_flag==1||down_flag==1)
- {
- Write1302(0x8e,0x00); //写入允许
- Write1302(0x8c,temp); //写入新的年数
- Write1302(0x8e,0x80); //禁止写入
- up_flag=0;
- down_flag=0;
- }
- hide_year++;
- if(hide_year>3)
- hide_year=0;
- show_time();
- }while(count==8);break;
- case 8: count=0;hide_year=0; // 跳出调整模式,返回默认显示状态
- Second=Read1302(DS1302_SECOND);
- Write1302(0x8e,0x00); //写入允许
- Write1302(0x80,Second&0x7f);
- Write1302(0x8E,0x80); //禁止写入
- done=0;
- break; //count=7,开启中断,标志位置0并退出
- default:break;
- }
- }
- void show_time() //液晶显示程序
- {
- DS1302_GetTime(&CurrentTime); //获取时钟芯片的时间数据
- TimeToStr(&CurrentTime); //时间数据转换液晶字符
- DateToStr(&CurrentTime); //日期数据转换液晶字符
- ReadTemp(); //开启温度采集程序
- temp_to_str(); //温度数据转换成液晶字符
- GotoXY(12,1); //液晶字符显示位置
- Print(TempBuffer); //显示温度
- GotoXY(0,1);
- Print(CurrentTime.TimeString); //显示时间
- GotoXY(0,0);
- Print(CurrentTime.DateString); //显示日期
- GotoXY(15,0);
- Print(week_value); //显示星期
- GotoXY(11,0);
- Print("Week"); //在液晶上显示 字母 week
- Delay1ms(400); //扫描延时
- }
- main()
- {
- flag=1; //时钟停止标志
- LCD_Initial(); //液晶初始化
- Init_DS18B20( ) ; //DS18B20初始化
- Initial_DS1302(); //时钟芯片初始化
- up_flag=0;
- down_flag=0;
- done=0; //进入默认液晶显示
- wireless_1=0;
- wireless_2=0;
- wireless_3=0;
- wireless_4=0;
- while(1)
- {
- while(done==1)
- keydone(); //进入调整模式
- while(done==0)
- {
- show_time(); //液晶显示数据
- flag=0;
- Setkey(); //扫描各功能键
- if((CurrentTime.TimeString[0]=='0'
- && CurrentTime.TimeString[1]=='7'
- && CurrentTime.TimeString[3]=='0'
- &&CurrentTime.TimeString[4]=='0')
- || (CurrentTime.TimeString[0]=='1'
- && CurrentTime.TimeString[1]=='1'
- &&CurrentTime.TimeString[3]=='0
- && CurrentTime.TimeString[4]=='0')
- ||(CurrentTime.TimeString[0]=='1'
- &&CurrentTime.TimeString[1]=='7'
- && CurrentTime.TimeString[3]=='0'
- && CurrentTime.TimeString[4]=='0') )
- fanmang=0;
- else fanmang=1;
- if((CurrentTime.TimeString[0]=='0'
- && CurrentTime.TimeString[1]=='9'
- && CurrentTime.TimeString[3]=='0'
- &&CurrentTime.TimeString[4]=='0')
- || (CurrentTime.TimeString[0]=='1'
- && CurrentTime.TimeString[1]=='4'
- &&CurrentTime.TimeString[3]=='0'
- && CurrentTime.TimeString[4]=='0')
- ||(CurrentTime.TimeString[0]=='1'
- &&CurrentTime.TimeString[1]=='9'
- && CurrentTime.TimeString[3]=='0'
- && CurrentTime.TimeString[4]=='0') )
- zhengchang=0;
- else zhengchang=1;
- }
- }
- }
复制代码
|