首页 毕业设计(论文)-基于单片机的万年历的设计

毕业设计(论文)-基于单片机的万年历的设计

举报
开通vip

毕业设计(论文)-基于单片机的万年历的设计毕业设计(论文)-基于单片机的万年历的设计 基于单片机的万年历的设计 摘 要 单片机也称为单片微计算机。单片机在很多领域上都得到了广泛 的应用~其功能也越来越强大。由于单片机操作简便~简单易学~不仅许多 大专、本科院校都开设了有关单片机方面的课程~社会上也出现了很多单片 机爱好者。 本设计就是根据单片机的原理来进行万年历的设计的。设计的主控制器 采用的是一款使用起来较方便的STC89C52RC单片机~采用了带中文字库 的12864液晶作为显示器~时间和日历的功能采用DS1302时钟芯片来实现~ 并采用DS18...

毕业设计(论文)-基于单片机的万年历的设计
毕业设计(论文)-基于单片机的万年历的设计 基于单片机的万年历的设计 摘 要 单片机也称为单片微计算机。单片机在很多领域上都得到了广泛 的应用~其功能也越来越强大。由于单片机操作简便~简单易学~不仅许多 大专、本科院校都开设了有关单片机方面的课程~社会上也出现了很多单片 机爱好者。 本设计就是根据单片机的原理来进行万年历的设计的。设计的主控制器 采用的是一款使用起来较方便的STC89C52RC单片机~采用了带中文字库 的12864液晶作为显示器~时间和日历的功能采用DS1302时钟芯片来实现~ 并采用DS18B20温度传感器来对温度的实时传输。本设计在公历显示完成 的基础上添加了农历显示~使万年历的功能更加完善。本设计在进行公历和 农历的转换时实现得很困难~但最终还是成功了。随着科技的进步~单片机 的功能越来越强大~越来越方便用户的使用。 关键词: 单片机~万年历~DS1302时钟芯片~12864液晶 ABSTRACT Microcontroller is also a single chip microcomputer. It have been used widely in many areas, and its function has become more powerful. Because microcontroller easy in using and learning, many colleges have opened the courses about the Principle and application of microcontroller. There are also many lovers of microcontroller in society. I The design of calendar is based on the principle of single-chip microcomputer. The main controller of my design is a microcontroller of STC89C52RC that used conveniently. And the display is 12846LCD that has chinese word stock. The functions of the time and calendar will be realized by using the clock chip of DS1302. The temperature sensor of DS18B20 is used to transmit temperature. On the basis of the display of calendar, I add a function that can display the lunar in my design. And the functions of calendar looks more perfect. It is very hard to realize the conversion of the Gregorian calendar and the lunar calendar by using program.But I complete it at last. With the progress of technology, the microcontroller function has become more powerful and it will be convenient to the users. Key Words: Microcontroller, Calendar, The clock chip of DS1302, 12864Lcd II 目 录 1. 绪论 ................................................................... 1 1.1 设计目的 ........................................................... 1 1.2 发展现状 ........................................................... 1 1.3 解决问题 ........................................................... 2 2. 硬件系统设计 ........................................................... 3 2.1本设计所需的元器件 ................................................. 3 2.2 STC89C52RC单片机 .................................................. 4 2.2.1单片机主要特性 ................................................ 4 2.2.2单片机引脚说明 ................................................ 5 2.3 MAX232电平转换芯片 ................................................ 7 2.4 DS1302时钟芯片 .................................................... 8 2.5 DS18B20温度传感器 ................................................ 10 2.6 12864液晶 ........................................................ 11 2.7 独立按键 .......................................................... 12 3. 软件设计 .............................................................. 13 3.1阳历与阴历转换 .................................................... 14 3.2 DS1302时钟芯片程序设计 ........................................... 14 3.3 DS18B20程序设计 .................................................. 21 3.4 12864液晶显示程序设计 ............................................ 22 4. 调试 .................................................................. 26 总结 ..................................................................... 30 参考文献 ................................................................. 31 答 谢 .................................................................. 32 附录一 ................................................................... 33 附录二 ................................................................... 34 III 1. 绪论 1.1 设计目的 古人依靠日冕,漏刻记录时间。从古至今,人们的日常生活和工作都离不开对时间的准确把握。而随着科技的发展,电子万年历成为了日渐流行的日常计时工具。目前市场上的万年历功能强大,简单直观,给人们带来很大的方便。 对于万年历设计有很多实现的方法,本设计主要是采用51单片机来实现。自己动手设计与制作可以对硬件的结构和功能有更深的认识,并与软件结合,以达到理论与实践更好的结合,进一步提高综合运用所学知识进行设计的能力。这是对自己大学四年的学习的检验,具有重要的意义。 1.2 发展现状 据了解,目前市场上的电子万年历并不是采用51单片机作为主控制器的,基于单片机的万年历一般是学生和单片机爱好者在进行设计,谈不上占有市场。也许就是这样,研究单片机万年历的人不在少数,并且都在努力。努力不是单方面的,单片机的功能也应该要提高,STC89C52单片机就是这样的例子,其功能虽然没有大幅度提高,但使用起来更方便了。我相信,在不久的将来肯定会有功能更强大成本更低的单片机出现,给我们的设计带来更多的便利。 1 1.3 解决问题 [1]本课题主要通过单片机的功能和应用,利用Keil编程软件和Proteus [2]仿真软件进行设计,并制作实物。设计要达到预期的效果要解决以下问题: [3](1)认真设计好万年历的逻辑原理图; [4](2) 熟练使用C语言,运用Keil编程软件进行软件设计; (3) 在Proteus仿真平台上,对程序进行编译仿真; [5] (4) 认真仔细地对万年历进行组装焊接; (5) 在确认没有问题的硬件实物上进行程序下载调试,以达到预期的效果要求; 2 2. 硬件系统设计 本设计将最小系统与所有用到的模块元件集合在一块万能板上,最小系统和其它模块均由自己动手焊接。首先是在万能板上布局,以达到合理的规划,保证设计的美观性。然后为了保证设计的质量,我将各元件的Vcc电源口与GND接地口再一次规划,用焊锡分别将Vcc电源线和GND接地线固定在了特定的位置,以降低线路的复杂度,让人一目了然。最后各模块元件与单片机之间均用电线直接由引脚处连接(万能板的焊点处)。为了保护各模块元件,本设计使用到的一些重要元件都使用的IC插槽或排母进行焊接,连线(见实物图4.1)。本设计的系统框架如图2.1所示: 12864液晶模块 DS1302时钟 芯片模块 单 片独立按键模块 机 块 DS18B20温度 传感器模块 STC89C52RC 内部时钟、 复位模块 图2.1万年历的系统框架图 2.1本设计所需的元器件 ?单片机:STC89C52RC; ?电平转换芯片:MAX232; ?时钟芯片:DS1302; ?温度传感器:DS18B20; 3 ?液晶:QC12864B汉字图形点阵液晶; ?按钮开关; 2.2 STC89C52RC单片机 [6]本设计采用STC89C52RC单片机,8K字节可编程闪烁存储器。STC89系列单片机是MCS-51系列单片机的派生产品,它们在指令系统、硬件结构和片内资源上与 标准 excel标准偏差excel标准偏差函数exl标准差函数国标检验抽样标准表免费下载红头文件格式标准下载 8052单片机完全兼容。STC89系列是以8051为基核开发出的CMOS工艺单片机,DIP封装系列与8051为pin-to-pin(引脚对引脚)兼容。STC89系列单片机高速,低功耗,其程序写入时可通过串口采用STC-ISP.exe软件下载,不占用用户资源,学习单片机时较好的选择。 2.2.1单片机主要特性 (1) CPU:由运算和控制逻辑组成,同时还包括中断系统和部分外部特殊功能寄存器。中央处理器(CPU)是整个单片机的核心部件,是8位数据宽度的处理器,能处理8位二进制数据或代码,CPU负责控制、指挥和调度整个单元系统协调的工作,完成运算和控制输入输出功能等操作。 (2) RAM:用于存放要读写的数据,如运算的中间结果、最终结果以及欲显示的数据。8051内部有128个8位用户数据存储单元和128个专用寄存器单元,它们是统一编址的,专用寄存器只能用于存放控制指令数据,用户只能访问,而不能用于存放用户数据,所以,用户能使用的RAM只有128个,可存放读写的数据,运算的中间结果或用户定义的字型表。 (3) ROM:8051共有4096个8位掩膜ROM,用于存放用户程序,原始数据或表格。 4 (4) I/O口:四组8位并行I/O口(P0、 P1、P2和P3),既可用作输入,也可用作输出。 (5) T/C定时/计数器:两个16位的可编程定时/计数器,既可以工作在定时模式,也可以工作在计数模式。 (6) 5个中断源的中断控制系统:8051具备较完善的中断功能,有两个外中断、两个定时/计数器中断和一个串行中断,可满足不同的控制要求,并具有2级的优先级别选择。 (7) 全双工串行口:一个全双工UART(通用异步接收发送器)的串行I/O口,用于实现单片机之间或单片机与微机之间的串行通信; (8) 片内振荡器和时钟产生电路,石英晶体和微调电容需要外接。最高振荡频率取决于单片机型号及性能。 2.2.2单片机引脚说明 图2.2 STC89C52RC封装引脚图 5 STC89C52RC的引脚封装和8051的引脚封装是一样的,均采用40Pin封装的双列直插DIP结构。下图是它们的引脚配置,40个引脚中,Pin40为正电源,Pin20为地线;外置石英振荡器的时钟线Pin18和Pin19两根;4组8位共32个I/O口(P0、P1、P2和P3),中断口线与P3口线复用。本设计只是实现简单的读写功能,不需要用到I/O口的第二功能。这里就不对单片机的引脚作出太详细的介绍,简单介绍一些专用引脚,其它引脚用到时再介绍。单片机引脚封装如图2.2所示。 (1) Pin9:RST复位信号复用脚,当STC89C52RC通电,时钟电路开始工作,在RST引脚上出现24个时钟周期以上的高电平,系统即初始复位。初始化后,程序计数器PC指向0000H,P0-P3输出口全部为高电平,堆栈指针写入07H,其它专用寄存器被清“0”。RST由高电平下降为低电平后,系统即从0000H地址开始执行程序。然而,初始复位不改变RAM(包括工作寄存器R0-R7)的状态,STC89C52RC的初始态。 STC89C52RC的复位方式可以是自动复位,也可以是手动复位(按键电平复位和按键脉冲复位)。此外,RST还是一个复引脚,Vcc掉电期间,此引脚可接上备用电源,以保证单片机内部RAM的数据不丢失。本设计采用的是手动复位电路:按键电平复位。复位电路如图2.3所示。 图2.3 按键电平复位电路 (2) Pin18、19:XTAL1和XTAL2时钟产生电路引脚,这里使用的石英晶体振荡频率为11.0592MHz。时钟振荡电路如图2.4所示。 6 图2.4 时钟振荡电路 PSEN(3) Pin29:当访问外部程序存储器时,此引脚输出负脉冲选通信号,PC的16位地址数据将出现在P0和P2口上,外部程序存储器则把指令数据放到P0口上,由CPU读入并执行。 PROG(4) Pin30:ALE/当访问外部程序器时,ALE(地址锁存)的输出用于锁存地址的低位字节。而访问内部程序存储器时,ALE端将有一个1/6时钟频率的正脉冲信号,这个信号可以用于识别单片机是否工作,也可以当作一个时钟向外输出。它还有一个特点是,当访问外部程序存储器时,ALE会跳过一个脉冲。 (5) Pin31:/Vpp程序存储器的内外部选通线,对于STC89C52RCEA 来说,内置有8kB的程序存储器,当EA为高电平并且程序地址小于8kB时, EA读取内部程序存储器指令数据,而超过8kB地址则读取外部指令数据。如为低电平,则不管地址大小,一律读取外部程序存储器指令。 当使用到外部程序存储器时,要使用到一些特定功能的引脚,本设计中没有使用外部存储器。 2.3 MAX232电平转换芯片 当我们用STC-ISP.exe这个软件给STC89C52RC下载程序时,采用的是计算机串口(RS-232电平:高 -12V 低+12V)通信,所以计算机与单片机 [7]之间进行通信时需要电平转换芯片MAX232。 7 MAX232含有两个RS-232发送驱动器和接收驱动器,其中发送器的输入为TTL/CMOS电平,输出为RS-232电平。MAX232接收器的输入为RS-232电平,输出为TTL/CMOS电平。不使用的输入输出端可以悬空。MAX232的工作温度范围为0?至70?。 MAX232的Pin14(T1OUT)引脚与串行口的2引脚连接,Pin13(R1IN)引脚与串行口的3引脚连接。Pin12(R1OUT)引脚和Pin11(T1IN)引脚分别与单片机的P3.0 /RXD(串行输入口)、P3.1 /TXD(串行输出口)连接。这样单片机所需要的程序就可以从计算机下载了。计算机与单片机通信如图2.5所示。 图2.5 计算机与单片机通信连接图 2.4 DS1302时钟芯片 [8]市场上可以选择的时钟芯片很多,功能也不尽相同,价格各异。DS1302 是美国DALLAS 公司推出的一种串行接口实时时钟芯片。芯片内部具有可编程日历时钟和31个字节的静态RAM,它可以对年、月、日、星期、时、分、秒进行计时,日历时钟可自动进行闰年补偿,及时准确,接口简单,使用方便,工作电压范围宽,功耗低,芯片自身还具有对备份电池进行涓流 8 充电功能,可以有效地延长备份电池的使用寿命。DS1302引脚封装如图2.6所示。 图2.6 DS1302封装引脚图 DS1302时钟芯片的引脚功能如下: (1) Pin1:Vcc2为主电源。 (2) Pin8:Vcc1为后备电源。在主电源关闭的情况下,也能保持时钟的连续运行。DS1302由Vcc1或Vcc2两者中的较大者供电。当Vcc2大于Vcc1,0.2V时,Vcc2给DS1302供电。当Vcc2小于Vcc1时,DS1302由Vcc1供电。 (3) Pin2、3:X1、X2是振荡源,外接32.768kHz晶振。 RSTRST(4) Pin5:是复位/片选线,通过把输入驱动置高电平来启动 RSTRST所有的数据传送。输入有两种功能:首先,接通控制逻辑,允许 RST地址/命令序列送入移位寄存器;其次,提供终止单字节或多字节数 RST据的传送手段。当为高电平时,所有的数据传送被初始化,允许对 RSTDS1302进行操作。如果在传送过程中置为低电平,则会终止此次数 RST据传送,I/O引脚变为高阻态。上电运行时,在Vcc>2.0V之前,必 RST须保持低电平。只有在SCLK为低电平时,才能将置为高电平。 (5) Pin6:I/O为串行数据输入输出端(双向),在控制指令字输入后的下一个SCLK时钟的上升沿时,数据被写入DS1302,数据输入从低位即位0开始。同样,在紧跟8位的控制指令字后的下一个SCLK脉冲 9 的下降沿读出DS1302的数据,读出数据时从低位0位到高位7。 (6) Pin7:SCLK为时钟输入端。 DS1302与单片机连接图如图2.7所示,具体的读写操作在第3部分软件设计中介绍。 2.7(a) DS1302 2.7(b) 单片机 图2.7 单片机与DS1302的连接引脚图 2.5 DS18B20温度传感器 温度是一个基本的物理量,自然界中的一切过程与温度密切相关。设计 [9]里加入DS18B20温度传感器可以使万年历的功能更齐全,可以随时了解温度的变化。DS18B20的封装引脚如图2.8和单片机与DS18B20的连接如图2.9。DS18B20温度传感器的I/O(DQ)引脚连接的是单片机的P3.3引脚。 图2.8 DS18B20封装引脚图 图2.9 单片机与DS18B20的连接图 DS18B20温度传感器是美国DALLS公司推出的DS1820的替代产品,其主要特性如下。 (1) 适应电压范围更宽,电压范围:3.0,5.5V,Vcc为外接供电电源输 10 入端,在寄生电源方式下可由数据线供电,GND为电源地。 (2) 独特的单线接口方式,DS18B20在与单片机连接时仅需要一个引脚(DQ)即可实现微处理器与DS18B20的双向通讯。 (3) DS18B20在使用中不需要任何外围元件,全部 传感元件及转换电路集成在形如一只三极管的集成电路内。 (4) 温范围:55?,,125?,在-10,+85?时精度为?0.5?。 (5) 可编程的分辨率为9,12位,对应的可分辨温度分别为0.5?、0.25?、0.125?和0.0625?,可实现高精度测温。 (6) 负压特性:电源极性接反时,芯片不会因发热而烧毁, 但不能正常工作。 2.6 12864液晶 在本设计中,要显示阳历、阴历、星期、时间、生肖、温度及节日提醒, [10]因此对显示器的要求较高。在这里我采用QC12864B汉字图形点阵液晶作为显示模块,它可显示汉字及图形,内置 8192 个中文汉字(16X16 点阵)、128 个字符(8X16 点阵)。可同时显示汉字个数为4x8=32个,同时显示字符的个数为4x16=64个。它与单片机连接如图2.10所示。 2.10(a) LCD12864引脚 2.10(b) 单片机 图2.10 12864液晶引脚电路图 11 在进行进行这个模块的设计时,考虑到12864液晶的显示方向,而单片机的P2引脚刚好与其它的引脚排列顺序相反(可看图2.2),所以本设计中单片机与液晶进行数据传输时,用单片机的P2引脚与液晶的8位双向三态数据线(DB0-DB7)进行连接,这样大大简化了线路的复杂度。 2.7 独立按键 本设计采用的4个独立按键,分别是:“设置”、“确认”、“加”、“减”。判断“设置”键是否按下及第几次按下可以分别对日期、时间、星期选定,然 ”、“减”即可进行设置,“确认”键按下后,新数据即写入时钟芯片。后按“加 例如,“设置”键按1下,即可以对年份进行设置,按4下,即是可以对小时进行设置。其电路设计如图2.11所示。 图2.11 时间设置电路 12 3. 软件设计 本设计利用Keil(C51) 软件进行程序的编写。C语言是一种编译型程序设计语言,它兼顾了许多种高级语言的特点,并且具备汇编语言的功能,C 语言可以像汇编语言一样对位、字节和地址进行操作。目前,使用C语言进行程序设计已经成为软件开发的一个主流。C语言程序本身不依赖于机器硬件系统,基本上不作修改就可将程序从不同的单片机中移植过来。用C语言开发系统可以大大缩短开发周期,明显增强程序的可读性,便于改进、扩充和移植。而针对8051的C语言日趋成熟,成为了专业化的实用高级语言。虽然汇编语言效率高,对硬件的可操控性更强,体积小,但是不易维护,可移植性差。本设计程序实现了阳历日期、阴历日期、时间、星期、温度、节日提醒等功能。图3.1是主程序流程图。 开始 LCD、DS1302、DS18B20及相关变量初始化 调液晶显示程序 N 设置键是否按下 Y 调用设置子程序 刷新 图3.1 主程序流程图 13 3.1阳历与阴历转换 阳历与阴历的转换,这部分程序是参考杜阳老师的程序,程序中总共有199(1901-2099)年的阳历对应阴历数据。 阳历对应的阴历数据(每年占用单片机三字节) 格式 pdf格式笔记格式下载页码格式下载公文格式下载简报格式下载 :第一字节BIT7-4位表示闰月月份,为0,则无闰月;BIT3-0位对应阴历第1-4月的大小。第二字节BIT7-0位对应阴历第5-12月大小。第三字节BIT7位表示阴历第13月大小(月分对应的位为1,表示农历月大(30天)为0表示小(29天));第三字节BIT6-5位表示春节的阳历月份,BIT4-0位表示春节公历日期。 例:阳历2011年对应阴历2011年数据为:0X0B,0X4A,0X43。 0X0B和0X4A的二进制数表示为:00001011,01001010,这两个字节表示阴历2011年没有闰月,也就是没有第十三个月。一,三,四,六,九,十一月都是月大(30天),其它都是月小(29天)。 0X43的二进制数表示为:01000011,这里的BIT6-5位为10(2位可表示4个月),表示阴历2011年的春节在阳历2011年的2月份。BIT4-0位为00011(00000-11111,5位最大数为31),表示3日。整个字节就表示阴历2011年的春节在阳历2011年的2月3日。 3.2 DS1302时钟芯片程序设计 在本设计中,要实现万年历的功能,DS1302时钟芯片是少不得的,虽然只用单片机也可以实现,但是将会造成时间误差较大,不好调节。用DS1302时钟芯片不仅可以大大的避免那样的问题,还可以减小程序的编写量。这个模块是本设计中是很重要的模块。如图3.2是DS1302时钟芯片工 14 作的流程图。 开始 相关变量初始化 DS1302去保护 复位端产生复位端产生 一个高电平 一个高电平 写DS1302地址 写DS1302地址 延时一段时间 延时一段时间 将该地址数据读出 向该地址写数据 地址增加 地址增加 N N 数据写完否 数据读完否 Y Y 显示数据 图3.2 DS1302时钟芯片工作流程图 通过流程图我们对DS1302时钟芯片是如何工作的有了个大概的了解, 现在进一步了解DS1302时钟芯片关于日历、时间的存储器,先看表3.1。 15 表3.1 读写寄存器地址 读寄存器 写寄存器 bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0 范围 81H 80H CH 00~59 10秒 秒 83H 82H 00~59 10分 分 10 时 85H 84H 1/0 0 1~12/ 时 AM/PM 0~23 10日 87H 86H 0 0 1~31 日 0 10月 89H 88H 0 0 1~12 月 0 0 8BH 8AH 0 0 0 1~7 星期 10年 8DH 8CH 0~99 年 8FH 8EH WP 0 0 0 0 0 0 0 — 由表可以看出存放秒到年的7个寄存器的地址是固定且有规律的。写寄存器都是偶数(80H~8Ch),读寄存器都是奇数(81H~8Ch),存放的数据格式为BCD码形式。 秒寄存器(写80H、读81H)的bit7位定义为时钟暂停标志(CH)当该位置为1时,时钟振荡器停止,DS1302时钟芯片处于低功耗状态,为0时,时钟开始运行。 时寄存器(写84H、读85H)的bit7位为1时,DS1302时钟芯片运行于12小时模式,bit7位为0时,DS1302时钟芯片运行于24小时模式(本设计是24小时模式)。当运行于12小时模式时,bit5位为0时,表示AM,bit5位为1时,表示PM。当运行于24小时模式时,bit5位和bit4位一起表示24小时的10位。 控制寄存器(写8EH、读8FH)的bit7位是写保护位(WP),其它7位 16 均置0,在任何的对时钟和RAM的写操作之前,WP位必须为0。当WP位为1时,写保护位防止对任一寄存器的写操作。也就是说在电路上电的初始态WP为1,这时不能改写表中任何一个时间寄存器,只有将WP改写为0,才能进行寄存器的写操作。 下面举例整体说明: void InitDS1302() { WriteDs1302(0x8e,0x00); //控制命令,bit7位WP为0,写入不保护指令 delay(5); //延时给DS1302准备时间,以下相同 WriteDs1302(0x80,0x30); //写入秒 30秒 delay(5); WriteDs1302(0x82,0x30); //写入分钟 30分 delay(5); WriteDs1302(0x84,0x12); //写入小时 12点 delay(5); WriteDs1302(0x86,0x15); //写入日期 15日 delay(5); WriteDs1302(0x88,0x05); //定入月分 5月 delay(5); WriteDs1302(0x8a,0x07); //写入星期日 delay(5); WriteDs1302(0x8c,0x11); //写入年份11年 delay(5); 17 WriteDs1302(0x8e,0x80); //控制命令,bit7位WP为1,禁止写操作 } DS1302写操作时序: RST只要有数据操作,就要拉高,否则不能操作。SCLK是一个命令字节和一个写数据字节的时钟脉冲(上升沿),前8个是负责命令字节的,后8个负责数据字节。在命令字节第8个时钟之后紧接着的这个上升沿(D0),数据就会写入。I/O就是要传输的一个命令字节(数据要存入的地址,如:秒的写寄存器地址80H)和一个数据字节,数据由低位(D0)开始写入。 程序实现说明: (1) 此程序为写字节程序: void WriteDs1302Byte(unsigned char temp) { unsigned char i; for (i=8;i>0;i--) { DS1302_IO=temp&0x01; //取出temp的第0位数据写入1302 DS1302_CLK=0; 将SCLK置于低电平,为上升沿写入做准备 DS1302_CLK=1; //上升沿写入数据 temp>>=1; 18 } } (2) 此程序为写数据程序: void WriteDs1302( unsigned char address,unsigned char dat ) { DS1302_RST=0; //禁止数据传递 DS1302_CLK=0; //确保写数据前SCLK被拉低 DS1302_RST=1; //启动数据传输 delay(1); WriteDs1302Byte(address); //写地址 WriteDs1302Byte(dat); //写数据 DS1302_RST=0; //禁止数据传递 } DS1302读操作时序: SCLK在读操作时,在写命令字节第8个时钟之后紧接着的变成了下降沿,开始读数据。 程序实现说明: (1) 读字节程序: unsigned char read_byte() { 19 unsigned char i; for(i=8;i>0;i--) { if(DS1302_IO) temp0=temp0|0x80; //将数据取出,写在temp0的最高位 DS1302_CLK=1; //将SCLK置于高电平,为下降沿读出做准备 DS1302_CLK=0; //拉低SCLK,形成脉冲下降沿 temp0=temp0>>1; } return temp0; } (2) 读取数据程序: unsigned char ReadDs1302(unsigned char address) { unsigned char temp; DS1302_RST=0; DS1302_CLK=0; DS1302_RST=1; WriteDs1302Byte(address); //写入命令字 temp=read_byte(); //读出数据 DS1302_RST=0; //禁止数据传递 DS1302_CLK=1; //将时钟电平置于已知状态 return temp; 20 } 3.3 DS18B20程序设计 DS18B20温度传感器在本设计中负责实时传输当前温度,在液晶上显示 出来。DS18B20温度传感器操作简单,一个引脚就可以进行双向传输。这里 就简单说一下,它是如何工作的,先看看DS18B20工作流程图,再看看在程序上的实现。 DS18B20工作流程图如图3.3所示。 开始 发出读温度命令 DS18B20初始化 延时 跳过ROM匹配 数据读出 发出温度转换命令 温度转换常数 延时 显示数据 图3.3 DS18B20工作流程图 程序上的实现: int GetTemp() { float tt; unsigned char a,b; 21 DS18B20Init(); //DS18B20初始化 TempWriteByte(0xcc); // 跳过ROM匹配 TempWriteByte(0x44); //发出温度转换命令 delayb(100); DS18B20Init(); //DS18B20初始化 TempWriteByte(0xcc); //跳过ROM匹配 TempWriteByte(0xbe); //发出读温度命令 delayb(200); a=TempRead(); //数据读出 b=TempRead(); temp=b; temp<<=8; temp=temp|a; tt=temp*0.0625; //温度转换常数 temp=tt*10+0.5; return temp; } DS18B20温度传感器就介绍到这里了,具体的读写操作就不介绍了,原理和DS1302时钟芯片的读写操作差不多,可以参考,当然DS18B20的资料介绍得更清楚一些。 3.4 12864液晶显示程序设计 下面先看一下12864液晶并行连接的读写时序: 22 LCD的写时序: RS引脚有H(高)L(低)两种状态,RS=H时,单片机将进行写数据操作;RS=L时,单片机将进行写指令操作。R/W引脚要与RS的状态结合,具体操作看表3.2,R/W在这里不管RS是高低都是进行写操作。E引脚为使能信号,E=H?L时,配合R/W进行写数据或指令;E=H时,配合R/W进行读数据或读指令。程序操作时注意延时,延时的大小很重要。 LCD的读时序: 读时序与写时序差不多,区别是R/W在读操作时是拉高的,与写操作相反,其它都一样。同样要注意延时。 12864液晶与单片机通信时,可以采用串行数据通信和并行数据通信,我这里将液晶的DB0~DB7引脚对应的接上单片机的P2.0~P2.7引脚,因此 23 采用的是并行数据通信。 部分基本指令介绍见表3.2。 表3.2 部分基本指令介绍 RS R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0 清除显示 L L L L L L L L L H 显示状态 L L L L L L H D C B 写RAM H L D7 D6 D5 D4 D3 D2 D1 D0 读RAM H H D7 D6 D5 D4 D3 D2 D1 D0 读忙状态 L H BF AC6 AC5 AC4 AC3 AC2 AC1 AC0 表中的RS=H表示DB0-DB7为显示数据,RS=L表示DB0-DB7为显示指令数据。R/W=H且E(使能信号)=H表示数据被读到DB0-DB7,RW=L且E=H?L表示DB0-DB7的数据被写到RAM。DB0-DB7表示数据口。H表示高电平,L表示低电平。 (1) 清除显示功能:清除显示屏幕,把DDRAM位地址计数器调整为“00H”。 (2) 显示状态功能:D=1表示整体显示开(ON),C=1表示游标开,B=1表示游标位置开。 (3) 写RAM(写资料到RAM)功能:写资料到内部的RAM。(DDRAM/CGRAM/TRAM/GDRAM)。 (4) 读RAM(读出RAM的值)功能:从内部RAM读取资料(DDRAM/CGRAM/TRAM/GDRAM)。 (5) 读忙状态(读取忙碌状态和位址)功能:读取忙碌状态(BF)可以确认内部动作是否完成,同时可以读出位址计数器(AC)的值。 字符显示RAM在液晶模块中的地址为80H~9FH,字符显示的RAM的地址与32个字符显示区域有着一一对应的关系,其对应关系如下表3.3所示。 24 表3.3 RAM地址与显示区域对应关系 80H 81H 82H 83H 84H 85H 86H 87H 90H 91H 92H 93H 94H 95H 96H 97H 88H 89H 8AH 8BH 8CH 8DH 8EH 8FH 98H 99H 9AH 9BH 9CH 9DH 9EH 9FH 由表3.3可以看出,液晶显示内容时可以在相应的地址上进行操作,要注意选择显示的地址,避免地址重复选择,不然可能出现显示混乱或者不显示的结果。 25 4. 调试 在本设计中采用的是带有中文字库的的液晶显示器,而Proteus仿真软件里的库里没有带中文字库的液晶,功能也与带中文字库的有一定区别,所以在采用Proteus仿真软件进行调试时出现了很多问题,比如不显示或显示乱码等等。因此我主要采用硬件直接进行调试。 直接采用硬件调试难度不比先采用软件调试的难度低,直接采用硬件进行调试一定要保证硬件是没问题的,所以硬件焊接时一定要小心,尽量做到一次能完成,不然修改起来会很麻烦,元件也容易损坏。本设计是焊接与调试一起进行的,每一个元件焊接好后都用万用表进行测量,确认每条线路都通路。每个模块完成后要下载程序进行调试。例如:计算机与单片机通信用到的串行口模块,焊接好以后先进行程序下载,如果可以下载成功,说明该模块没问题。测试单片机各引脚是否正常,我是用点亮发光二极管进行测试,下载程序逐个驱动看发光二极管是否点亮,点亮说明单片机引脚没问题。各模块的测试就下载相应模块的程序进行测试。 本设计是要实现万年历的功能,让其内容在液晶屏上显示出来的。所以,我首先要确定液晶屏能正常显示,然后将各模块要显示的内容单独在液晶上显示出来,最后再将所有模块的内容在液晶上显示出来。若在硬件没问题时还不能按照设计要求正常显示,就得从程序上入手了。调试程序同样要很小心,一点小问题就可以导致不能正常显示。万年历用到的各模块组装完成后 26 的实物图如图4.1所示。 图4.1 万年历实物图 下面是一些调试的过程: 由图4.2可以看出,液晶显示的结果不正确,不符合设计的要求。程序里我写入的日期是2011年5月24日,时间是12:50:30,图4.2并没有正确显示,温度也没有显示。 27 图4.2 错误结果 这样的错误其实都是一些细节造成的,温度不显示的原因是有的地方是绝不可以延时的,延时很重要,但不该延时的地方也要注意。日期和时间为什么没有按要求正确显示,还出现乱码,出现这样的结果,我想应该是时钟数据没能够正确的写入时钟芯片,结合时钟芯片工作的写时序,可以发现问题。看以下程序: void WriteDs1302Byte(unsigned char temp) { unsigned char i; for (i=8;i>0;i--) { DS1302_IO=temp&0x01; //取出temp的第0位数据写入1302 DS1302_CLK=1; //上升沿写入数据 28 DS1302_CLK=0; //拉低SCLK,形成脉冲 temp>>=1; } } 问题出现在DS1302_CLK=1; DS1302_CLK=0;这两句程序里,把这两句程序的顺序换一下就好了。根据时钟芯片写时序操作可以知道,数据或指令写入时是上升沿有效,SCLK必须先拉低,再拉高才能形成上升沿。这里可以看出程序的先后顺序同样重要,我们不要忽视这些小细节。图4.3是调试好的结果。 图4.3 正确结果 29 总结 本设计在进行Proteus软件仿真时,没有成功,因为Proteus软件的元件库里没有带中文字库的液晶,要想成功仿真,需要对程序进行大范围的修改,最终决定放弃Proteus软件仿真,直接采用硬件进行调试。 硬件的线路设计和元件组装都花了较长的时间,虽然以前也焊接过一些东西,但不能说那就是有经验。以前焊接的东西都是有现成的电路板,只是把元件对应的组装起来就基本没问题了。在本设计中,并没有现成的电路板,而是用万能板自己搭线组装。在整个硬件焊接到一半时,才发现将液晶屏的引脚弄反了,只能进行修改。 在进行程序调试时,一些大问题比较容易找出并改正,而一些小的问题就比较麻烦,因此一定要注意细小的问题。 本设计总体最终实现了阳历及阴历的日期显示、时间、星期、温度的显示,独立按键调节时间、日期、星期等功能。本设计整个过程还算顺利,没出现太大的问题,不足的地方是没有整点报时和闹铃的功能。 30 参考文献 [1]郑郁正.单片机原理及应用.成都:四川大学出版社,2003.9. [2]王怀平,王仁波,胡开明.Proteus仿真设计基于单片机AT89C51的电子万年历[J].科技 广场,2008,10:197-198. [3]潘永雄,沙河.电子线路CAD实用 教程 人力资源管理pdf成真迷上我教程下载西门子数控教程protel99se入门教程fi6130z安装使用教程 (第三版).西安:西安电子科技大学出版社, 2007.7(2009.8重印). [4]谭浩强.C程序设计(第三版).北京:清华大学出版社,2005(2007重印). .北京:[5](日)晶体管技术编辑部编,杨洋等译,宗光华校.电子技术:原理?制作?实验 科学出版社,2005. [6]赵全利,肖兴达.单片机原理及应用教程(第二版).北京:机械工业出版社,2007.7. [7]雷伏容,张小林,崔浩.51单片机常用模块设计查询 手册 华为质量管理手册 下载焊接手册下载团建手册下载团建手册下载ld手册下载 .北京:清华大学出版社, 2010.01. [8]肖炎根,舒望.基于实时时钟芯片的电子万年历的设计[J].电子技术,2007,Z3:91-94. [9]刘畅生,宣宗强,雷振亚等.传感器简明手册及应用电路——温度传感器分册( 上册 三年级上册必备古诗语文八年级上册教案下载人教社三年级上册数学 pdf四年级上册口算下载三年级数学教材上册pdf ).西安:电子科技大学出版社,2005.11. [10]李宏,张家田.液晶显示器件应用技术.北京:机械工业出版社,2004.2. [11]刘德全.可调家用万年历的单片机原理[J].宁夏师范学院报,2008,03:95-98. [12]姜志海、刘连鑫.单片微型计算机原理及应用.机械工业出版社,2007.1. [13]冯育才、雷思孝.单片机系统设计及工程应用.西安电子科技大学出版社,2005.5. [14]李珍、付植桐.单片机原理与应用技术.清华大学大学出版社,2004.2. [15]沈红卫.基于单片机的智能系统设计与实现.电子工业出版社,2005.9. 31 答 谢 在这里我要首先感谢我的指导老师——王书志老师,本设计在王老师的悉心指导下完成的。从选题到设计完成王老师都是严格要求,帮助我解决各种难题。王老师对学术的严谨和精益求精的工作作风给我留下了深刻的印象,受益匪浅。其次我要感谢在大学四年时间里教我专业课和帮助过我的所有老师,没有老师们教的专业知识,我想完成这设计是不可能的。最后我要感谢梁丹丹,杜超,任前山几位在本设计中给予我帮助的同学朋友;由衷感谢吴志明,杨金澎,董飞飞这几位舍友,感谢他们在我困难的时候给予我鼓励和帮助。谢谢~ 32 附录一 万年历硬件电路原理图: 33 附录二 源程序: #include #include #define Busy 0x80 #define LCD_Data P2 unsigned char sec,min,hour,day,month,year,cen,week; unsigned char LunarMonth,LunarDay,LunarYear; unsigned char next; int temp; unsigned char temp0; bit c_moon; bit cenbit=1; bit w; sbit LCD_RS=P1^7; sbit LCD_RW=P1^6; sbit LCD_E=P1^5; sbit PSB=P1^4; //H:并口方式 L:串口方式 sbit rst=P1^3; sbit DS1302_CLK=P1^0; //位定义DS1302芯片的接口,时钟输出端口 sbit DS1302_IO=P1^1; //位定义DS1302芯片的接口,数据输出端口 sbit DS1302_RST=P1^2; //位定义DS1302芯片的接口,复位端口 sbit DS18B20=P3^3; //位定义DS18B20芯片的接口,数据传输端口 void DisplayShengXiao(void); //生肖 sbit SetKey=P3^4; //按键功能:设置 sbit SureKey=P3^5; //按键功能:确认 sbit PlusKey=P3^6; //按键功能:加 sbit ReduceKey=P3^7; //按键功能:减 void delay(unsigned int a) //延时 1MS/次 { unsigned char i; while(--a) { for(i=0;i<125;i++) ; } } void delayb(unsigned int count) { while(count--); } /***************DS18B20初始化函数***************/ 34 unsigned char DS18B20Init() { unsigned char x; DS18B20=1; delayb(2); DS18B20=0; delayb(80); DS18B20=1; delayb(5); x=DS18B20; delayb(20); return x; } /***************DS18B20读函数***************/ unsigned char TempRead(void) { unsigned char i,dat; DS18B20=1; delayb(1); for(i=0;i<8;i++) { DS18B20=0; dat=dat>>1; DS18B20=1; if(DS18B20) dat=dat|0x80; delayb(4); DS18B20=1; delayb(2); } return dat; } /***************DS18B20写字节函数***************/ void TempWriteByte(unsigned char dat) { unsigned int i; DS18B20=1; delayb(2); for(i=0;i<8;i++) { DS18B20=0; DS18B20=dat&0x01; 35 delayb(8); DS18B20=1; dat=dat>>1; delayb(2); } } /************DS18B20读取温度函数**************/ int GetTemp() { float tt; unsigned char a,b; DS18B20Init(); //DS18B20初始化 TempWriteByte(0xcc); // 跳过ROM匹配 TempWriteByte(0x44); //发出温度转换命令 delayb(100); DS18B20Init(); //DS18B20初始化 TempWriteByte(0xcc); //跳过ROM匹配 TempWriteByte(0xbe); //发出读温度命令 delayb(200); a=TempRead(); //数据读出 b=TempRead(); temp=b; temp<<=8; temp=temp|a; tt=temp*0.0625; //温度转换常数 temp=tt*10+0.5; return temp; } /********************************************* 向1302写一个字节数据 *********************************************/ void WriteDs1302Byte(unsigned char temp) { unsigned char i; for (i=8;i>0;i--) { DS1302_IO=temp&0x01;//取出temp的第0位数据写入1302 DS1302_CLK=0; //拉低SCLK,为脉冲上升沿写入数据做好准备 DS1302_CLK=1; //上升沿写入数据 temp>>=1; } } 36 /*********************************************** 根据命令字,向1302写一个字节数据 入口参数:address,储存命令字;dat,储存待写的数据 ***********************************************/ void WriteDs1302( unsigned char address,unsigned char dat ) { DS1302_RST=0; //禁止数据传递 DS1302_CLK=0; //确保写数居前SCLK被拉低 DS1302_RST=1; //启动数据传输 delay(1); WriteDs1302Byte(address); //写入命令字 WriteDs1302Byte(dat); //写数据 DS1302_RST=0; //禁止数据传递 } /*************从1302读一个字节**************/ unsigned char read_byte() { unsigned char i; for(i=8;i>0;i--) { if(DS1302_IO) temp0=temp0|0x80; //将数据取出,写在temp0的最高位 DS1302_CLK=1; //将SCLK置于高电平,为下降沿读出做准备 DS1302_CLK=0; //拉低SCLK,形成脉冲下降沿 temp0=temp0>>1; } return temp0; } /*****根据命令字,从1302读取数据*****/ unsigned char ReadDs1302(unsigned char address) { unsigned char temp; DS1302_RST=0; DS1302_CLK=0; DS1302_RST=1; WriteDs1302Byte(address); //写入命令字 temp=read_byte(); //读出数据 DS1302_RST=0; //禁止数据传递 DS1302_CLK=1; //将时钟电平置于已知状态 return temp; } void InitDS1302() { 37 WriteDs1302(0x8e,0x00); //控制命令,WP为0,写入不保护指令 delay(5); WriteDs1302(0x8c,0x11); //写入年份11年 delay(5); WriteDs1302(0x8a,0x02); //写入星期二 delay(5); WriteDs1302(0x88,0x05); //写入月分 5月 delay(5); WriteDs1302(0x86,0x24); //写入日期 24日 delay(5); WriteDs1302(0x84,0x12); //写入小时 12点 delay(5); WriteDs1302(0x82,0x50); //写入分钟 50分 delay(5); WriteDs1302(0x80,0x30); //写入秒 30秒 delay(5); WriteDs1302(0x8e,0x80); //控制命令,WP为1,禁止写操作 } /*阳历对应的阴历数据,每年三字节, 格式第一字节BIT7-4位表示闰月月份,为0,则无闰月,BIT3-0对应阴历第1-4月的大小, 第二字节BIT7-0对应阴历第5-12月大小,第三字节BIT7表示阴历第13月大小 月分对应的位为1,表示农历月大(30天)为0表示小(29天) 第三字节BIT6-5表示春节的公历月份,BIT4-0表示春节公历日期 */ code unsigned char YearCode[597]= { 0x04,0xAe,0x53, //1901 0 0x0A,0x57,0x48, //1902 3 0x55,0x26,0xBd, //1903 6 0x0d,0x26,0x50, //1904 9 0x0d,0x95,0x44, //1905 12 0x46,0xAA,0xB9, //1906 15 0x05,0x6A,0x4d, //1907 18 0x09,0xAd,0x42, //1908 21 0x24,0xAe,0xB6, //1909 0x04,0xAe,0x4A, //1910 0x6A,0x4d,0xBe, //1911 0x0A,0x4d,0x52, //1912 0x0d,0x25,0x46, //1913 0x5d,0x52,0xBA, //1914 0x0B,0x54,0x4e, //1915 0x0d,0x6A,0x43, //1916 0x29,0x6d,0x37, //1917 0x09,0x5B,0x4B, //1918 38 0x74,0x9B,0xC1, //1919 0x04,0x97,0x54, //1920 0x0A,0x4B,0x48, //1921 0x5B,0x25,0xBC, //1922 0x06,0xA5,0x50, //1923 0x06,0xd4,0x45, //1924 0x4A,0xdA,0xB8, //1925 0x02,0xB6,0x4d, //1926 0x09,0x57,0x42, //1927 0x24,0x97,0xB7, //1928 0x04,0x97,0x4A, //1929 0x66,0x4B,0x3e, //1930 0x0d,0x4A,0x51, //1931 0x0e,0xA5,0x46, //1932 0x56,0xd4,0xBA, //1933 0x05,0xAd,0x4e, //1934 0x02,0xB6,0x44, //1935 0x39,0x37,0x38, //1936 0x09,0x2e,0x4B, //1937 0x7C,0x96,0xBf, //1938 0x0C,0x95,0x53, //1939 0x0d,0x4A,0x48, //1940 0x6d,0xA5,0x3B, //1941 0x0B,0x55,0x4f, //1942 0x05,0x6A,0x45, //1943 0x4A,0xAd,0xB9, //1944 0x02,0x5d,0x4d, //1945 0x09,0x2d,0x42, //1946 0x2C,0x95,0xB6, //1947 0x0A,0x95,0x4A, //1948 0x7B,0x4A,0xBd, //1949 0x06,0xCA,0x51, //1950 0x0B,0x55,0x46, //1951 0x55,0x5A,0xBB, //1952 0x04,0xdA,0x4e, //1953 0x0A,0x5B,0x43, //1954 0x35,0x2B,0xB8, //1955 0x05,0x2B,0x4C, //1956 0x8A,0x95,0x3f, //1957 0x0e,0x95,0x52, //1958 0x06,0xAA,0x48, //1959 0x7A,0xd5,0x3C, //1960 0x0A,0xB5,0x4f, //1961 0x04,0xB6,0x45, //1962 0x4A,0x57,0x39, //1963 39 0x0A,0x57,0x4d, //1964 0x05,0x26,0x42, //1965 0x3e,0x93,0x35, //1966 0x0d,0x95,0x49, //1967 0x75,0xAA,0xBe, //1968 0x05,0x6A,0x51, //1969 0x09,0x6d,0x46, //1970 0x54,0xAe,0xBB, //1971 0x04,0xAd,0x4f, //1972 0x0A,0x4d,0x43, //1973 0x4d,0x26,0xB7, //1974 0x0d,0x25,0x4B, //1975 0x8d,0x52,0xBf, //1976 0x0B,0x54,0x52, //1977 0x0B,0x6A,0x47, //1978 0x69,0x6d,0x3C, //1979 0x09,0x5B,0x50, //1980 0x04,0x9B,0x45, //1981 0x4A,0x4B,0xB9, //1982 0x0A,0x4B,0x4d, //1983 0xAB,0x25,0xC2, //1984 0x06,0xA5,0x54, //1985 0x06,0xd4,0x49, //1986 0x6A,0xdA,0x3d, //1987 0x0A,0xB6,0x51, //1988 0x09,0x37,0x46, //1989 0x54,0x97,0xBB, //1990 0x04,0x97,0x4f, //1991 0x06,0x4B,0x44, //1992 0x36,0xA5,0x37, //1993 0x0e,0xA5,0x4A, //1994 0x86,0xB2,0xBf, //1995 0x05,0xAC,0x53, //1996 0x0A,0xB6,0x47, //1997 0x59,0x36,0xBC, //1998 0x09,0x2e,0x50, //1999 294 0x0C,0x96,0x45, //2000 297 0x4d,0x4A,0xB8, //2001 300 0x0d,0x4A,0x4C, //2002 303 0x0d,0xA5,0x41, //2003 306 0x25,0xAA,0xB6, //2004 309 0x05,0x6A,0x49, //2005 312 0x7A,0xAd,0xBd, //2006 315 0x02,0x5d,0x52, //2007 318 0x09,0x2d,0x47, //2008 321 40 0x5C,0x95,0xBA, //2009 324 0x0A,0x95,0x4e, //2010 327 0x0B,0x4A,0x43, //2011 0x4B,0x55,0x37, //2012 0x0A,0xd5,0x4A, //2013 0x95,0x5A,0xBf, //2014 0x04,0xBA,0x53, //2015 0x0A,0x5B,0x48, //2016 0x65,0x2B,0xBC, //2017 0x05,0x2B,0x50, //2018 0x0A,0x93,0x45, //2019 0x47,0x4A,0xB9, //2020 0x06,0xAA,0x4C, //2021 0x0A,0xd5,0x41, //2022 0x24,0xdA,0xB6, //2023 0x04,0xB6,0x4A, //2024 0x69,0x57,0x3d, //2025 0x0A,0x4e,0x51, //2026 0x0d,0x26,0x46, //2027 0x5e,0x93,0x3A, //2028 0x0d,0x53,0x4d, //2029 0x05,0xAA,0x43, //2030 0x36,0xB5,0x37, //2031 0x09,0x6d,0x4B, //2032 0xB4,0xAe,0xBf, //2033 0x04,0xAd,0x53, //2034 0x0A,0x4d,0x48, //2035 0x6d,0x25,0xBC, //2036 0x0d,0x25,0x4f, //2037 0x0d,0x52,0x44, //2038 0x5d,0xAA,0x38, //2039 0x0B,0x5A,0x4C, //2040 0x05,0x6d,0x41, //2041 0x24,0xAd,0xB6, //2042 0x04,0x9B,0x4A, //2043 0x7A,0x4B,0xBe, //2044 0x0A,0x4B,0x51, //2045 0x0A,0xA5,0x46, //2046 0x5B,0x52,0xBA, //2047 0x06,0xd2,0x4e, //2048 0x0A,0xdA,0x42, //2049 0x35,0x5B,0x37, //2050 0x09,0x37,0x4B, //2051 0x84,0x97,0xC1, //2052 0x04,0x97,0x53, //2053 41 0x06,0x4B,0x48, //2054 0x66,0xA5,0x3C, //2055 0x0e,0xA5,0x4f, //2056 0x06,0xB2,0x44, //2057 0x4A,0xB6,0x38, //2058 0x0A,0xAe,0x4C, //2059 0x09,0x2e,0x42, //2060 0x3C,0x97,0x35, //2061 0x0C,0x96,0x49, //2062 0x7d,0x4A,0xBd, //2063 0x0d,0x4A,0x51, //2064 0x0d,0xA5,0x45, //2065 0x55,0xAA,0xBA, //2066 0x05,0x6A,0x4e, //2067 0x0A,0x6d,0x43, //2068 0x45,0x2e,0xB7, //2069 0x05,0x2d,0x4B, //2070 0x8A,0x95,0xBf, //2071 0x0A,0x95,0x53, //2072 0x0B,0x4A,0x47, //2073 0x6B,0x55,0x3B, //2074 0x0A,0xd5,0x4f, //2075 0x05,0x5A,0x45, //2076 0x4A,0x5d,0x38, //2077 0x0A,0x5B,0x4C, //2078 0x05,0x2B,0x42, //2079 0x3A,0x93,0xB6, //2080 0x06,0x93,0x49, //2081 0x77,0x29,0xBd, //2082 0x06,0xAA,0x51, //2083 0x0A,0xd5,0x46, //2084 0x54,0xdA,0xBA, //2085 0x04,0xB6,0x4e, //2086 0x0A,0x57,0x43, //2087 0x45,0x27,0x38, //2088 0x0d,0x26,0x4A, //2089 0x8e,0x93,0x3e, //2090 0x0d,0x52,0x52, //2091 0x0d,0xAA,0x47, //2092 0x66,0xB5,0x3B, //2093 0x05,0x6d,0x4f, //2094 0x04,0xAe,0x45, //2095 0x4A,0x4e,0xB9, //2096 0x0A,0x4d,0x4C, //2097 0x0d,0x15,0x41, //2098 42 0x2d,0x92,0xB5, //2099 }; /******************忙检测函数***************/ void CheckBusy(void) { LCD_RS=0; LCD_RW=1; LCD_E=1; LCD_Data=0xff; while((LCD_Data&0x80)==0x80); LCD_E=0; } /***************液晶写数据函数***************/ void WriteDataLCD(unsigned char dat) { CheckBusy(); LCD_RS =1; LCD_RW=0; LCD_E=1; LCD_Data=dat; delayb(5); LCD_E =0; delayb(5); } /***************液晶写指令函数***************/ void WriteCommandLCD(unsigned char udat) { CheckBusy(); LCD_RS=0; LCD_RW=0; LCD_E=1; LCD_Data=udat; delayb(5); LCD_E = 0; delayb(5); } /***************液晶初始化函数***************/ void LCDInit(void) { WriteCommandLCD(0x30); WriteCommandLCD(0x01); 43 WriteCommandLCD(0x06); WriteCommandLCD(0x0C); } /***************液晶清屏函数***************/ void LCDClear(void) { WriteCommandLCD(0x01); //清屏 WriteCommandLCD(0x34); WriteCommandLCD(0x30); } /***************液晶汉字函数***************/ void LCDSendWord(unsigned char *p) { while(*p>0) { WriteDataLCD(*p); p++; } } void LCDTestWord(bit i,unsigned char word) { if(i==0) { WriteCommandLCD(word); } else { WriteDataLCD(word); } } void DisplayYear(void) { year=ReadDs1302(0x8d); LCDTestWord(0,0x81); LCDTestWord(1,(year/16)+0x30); LCDTestWord(1,year%16+0x30); LCDTestWord(0,0x82); LCDSendWord("年"); DisplayShengXiao(); } 44 void DisplayMonth(void) { month=ReadDs1302(0x89); LCDTestWord(0,0x83); if(month/16!=0) { LCDTestWord(1,(month/16)+0x30); } else { LCDTestWord(1,0x30); } LCDTestWord(1,month%16+0x30); LCDTestWord(0,0x84); LCDSendWord("月"); DisplayShengXiao(); } void DisplayWeek(void) { week=ReadDs1302(0x8b); LCDTestWord(0,0x95); LCDSendWord("星期"); LCDTestWord(0,0x97); // LCDTestWord(1,week+0x30); if(week==7) {LCDSendWord("日");} if(week==6) {LCDSendWord("六");} if(week==5) {LCDSendWord("五");} if(week==4) {LCDSendWord("四");} if(week==3) {LCDSendWord("三");} if(week==2) {LCDSendWord("二");} if(week==1) {LCDSendWord("一");} } void DisplayDay(void) { day=ReadDs1302(0x87); LCDTestWord(0,0x85); if(day/16!=0) { LCDTestWord(1,(day/16)+0x30); } else { LCDTestWord(1,0x30);} LCDTestWord(1,day%16+0x30); LCDTestWord(0,0x86); LCDSendWord("日"); 45 DisplayShengXiao(); } void DisplayHour(void) { hour=ReadDs1302(0x85); LCDTestWord(0,0x88); LCDTestWord(1,(hour/16)+0x30); LCDTestWord(1,hour%16+0x30); } void DisplayMin(void) { min=ReadDs1302(0x83); LCDTestWord(0,0x89); LCDTestWord(1,0x3a); LCDTestWord(1,(min/16)+0x30); LCDTestWord(1,min%16+0x30); LCDTestWord(1,0x3a); } void DisplaySec(void) { unsigned char i=0; unsigned int a=0,b=0,c=0; sec=ReadDs1302(0x81); LCDTestWord(0,0x8b); LCDTestWord(1,(sec/16)+0x30); LCDTestWord(1,sec%16+0x30); } void DisplayTemp(void) { unsigned int i; unsigned char a,b,c; WriteCommandLCD(0x8c); LCDTestWord(0,0x8d); i=GetTemp(); a=i/100; LCDTestWord(1,a+0x30); b=i%100/10; LCDTestWord(1,b+0x30); LCDTestWord(1,0x2e); c=i-a*100-b*10; LCDTestWord(1,c+0x30); LCDSendWord("?"); 46 } code unsigned char DayCode1[9]={0x00,0x1f,0x3b,0x5a,0x78,0x97,0xb5,0xd4,0xf3}; code unsigned int DayCode2[3]={0x111,0x130,0x14e}; //计算公历日离当年元旦的天数,为了减少运算,用了两个表 DayCode1[9],DayCode2[3] //如果公历月在九月或前,天数会少于0xff,用表DayCode1[9],在九月后,天数大于0xff,用表DayCode2[3] //如输入公历日为8月10日,则公历日离元旦天数为DayCode1[8-1]+10-1,如输入公历日为11月10日,则公历日离元旦天数为DayCode2[11-10]+10-1 /*读取数据表中农历月的大月或小月,如果该月为大返回1,为小返回0*/ bit GetMoonDay(unsigned char LunarMonth,unsigned int TableAddr) { unsigned char temp; switch (LunarMonth) //LunarMonth指向农历月份 { case 1: { temp=YearCode[TableAddr]&0x08; //1月,对应年份表里第一字节的BIT3位,如果是1,则月 大,如果是0,则月小 if (temp==0) return(0); //为0,月小 else return(1); //为1,月大 } case 2: { temp=YearCode[TableAddr]&0x04; //2月,对应年份表里第一字节的BIT2位,如果是1,则月 大,如果是0,则月小 if (temp==0) return(0); //为0,月小 else return(1); //为1,月大 } case 3: { temp=YearCode[TableAddr]&0x02; //3月,对应第一字节的BIT1位,如果是1,则月大,如 果是0,则月小 if (temp==0) return(0); //为0,月小 else return(1); //为1,月大 } case 4: { temp=YearCode[TableAddr]&0x01; //1月,对应第一字节的BIT0位,如果是1,则月大,如 果是0,则月小 if (temp==0) return(0); else return(1); } case 5: { temp=YearCode[TableAddr+1]&0x80; //5月,对应第二字节的BIT7位,如果是1,则月大,如 果是0,则月小 47 if (temp==0) return(0); else return(1); } case 6: { temp=YearCode[TableAddr+1]&0x40; //6月,对应第二字节的BIT6位,如果是1,则月大,如 果是0,则月小 if (temp==0) return(0); else return(1); } case 7: { temp=YearCode[TableAddr+1]&0x20; //7月,对应第二字节的BIT5位,如果是1,则月大,如 果是0,则月小 if (temp==0) return(0); else return(1); } case 8: { temp=YearCode[TableAddr+1]&0x10; //8月,对应第二字节的BIT4位,如果是1,则月大,如 果是0,则月小 if (temp==0) return(0); else return(1); } case 9: { temp=YearCode[TableAddr+1]&0x08; //9月,对应第二字节的BIT3位,如果是1,则月大,如 果是0,则月小 if (temp==0) return(0); else return(1); } case 10: { temp=YearCode[TableAddr+1]&0x04; //10月,对应第二字节的BIT2位,如果是1,则月大,如 果是0,则月小 if (temp==0) return(0); else return(1); } case 11: { temp=YearCode[TableAddr+1]&0x02; //11月,对应第二字节的BIT1位,如果是1,则月大, 如果是0,则月小 if (temp==0) return(0); else return(1); } 48 case 12: { temp=YearCode[TableAddr+1]&0x01; //12月,对应第二字节的BIT0位,如果是1,则月大, 如果是0,则月小 if (temp==0) return(0); else return(1); } case 13: { temp=YearCode[TableAddr+2]&0x80; //13月,对应第三字节的BIT7位,如果是1,则月大, 如果是0,则月小 if (temp==0) return(0); else return(1); } } } void Conversion(bit cenbit,unsigned char year,unsigned char month,unsigned char day) { unsigned char temp1,temp2,temp3,MonthP;//temp3,temp4分别表示春节距元旦的天数,公历日离元 旦的天数 unsigned int temp4,TableAddr; bit flag2,flag_y; temp1=year/16; //BCD->hex 先把数据转换为十六进制 高位 temp2=year%16; //低位 year=temp1*10+temp2; //把 年 数据 转换成10进制 temp1=month/16; //月份 高位 temp2=month%16; //月份 低位 month=temp1*10+temp2; //把 月 数据 转换成10进制 temp1=day/16; //日期 高位 temp2=day%16; //日期 低位 day=temp1*10+temp2; //把 日 数据 转换成16进制 //如果是21世纪 TableAddr=(year+0x64-1)*0x03; //定位数据表地址 year对应的年份表中的地址是 (year+99)*3 如2010年 其地址是327 LCDTestWord(0,0x80); LCDSendWord("20"); temp1=YearCode[TableAddr+2]&0x60; //取当年春节所在的公历月份 年份表中第三字节 BIT6-5表示春节的公历月份 temp1=_cror_(temp1,5); //循环右移5位,得到 春节所在的公历月份 temp2=YearCode[TableAddr+2]&0x1f; //取当年春节所在的公历日 年份表中第三字节 BIT4-0表示当年春节所在的公历日 if(temp1==0x01) // 计算当年春年离当年元旦的天数,春节只会在公历 49 1月或2月 temp3=temp2-1; //假如春节在公历1月,则元旦离春节的天数为 temp2-1 天 else temp3=temp2+0x1f-1; //假如春节在公历2月,则元旦离春节的天数为 temp2+0x1f-1 天 if (month<10) temp4=DayCode1[month-1]+day-1; //0到8月某日距元旦的天数 else temp4=DayCode2[month-10]+day-1; //9月开始的某一天距元旦的天数 if ((month>0x02)&&(year%0x04==0)) //如果公历月大于2月并且该年的2月为闰月,天数加1 temp4+=1; //计算机出公历日距元旦的天数和春节距元旦的天数,则 是为了比较公历日是在春节前还是春节后 //如果temp3>temp4 则 公历日在春节之前 if (temp4>=temp3) //公历日在春节后或就是春节当日使用下面代码进行运算 { temp4-=temp3; //公历日离春节的天数 因为公历日在春节后 所以为temp4-temp3 month=0x01; MonthP=0x01; //LunarMonth为月份指向,公历日在春节前或就是春节当日LunarMonth指向首月 flag2=GetMoonDay(MonthP,TableAddr); //检查该农历月为大小还是小月,大月返回1,小月返回0 flag_y=0; if(flag2==0) //GetMoonDay()函数返回的是0 {temp1=0x1d;} //小月29天 else //GetMoonDay()函数返回的是1 {temp1=0x1e;} //大月30天 temp2=YearCode[TableAddr]&0xf0; //年份数据表中第1字节BIT7-4为闰月,为0则这 年无闰月,如为1,表示有闰月 temp2=_cror_(temp2,4); //从数据表中取该年的闰月月份,如为0,则该年无闰月 BIT3-0表 示阴历1到4月的大小 1为大 0 为小 while(temp4>=temp1) { temp4-=temp1; MonthP+=1; if(month==temp2) { flag_y=~flag_y; if(flag_y==0) {month+=1;} } else { month+=1; } flag2=GetMoonDay(MonthP,TableAddr); if(flag2==0) { temp1=0x1d; 50 } else { temp1=0x1e; } } day=temp4+1; } else { //公历日在春节前使用下面代码进行运算 temp3-=temp4; //公历日离春节的天数 因为公历日在春节前 所以为temp3-temp4 if (year==0x00){year=0x63;cenbit=1;} else year-=1; TableAddr-=0x03; month=0x0c; temp2=YearCode[TableAddr]&0xf0; //格式第一字节BIT7-4位表示闰月月份,为0,则无闰 月,BIT3-0对应阴历第1-4月的大小, temp2=_cror_(temp2,4); if (temp2==0)MonthP=0x0c; else MonthP=0x0d; // /* MonthP为月份指向,如果当年有闰月,一年有十三个月,月指向13,无闰月指向12*/ flag_y=0; flag2=GetMoonDay(MonthP,TableAddr); if(flag2==0)temp1=0x1d; else temp1=0x1e; while(temp3>temp1) { temp3-=temp1; MonthP-=1; if(flag_y==0)month-=1; if(month==temp2)flag_y=~flag_y; flag2=GetMoonDay(MonthP,TableAddr); if(flag2==0)temp1=0x1d; else temp1=0x1e; } day=temp1-temp3+1; } c_moon=cenbit; temp1=year/10; temp1=_crol_(temp1,4); temp2=year%10; LunarYear=temp1|temp2; temp1=month/10; temp1=_crol_(temp1,4); temp2=month%10; 51 LunarMonth=temp1|temp2; temp1=day/10; temp1=_crol_(temp1,4); temp2=day%10; LunarDay=temp1|temp2; } void DisplayShengXiao(void) { unsigned char LunarYearD,ReYear; //农历年份的十进制数 和取模后的余数 if(cen==0x19) {cenbit=1;} if(cen==0x20) {cenbit=0;} Conversion(cenbit,year,month,day); //将公历日期转换成农历 LCDTestWord(0,0x90); LCDSendWord("农"); LCDTestWord(0,0x91); //显示在LCD的0X91位置上 LCDTestWord(1,LunarYear/16+0x30); //农历年十位 LCDTestWord(1,LunarYear%16+0x30); //农历年个位 LCDTestWord(1,'-'); //用-隔开 LCDTestWord(1,LunarMonth/16+0x30); //农历月十位 LCDTestWord(1,LunarMonth%16+0x30); //农历月个位 LCDTestWord(1,'-'); LCDTestWord(1,LunarDay/16+0x30); //农历日十位 LCDTestWord(1,LunarDay%16+0x30); //农历日个位 LunarYearD=(LunarYear/16)*10+LunarYear%16; //农历年转换成10进制数 ReYear=LunarYearD%12; //农历年模12,取余运算 switch(ReYear) { case 0: LCDTestWord(0,0x87);LCDSendWord("龙");break; //余0即整除 农历 龙年 case 1: LCDTestWord(0,0x87);LCDSendWord("蛇");break; //蛇年 case 2: LCDTestWord(0,0x87);LCDSendWord("马");break; case 3: LCDTestWord(0,0x87);LCDSendWord("羊");break; case 4: LCDTestWord(0,0x87);LCDSendWord("猴");break; case 5: LCDTestWord(0,0x87);LCDSendWord("鸡");break; case 6: LCDTestWord(0,0x87);LCDSendWord("狗");break; case 7: LCDTestWord(0,0x87);LCDSendWord("猪");break; case 8: LCDTestWord(0,0x87);LCDSendWord("鼠");break; case 9: LCDTestWord(0,0x87);LCDSendWord("牛");break; case 10:LCDTestWord(0,0x87);LCDSendWord("虎");break; case 11:LCDTestWord(0,0x87);LCDSendWord("兔");break; } } /***************节假日提示********************/ void Holidays(void) { 52 LCDTestWord(0,0x98); if(next==0) { if(LunarMonth==0x01&&LunarDay==0x01) {LCDSendWord(" 春节快乐~ ");} if(LunarMonth==0x01&&LunarDay==0x15) {LCDSendWord(" 元宵节快乐~ ");} if(LunarMonth==0x05&&LunarDay==0x05) {LCDSendWord(" 端午节快乐~ ");} if(LunarMonth==0x07&&LunarDay==0x07) {LCDSendWord("中国情人节快乐~");} if(LunarMonth==0x08&&LunarDay==0x15) {LCDSendWord(" 中秋节快乐~ ");} if(LunarMonth==0x09&&LunarDay==0x09) {LCDSendWord(" 重阳节快乐~ ");} if(LunarMonth==0x12&&LunarDay==0x08) {LCDSendWord(" 腊八节~ ");} if(LunarMonth==0x12&&LunarDay==0x29) {LCDSendWord("还有一天新年啦~");} //以上是农历节日,以下是公历节日 if(month==0x01&&day==0x01) {LCDSendWord(" 元旦节快乐~ ");} if(month==0x02&&day==0x02) {LCDSendWord(" 世界湿地日~ ");} if(month==0x02&&day==0x10) {LCDSendWord(" 世界气象日~ ");} if(month==0x02&&day==0x14) {LCDSendWord(" 情人节快乐~ ");} if(month==0x03&&day==0x08) {LCDSendWord(" 世界湿地日~ ");} if(month==0x03&&day==0x12) {LCDSendWord(" 中国植树节~ ");} if(month==0x03&&day==0x15) {LCDSendWord(" 消费者权益日 ");} if(month==0x03&&day==0x22) {LCDSendWord(" 世界水日~ ");} if(month==0x04&&day==0x01) {LCDSendWord(" 愚人节快乐~ ");} if(month==0x04&&day==0x05) {LCDSendWord(" 中国清明节~ ");} if(month==0x04&&day==0x22) {LCDSendWord(" 世界地球日~ ");} if(month==0x05&&day==0x01) {LCDSendWord(" 国际劳动节~ ");} if(month==0x05&&day==0x04) {LCDSendWord("中国五四青年节~");} if(month==0x05&&day==0x12) {LCDSendWord(" 世界护士节~ ");} if(month==0x06&&day==0x01) {LCDSendWord(" 国际儿童节~ ");} if(month==0x06&&day==0x05) {LCDSendWord(" 世界环境日~ ");} if(month==0x06&&day==0x06) {LCDSendWord(" 国际爱眼日~ ");} if(month==0x06&&day==0x25) {LCDSendWord(" 世界土地日~ ");} if(month==0x06&&day==0x26) {LCDSendWord(" 国际禁毒日~ ");} if(month==0x07&&day==0x01) {LCDSendWord("香港回归纪念日~");} if(month==0x08&&day==0x01) {LCDSendWord(" 中国建军节~ ");} if(month==0x09&&day==0x10) {LCDSendWord("中国教师节快乐~");} if(month==0x09&&day==0x18) {LCDSendWord("九一八事件纪念日");} if(month==0x10&&day==0x01) {LCDSendWord(" 中国国庆节~ ");} if(month==0x10&&day==0x04) {LCDSendWord(" 世界动物日~ ");} if(month==0x10&&day==0x09) {LCDSendWord(" 世界邮政日~ ");} if(month==0x10&&day==0x10) {LCDSendWord("辛亥革命纪念日~");} if(month==0x12&&day==0x20) {LCDSendWord("澳门回归纪念日~");} if(month==0x12&&day==0x24) {LCDSendWord(" 平安夜快乐~ ");} if(month==0x12&&day==0x25) {LCDSendWord(" 圣诞节快乐~ ");} else 53 { if(((temp-0.5)/10)>2&((temp-0.5)/10)<34) { if(SetKey==1&&SureKey==1&&PlusKey==1&&ReduceKey==1) { if((hour/16*10+hour%16)>=6&&(hour/16*10+hour%16)<22) { if((hour/16*10+hour%16)>12&&(hour/16*10+hour%16)<14) LCDSendWord("午休时间(^@^)~~"); else LCDSendWord("快乐每一天^_^‘’"); } if((hour/16*10+hour%16)>22||(hour/16*10+hour%16)<=5) LCDSendWord("夜深了注意休息喔"); } } if(((temp-0.5)/10)<=2) LCDSendWord("天气寒冷注意保暖"); else LCDSendWord("天气炎热注意防暑"); } } else LCDTestWord(0,0x9a); if(next==1) LCDSendWord("年份请按+ - "); if(next==2) LCDSendWord("月份请按+ -"); if(next==3) LCDSendWord("日期请按+ - "); if(next==4) LCDSendWord("小时请按+ - "); if(next==5) LCDSendWord("分钟请按+ - "); if(next==6) LCDSendWord("秒钟请按+ - "); if(next==7) LCDSendWord("星期请按+ - "); } void UpDate(void) { DisplayYear(); DisplayMonth(); DisplayDay(); DisplayWeek(); 54 DisplayHour(); DisplayMin(); DisplaySec(); DisplayShengXiao(); } void SetTime(unsigned char count) { unsigned char address,item; unsigned char max,mini; LCDTestWord(0,0x98); LCDSendWord("调整"); if(count==1) {LCDSendWord("年份 ");address=0x8d; max=99;mini=0;} if(count==2) {LCDSendWord("月份 ");address=0x89; max=12;mini=1;} if(count==3) {LCDSendWord("日期 ");address=0x87; max=31; mini=1;} if(count==4) {LCDSendWord("小时 ");address=0x85; max=23;mini=0;} if(count==5) {LCDSendWord("分钟 ");address=0x83; max=59;mini=0;} if(count==6) {LCDSendWord("秒钟 ");address=0x81; max=59;mini=0;} if(count==7) {LCDSendWord("星期 ");address=0x8b; max=7;mini=1;} item=ReadDs1302(address);//读取DS1302某地址上的数值赋给item item=(item/16)*10+item%16; if(PlusKey==0) //PlusKey-加 item++; //数加 1 if(ReduceKey==0) //ReduceKey-减 item--; //数减 1 if(item>max) item=mini; //查看数值有效范围 if(item7) {next= 0;} SetTime(next); //调整 } while(SetKey==0); //等待键松开 } if(SureKey==0) // 当在调时状态时就退出调时 { delay(10); //按键消抖 if(SureKey==0&&w==1) { w=0; next=0; Holidays(); } while(SureKey==0); //等待键松开 } if (PlusKey==0) //加调整 { delay(10); //按键消抖 if(PlusKey==0&&w==1) { SetTime(next); //调整 } while(PlusKey==0); //等待键松开 } if (ReduceKey==0) //减调整 { delay(10); //按键消抖 if(ReduceKey==0&&w==1) { SetTime(next); //调整 } while(ReduceKey==0); //等待键松开 } } void main() { SetKey=1; SureKey=1; PlusKey=1; 56 ReduceKey=1; PSB=1; next=0; delayb(100); LCDInit(); LCDClear(); InitDS1302(); while(1) { Key(); DisplayYear(); //显示年 DisplayMonth(); //显示月 DisplayDay(); //显示日 DisplayHour(); //显示时 DisplayMin(); //显示分 DisplaySec(); //显示秒 DisplayWeek(); //显示星期 Holidays(); //显示节日 DisplayTemp(); //显示温度 } } 57
本文档为【毕业设计(论文)-基于单片机的万年历的设计】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
该文档来自用户分享,如有侵权行为请发邮件ishare@vip.sina.com联系网站客服,我们会及时删除。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。
本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。
网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。
下载需要: 免费 已有0 人下载
最新资料
资料动态
专题动态
is_321635
暂无简介~
格式:doc
大小:362KB
软件:Word
页数:75
分类:工学
上传时间:2018-01-10
浏览量:36