单片机交通灯实验设计报告(C语言)



东北林业大学

单片机原理实验课程设计

总  结  报  告

                                                              

                                                                        

设计项目:        电子交通灯的设计         

项目完成人:                                           

指导教师:                                         

学    院:           信息与计算机工程学院             

专    业:          电子信息工程20##级3班           

20##年   5月  23 日


综合电子课程设计任务书

  摘要

 

随着目前微电子技术的发展, 技术工艺的不断提高, 单片机技术也有了长足的进步。单片机即单片微型计算机, 它是微型计算机的一个分支, 它是在一块芯片上集成了 CPU、 RAM 和 ROM 存储器 I/O 接口等而构成的微型计算机。因为它主要应用于工业测控领域故又叫做微控器或嵌入式控制器。单片机的核心是 CPU用超大规模集成技术把 CPU 集成在一块芯片上, 称为微处理器。单片机在最近几年中得到了极大的发展, 目前世界范围内单片机发展的主要领域有 4 个: 一是欧美, 最新开发产品及主要厂商有:National semicundutor 的cop3 系列单片机,美国的 Scenix的八位单片机,荷兰 PHILIPS的51 系列单片机,,美国 AMD公司 186 系列 16位嵌入式微机控制器, MOTOROLA 的各个系列单片机; 二是日本, TOSHIBA公司开发了从 4位到 64 位多系列单片机,日立公司也有从 4 位到 32 位多系列单片机, NEC公司的 75X、78X系列微机; 三是台湾地区,主要有 WINBOND的 W741/ W516,W78/W77 等系列产品微控制器;四是韩国,主要有HYUNDAI microelectrionics的GMS800、GMS30 系列微控制器。 另外还有 LG公司也生产单片机, 可见单片机发展到今天可以说种类繁多、 性能各异。但目前我国的许多单片机应用单位仍停留在采用片内无 ROM等低档单片机状态。

十字路口车辆穿梭,行人熙攘,车行车道,人行人道,有条不紊。那么靠什么来实现这井然秩序呢?靠的就是交通信号灯的自动指挥系统。交通信号灯控制方式很多。本系统采用MSC-51系列单片机ATSC51和可编程并行I/O接口芯片CH451为中心器件来设计交通灯控制器,实现了能根据实际车流量通过8051芯片的P1口设置红、绿灯燃亮时间的功能;红绿灯循环点亮,倒计时剩5秒时黄灯闪烁警示(交通灯信号通过PA口输出,显示时间直接通过CH451的PC口输出至双位数码管);车辆闯红灯报警;绿灯时间可检测车流量并可通过双位数码管显示。本系统实用性强、操作简单、扩展功能强。

关键词: 单片机、交通灯、LJD-SY-5200单片机实验系统

基于单片机的交通灯设计

目录

1绪论............................................................ 1

1.1引言 1../../../../../Documents and Settings/Administrator/桌面/新建文件夹/基于单片机的交通灯设计初稿.doc - _Toc196215817#_Toc196215817

          1.2 系统方案设计....................................................2

芯片简介 2../../../../../Documents and Settings/Administrator/桌面/新建文件夹/基于单片机的交通灯设计初稿.doc - _Toc196215818#_Toc196215818

2.1 MSC-51芯片简介................................................. 2

2.2 DS1302芯片简介................................................. 5

2.3 74HC573简介.................................................... 7

          2.4 CH451芯片简介...................................................7

          2.5 74HC138芯片简介.................................................10

        

系统硬件设计............................................... 11

3.1 系统硬件设计.................................................. 11

3.2 LCD液晶....................................................... 11

3.3 8段数码管..................................................... 12

系统的软件设计............................................. 12

4.1 程序设计与调试步骤............................................ 12

4.2 计数器初值计算................................................ 12

4.2.1 1秒方法..................................................... 13

4.3 时间显示...................................................... 13

4.3.1 写保护寄存器操作............................................ 13

4.3.2 时钟停止位操作.............................................. 13

4.3.3 多字节传送方式.............................................. 13

5 结论 .......................................................... 14

         

     

          附录

          

        1、总体电路图.....................................................15     

          2、流程图.........................................................15

          3、程序代码 ...........................................17         

                                                                           

     参考文献 

1绪论   

   

     1.1引言

     所谓单片机是指在一个集成芯片中,集成微处理器(CPU)、存储器、基本I/O接口以及定时/计数、通信部件,即在一个芯片上实现一台微型计算机的基本功能。1970年微型计算机研制成功之后,随着就出现了单片机。美国Intel公司1971年生产的4位单片机4004和1972年生产的雏形8位单片机8008,特别是1976年MCS-48单片机问世以来,在短短的二十几年间,经历了四次更新换代,其发展速度大约每二、三年要更新一代、集成度增加一倍、功能翻一番。其发展速度之快、应用范围之广,已达到了惊人的地步,它已渗透到生产和生活的各个领域。尽管目前单片机的品种很多,但其中最具典型性的当数Intel公司的MCS-51系列单片机。MCS-51是在MCS-48的基础上于80年代初发展起来的,虽然它仍然是8位的单片机,但其功能有很大的增强。由于PHILIPS、ATMEL、WELBORD、LG等近百家IC制造商都主产51系列兼容产品,具有品种全、兼容性强、软硬件资料丰富等特点。因此,MCS-51应用非常广泛,成为继MCS-48之后最重要的单片机品种。直到现在MCS-51仍不失为单片机中的主流机型。国内尤以Intel的MCS-51系列单片机应用最广。由于8位单片机的高性能价格比,估计近十年内,8位单片机仍将是单片机中的主流机型。

   

     随着计算机技术的发展和在控制系统中的广泛应用,以及设备向小型化、智能化发展,作为高新技术之一的单片机以其体积小、功能强、价格低廉、使用灵活等优势,显示出很强的生命力。它和一般的集成电路相比有较好的抗干扰能力,对环境的温度和湿度都有较好的适应性,可以在工业条件下稳定工作。且单片机广泛地应用于各种仪器仪表,使仪器仪表智能化,提高它们的测量速度和测量精度,加强控制功能。如MCS-51系列单片机控制的“船舶航行状态自动记录仪”、“烟叶水分测试仪”、“智能超声波测厚仪”等。单片机也广泛地应用于实时控制系统中,例如对工业上各种窑炉的温度、酸度、化学成分的测量和控制。将测量技术、自动控制技术和单片机技术相结合,充分发挥其数据处理功能和实时控制功能,使系统工作处于最佳状态,提高系统的生产效率和产品质量。从航空航天、地质石油、冶金采矿、机械电子、轻工纺织等行业的分布系统与智能控制以及机电一体化设备和产品,到邮电通信、日用设备和器械,单片机都发挥了巨大作用。其应用大致可分为以下几方面:①机电一体化设备的控制核心。机电一体化是机械设备发展的方向。单片机的出现促进了机电一体化技术的发展,它作为机电产品的控制器,充分发挥其自身优点,大大强化了机器的功能,提高了机器的自动化、智能化程度。最典型的机电产品机器人,每个关节或动作部位都是一个单片机控制系统。②数据采集系统的现场采集单元。大型数据采集系统,要求数据采集的同步性和实时性要好。使用单片机作为系统的前端采集单元,由主控计算机发出采集命令,再将采集到的数据逐一送到主计算机中进行处理。如有些气象部门、油田采油部门以及电厂等均可采用这样的系统。③分布控制系统的前端控制器。在直接控制级的计算机分布控制系统(DCS)中,单片机作为过程控制中每一分部操作或控制的控制器,进行数据采集、反馈计算、控制输出,并在上位机命令的指挥下进行相应协调工作。(④智能化仪表的机芯。自动化仪表的智能化程度越来越高。采用单片机的智能化仪表可具有自整定、自校正、自动补偿和自适应功能,还可进行数字PID调节,软件消除电流热噪声等等,解决传统仪表所不能解决的难题。单片机的应用使这种性能如虎添翼,如自动计费电度表、燃气表中已有这方面的应用。许多工业仪表中的智能流量计,气体分析仪、成分分析仪等也采用了这项技术。甚至有的保健治疗仪中也采用了单片机控制。⑤消费类电子产品控制。该应用主要反映在家电领域,如洗衣机、空调器、保安系统、VCD视盘机、电子秤、IC卡、手机、BP机等。这些设备中使用了单片机机芯后,大大提高了其控制功能和性能,并实现了智能化、最优化控制。⑥终端及外围设备控制。计算机网络终端设备,如银行终端、商业POS(自动收款机)以及计算机外围设备如打印机、通信终端和智能化UPS等。在这些设备中使用单片机,使其具有计算、存储、显示、输入等功能,具有和计算机连接的接口,使计算机的能力及应用范围大大提高。
   总之,随着信息时代的到来,传统单片机固有的结构缺陷,使其呈现出诸多弊端。其速度、规模、性能等指标越来越难以满足用户需求、因此单片机芯片的开发,升级面临着新的挑战。

1.2 系统方案设计

   利用LJD-SY-5200单片机主板和CH451、DS1302等芯片分别实现(1)通过8*8点阵简单实现直行、转弯的交通指挥设计(2)显示年、月、日、星期、时、分、秒(3)能够通过键盘输入日期和时间的初值(4)通过功能键能够实现数据储存、查询、上传(串行通讯)功能(5)通过功能键能够实现外中断和定时中断功能。在整合各部分代码实现整体的功能实现。通过CH451实现键盘的扫描,判断按键代码触发相应的程序。修改8*8点阵来显示各种交通指挥图标。

拟用p0(0.0-0.7)口控制74hc573实现对于led8*8的控制,用单片机控制ch451从而控制数码管与键盘(p1.2 控制load ,p3.4控制dclk,p3.5控制din,p3.3控制dout)详细连接将在硬件部分叙述。该方案最终可实现交通灯以及简单的表的功能。在可实现性方面由于所使用的cpu管脚不冲突,按键输入是中断方式,加之采用时钟中断为记数脉冲所以是可以实现的。

芯片简介

2.1 MSC-51芯片简介

MCS-51单片机内部结构

   8051是MCS-51系列单片机的典型产品,包含中央处理器、程序存储器(ROM)、数据存储器(RAM)、定时/计数器、并行接口、串行接口和中断系统等几大单元及数据总线、地址总线和控制总线等三大总线,现在我们分别加以说明:

·中央处理器:

    中央处理器(CPU)是整个单片机的核心部件,是8位数据宽度的处理器,能处理8位二进制数据或代码,CPU负责控制、指挥和调度整个单元系统协调的工作,完成运算和控制输入输出功能等操作。

·数据存储器(RAM)

    8051内部有128个8位用户数据存储单元和128个专用寄存器单元,它们是统一编址的,专用寄存器只能用于存放控制指令数据,用户只能访问,而不能用于存放用户数据,所以,用户能使用的RAM只有128个,可存放读写的数据,运算的中间结果或用户定义的字型表。   

图1  8051内部结构框图

·程序存储器(ROM):

8051共有4096个8位掩膜ROM,用于存放用户程序,原始数据或表格。

·定时/计数器(ROM):

8051有两个16位的可编程定时/计数器,以实现定时或计数产生中断用于控制程序转向。

·并行输入输出(I/O)口:

8051共有4组8位I/O口(P0、 P1、P2或P3),用于对外部数据的传输。

·全双工串行口:

8051内置一个全双工串行通信口,用于与其它设备间的串行数据传送,该串行口既可以用作异步通信收发器,也可以当同步移位器使用。

·中断系统:

8051具备较完善的中断功能,有两个外中断、两个定时/计数器中断和一个串行中断,可满足不同的控制要求,并具有2级的优先级别选择。

·时钟电路:

8051内置最高频率达12MHz的时钟电路,用于产生整个单片机运行的脉冲时序,但8051单片机需外置振荡电容。

单片机的结构有两种类型,一种是程序存储器和数据存储器分开的形式,即哈佛(Harvard)结构,另一种是采用通用计算机广泛使用的程序存储器与数据存储器合二为一的结构,即普林斯顿(Princeton)结构。INTEL的MCS-51系列单片机采用的是哈佛结构的形式,而后续产品16位的MCS-96系列单片机则采用普林斯顿结构。

下图是MCS-51系列单片机的内部结构示意图2。

8051内部结构示意图

                            图2

MCS-51的引脚说明:

MCS-51系列单片机中的8031、8051及8751均采用40Pin封装的双列直接DIP结构,右图是它们的引脚配置,40个引脚中,正电源和地线两根,外置石英振荡器的时钟线两根,4组8位共32个I/O口,中断口线与P3口线复用。现在我们对这些引脚的功能加以说明:

MCS-51的引脚说明:

MCS-51系列单片机中的8031、8051及8751均采用40Pin封装的双列直接DIP结构,右图是它们的引脚配置,40个引脚中,正电源和地线两根,外置石英振荡器的时钟线两根,4组8位共32个I/O口,中断口线与P3口线复用。现在我们对这些引脚的功能加以说明:如图3

8051引脚图

                                             

                                        图3

Pin9:RESET/Vpd复位信号复用脚,当8051通电,时钟电路开始工作,在RESET引脚上出现24个时钟周期以上的高电平,系统即初始复位。初始化后,程序计数器PC指向0000H,P0-P3输出口全部为高电平,堆栈指针写入07H,其它专用寄存器被清“0”。RESET由高电平下降为低电平后,系统即从0000H地址开始执行程序。然而,初始复位不改变RAM(包括工作寄存器R0-R7)的状态,8051的初始态。

8051的复位方式可以是自动复位,也可以是手动复位,见下图4。此外,RESET/Vpd还是一复用脚,Vcc掉电其间,此脚可接上备用电源,以保证单片机内部RAM的数据不丢失。

8051时钟电路8051时钟电路    

图4

·Pin30:ALE/当访问外部程序器时,ALE(地址锁存)的输出用于锁存地址的低位字节。而访问内部程序存储器时,ALE端将有一个1/6时钟频率的正脉冲信号,这个信号可以用于识别单片机是否工作,也可以当作一个时钟向外输出。更有一个特点,当访问外部程序存储器,ALE会跳过一个脉冲。

如果单片机是EPROM,在编程其间,将用于输入编程脉冲。

·Pin29:当访问外部程序存储器时,此脚输出负脉冲选通信号,PC的16位地址数据将出现在P0和P2口上,外部程序存储器则把指令数据放到P0口上,由CPU读入并执行。

·Pin31:EA/Vpp程序存储器的内外部选通线,8051和8751单片机,内置有4kB的程序存储器,当EA为高电平并且程序地址小于4kB时,读取内部程序存储器指令数据,而超过4kB地址则读取外部指令数据。如EA为低电平,则不管地址大小,一律读取外部程序存储器指令。显然,对内部无程序存储器的8031,EA端必须接地。

在编程时,EA/Vpp脚还需加上21V的编程电压。

2.2 DS1302芯片简介

    DS1302 是 DALLAS 公司推出的涓流充电时钟芯片 内含有一个实时时钟/日历和 31 字节静态 RAM 通过简单的串行接口与单片机进行通信 实时时钟/日历电路提供秒 分 时 日 日期 月 年的信息 每月的天数和闰年的天数可自动调整 时钟操作可通过 AM/PM 指示决定采用 24 或 12 小时格式 DS1302 与单片机之间能简单地采用同步串行的方式进行通信 仅需用到三个口线 1 RES 复位 2 I/O 数据线 3 SCLK串行时钟 时钟/RAM 的读/写数据以一个字节或多达 31 个字节的字符组方式通信 DS1302 工作时功耗很低 保持数据和时钟信息时功率小于 1mW。

DS1302 是由 DS1202 改进而来 增加了以下的特性 双电源管脚用于主电源和备份电源供应 Vcc1 为可编程涓流充电电源 附加七个字节存储器 它广泛应用于电话 传真 便携式仪器以及电池供电的仪器仪表等产品领域 下面将主要的性能指标作一综合:

· 实时时钟具有能计算 2100 年之前的秒 分 时 日 日期 星期 月 年的能力 还有闰年调整能力

· 31 8 位暂存数据存储 RAM

·  串行 I/O 口方式使得管脚数量最少

· 宽范围工作电压 2.0 5.5V

·  工作电流 2.0V 时,小于 300nA

· 读/写时钟或 RAM 数据时 有两种传送方式 单字节传送和多字节传送 字符组方式

· 8 脚 DIP 封装或可选的 8 脚 SOIC封装 根据表面装配

·  简单 3 线接口

·  与 TTL兼容 Vcc=5V

·  可选工业级温度范围 -40 +85

·  与 DS1202 兼容

·  在 DS1202 基础上增加的特性

对 Vcc1 有可选的涓流充电能力

双电源管用于主电源和备份电源供应

备份电源管脚可由电池或大容量电容输入

附加的 7 字节暂存存储器

1 DS1302的基本组成和工作原理

DS1302 的管脚排列及描述如下图及表所示

管脚描述 管脚描述 管脚描述 管脚描述

    X1 X2 32.768KHz 晶振管脚

    GND     地

RST 复位脚

I/O 数据输入/输出引脚              

SCLK 串行时钟

Vcc1,Vcc2 电源供电管脚

订单信息 订单信息 订单信息 订单信息

部分#   描述

DS1302   串行时钟芯片 8 脚 DIP

DS1302S   串行时钟芯片 8 脚 SOIC 200mil

DS1302Z   串行时钟芯片 8脚 SOIC 150mil

2. DS1302内部寄存器

CH: 时钟停止位 寄存器 2 的第 7 位 12/24 小时标志

CH=0 振荡器工作允许 bit7=1,12 小时模式

CH=1 振荡器停止 bit7=0,24 小时模式

WP:  写保护位 寄存器 2 的第 5 位:AM/PM 定义

WP=0 寄存器数据能够写入  AP=1    下午模式

WP=1 寄存器数据不能写入  AP=0    上午模式

     TCS:  涓流充电选择                    DS:  二极管选择位

     TCS=1010    使能涓流充电            DS=01      选择一个二极管

     TCS=其它     禁止涓流充电            DS=10      选择两个二极管

     DS=00 或 11,  即使TCS=1010, 充电功能也被禁止

2.3  74HC573芯片简介

74HC573 是一种带三态门的8D锁存器,其管脚示意图如下示:

 

其中:1D-8D为8个输入端。

      1Q-8Q为8个输出端。

      LE为数据打入端:当C为“1”时,锁存器输出

      状态同输入状态;当C由“1”变“0”时,数据

      打入锁存器

      OC为输出允许端:当OC=0时,三态门打开;

      当OC=1时,三态门关闭,输出高阻。

2.4  CH451芯片简介

1概述

    CH451 是一个整合了数码管显示驱动和键盘扫描控制以及μP 监控的多功能外围芯片。CH451 内

置RC振荡电路,可以动态驱动8位数码管或者64位 LED,具有 BCD译码、闪烁、移位等功能;同时

还可以进行 64 键的键盘扫描;CH451 通过可以级联的串行接口与单片机等交换数据;并且提供上电

复位和看门狗等监控功能。

2、特点

2.1. 显示驱动

● 内置大电流驱动级,段电流不小于25mA,字电流不小于150mA。

● 动态显示扫描控制,直接驱动 8位数码管或者 64 位发光管 LED。

● 可选数码管的段与数据位相对应的不译码方式或者 BCD 译码方式。

● 数码管的字数据左移、右移、左循环、右循环。

● 各数码管数字独立闪烁控制。

● 通过占空比设定提供16级亮度控制。

● 支持段电流上限调整,可以省去所有限流电阻。

● 扫描极限控制,支持1到8个数码管,只为有效数码管分配扫描时间。

2.2. 键盘控制

● 内置64 键键盘控制器,基于8×8矩阵键盘扫描。

● 内置按键状态输入的下拉电阻,内置去抖动电路。

● 键盘中断,低电平有效输出。

● 提供按键释放标志位,可供查询按键按下与释放。

2.3. 外部接口

● 高速的 4线串行接口,支持多片级联,时钟速度从0 到 10MHz。

● 串行接口中的DIN和DCLK信号线可以与其它接口电路共用,节约引脚。

● 完全内置时钟振荡电路,通常不需要外接晶体或者阻容振荡。

● 内置上电复位和看门狗Watch-Dog,提供高电平有效和低电平有效复位输出。

3功能

   CH451 是一个多功能外围芯片,通过可以级联的 4 线串行接口与单片机、DSP、微处理器等交换

数据。CH451 包含三个功能:数码管显示驱动、键盘扫描控制、μP 监控,三个功能之间相互独立,

单片机可以通过操作命令分别启用、关闭、设定CH451的任何一个功能。CH451 的串行接口是由硬件

实现的,单片机可以频繁地通过串行接口进行高速操作,而绝不会降低 CH451 的工作效率。

4操作命令

4.1 空操作:0000xxxxxxxxB

空操作命令不对CH451产生任何影响。该命令可以在多个 CH451 级联的应用中,透过前级 CH451

向后级CH451发送操作命令而不影响前级的状态。例如,要将操作命令 001000000001B发送给两级级

联电路中的后级 CH451(后级 CH451 的 DIN 连接前级 CH451 的 DOUT),只要在该命令后添加空操作命令 000000000000B 再发送,简化描述是 1^0^0^0^0^0^0^0^0^1^0^0^0^0^0^0^0^0^0^0^0^0^0^0^↑,

那么,该操作命令将经过前级 CH451 到达后级 CH451,而空操作命令留给了前级 CH451。另外,为了

在不影响CH451的前提下变化DCLK以清除看门狗计时,也可以发送空操作命令,在非级联的应用中,

空操作命令可以只发送有效数据B8~B11,简化描述是 0^0^0^0^↑。

4.2. 芯片内部复位:001000000001B

内部复位命令将CH451的各个寄存器和各种参数复位到默认的状态。芯片上电时,CH451总是被

复位,此时各个寄存器均复位为0,各种参数均恢复为默认值。

4.3. 字数据左移:001100000000B

字数据左移命令将 CH451 的字数据左移一次,即从 DIG0 向 DIG7 移动一位,然后最右边的 DIG0

补进数据 00H。例如,在数码管 DIG7~DIG0 显示“87654321”时,执行字数据左移命令,显示变为

“7654321 ” (不译码方式)或者“76543210” (BCD 译码方式)。

 4.4. 字数据右移:001100000010B

字数据右移命令将 CH451 的字数据右移一次,即从 DIG7 向 DIG0 移动一位,然后最左边的 DIG7

补进数据 00H。例如,在数码管 DIG7~DIG0 显示“87654321”时,执行字数据右移命令,显示变为

“ 8765432” (不译码方式)或者“08765432” (BCD 译码方式)。

 4.5. 字数据左循环:001100000001B

字数据左循环命令将 CH451 的字数据左循环一次,即从 DIG0 向 DIG7 移动一位,然后最右边的

DIG0 补进原 DIG7 的数据。例如,在数码管 DIG7~DIG0 显示“87654321”时,执行字数据左循环命

令,显示变为“76543218” 。

4.6. 字数据右循环:001100000011B

字数据右循环命令将 CH451 的字数据右循环一次,即从 DIG7 向 DIG0 移动一位,然后最左边的

DIG7 补进原 DIG0 的数据。例如,在数码管 DIG7~DIG0 显示“87654321”时,执行字数据右循环命

令,显示变为“18765432” 。 

4.7. 设定系统参数:010000000[WDOG][KEYB][DISP]B

设定系统参数命令用于设定CH451的系统级参数:看门狗使能 WDOG,键盘扫描使能 KEYB,显示

驱动使能 DISP。各个参数均通过 1 位数据控制,将相应的数据位置为 1 则启用该功能,否则关闭该

功能(默认值)。例如,命令数据 010000000011B 表示关闭看门狗的功能、启用键盘扫描的功能、启

用显示扫描驱动的功能。

4.8. 设定显示参数:0101[MODE][LIMIT][INTENSITY] B

设定显示参数命令用于设定 CH451 的显示参数:译码方式 MODE,扫描极限 LIMIT,显示亮度

INTENSITY。 译码方式MODE通过1位数据控制,置1时选择 BCD译码方式,置0时选择不译码方式 (默

认值) 。扫描极限LIMIT通过3位数据控制,数据 001B~111B和 000B分别设定扫描极限为 1~7和8(默认值) 。显示亮度INTENSITY通过4位数据控制,数据 0001B~1111B和 0000B分别设定显示驱动占空比为 1/16~15/16 和 16/16(默认值)。例如,命令数据 010101110000B 表示选择不译码方式、

扫描极限为 7、显示驱动占空比为 16/16;命令数据 010110001010B 表示选择 BCD 译码方式、扫描极

限为8、显示驱动占空比为10/16。

4.9. 设定闪烁控制:0110[D7S][D6S][D5S][D4S][D3S][D2S][D1S][D0S]B

设定闪烁控制命令用于设定 CH451 的闪烁显示属性:D7S~D0S 分别对应于 8 个字驱动 DIG7~

DIG0。闪烁属性 D7S~D0S 分别通过 1 位数据控制,将相应的数据位置为 1 则使能闪烁显示,否则为

正常显示,不闪烁(默认值)。例如,命令数据 011000100001B 表示设定数码管 DIG5 和 DIG0 闪烁显

示,其余数码管正常显示,不闪烁。

4.10. 加载字数据:1[DIG_ADDR][DIG_DATA]B

加载字数据命令用于将字数据 DIG_DATA 写入 DIG_ADDR 指定地址的数据寄存器中。DIG_ADDR 通

过 3 位数据指定数据寄存器的地址,数据 000B~111B 分别指定地址 0~7,对应于 DIG0~DIG7 引脚

驱动的 8 个数码管。DIG_DATA 是 8 位的字数据。例如,命令数据 100001111001B 表示将字数据 79H

写入第 1 个数据寄存器,如果是不译码方式,则 DIG0 引脚驱动的数码管将显示 E;命令数据

110010001000B 表示将字数据 88H 写入第 5 个数据寄存器,如果是 BCD 译码方式,则 DIG4 引脚驱动的数码管将显示8.。

 4.11. 读取按键代码:0111xxxxxxxxB

读取按键代码命令用于获得 CH451 最近检测到的有效按键的按键代码。该命令是唯一的具有数

据返回的命令,CH451从DOUT引脚输出按键代码,按键代码总是 7 位数据,最高位是状态码,位 5~

位 0 是扫描码。读取按键代码命令的位数据 B0~B7 可以是任意值,所以单片机可以将该操作命令缩短为 4 位数据 B8~B11。例如,CH451 检测到有效按键并中断,按键代码是 5EH,简化描述 1^1^1^0^

↑H^L^H^H^H^H^L^表示先向 CH451 发出读取按键代码命令 0111xxxxxxxxB,然后从 DOUT 获得按键代

码5EH。

 

5 键盘扫描

     CH451 的键盘扫描功能支持 8×8 矩阵的 64 键键盘。在键盘扫描期间,DIG7~DIG0 引脚用于列

扫描输出,SEG7~SEG0 引脚都带有内部下拉电阻,用于行扫描输入;当启用键盘扫描功能后,DOUT引脚的功能由串行接口的数据输出变为键盘中断以及数据输出。 CH451 定期在显示驱动扫描过程中插入键盘扫描。在键盘扫描期间,DIG7~DIG0 引脚按照 DIG0至DIG7的顺序依次输出高电平,其余7个引脚输出低电平;SEG7~SEG0引脚的输出被禁止,当没有键被按下时,SEG7~SEG0都被下拉为低电平;当有键被按下时,例如连接 DIG3与 SEG4的键被按下,则当DIG3输出高电平时SEG4检测到高电平; 为了防止因为按键抖动或者外界干扰而产生误码, CH451实行两次扫描,只有当两次键盘扫描的结果相同时,按键才会被确认有效。如果 CH451检测到有效的按键,则记录下该按键代码,并通过 DOUT 引脚产生低电平有效的键盘中断,此时单片机可以通过串行接口读取按键代码;在没有检测到新的有效按键之前,CH451 不再产生任何键盘中断。CH451 不支持组合键,也就是说,同一时刻,不能有两个或者更多的键被按下;如果多个键同时按下,那么按键代码较小的按键优先。

    CH451 所提供的按键代码为 7 位,位 2~位 0 是列扫描码,位 5~位 3 是行扫描码,位 6 是状态码(键按下为 1,键释放为 0)。例如,连接 DIG3 与 SEG4 的键被按下,则按键代码是 1100011B 或者63H,键被释放后,按键代码通常是 0100011B 或者 23H,其中,对应 DIG3 的列扫描码为 011B,对应SEG4 的行扫描码为 100B。单片机可以在任何时候读取按键代码,但一般在 CH451 检测到有效按键而产生键盘中断时读取按键代码,此时按键代码的位6 总是1,另外,如果需要了解按键何时释放,单片机可以通过查询方式定期读取按键代码,直到按键代码的位 6 为 0。 下表是连接在DIG7~DIG0与SEG7~SEG0之间的键被按下时,CH451所提供的按键代码。这些按键代码具有一定的规律,如果需要键被释放时的按键代码,则将表中的按键代码的位 6置 0,也就是将表中的按键代码减去40H。

2.5   74HC138芯片简介

1.  74HC138 概述

  74HC138是一款高速CMOS器件,74HC138引脚兼容低功耗肖特基TTL(LSTTL)系列。
  74HC138译码器可接受3位二进制加权地址输入(A0, A1和A3),并当使能时,提供8个互斥的低有效输出(Y0至Y7)。74HC138特有3个使能输入端:两个低有效(E1和E2)和一个高有效(E3)。除非E1和E2置低且E3置高,否则74HC138将保持所有输出为高。利用这种复合使能特性,仅需4片74HC138芯片和1个反相器,即可轻松实现并行扩展,组合成为一个1-32(5线到32线)译码器。任选一个低有效使能输入端作为数据输入,而把其余的使能输入端作为选通端,则74HC138亦可充当一个8输出多路分配器,未使用的使能输入端必须保持绑定在各自合适的高有效或低有效状态。
  74HC138与74HC238逻辑功能一致,只不过74HC138为反相输出。

2.  74HC138 特性

  • 多路分配功能
  • 复合使能输入,轻松实现扩展                      
  • 兼容JEDEC标准no.7A
  • 存储器芯片译码选择的理想选择
  • 低有效互斥输出
  • ESD保护
  •  
    • HBM EIA/JESD22-A114-C超过2000 V
    • MM EIA/JESD22-A115-A超过200 V
  • 温度范围
    • -40~+85 ℃
    • -40~+125 ℃

 3 系统的硬件设计

3.1.系统硬件设计

选用LJD-SY-5200单片机实验系统,主要使用cpu的p0口控制74ch573以及lcd,74ls138接

p1.5,1.6,1.7.ch451的load接p1.2,din p3.5 dclk p3.4 dout p3.3.ds1302 sclk p1.1 I/O p1.0 rst p1.4.存储器24c02 scl p1.1,sda p1.0 .

3.2  Lcd 液晶

下面介绍其工作原理,其中有四个寄存器AC,GGRAM,DDRAM,GGROM.

GGROM:提供8192个触发器选择屏幕的开和关。Dff =1的时候 就将DDRAM的内容显示在lcd上。

DDRAM:实际上是提供了64*2个位组的空间,而且可以控制字型。(也就是要输出的内容)

GGROM:可提供四组图象自定义空间,可以将没有的字自定义到GGROM,在显示到lcd上。

3.3  8段数码管

下面介绍其工作原理,由于是跟ch451相连,dig0-7实现的是数码管选择,而seg0-7实现的各段的点亮。所以控制的核心就是cpu与ch451的控制

4 系统的软件设计

4.1程序设计与调试步骤

(1) 选题构思,研究实现功能的方法。

(2) 画出硬件图。

(3) 编写程序,并保存。

(4) 按硬件接线图接线。

(5) 联机并编译。

(6) 检查并修改语法错误,并保存。

(7) 编译文件,并运行,观察调试结果。

(8) 如不能正常运行,采用单步运行法进行调试,按部就班,直至能够达到设计要求。

(9) 记录最终的程序,并保存设计图与电路图。

4.2 计数器初值计算

     延时方法可以有两种一中是利用MCS-51内部定时器才生溢出中断来确定1秒的时间,另一种是采用软延时的方法。

 定时器工作时必须给计数器送计数器初值,这个值是送到TH和TL中的。他是以加法记数的,并能从全1到全0时自动产生溢出中断请求。因此,我们可以把计数器记满为零所需的计数值设定为C和计数初值设定为TC 可得到如下计算通式:

          TC=M-C

式中,M为计数器摸值,该值和计数器工作方式有关。在方式0时M为213 ;在方式1时M的值为216;在方式2和3为28

4.2.1 1秒的方法

  我们采用在主程序中设定一个初值为20的软件计数器和使T0定时50毫秒.这样每当T0到50毫秒时CPU就响应它的溢出中断请求,进入他的中断服务子程序。在中断服务子程序中,CPU先使软件计数器减1,然后判断它是否为零。为零表示1秒已到可以返回到输出时间显示程序。

4.3  时间显示

4.3.1 写保护寄存器操作

  当写保护寄存器的最高位为 0 时 允许数据写入寄存器 写保护寄存器可以通过命令字节 8E 8F 来 规定禁止写入/读出 写保护位不能在多字节传送模式下写入

        Write_Enable:­

                MOV     Command,#8Eh   ;命令字节为 8E

                MOV     ByteCnt,#1     ;单字节传送模式

                MOV     R0,#XmtDat      数据地址覆给 R0

                MOV     XmtDat,#00h     数据内容为 0 写入允许

                ACALL   Send_Byte       调用写入数据子程序

                RET                     返回调用本子程序处

4.3.2 时钟停止位操作

    当把秒寄存器的第 7 位 时钟停止位 设置为 0 时 起动时钟开始

   Osc_Enable:­

         MOV     Command,#80h       ; 命令字节为 80

         MOV     ByteCnt,#1         ; 单字节传送模式

         MOV     R0,#XmtDat          数据地址覆给 R0

         MOV     XmtDat,#00h         数据内容为 0 振荡器工作允许

         ACALL   Send_Byte           调用写入数据子程序

         RET                         返回调用本子程序处

4.3.3. 多字节传送方式

当命令字节为 BE 或 BF 时 DS1302 工作在多字节传送模式 8 个时钟/日历寄存器从寄存器 0 地址开始连续读写从 0 位开始的数据 当命令字节为 FE 或 FF 时 DS1302 工作在多字节 RAM 传送模式 31 个RAM 寄存器从 0 地址开始连续读写从 0 位开始的数据

例如 写入 00 年 6 月 21日 星期三 13 时 59 分 59 秒 程序设置如下

    Write_Multiplebyte:­

            MOV     Command,#0BEh     ;命令字节为 BEh

            MOV     ByteCnt,#8        ;多字节写入模式 此模块为 8 个

            MOV     R0,#XmtDat         数据地址覆给 R0

            MOV     XmtDat,#59h        秒单元内容为 59h

           MOV     XmtDat+1,#59h      分单元内容为 59h

           MOV     XmtDat+2,#13h      时单元内容为 13h

           MOV     XmtDat+3,#21h      日期单元内容为 21h

           MOV     XmtDat+4,#06h      月单元内容为 06h

           MOV     XmtDat+5,#03h      星期单元内容为 03h

           MOV     XmtDat+6,#0        年单元内容为 00h

           MOV     XmtDat+7,#0        写保护单元内容为 00h

           ACALL   Send_Byte          调用写入数据子程序

           RET                        返回调用本子程序处

­  读出寄存器 0-7 的内容 程序设置如下

    Read_Multiplebyte:

                MOV     Command,#0BFh   ;命令字节为 BFh

                MOV     ByteCnt,#8      ;多字节读出模式 此模块为 8 个

                MOV     R1,#RcvDat       数据地址覆给 R1

                ACALL   Receive_Byte     调用读出数据子程序

                RET                      返回调用本子程序处

5结论

   本系统就是充分利用了8051芯片的I/O引脚。系统统采用MSC-51系列单片机来设计交通灯控制器,  实现了数码管上显示hello,大约2秒之后数码管清屏,然后8*8led开始显示交通指示灯,此时数码管上同步显示到记时3,2,1。然后转向下一个指示灯(转向)。完成之后,用键盘键入初值,依次在数码管上显示。由于使用的是单片机作为核心的控制元件,使得电路的可靠性比较高,功能也比较强大,而且可以随时的更新系统,进行不同状态的组合。但是在我们设计和调试的过程中,也发现了一些问题,譬如红灯和绿灯的切换还不够迅速,红绿灯规则不效率还不是很高,在数码管1秒定时显示时结果初值获取不正确,导致显示的值与意向中得值不同。这需要在实践中进一步完善。 通过这次单片机实验设计,使我得到了一次用专业知识、专业技能分析和解决问题全面系统的锻炼。使我在单片机的基本原理、单片机应用系统开发过程,以及在常用编程设计思路技巧(特别是汇编语言)的掌握方面都能向前迈了一大步,为日后成为合格的应用型人才打下良好的基础。

附录:

系统总原理图和程序

1、系统总体原理图

2、流程图

按键流图

总流程图

3、程序代码

                 /*******************************************************************************/

*  描述:                                                                       *

*         通过8位数码管显示当前的时间包括时分秒通过时间设置键可对当前的时间进  *

*       行设置。共有3个设置按键按键1和按键2及按键3;按键1为选定键。按键1首次被 *

*       按下时钟停止走时并且秒时间开始闪烁,此时可通过按键2对秒时间进行设置,  *

*       按键2按下一次秒时间数值加一。当按键1第二次被按下时分时间被选中并不停   *

*       地闪烁,此时通过按键2可对分时间进行设置,按键2按下一次分时间加一。按键 *

*       第三次被按下时小时时间被选中并且不停的闪烁,此时通过按键2可对小时时间进 *

*       行设置,按键2每按下一次小时时间加一按键1第四次被按下后时间设置结束,时 *

*       钟从设定的时间开始走时。按键3为日期和时间的切换键,按键3按下一次显示日 *

*       期,再按下一次显示时间。使用按键3可在日期和时间之间来回进行切换。      *    

*                                                                              *

*                                                                              *

********************************************************************************

* 【版权】 Copyright(C)微芯科技 http://www.bluemcu.com    All Rights Reserved  *         

* 【声明】 此程序仅用于学习与参考,引用请注明版权和作者信息!                  *

*******************************************************************************/

#include <reg52.h>

//#include<stdio.h>

#include<absacc.h>

#include<intrins.h>

/*须主程序定义的参数*/

/*如果使用键盘中断请定义

#define  USE_KEY  */

/*须主程序定义的参数*/

#define CH451_RESET     0x0201                 /*复位*/

#define CH451_LEFTMOV   0x0300                 /*设置移动方式-作移*/

#define CH451_LEFTCYC   0x0301                 /*设置移动方式-左循*/

#define CH451_RIGHTMOV  0x0302                 /*设置移动方式-右移*/

#define CH451_RIGHTCYC  0x0303                 /*设置移动方式-右循*/

#define CH451_SYSOFF    0x0400                 /*关显示、键盘、看门狗*/

#define CH451_SYSON1    0x0401                 /*开显示*/

#define CH451_SYSON2    0x0403                 /*开显示、键盘*/

#define CH451_SYSON3    0x0407                 /*开显示、键盘、看门狗功能*/

#define CH451_DSP       0x0500                 /*设置默认显示方式*/

#define CH451_BCD       0x0580                 /*设置BCD译码方式*/

#define CH451_TWINKLE   0x0600                 /*设置闪烁控制*/

#define CH451_DIG0      0x0800                 /*数码管位0显示*/

#define CH451_DIG1      0x0900                 /*数码管位1显示*/

#define CH451_DIG2      0x0a00                 /*数码管位2显示*/

#define CH451_DIG3      0x0b00                 /*数码管位3显示*/

#define CH451_DIG4      0x0c00                 /*数码管位4显示*/

#define CH451_DIG5      0x0d00                 /*数码管位5显示*/ 

#define CH451_DIG6      0x0e00                 /*数码管位6显示*/ 

#define CH451_DIG7      0x0f00                 /*数码管位7显示*/

#define  uchar  unsigned char

#define  uint   unsigned int

sbit  aa=P2^0;

sbit  bb=P2^3;

sbit SDA  = P1^0;                     /* iic数据传送位 */

sbit SCL  = P1^1;                     /* iic时钟控制位 */

sbit P1_4 = P1^4;

sbit P1_3 = P1^3;

sbit P1_5 = P1^5;

/* 函数声明 */

void iic_wait(void);                  /* iic延时       */

void iic_start(void);                 /* 开启iic总线   */

void iic_stop(void);                  /* 关闭iic总线   */

void iic_ack(void);                   /* 发送ACK信号   */

void iic_no_ack(void);                /* 发送NOACK信号 */

bit iic_wait_ack(void);               /* 等待ACK信号   */

void iic_send_byte(uchar demand);     /* MCU向iic设备发送一个字节 */

uchar iic_receive_byte(void);         /* MCU从iic设备接收一个字节 */

#define LED8X8R   XBYTE[0xFD00]         /*红色LED电亮地址定义*/

#define LED8X8G   XBYTE[0xFB00]         /*绿色LED点亮地址定义*/

#define    uchar  unsigned char

#define uint  unsigned int

sbit     T_CLK      = P1^1;   /*实时时钟时钟线引脚 */

sbit     T_IO       = P1^0;   /*实时时钟数据线引脚 */

sbit     T_RST      = P1^4;   /*实时时钟复位线引脚 */

sbit     ch451_din  = P3^5;

sbit     ch451_clk  = P3^4;

sbit     ch451_load = P1^2;

sbit     ch451_dout = P3^3;

sbit  ACC0 = ACC^0;

sbit  ACC7 = ACC^7;

sbit  flag_time = PSW^5;

uchar count;

uchar M;

uchar N;

uchar ch451_key;

uchar key_flag;

uchar g;

uchar p;

  uchar ds_hour;

  uchar ds_min;

  uchar ds_sec;

  uchar ds_date;

  uchar ds_month;

  uchar ds_year;

unsigned char z,x;

unsigned char  y;

/*void   RTInputByte(uchar);       /* 输入 1Byte */

/*uchar  RTOutputByte(void);       /* 输出?1Byte */

/*void   W1302(uchar, uchar);

/*uchar  R1302(uchar);

/*void   Set1302(uchar *);         /* 设置时间 */

/*void   Get1302(uchar curtime[]); /* 读取1302当前时间 */

uchar    WClock[7]={0x00,0x01,0x02,0x22,0x07,0x06,0x06};

uchar    SClock[7];

uchar  G_code[8]={0x1f,0x3f,0x5f,0x7f,0x9f,0xbf,0xdf,0xff};

uchar Code4[8]={0xEF,0xCF,0x80,0x00,0x00,0x80,0xCF,0xEF};

uchar Code5[8]={0xFF,0x03,0x01,0x01,0xF1,0xE0,0xF1,0xFB};

uchar Code6[8]={0xFB,0xF1,0xE0,0xF1,0x01,0x01,0x03,0xFF};

uchar Code7[8]={0xDF,0x8F,0x07,0x8F,0x80,0x80,0xC0,0xFF};

uchar DATA ;

uchar idata test_write[104];

uchar idata test_read[104];

unsigned char k;

void delayd(void)

{

  uint i;

  uint j;

  for(i=0;i<300;i++)

  for(j=0;j<100;j++);

}

void delayq(void)

{

  uchar i;

  uchar j;

  for(i=0;i<100;i++)

  for(j=0;j<100;j++);

}

/* iic延时       */

void iic_wait(void)

{

 ;

 ;

 ;

 ;

 ;

 ;  

 ;

 ;

 ;

 ;

 ;

 ;

 ;

 ;

}

/* 开启iic总线   */

void iic_start(void)

{

  SDA = 1;

  SCL = 1;

  iic_wait();

  SDA = 0;

  iic_wait();

  SCL = 0;

}

/* 关闭iic总线   */

void iic_stop(void)

{

  SDA=0;

  SCL=0;

  iic_wait();

  SCL=1;

  iic_wait();

  SDA=1;

}

/* 发送ACK信号   */

void iic_ack(void)

{

  SDA=0;

  iic_wait();

  SCL=1;

  iic_wait();

  SCL=0;

}

/* 发送NOACK信号 */

void iic_no_ack(void)

{

  SDA=1;

  iic_wait();

  SCL=1;

  iic_wait();

  SCL=0;

}

/********************************************************************

函 数 名:iic_wait_ack  

功    能:等待ACK信号

说    明:

入口参数:

返 回 值:1-ACK         0-ERROR

设    计:蓝海微芯          

***********************************************************************/

bit iic_wait_ack(void)

{

  uchar errtime=255;

  //uint errtime = 1000;

  SDA=1;

  iic_wait();

  SCL=1;

  iic_wait();

  while(SDA)

  {

    errtime--;

    if(!errtime)

    return 0;

  }

  SCL=0;

  return 1;

}

/********************************************************************

函 数 名:iic_send_byte  

功    能:MCU向iic设备发送一个字节

说    明:

入口参数:sbyte                  待发送的字节数据

返 回 值:

设    计:蓝海微芯          

***********************************************************************/

void iic_send_byte(uchar sbyte)

{

  uchar i = 8;

  while(i--)

  {

    SCL = 0;

   ;

    SDA = (bit)(sbyte&0x80);

    sbyte <<= 1;

    iic_wait();

    SCL = 1;

    iic_wait();

  }

 SCL=0;

}

/********************************************************************

函 数 名:iic_receive_byte 

功    能:MCU从iic设备接收一个字节

说    明:

入口参数:

返 回 值:ddata   接收数据

设    计:蓝海微芯          

***********************************************************************/

uchar iic_receive_byte(void)

{

  uchar i=8;

  uchar ddata=0;

  SDA=1;

  while(i--)

  {

    ddata <<= 1;

    SCL=0;

    iic_wait();

    SCL=1;

    iic_wait();

    ddata |= SDA;

  }

  SCL=0;

  return ddata;

}

/********************************************************************

函 数 名:write_iic_data

功    能:向iic设备写入N个字节

说    明:

入口参数:write_data              存放写入字节的数组

          address                 指定从address处开始写入

          num                     写入的字节数 

返 回 值:无

设    计:蓝海微芯          

***********************************************************************/

void write_iic_data(uchar write_data[], uchar address, uchar num)

{

  uchar n;

  iic_start();

  iic_send_byte(0xa0);

  iic_wait_ack();

  iic_send_byte(address);

  iic_wait_ack();

   

  for(n=0; n<num; n++)

  {

    iic_send_byte(write_data[n]);

    iic_wait_ack();

  }

  iic_stop();

}

/********************************************************************

函 数 名:read_iic_data

功    能:从iic设备读取N个字节

说    明:

入口参数:read_data               存放读取字节的数组

          address                 指定从address处开始读取

          num                     读取的字节数

返 回 值:无

设    计:蓝海微芯          

***********************************************************************/

void read_iic_data(uchar read_data[], uchar address, uchar num)

{

  uchar n;

  uchar *pread_data;

  pread_data = read_data;

  iic_start();

  iic_send_byte(0xa0);

  iic_wait_ack();

  iic_send_byte(address);

  iic_wait_ack();

  iic_start();

  iic_send_byte(0xa1);

  iic_wait_ack();

  for (n=0; n<num; n++)

  {

    *pread_data = iic_receive_byte();

    pread_data++;

    if (n != (num - 1))     //最后一个数据不应答

    {

      iic_ack();

    }

  }

  iic_no_ack();

  iic_stop();

}

void delay(void)

{

  uint i;

  uint j;

  for(i=0;i<200;i++);

  for(j=0;j<500;j++);

}

void delay_10us(void)

{

  uchar i;

  for(i=0;i<20;i++);

}

/********************************************************************

函 数 名:RTInputByte()

功    能:实时时钟写入一字节

说    明:往DS1302写入1Byte数据 (内部函数)

入口参数:d 写入的数据

返 回 值:无 

设    计:蓝海微芯         

***********************************************************************/

void RTInputByte(uchar d)

{

  uchar i;

  ACC = d;

  for(i=8; i>0; i--)

  {

    T_IO = ACC0;           /*相当于汇编中的 RRC */

    T_CLK = 1;

    T_CLK = 0;

    ACC = ACC >> 1;

  }

}

/********************************************************************

函 数 名:RTOutputByte()

功    能:实时时钟读取一字节

说    明:从DS1302读取1Byte数据 (内部函数)

入口参数:无 

返 回 值:ACC

设    计:蓝海微芯          

***********************************************************************/

uchar RTOutputByte(void)

{

  uchar i;

  for(i=8; i>0; i--)

  {

    ACC = ACC >>1;         /*相当于汇编中的 RRC */

    ACC7 = T_IO;

    T_CLK = 1;

    T_CLK = 0;

  }

  return(ACC);

}

/********************************************************************

函 数 名:W1302()

功    能:往DS1302写入数据

说    明:先写地址,后写命令/数据 (内部函数)

调    用:RTInputByte() , RTOutputByte()

入口参数:ucAddr: DS1302地址, ucData: 要写的数据

返 回 值:无

设    计:蓝海微芯        

***********************************************************************/

void W1302(uchar ucAddr, uchar ucDa)

{

  T_RST = 0;

  T_CLK = 0;

  T_RST = 1;

  RTInputByte(ucAddr);       /* 地址,命令 */

  RTInputByte(ucDa);         /* 写1Byte数据*/

  T_CLK = 1;

  T_RST = 0;

}

/********************************************************************

函 数 名:R1302()

功    能:读取DS1302某地址的数据

说    明:先写地址,后读命令/数据 (内部函数)

调    用:RTInputByte() , RTOutputByte()

入口参数:ucAddr: DS1302地址

返 回 值:ucData :读取的数据

设    计:蓝海微芯          

***********************************************************************/

uchar R1302(uchar ucAddr)

{

  uchar ucData;

  T_RST = 0;

  T_CLK = 0;

  T_RST = 1;

  RTInputByte(ucAddr);             /* 地址,命令 */

  ucData = RTOutputByte();         /* 读1Byte数据 */

  T_CLK = 1;

  T_RST = 0;

  return(ucData);

}

/********************************************************************

函 数 名:Set1302()

功    能:设置初始时间

说    明:先写地址,后读命令/数据(寄存器多字节方式)

调    用:W1302()

入口参数:pClock: 设置时钟数据

地址格式为: 秒 分 时 日 月 星期 年

7Byte (BCD码)1B 1B 1B 1B 1B  1B  1B

返 回 值:无

设    计:蓝海微芯          

***********************************************************************/

void Set1302(uchar *pClock)

{

  uchar i;

  uchar ucAddr = 0x80;

  W1302(0x8e,0x00);           /* 控制命令,WP=0,写操作?*/

  for(i =7; i>0; i--)

  {

    W1302(ucAddr,*pClock);    /* 秒 分 时 日 月 星期 年 */

    pClock++;

    ucAddr +=2;

  }

  W1302(0x8e,0x80);           /* 控制命令,WP=1,写保护?*/

}

/********************************************************************

函 数 名:Get1302()

功    能:读取DS1302当前时间

说    明:

调    用:R1302()

入口参数:ucCurtime: 保存当前时间地址。

当前时间格式为: 秒 分 时 日 月 星期 年

7Byte (BCD码)   1B 1B 1B 1B 1B  1B  1B

返 回 值:无

设    计:蓝海微芯          

***********************************************************************/

void Get1302(uchar ucCurtime[])

{

  uchar i;

  uchar ucAddr = 0x81;

  for (i=0; i<7; i++)

  {

    ucCurtime[i] = R1302(ucAddr);  /*格式为: 秒 分 时 日 月 星期 年 */

    ucAddr += 2;

  }

}

/********************************************************************

函 数 名:ch451_bcd()

功    能:对从DS1302读取的时间值进行转化

说    明:把一字节的BCD码格式的时间值分成两字节

调    用:

入口参数:ds_bcd

返 回 值:

设    计:蓝海微芯           

***********************************************************************/

void ch451_bcd(uchar ds_bcd)

{

  M = ds_bcd&0xf0;

  M>>=4;

  N = ds_bcd&0x0f;

}

/********************************************************************

函 数 名:ch451_init()

功    能:对ch451芯片进行初始化

说    明:

调    用:

入口参数:

返 回 值:

设    计:蓝海微芯          

***********************************************************************/

void ch451_init(void)

{

  ch451_din  = 0;           /*先低后高,选择4线输入*/

  ch451_din  = 1;

   

  ch451_load = 1;

  ch451_clk  = 1;

  _nop_();

   

}

/********************************************************************

函 数 名:ch451_write()

功    能:把12位的指令或数据写入ch451

说    明:12位数据或指令的写入顺序是低位

调    用:

入口参数:command

返 回 值:

设    计:蓝海微芯          

***********************************************************************/

void ch451_write(unsigned int command)

{

  unsigned char i;

  #ifdef USE_KEY

    EX1=0;                             /*禁止键盘中断*/

  #endif  

  ch451_load=0;                        /*命令开始*/   

  for(i=0;i<12;i++)

  {                                    /*送入12位数据,低位在前*/

    ch451_din=command&1;          

    ch451_clk=0;

    command>>=1;

    ch451_clk=1;                       /*上升沿有效*/

  }

  ch451_load=1;                        /*加载数据*/

  #ifdef USE_KEY

    EX1=1;

  #endif

}

/********************************************************************

函 数 名:ds1302主程序

功    能:读取ds1302的时间值并通过数码管进行显示

说    明:

调    用:

入口参数:

返 回 值:

设    计:蓝海微芯          

***********************************************************************/

void SET_RTC(void)

{

  Get1302(&SClock);         /*把当前的时间值读取到数组SClock[3]中去*/

  SClock[0]|=0x80;          /*使ds1302的时间停止运行*/

  Set1302(SClock);  

  T_RST=1;

  T_RST=0;   

  ch451_key = 0xff;

  key_flag++;

  if(key_flag==4)

  {

    key_flag = 0;

    ch451_write(0x600);

    SClock[0]&=0x7f;

    Set1302(SClock);

    T_RST=1;

    T_RST=0;

  }

  switch(key_flag)

  {

    case 1:

    ch451_write(0x6c0);     /*让秒显示位进行闪烁*/

    break;

    case 2:  

    ch451_write(0x618);     /*让分显示位进行闪烁*/

    break;

    case 3:

    ch451_write(0x603);     /*让小时显示位进行闪烁*/

    break;

    default:break;

  }

}

/********************************************************************

函 数 名:SET_TIME()

功    能:根据按键值&按键被按下的次数作相应的处理

说    明:

调    用:

入口参数:

返 回 值:

设    计:蓝海微芯          

***********************************************************************/

void SET_TIME(void)

{

  switch(key_flag)

  {

      

    case 1:  

             

          

    SClock[0]&=0x7f;

    ch451_bcd(SClock[0]);     /*将秒显示位的十位和个位进行分离*/

    N++;                      /*秒值加一*/

    if(N==10)

    {

      N = 0;

      M++;

      if(M==6)

      {

        M = 0;

      }

    }

    M<<=4;

    SClock[0] = M|N|0x80;

    W1302(0x8e,0x00);           /*控制命令,wp=0,可以进行写操作*/

    W1302(0x80, SClock[0]);     /*将修改过的秒值写到ds1302中*/

    T_RST=1;

    T_RST=0;

    ch451_key=0xff;

    break;

    case 2:

          

          

    ch451_bcd(SClock[1]);      /*将分显示位的十位和个位进行分离*/

    N++;                       /*分值加一*/

    if(N==10)

    {

      N = 0;

      M++;

      if(M==6)

      {

        M = 0;

      }

    }

    M<<=4;

    SClock[1] = M|N;

    W1302(0x8e,0x00);         /*控制命令,wp=0,可以进行写操作*/

    W1302(0x82, SClock[1]);   /*将修改过的分值写到ds1302中*/

    T_RST=1;

    T_RST=0;

    ch451_key=0xff;

    break;

    case 3:

    ch451_bcd(SClock[2]);     /*将小时显示位的十位和个位进行分离*/

    N++;                      /*小时值加一*/

    if(N==4)

    {

      if(M==2)                /*小时十位为2时个位最大值不能超过3*/

      {

        M = 0;

        N = 0;

      }

    }

    if(N==10)

    {

      N = 0;

      M++;

    }

    M<<=4;

    SClock[2] = M|N;

    W1302(0x8e,0x00);        /*控制命令,wp=0,可以进行写操作*/

    W1302(0x84,SClock[2]);   /*将修改过的分值写到ds1302中*/

    T_RST=1;

    T_RST=0;

    ch451_key=0xff;

    break;

    default:break;

  }

  ch451_key = 0xff;

}

/********************************************************************

函 数 名:ch451中断服务程序

功    能:获取键值

说    明:

调    用:

入口参数:

返 回 值:ch451_key

设    计:蓝海微芯          

***********************************************************************/

void ch451_inter() interrupt 2 using 1

{

  unsigned char i;                     /*定义循环变量*/

  unsigned char command,keycode;       /*定义控制字寄存器,和中间变量定时器*/

  command=0x07;              /*读取键值命令的高4位0111B*/

  EX1 = 0;

  ch451_load=0;              /*命令开始*/

  for(i=0;i<4;i++)

  {

    ch451_din=command&1;     /*低位在前,高位在后*/

    ch451_clk=0;

    command>>=1;             /*右移一位*/

    ch451_clk=1;             /*产生时钟上升沿锁通知CH451输入位数据*/

  }

  ch451_load=1;              /*产生加载上升沿通知CH451处理命令数据*/

  keycode=0;                 /*清除keycode*/

  for(i=0;i<7;i++)

  {

    keycode<<=1;             /*数据作移一位,高位在前,低位在后*/

    keycode|=ch451_dout;     /*从高到低读入451的数据*/

    ch451_clk=0;             /*产生时钟下升沿通知CH451输出下一位*/

    ch451_clk=1; 

  }

  ch451_key=keycode;         /*保存上次的键值*/

      

  EX1 = 1;

  IE1=0;                     /*清中断标志*/

}

void SET_DATE(void)

{

  switch(key_flag)

  {

      

    case 1:  

             

          

    SClock[0]&=0x7f;

    ch451_bcd(SClock[3]);     /*将秒显示位的十位和个位进行分离*/

    N++;                      /*秒值加一*/

    if(N==10)

    {

      N = 0;

      M++;

         

    }

    if(M==3)

           { if(N==2)

             {M=0;N=0;}

       }

    M<<=4;

    SClock[3] = M|N|0x80;

    W1302(0x8e,0x00);           /*控制命令,wp=0,可以进行写操作*/

    W1302(0x86, SClock[3]);     /*将修改过的秒值写到ds1302中*/

    T_RST=1;

    T_RST=0;

    ch451_key=0xff;

    break;

    case 2:

          

          

    ch451_bcd(SClock[4]);      /*将分显示位的十位和个位进行分离*/

    N++;                       /*分值加一*/

    if(N==10)

    {

      N = 0;

      M++;

     }

     if(M==1)

         {   if(N==3)

        {   M=0;N=0;}

        }

    M<<=4;

    SClock[4] = M|N;

    W1302(0x8e,0x00);         /*控制命令,wp=0,可以进行写操作*/

    W1302(0x88, SClock[4]);   /*将修改过的分值写到ds1302中*/

    T_RST=1;

    T_RST=0;

    ch451_key=0xff;

    break;

    case 3:

    ch451_bcd(SClock[6]);     /*将小时显示位的十位和个位进行分离*/

    N++;                      /*小时值加一*/

  

    if(N==10)

    {

      N = 0;

      M++;

     if(M==10){M=0;N=0;}

    }

     

    M<<=4;

    SClock[6] = M|N;

    W1302(0x8e,0x00);        /*控制命令,wp=0,可以进行写操作*/

    W1302(0x8C,SClock[6]);   /*将修改过的分值写到ds1302中*/

    T_RST=1;

    T_RST=0;

    ch451_key=0xff;

    break;

    default:break;

  }

  ch451_key = 0xff;

}

/********************************************************************

函 数 名:disp_time()

功    能:显示当前的时间(时分秒)

说    明:

调    用:

入口参数:

返 回 值:

设    计:蓝海微芯          

***********************************************************************/

void disp_time(void)

{

  ds_sec  = SClock[0]&0x7f;         /*得到当前时间秒值*/

  ds_min  = SClock[1];              /*得到当前时间分值*/

  ds_hour = SClock[2];              /*得到当前时间小时值*/

  ch451_write(CH451_DIG5|0x12);     /*数码管的第2位显示"-"*/

  ch451_write(CH451_DIG2|0x12);     /*数码管的第7位显示"-"*/

  ch451_bcd(ds_hour);               /*对小时值进行转化*/

  ch451_write(CH451_DIG0|M);        /*写小时高位*/

  ch451_write(CH451_DIG1|N);        /*写小时低位*/

  ch451_bcd(ds_min);                /*对分钟值进行转化*/

  ch451_write(CH451_DIG3|M);        /*写分钟高位*/

  ch451_write(CH451_DIG4|N);        /*写分钟低位*/

  ch451_bcd(ds_sec);                /*对秒值进行转化*/

  ch451_write(CH451_DIG6|M);        /*写秒值高位*/

  ch451_write(CH451_DIG7|N);        /*写秒值低位*/

}

/********************************************************************

函 数 名:disp_date()

功    能:显示当前的日期(年月日)

说    明:

调    用:

入口参数:

返 回 值:ch451_key

设    计:蓝海微芯          

***********************************************************************/

void disp_date(void)

{

 // key_flag = 0;

 // ch451_write(0x600);

  ds_date   = SClock[3];            /*得到当前时间秒值*/

  ds_month  = SClock[4];            /*得到当前时间分值*/

  ds_year   = SClock[6];            /*得到当前时间小时值*/

    ch451_write(CH451_DIG5|0x12);      /*数码管的第2位显示"-"*/

  ch451_write(CH451_DIG2|0x12);     /*数码管的第7位显示"-"*/

  ch451_bcd(ds_year);                /*对年值进行转化*/

  ch451_write(CH451_DIG0|M);        /*写年值高位*/

  ch451_write(CH451_DIG1|N);        /*写年值低位*/

  ch451_bcd(ds_month);              /*对月值进行转化*/

  ch451_write(CH451_DIG3|M);        /*写月值高位*/

  ch451_write(CH451_DIG4|N);        /*写月值低位*/

  ch451_bcd(ds_date);                /*对日期值进行转化*/

  ch451_write(CH451_DIG6|M);        /*写日期值高位*/

  ch451_write(CH451_DIG7|N);        /*写日期值低位*/

}

void l_z()

{

   unsigned char i;

   LED8X8G  = 0x00;

  LED8X8R  = 0xFF;

  for(i=0;i<100;i++)

  { 

    P1        = G_code[0];

    LED8X8G  =  Code4[0];

    delay_10us();

    P1       = G_code[1];

    LED8X8G  =  Code4[1];

    delay_10us();

    P1       = G_code[2];

    LED8X8G  =  Code4[2];

    delay_10us();

    P1       = G_code[3];

    LED8X8G  =  Code4[3];

    delay_10us();

    P1       = G_code[4];

    LED8X8G  =  Code4[4];

    delay_10us();

    P1       = G_code[5];

    LED8X8G  =  Code4[5];

    delay_10us();

    P1       = G_code[6];

    LED8X8G  =  Code4[6];

    delay_10us();

    P1       = G_code[7];

    LED8X8G  =  Code4[7]; 

    delay_10us();

  }

}

void h_y()

{

   unsigned char i;

   LED8X8R  = 0x00;

   LED8X8G  = 0xFF;;

  for(i=0;i<200;i++)

  { 

    P1       = G_code[0];

    LED8X8R  = Code5[0];

    delay_10us();

    P1       = G_code[1];

    LED8X8R  = Code5[1];

    delay_10us();

    P1       = G_code[2];

    LED8X8R  = Code5[2];

    delay_10us();

    P1       = G_code[3];

    LED8X8R  = Code5[3];

    delay_10us();

    P1       = G_code[4];

    LED8X8R  = Code5[4];

    delay_10us();

    P1       = G_code[5];

    LED8X8R  = Code5[5];

    delay_10us();

    P1       = G_code[6];

    LED8X8R  = Code5[6];

    delay_10us();

    P1       = G_code[7];

    LED8X8R  = Code5[7];

    delay_10us();

  }

}

void hu_z()

{

  unsigned char i;

   LED8X8G  = 0x00;

  LED8X8R  = 0xFF;

  for(i=0;i<200;i++)

  {

    LED8X8R  = 0x00;

    LED8X8G  = 0xFF;

    P1       = G_code[0];

    LED8X8R  = Code6[0];

    delay_10us();

    P1       = G_code[1];

    LED8X8R  = Code6[1];

    delay_10us();

    P1       = G_code[2];

    LED8X8R  = Code6[2];

    delay_10us();

    P1       = G_code[3];

    LED8X8R  = Code6[3];

    delay_10us();

    P1       = G_code[4];

    LED8X8R  = Code6[4];

    delay_10us();

    P1       = G_code[5];

    LED8X8R  = Code6[5];

    delay_10us();

    P1       = G_code[6];

    LED8X8R  = Code6[6];

    delay_10us();

    P1       = G_code[7];

    LED8X8R  = Code6[7];

    delay_10us();

   

    }

}

void cun()

{

  unsigned char j;

  Get1302(&test_write);         /*把当前的时间值读取到数组SClock[3]中去*/

  if(k) z=y;

    else  x=y ;

  P1_4=1;

  P1_4=0;

  P1_5=1;

  P1_3=1;

  for(j=0;j<8;j++)                                 

    {

      write_iic_data(&test_write[j*8],j*8+k*64,8);           /*向24C02的0-63地址存储单元写数据*/

      delayq();

    }

    k++;k=k%2;

}

void xianshi()

{

    unsigned char j,s;

   

    P1_4=1;

    P1_4=0;

    P1_5=1;

    P1_3=1;

    while(ch451_key==0x42);

    if(ch451_key==0x48){ p=0; s=z;}

    if(ch451_key==0x49){ p=1; s=x;}

   for(j=0;j<8;j++)                                 

    {

      read_iic_data(&test_read[j*8], j*8+p*64,8);     /*向24C02的0-63地址存储单元写数据*/

      delayq();

    }

    for(j=0;j<7;j++)

    SClock[j]=test_read[j] ;

   

   ch451_write(CH451_RESET);

           ch451_init();                     /*对ch451进行初始化*/

            ch451_write(0x403);               /*关看门狗开键盘显示*/

            ch451_write(0x580);               /*以译码方式进行显示*/

   

   disp_date();  

   while(ch451_key==0x48); 

   while(ch451_key==0x49); 

   disp_time();

   while(ch451_key!=0x53);

   ch451_write(CH451_RESET);

           ch451_init();                     /*对ch451进行初始化*/

            ch451_write(0x403);               /*关看门狗开键盘显示*/

            ch451_write(0x580);               /*以译码方式进行显示*/

            if(s<20)       ch451_write(CH451_DIG7|1);

           else if(s<40)  ch451_write(CH451_DIG7|2);

           else if(s<60)  ch451_write(CH451_DIG7|3);

   while(ch451_key!=0x52);  

}

void isr_int1(void) interrupt 0

{

   int i;

  EX0=0;

  for(i=0;i<=2;i++)

  {

            bb=0;

            delayd();

             delayd();

            bb=1;

             delayd();

              delayd();

           }

  EX0=1;

  IE0=0;

}

void timer0() interrupt 1 //定时器

{

 TH0=(65536-50000)/256;

 TL0=(65536-50000)%256;

 count++;

 if(count==100)  //100次大约为5秒

  {

         aa=0;

     delayd();

     delayd();

     aa=1;

     count=0;

  }

}

/********************************************************************

函 数 名:ds1302主程序

功    能:读取ds1302的时间值并通过数码管进行显示?

设    计:蓝海微芯          

***********************************************************************/                   

main()

{

 

  g=1;

  Set1302(WClock);

  P1_3  = 1;

  P1_5  = 1;

  T_RST = 1;

  T_RST = 0;                        /*对ds1302显示的时间初值进行设定*/

  ch451_init();                     /*对ch451进行初始化*/

  ch451_write(0x403);               /*关看门狗开键盘显示*/

  ch451_write(0x580);               /*以译码方式进行显示*/

  ch451_key = 0xff;

  key_flag  = 0;

  flag_time = 1;

  IT0 = 1;

  EX0  =1;

  IT1=1;         

  EX1=1;

 TMOD=0x01;             //定时器

 TH0=(65536-50000)/256; //设置延时

 TL0=(65536-50000)%256;

 EA=1;                  //开中断

 ET0=1;

 TR0=1;

  while(1)

  {

    if(g){

     if(ch451_key==0x5a)          /*判断设置按键是否按下*/

      {

         SET_RTC();

      }

     if(ch451_key==0x59)       /*判断修改时间键是否被按下*/

      {

      SET_TIME();

      }

      if(ch451_key==0x53)       /*判断修改时间键是否被按下*/

      {

      SET_DATE();

      }

      if(ch451_key==0x4b)       /*判断修改时间键是否被按下*/

     {

      cun();

      }

     if(ch451_key==0x42)       /*判断修改时间键是否被按下*/

    {

      xianshi();

    }

    Get1302(&SClock);                          /*获取当前时间值*/

    T_RST=1;

    T_RST=0;

    if(ch451_key==0x58)

    {

      flag_time=~flag_time;

      ch451_key = 0xff;

    }

    if(flag_time)

    {

      disp_time();

   

    }

    else

    {

      disp_date();

     

    }

    }

     else{

            ch451_write(CH451_RESET);

           ch451_init();                     /*对ch451进行初始化*/

            ch451_write(0x403);               /*关看门狗开键盘显示*/

            ch451_write(0x580);               /*以译码方式进行显示*/

            if(y<20)       ch451_write(CH451_DIG7|1);

           else if(y<40)  ch451_write(CH451_DIG7|2);

           else if(y<60)  ch451_write(CH451_DIG7|3);

        }

   if(ch451_key==0x43)          /*判断设置按键是否按下*/

      {

        g++;

        g=g%2;

       while(ch451_key==0x43);

      }

      y++;

      if(y<20)  l_z();

      else if(y<40)  h_y();

      else if(y<60)  hu_z();

      else   y=0;

     }

}

/*********************************************************************/

参考文献

(1)赵伟,MCS-51 系列单片机原理与应用,东北林业大学出版,2006

(2)LJD-SY-5200单片机实验系统实验指导书,北京蓝海微芯科技发展有限公司

(3)谢宋和, 甘勇编,单片机模糊控制系统设计与应用实例,电子工业出版社,1999.7

(4)杜树春编著,单片机C语言和汇编语言混合编程实践,北京航空航天大学出版社,2008.3

   

相关推荐