单片机电子时钟实验报告

课程设计报告

设计题目: 电子时钟设计

指导教师:

姓 名:

学 号:

班 级:

专 业:

日 期: 20**-1-5

控制电路的C语言源程序 10

单片机电子时钟

摘要:单片机 即单片微型计算机。(Single-Chip Microcomputer ),是 集 CPU ,RAM ,ROM ,定时,计数和多种接口于一体的微控制器。他体积小,成本低,功能强,广泛应用于工业自动化上和智能产品。时钟,自从它被发明的那天起,就成为了人类的好朋友,但随着时间的推移,科学技术的不断发展,时钟的应用越来越广范,人们对时间计量的精度要求也越来越高。怎样让时钟更好的为人民服务,怎样让我们的老朋友再次焕发青春呢?这就要求我们不断设计出新型的时钟,来不断满足人们的日常生活需要。然而市场上的时钟便宜的比较笨重,简单实用的又比较昂贵。那么,有没有一款既简单实用价格又便宜的时钟呢?

我们课程设计小组设想:可不可以利用单片机功能集成化高,价格又便宜的特点设计一款结构既简单,价格又便宜的单片机电子时钟呢?

基于这种情况,我们课程设计小组成员多方查阅资料,反复论证设计出了这款既简单实用,又价格便宜的——单片机电子时钟。

关键词:单片机 时钟 计时

第一章 系统设计要求

1.1 基本功能

(1)能够显示时分秒

(2)能够调整时分秒

1.2 扩展功能

(1)能够任意设置定时时间

(2)定时时间到闹铃能够报警

(3)实现了秒表功能

第二章 硬件总体设计方案

本次设计时钟电路,使用了STC89C51单片机芯片控制电路,单片机控制电路简单且省去了很多复杂的线路,使得电路简明易懂,使用键盘键上的按键来调整时钟的时、分、秒,用一扬声器来进行定时提醒,同时使用C语言程序来控制整个时钟显示,使得编程变得更容易,这样通过四个模块:键盘、芯片、扬声器、显示屏即可满足设计要求。

2.1系统功能实现总体设计思路

此设计原理框图如图2-1所示,此电路包括以下四个部分:单片机,键盘,闹铃电路及显示电路。

单片机电子时钟实验报告

图2-1 设计原理框图

经多方论证硬件我们小组采用AT89C51单片机和7SED八位共阳极数码管等来实现单片机电子时钟的功能。

详细元器件列表如表2.1所示:

表2.1 详细元器件列表

(1)单片机发送的信号通过程序控制最终在数码管上显示出来。

(2)单片机通过输出各种电脉冲信号来驱动控制各部分正常工作。

(3)为使时钟走时与标准时间一致,校时电路是必不可少的,键盘用来校正数码管上显示的时间。

(4)单片机通过控制闹铃电路来完成 定时闹钟的功能。

2.3系统工作原理

设计的电路主要由四模块构成:单片机控制电路,显示电路、闹铃电路以及校正电路。

详细电路功能图如图2-2:

https://upload.fanwen118.com/wk-img/img100/4021215_1.jpg

本设计采用C语言程序设计,使单片机控制数码管显示时、分、秒,当秒计数计满60时就向分进位,分计数器计满60后向时计数器进位,小时计数器按“23翻0”规律计数。时、分、秒的计数结果经过数据处理可直接送显示器显示。当计时发生误差的时候可以用校时电路进行校正。设计采用的是时、分、秒显示,单片机对数据进行处理同时在数码管上显示。

2.4时钟各功能分析及图解

2.4.1电路各功能图解分析

(1) 时钟运行图

仿真开始运行时,或按下key4键时,时钟从12:00:00开始运行,其中key2键对分进行调整,key3对小时进行调整,key6可以让时钟暂停。

时钟运行图如图 2-3 所示:

https://upload.fanwen118.com/wk-img/img100/4021215_2.jpg

(2)秒表计时图

当按下key1键进入秒表计时状态,key6是秒表暂停键,可按key4键跳出秒表计时状态。

如图2-4:

https://upload.fanwen118.com/wk-img/img100/4021215_3.jpg

(3)闹铃设置图及运行图

当按下key5,开始定时,分别按key2调分,key3调时设置闹铃时间,然后按下key4键恢复时钟运行状态(图2-5)当闹铃设置时间到时,蜂鸣器将发出10秒中蜂鸣声(图2-6)。

https://upload.fanwen118.com/wk-img/img100/4021215_4.jpg

该数字钟是用一片STC89C52单片机通过编程去驱动8个数码管实现的。通过6个开关控制,从上到下6个开关KEY1-KEY6的功能分别为:KEY1,切换至秒表;KEY2,调节时间,每调一次时加1;KEY3, 调节时间,每调一次分加1;KEY4,从其它状态切换至时钟状态;KEY5,切换至闹钟设置状态,也可以对秒表清零;KEY6,秒表暂停.控制键分别与P1.0~P1.5口连接.其中:

A通过P2口和P3口去控制数码管的显示如图所示P2口接数码管的a——g端,是控制输出编码,P3口接数码管的1——8端,是控制动态扫描输出.

B从P0.0输出一个信号使二极管发光,二极管在设置的闹钟时间到了时候发光,若有乐曲可以去驱动扬声器实现。

2.4.2电路功能使用说明

(1) 各个控制键的功能:可对时间进行校准调节(只能加1);按下设置键数字时钟进入闹钟设置状态,设置闹钟的时间;时加1、分加1键是在校准时间时或设置闹钟时间对小时数或分钟数调节而设置的;按下秒切换键就可以进入秒表模式,同时秒表也开始计时,按下秒表暂停、复位键就暂停、归零,如果要重新对秒计时则可以按秒表开始、复位;清零键可以对闹钟清零。

STC89C51单片机,通过编写程序对数码显示进行控制。

(2)八个7段数码管显示时钟和秒表信号。

第三章软件总体设计方案

控制电路的C语言源程序

#include <reg52.h>

#include <intrins.h>

unsigned char data dis_digit;

unsigned char key_s, key_v;

unsigned char code dis_code[11]={0xc0,0xf9,0xa4,0xb0, // 0, 1, 2, 3

0x99,0x92,0x82,0xf8,0x80,0x90, 0xbf};// 4, 5, 6, 7, 8, 9,—

unsigned char data dis_buf[8];

unsigned char data dis_index;

unsigned char hour,min,sec;

unsigned char sec100;

sbit K1 = P1^1;

sbit K2 = P1^2;

bit scan_key();

void proc_key();

void inc_sec();

void inc_min();

void inc_hour();

void display();

void delayms(unsigned char ms);

void main(void)

{

P2 = 0xff;

P3 = 0xff;

TMOD = 0x11; // 定时器0, 1工作模式1, 16位定时方式

TH1 = 0xdc;

TL1 = 0;

TH0 = 0xFC;

TL0 = 0x17;

hour = 12;

min = 00;

sec = 00;

sec100 = 0;

dis_buf[0] = dis_code[hour / 10]; // 时十位

dis_buf[1] = dis_code[hour % 10]; // 时个位

dis_buf[3] = dis_code[min / 10]; // 分十位

dis_buf[4] = dis_code[min % 10]; // 分个位

dis_buf[6] = dis_code[sec / 10]; // 秒十位

dis_buf[7] = dis_code[sec % 10]; // 秒个位

dis_buf[2] = 0xbf; // 显示"-"

dis_buf[5] = 0xbf; // 显示"-"

dis_digit = 0xfe;

dis_index = 0;

TCON = 0x01;

IE = 0x8a; // 使能timer0,1 中断

TR0 = 1;

TR1 = 1;

key_v = 0x03;

while(1)

{

if(scan_key())

{

delayms(10);

if(scan_key())

{

key_v = key_s;

proc_key();

}

}

}

}

bit scan_key()

{

key_s = 0x00;

key_s |= K2;

key_s <<= 1;

key_s |= K1;

return(key_s ^ key_v);

}

void proc_key()

{

EA = 0;

if((key_v & 0x01) == 0) // K1

{

inc_hour();

// JJ=1;

}

else if((key_v & 0x02) == 0) // K2

{

min++;

// JJ=0;

if(min > 59)

{

min = 0;

}

dis_buf[3] = dis_code[min / 10]; // 分十位

dis_buf[4] = dis_code[min % 10]; // 分个位

}

EA = 1;

}

void timer0() interrupt 1

// 定时器0中断服务程序, 用于数码管的动态扫描

// dis_index --- 显示索引, 用于标识当前显示的数码管和缓冲区的偏移量

// dis_digit --- 位选通值, 传送到P2口用于选通当前数码管的数值, 如等于0xfe时,

// 选通P2.0口数码管

// dis_buf --- 显于缓冲区基地址

{

TH0 = 0xFC;

TL0 = 0x17;

P3 = 0xff; // 先关闭所有数码管

P2 = dis_buf[dis_index]; // 显示代码传送到P0口

P3= dis_digit; //

dis_digit = _crol_(dis_digit,1); // 位选通值左移, 下次中断时选通下一位数码管

dis_index++; //

dis_index &= 0x07; // 8个数码管全部扫描完一遍之后,再回到第一个开始下一次扫描

}

void timer1() interrupt 3

{

TH1 = 0xdc;

sec100++;

if(sec100 >= 100)

{

sec100 = 0;

inc_sec();

}

}

void inc_sec()

{

sec++;

if(sec > 59)

{

sec = 0;

inc_min();

}

dis_buf[6] = dis_code[sec / 10]; // 秒十位

dis_buf[7] = dis_code[sec % 10]; // 秒个位

}

void inc_min()

{

min++;

if(min > 59)

{

min = 0;

inc_hour();

}

dis_buf[3] = dis_code[min / 10]; // 分十位

dis_buf[4] = dis_code[min % 10]; // 分个位

}

void inc_hour()

{

hour++;

if(hour > 23)

{

hour = 0;

}

if(hour > 9)

dis_buf[0] = dis_code[hour / 10]; // 时十位

else

dis_buf[0] = 0xff; // 当小时的十位为0时不显示

dis_buf[1] = dis_code[hour % 10]; // 时个位

}

void delayms(unsigned char ms)

// 延时子程序

{

unsigned char i;

while(ms--)

{

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

}

}

第四章 课程设计结果分析

此时钟设计是利用protues仿真软件进行仿真,基本上实现了课程设计要求实现的功能。

硬件部分设置了的六个按键。当按键一按下时,进入秒表显示状态,秒表开始计时,当按键六按下时,秒表暂停;当按键四按下时恢复到时间显示功能;当按键二按下时,进入调分状态,按一次,分加一,60一循环;按键三按下时,进入调时状态,按一次,时加一,60一循环;按键五按下时,进入闹铃设置功能,紧接着按下按键二和按键三进行时和分的设置,再按下按键4恢复显示时间,当显示的时间和定时设置的时间一致时,蜂鸣器发出蜂鸣声,蜂鸣时间我们设置为10秒。

第五章 总结

经过一周的单片机课程设计,我组成员已基本完成课题要求。功能上基本达标:时钟的显示,秒表显示,定时功能,调时功能。时钟显示功能,精确度完全可以满足日常生活显示时间的需要;秒表功能,可以满足比赛计时的需要;调时功能,方便快捷;定时功能准确可靠,还有扩展成音乐闹钟的余地。硬件设施合乎要求,软件设计可以配合硬件实现要求功能。但是由于时间比较短,出现部分不足:使用定时和秒表功能时时间显示功能停止运行。经讨论只是软件部分还不完善。不过,我们相信,如果时间充足,将软件改进,我们完全可以很好实现所有功能。

另外,在本次设计的过程中,我们发现很多的问题,虽然以前没有做过这样的设计但通过这次设计我学会了很多东西,单片机课程设计重点就在于软件算法的设计,需要有很巧妙的程序算法,虽然以前写过几次程序,但我们觉的写好一个程序并不是一件简单的事,比如写一个程序看其功能很少认为编写程序简单,但到编的时候才发现一些细微的知识或低级错误经常犯做不到最后常常失败,所以有些东西只有学精弄懂并且要细心才行,只学习理论有些东西是很难理解的,更谈不上掌握。

从这次的课程设计中,我们真真正正的意识到,在以后的学习中,要理论联系实际,把我们所学的理论知识用到实际当中,学习单机片机更是如此,程序只有在经常的练习的过程中才能提高,我想这就是我在这次课程设计中的最大收获。

参考文献

[1] 李叶紫.王喜斌.胡辉.孙东辉.编著MCS_51单片机应用教程清华大学出版社.2008.6.

[2] 陆剑.单片机应用技术指导书 河南工业职业技术学院2005.12.

[3] 汪道辉.单片机系统设计与实践.电子工业出版社

( 50页 时、分、秒计时器设计,59页 键盘及接口技术).

[4] 第二版.51系列单片机设计实例.北京航空航天大学出版社(81—89页 数码管时钟电路的设计).

[5] 辛友顺、胡永生、薛小玲.单片机应用系统设计与实现.福建科学技术出版社(184-186页 LED显示接口,190-193页 键盘接口).

[6] 黄庆华、张永格.单片机开发 与实例.电子工业出版社(127-162页 数字式电子时钟的设计).

[7] 闫玉德、俞红.MCS-51单片机原理与应用(C语言版).机械工业出版社(49-104页 单片机的C程序设计).

[8] 求是科技.单片机典型模块设计实例导航.人民邮电出版社(85-90页 单片机数字时钟).

[9] 刘守义,王静霞。《单片机应用技术》.西安电子科技大学出版社,2002.

 

第二篇:单片机综合实验报告电子时钟

一、实验内容:

设计一个数字时钟,显示范围为00:00:00~23:59:59。通过5个开关进行控制,其中开关K1用于切换时间设置(调节时钟)和时钟运行(正常运行)状态;开关K2用于切换修改时、分、秒数值;开关K3用于使相应数值加1调节;开关K4用于减1调节;开关K5用于设定闹钟,闹钟同样可以设定初值,并且设定好后到时间通过蜂鸣器发声作为闹铃。

选做增加项目:还可增加秒表功能(精确到0.01s)或年月日设定功能。

二、实验电路及功能说明

1602显示器电路(不需接线)

单片机综合实验报告电子时钟

电子音响电路

按键说明:

单片机综合实验报告电子时钟

单片机综合实验报告电子时钟

三、实验程序流程图:

四、实验结果分析

定时程序设计:

单片机的定时功能也是通过计数器的计数来实现的,此时的计数脉冲来自单片机的内部,即每个机器周期产生一个计数脉冲,也就是每经过1个机器周期的时间,计数器加1。如果MCS-51采用的12MHz晶体,则计数频率为1MHz,即每过1us的时间计数器加1。这样可以根据计数值计算出定时时间,也可以根据定时时间的要求计算出计数器的初值。MCS-51单片机的定时器/计数器具有4种工作方式,其控制字均在相应的特殊功能寄存器中,通过对特殊功能寄存器的编程,可以方便的选择定时器/

单片机综合实验报告电子时钟

计数器两种工作模式和4种工作方式。

定时器/计数器工作在方式0时,为13位的计数器,由TLX(X=0、1)的低5位和THX的高8位所构成。TLX低5位溢出则向THX进位,THX计数溢出则置位TCON中的溢出标志位TFX.

当定时器/计数器工作于方式1,为16位的计数器。本设计师单片机多功能定时器,所以MCS-51内部的定时器/计数器被选定为定时器工作模式,计数输入信号是内部时钟脉冲,每个机器周期产生一个脉冲使计数器增1。

实时时钟实现的基本方法:

这次设计通过对单片机的学习、应用,以AT89S51芯片为核心,辅以必要的电路,设计了一个简易的电子时钟,它主要通过51单片机综合仿真实验仪实现,通过1602能够准确显示时间,调整时间,它的计时周期为24小时,从而到达学习、设计、开发软、硬件的能力。主要实现功能为显示时间,时间校准调时(采用手动按键调时),闹铃功能(设置定时时间,到点后闹铃发出响声)。通过键盘可以进行校时、定时。闹铃功能使用I/O 口定时翻转电平驱动的无源蜂鸣器。本文主要介绍了工作原理及调试实现。

四个按键K1、K2、K3、K4、一个蜂鸣器。

1602显示时钟、跑表。

时钟的最小计时单位是秒,但使用定时器的方式1,最大的定时时间也只能达到131ms。我们可把定时器的定时时间定为50ms。这样,计数溢出20次即可得到时钟的最小计时单位:秒。而计数20次可以用软件实现。

秒计时是采用中断方式进行溢出次数的累积,计满20次,即得到秒计时。从秒到分,从分到时是通过软件累加并进行比较的方法来实现的。要求每满1秒,则“秒”单元中的内容加1;“秒”单元满60,则“分”单元中的内容加1;“分”单元满60,则“时”单元中的内容加1;“时”单元满24,则将时、分、秒的内容全部清零。

实时时钟程序设计步骤:

先对系统进行初始化,如:LCD1602初始化,DS1302初始化等,然后才能进入主显示模块,即可在LCD1602上看到相应的信息。对于LCD1602的初始化,主要是对开启显示屏,清屏,设置显示初始行等操作。DS1302的初始化主要是先开启写功能,然后写入一个初始值。

本系统采用的是LCD1602液晶显示器,由于其是本身带有驱动模块的液晶屏,所以对于

LCD1602操作程序可分为开显示、设置显示初始行、写数据和清屏等部分。LCD1602的写命令程序和写数据程序分别以子程序的形式写在程序里,以便主程序中的调用。

(1)选择工作方式,计算初值;

(2)采用中断方式进行溢出次数累计;

(3)计时是通过累加和数值比较实现的;

(4)时钟显示缓冲区:时钟时间在方位数码管上进行显示,为此在内部RAM中要设置显示缓冲 区,共6个地址单元。显示缓冲区从左到右依次存放时、分、秒数值;

(5)主程序:主要进行定时器/计数器的初始化编程,然后反复调用显示子程序的方法等待中断 的到来;

(6)中断服务程序:进行计时操作;

(7)加1子程序:用于完成对时、分、秒的加操作,中断服务程序在秒、分、时加1时共有三种 条调用加1子程序,包括三项内容:合字、加1并进行十进制调整、分字。

程序说明:

按K1按键进入设定状态

按K2,依次进入闹钟功能是否启用,闹钟时,分秒,年,月,日及时间时,分,秒的设置,直到退出设置状态 按K3,调整是否起用闹钟和调节闹钟时,分,秒,年,月,日,时间的时,分,秒的数字

LCD第二排中间显示小喇叭,表示启用闹钟功能,无则禁止闹钟功能(可在调整状态进行设置) 正常状态,LCD上排最前面显示自定义字符,LCD下排最前面闪动"_"

设置状态,LCD上排最前面显示"P",下排最前面在设置闹钟时间时显示"alarm_",其它状态显示

"time"

年代变化2000--2099,星期自动转换

程序中有自定义字符写入

在整个系统中,在单片机的30H、31H和32H中存储当前时间的小时、分钟和秒。由于要用数码管显示当前的时间,必须用到分字和合字,因此在33H、34H、35H、36H、37H和38H中存储当前时间的时十位、时个位、分十位、分个位、秒十位和秒个位,方便显示。

本设计有由四个轻触按键组成的小键盘,这些按键可以任意改变当前的状态。按功能移位键一次,表示当前要校对小时的十位;按第二次,表示当前校对的是小时的个位;按第三次,则表示校对的是分钟的十位;第四次,表示的校对的是分钟的个位。按下数字“+” 键和数字“-”键可在当前校对的数字上相应加上1或者减去1。

本设计采用查表方式,在程序里预先存储两个表格,即日常作息时间表和考试时间表,可以通过手动按键来选择所要执行的时间表。并且用红、绿发光二极管来区别当前所执行的时间表。系统开机后,按功能移位键就可以调整当前的时间,整个系统操作简单,功能明确。

显示数据时,先把要显示的数据送到数据缓冲区SBUF中,再从SBUF中显示。串行口缓冲寄存器SBUF器是可直接寻址的专用寄存器。在物理上,它对应着两个寄存器,一个发送寄存器,一个接收寄存器。CPU写SBUF,就是修改发送寄存器;读SBUF,就是读接收寄存器。接收器是双缓冲的,以避免在接收下一帧数据之前,CPU未能及时响应接收器的中断,没有把上一帧数据读走,而产生两帧数据重叠的问题。对于发送器,为了保持最大的传输速率,一般不需要双缓冲,因为发送时CPU是主动的,不会产生写重叠的问题。

五、心得体会

在享受我们成果之时,懂得的重要性与高难度性,所以为期一周的单片机课程设计没有浪费,我们从中学到了很多知识.,也让我们对单片机有了更深一步的了解.虽然最后结果是出来了,可这与老师的精心指导是分不开的。

通过对这程序的制作,是我对单片机的基本知识的使用更加熟练,同时也增加了我对单片机的一些认识,在作业完成过程中通过和同学的交流,也增加了合作的技巧。通过查阅资料也学到了一些课本上没有的东西,拓宽了自己的知识面,增加了学好单片机的信心。

这次实训虽然其中会有些错误和失败,但总的来说是受益匪浅,在运用中发现问题,解决问题,就是最大的收获。专心做自己的事,是一种乐趣;互相交流,是大家一起进步的必要过程;上网查阅资料,是获得所需信息的有效途径。我想,这些练习和经验都将是我以后最宝贵的财富!

附录:实验主程序及注释 #include <reg51.h> #include <intrins.h> unsigned char code dis_week[]={"SUN,MON,TUE,WED,THU,FRI,SAT"};

unsigned char code para_month[13]={0,0,3,3,6,1,4,6,2,5,0,3,5};

void pro_timedate(); //时间日期处理程序

void pro_display(); //显示处理程序

void pro_key(); //按键处理程序

void time_alarm(); //定时报警 //星期月参变数

unsigned char data dis_buf1[16]; //lcd上排显示缓冲区

unsigned char data dis_buf2[16]; //lcd下排显示缓冲区

unsigned char data year,month,date,week;//年、月、日、星期

unsigned char data armhour,armmin,armsec;//闹钟时、分、秒

unsigned char data hour,min,sec,sec100; //时、分、秒、百分之一秒

unsigned char data flag,vkey,skey;//设置状态计数标志、按键先前值、按键当前值 bit alarm; //标识是否启用闹钟,1--启用,0--关闭

sbit rs = P2^0; //LCD数据/命令选择端(H/L)

sbit rw = P2^1; //LCD读/写选择端(H/L)

sbit ep = P2^2; //LCD使能控制

sbit PRE = P3^3; //调整键(AN3)

sbit SET = P3^4; //调整键(AN4)

sbit SPK = P3^6;

void delayms(unsigned char ms); //延时程序 bit lcd_busy(); //测试LCD忙碌状态程序

void lcd_wcmd(char cmd); //写入指令到LCD程序

void lcd_wdat(char dat); //写入数据到LCD程序

void lcd_pos(char pos); //LCD数据指针位置程序

void lcd_init(); //LCD初始化设定程序

功能(闹钟)

unsigned char scan_key(); //按键扫描程序

unsigned char week_proc(); //星期自动计算与显示函数

bit leap_year(); //判断是否为闰年

void lcd_sef_chr(); //LCD自定义字符程序 void update_disbuf(unsigned char t1,unsigned char t2[],unsigned char dis_h,unsigned char dis_m,unsigned char dis_s);

//更新显示缓冲区函数

// 延时程序

void delay(unsigned char ms) { while(ms--)

{ unsigned char i;

for(i = 0; i< 250; i++) {

_nop_(); //执行一条_nop_()指令为一个机器周期 _nop_(); _nop_(); _nop_(); } } }

//测试LCD忙碌状态 bit lcd_busy() {

bit result; rs = 0;

rw = 1; ep = 1; _nop_(); _nop_(); _nop_(); _nop_();

result =(bit)(P0&0x80); //LCD的D0--D7中,D7=1为忙碌,D7=0为空闲 ep = 0;

return result; }

//写入指令到LCD

void lcd_wcmd(char cmd)

{

while(lcd_busy()); //当lcd_busy为1时,再次检测LCD忙碌状态,lcd-busy为0时,开始写指令

rs = 0; rw = 0; ep = 0; _nop_(); _nop_(); P0 = cmd; _nop_(); _nop_(); _nop_(); _nop_(); ep = 1; _nop_(); _nop_(); _nop_(); _nop_(); ep = 0; }

//写入数据到LCD

void lcd_wdat(char dat) {

while(lcd_busy()); //当lcd_busy为1时,再次检测LCD忙碌状态,lcd-busy为0时,开始写数据

rs = 1; rw = 0; ep = 0; P0 = dat;

_nop_(); _nop_(); _nop_(); _nop_(); ep = 1; _nop_(); _nop_(); _nop_(); _nop_(); ep = 0; }

//LCD数据指针位置程序 void lcd_pos(char pos) {

lcd_wcmd(pos|0x80); //数据指针=80+地址码(00H~27H,40H~67H) }

//设定二个自定义字符,(注意:LCD1602中自定义字符的地址为0x00--0x07,即可定义8个字符) //这里我们设定把一个自定义字符放在0x00位置(000),另一个放在0x01位子(001) void lcd_sef_chr() { //第一个自定义字符

lcd_wcmd(0x40); //"01 000 000" 第1行地址 (D7D6为地址设定命令形式D5D4D3为字符存放位置(0--7),D2D1D0为字符行地址(0--7)) lcd_wdat(0x1f); //"XXX 11111" 第1行数据(D7D6D5为XXX,表示为任意数(一般用000),D4D3D2D1D0为字符行数据(1-点亮,0-熄灭) lcd_wcmd(0x41); //"01 000 001" 第2行地址

lcd_wdat(0x11); //"XXX 10001" 第2行数据

lcd_wcmd(0x42); //"01 000 010" 第3行地址

lcd_wdat(0x15); //"XXX 10101" 第3行数据

lcd_wcmd(0x43); //"01 000 011" 第4行地址

lcd_wdat(0x11); //"XXX 10001" 第4行数据

lcd_wcmd(0x44); //"01 000 100" 第5行地址

lcd_wdat(0x1f); //"XXX 11111" 第数据

lcd_wcmd(0x45); //"01 000 101" 第地址

lcd_wdat(0x0a); //"XXX 01010" 第数据

lcd_wcmd(0x46); //"01 000 110" 第5行6行6行7行数据 }

地址

lcd_wdat(0x1f); //"XXX 11111" 数据

lcd_wcmd(0x47); //"01 000 111" 地址

lcd_wdat(0x00); //"XXX 00000" 数据

//第二个自定义字符

lcd_wcmd(0x48); //"01 001 000" 地址

lcd_wdat(0x01); //"XXX 00001" 数据

lcd_wcmd(0x49); //"01 001 001" 地址

lcd_wdat(0x1b); //"XXX 11011" 数据

lcd_wcmd(0x4a); //"01 001 010" 地址

lcd_wdat(0x1d); //"XXX 11101" 数据

lcd_wcmd(0x4b); //"01 001 011" 地址

lcd_wdat(0x19); //"XXX 11001" 数据

lcd_wcmd(0x4c); //"01 001 100" 地址

lcd_wdat(0x1d); //"XXX 11101" 数据

lcd_wcmd(0x4d); //"01 001 101" 地址

lcd_wdat(0x1b); //"XXX 11011" 数据

lcd_wcmd(0x4e); //"01 001 110" 地址

lcd_wdat(0x01); //"XXX 00001" 数据

lcd_wcmd(0x4f); //"01 001 111" 地址

lcd_wdat(0x00); //"XXX 00000"

//LCD初始化设定 第7行void lcd_init()

{

第8行 lcd_wcmd(0x38); //设置LCD为16X2显示,5X7点阵,八位数据借口 第8行

delay(1);

lcd_wcmd(0x0c); //LCD开显示及光标设置(光标不闪烁,不显示"-") 第1行 delay(1);

lcd_wcmd(0x06); //LCD显示光标移动第1行设置(光标地址指针加1,整屏显示不移动) delay(1);

第2行 lcd_wcmd(0x01); //清除LCD的显示内容

第2行 delay(1); } 第3行

//闰年的计算 第3行bit leap_year() {

第4行 bit leap;

if((year%4==0&&year%100!=0)||year%400=第4行=0)//闰年的条件 leap=1; 第5行 else

leap=0; 第5行 return leap; } 第6行

//星期的自动运算和处理 第6行unsigned char week_proc() { unsigned char num_leap; 第7行 unsigned char c;

num_leap=year/4-year/100+year/400;//自第7行00年起到year所经历的闰年数

if( leap_year()&& month<=2 ) //既第8行是闰年且是1月和2月 c=5; 第

8行

else

c=6;

week=(year+para_month[month]+date+num_leap+c)%7;//计算对应的星期 return week; }

//更新显示缓冲区 void update_disbuf(unsigned char t1,unsigned char t2[],unsigned char dis_h,unsigned char dis_buf2[14]=dis_s/10+48; dis_buf2[15]=dis_s%10+48; }

//时间和日期处理程序 void pro_timedate() {

sec++;

dis_m,unsigned char dis_s)

{ dis_buf1[0]=t1; // dis_buf1[1]=0x20; //空格 dis_buf1[2]=50; //'2' dis_buf1[3]=48; //'0' dis_buf1[4]=year/10+48; dis_buf1[5]=year%10+48; dis_buf1[6]=0x2d;

dis_buf1[7]=month/10+48; dis_buf1[8]=month%10+48;

dis_buf1[9]=0x2d; //'-' dis_buf1[10]=date/10+48; dis_buf1[11]=date%10+48; dis_buf1[12]=0x20;

dis_buf1[13]=dis_week[4*week]; dis_buf1[14]=dis_week[4*week+1]; dis_buf1[15]=dis_week[4*week+2];

dis_buf2[0]=t2[0]; dis_buf2[1]=t2[1]; dis_buf2[2]=t2[2]; dis_buf2[3]=t2[3]; dis_buf2[4]=t2[4]; dis_buf2[5]=t2[5];

dis_buf2[6]=t2[6]; //空格 if (alarm)

dis_buf2[7]=0x01; //alarm=1,显示闹钟启用标致(第二个自定义字符) else

dis_buf2[7]=0x20; //alarm=0,不显示闹钟启用标致

dis_buf2[8]=dis_h/10+48; dis_buf2[9]=dis_h%10+48;

dis_buf2[10]=0x3a; //':' dis_buf2[11]=dis_m/10+48; dis_buf2[12]=dis_m%10+48; dis_buf2[13]=0x3a;

if(sec > 59) {sec = 0; min++; if(min>59) {min=0; hour++; if(hour>23) {hour=0; date++; if

(month==1||month==3||month==5||month==7||month==8||month==10||month==12) if (date>31) {date=1;month++;} //大月31天

if

(month==4||month==6||month==9||month==11)

if (date>30) {date=1;month++;} //小月30天

if (month==2) {if( leap_year()) //闰年的条件 {if (date>29) {date=1;month++;}} //闰年2月为29天

else {if (date>28) {date=1;month++;}} //平年2月为28天

} if (month>12) {month=1;year++;}

if (year>99) year=0; } }

}

week_proc();

if (sec==armsec && min==armmin && } hour==armhour) {if (alarm)

TR1=1; //闹钟启用时,报警时间到,启动Timer1 } }

//显示处理程序 void pro_display() { unsigned char i; lcd_pos(0x00);

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

{lcd_wdat(dis_buf1[i]);}

lcd_pos(0x40);

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

{lcd_wdat(dis_buf2[i]);} }

//Timer0中断处理程序,秒的产生 void timer0() interrupt 1 {

TH0=0xD8; TL0=0xF0; sec100++;

if(sec100 >= 100) //1秒时间(100*10ms=1000ms=1s) {sec100 = 0;

pro_timedate();//调用时间和日期处理程序 }

if (sec&0x01)

//"__run__"闪一秒,停一秒 update_disbuf(0x00,"

",hour,min,sec); //0x00表示显示00位置的自定义字符 else

update_disbuf(0x00,"__run__",hour,min,sec);

pro_display(); //调用显示处理函数

//按键扫描程序

unsigned char scan_key() {

skey=0x00; //给变量vkey置初值

skey|=PRE; //读取PRE键的状态

skey=skey<<1; //将PRE键的状态存于skey的B1位 skey|=SET;

//读取SET键的状态,并存于skey的B0位

return skey;

//返回skey的键值(即PRE,SET的状态) }

//外部中断INT0中断处理程序 void int0() interrupt 0 {

TR0=0; //禁止Timer0

IE=0; //禁止中断

lcd_wcmd(0x0e); //显示光标"_",整个光标不闪烁 alarm=1;

update_disbuf(0x50,"alarm_",armhour,armmin,armsec); //更新显示数据,0x50表示要显示"P"

pro_display(); //调用显示处理程序

lcd_pos(0x47); //使光标位于第一个调整项下

flag=0;

vkey=0x03;

while(flag^0x0a)

{skey = scan_key(); //扫描按键状态

if (skey^vkey)

//若skey与vkey相同,跳出循环,相异执行循环体

{ delay(10); //去按键抖动

skey = scan_key(); //转回扫描按键状态 if (skey^vkey)

//若skey与vkey相同,跳出循环,相异执行循环体

{ vkey=skey; //将skey的值付给vkey if (skey==0x01) //PRE键按下

{ flag++; //调整标志位加1

switch (flag) //将光标置于相应调整位置

{

case 1: lcd_pos(0x49);break; //光标置小时报警设置位置

case 2: lcd_pos(0x4c);break; //光标置分钟报警设置位置

case 3: lcd_pos(0x4f);break; //光标置秒时报警设置位置

case 4: update_disbuf(0x50,"time_ ",hour,min,sec);

pro_display();

lcd_pos(0x05);break; //光标置年调整位置

case 5: lcd_pos(0x08);break; //

光标置月调整位置

case 6: lcd_pos(0x0b);break; //光标置日调整位置

case 7: lcd_pos(0x49);break; //光标置时调整位置

case 8: lcd_pos(0x4c);break; //光标置分调整位置

case 9: lcd_pos(0x4f);break; //光标置秒调整位置

default:break;

} } if (skey==0x02) //SET键按下

{

pro_key(); //转设置按键处理程序 } }

}

}

lcd_wcmd(0x0c);

//设置LCD开显示及光标不闪烁,不显示"-"

lcd_wcmd(0x01); //清除LCD的显示内容

IE=0x8f; //CPU开中断,INT0,INT1,开中断 TR0=1; //Timer0启动 }

//主程序,初始化及初值设定 void main() {

lcd_init(); //初始化LCD

lcd_sef_chr(); //写入自定义字符号

hour=0;min=0;sec=0; //开机时的时,分,秒显示

armhour=0;armmin=0;armsec=0; //开机时的时,分,秒报警初值

year= 5; month=1;date=1; //开机时的年,月,日,星期显示 week_proc();

alarm=1; //初始开机,启用闹钟

IE = 0x8f;

//CPU开中断,INT0,INT1,Timer0,Timer1开中断

IP = 0x04; //设置INT0为中断最高优先级

IT0=0;IT1=0; //外部INT0,INT1设置为电平触发方式(注意,触发不要选边沿方式,易误动)

TMOD = 0x11; //Timer0,Timer1工作于模式1, 16位定时方式

TH0 = 0xdc;TL0 = 0x00; //Timer0置10ms定时初值 TH1 = 0xff;TL1 = 0x00; //Timer1置初值

TR0 = 1; //Timer0启动 TR1 = 0; while(1); }

//设置按键处理程序 void pro_key() {

switch (flag) {

case 0:alarm=!alarm; //启用或关闭闹钟(alarm=1:启用,alarm=0:关闭)

update_disbuf(0x50,"alarm_",armhour,armmin,armsec); //更新显示数据

pro_display();

//调用显示处理

lcd_pos(0x47);break;

//光标回到原调整位置

case 1:armhour++;

if (armhour>23) armhour=0;

update_disbuf(0x50,"alarm_",armhour,armmin,armsec); //更新显示数据

pro_display();

//调用显示处理 lcd_pos(0x49);break;

//光标回到原调整位置

case 2:armmin++;

if (armmin>59) armmin=0;

update_disbuf(0x50,"alarm_",armhour,armmin,armsec);

pro_display();

lcd_pos(0x4c);break; case 3:armsec++;

if (armsec>59) armsec=0;

update_disbuf(0x50,"alarm_",armhour,armmin,armsec);

pro_display();

lcd_pos(0x4f);break;

case 4:year++;

if (year> 99) year= 0;

week_proc(); //星期自动运算

update_disbuf(0x50,"time_ ",hour,min,sec);

pro_display();

lcd_pos(0x05);break; case 5:month++;

if (month>12) month=1; week_proc(); //星期自动运算

update_disbuf(0x50,"time_ ",hour,min,sec);

pro_display();

lcd_pos(0x08);break; case 6:date++; if

(month==1||month==3||month==5||month==7||month==8||month==10||month==12)

if (date>31) date=1; //大月31天 if

(month==4||month==6||month==9||month==11)

if (date>30) date=1; //小月30天

if (month==2) {if(leap_year()) //闰年的条件 {if (date>29) date=1;} //闰年2月为29天 else {if (date>28) date=1;}} //平年2月为28天 week_proc(); //星期自动运算

update_disbuf(0x50,"time_ ",hour,min,sec);

pro_display();

lcd_pos(0x0b);break;

case 7:hour++;

if (hour>23) hour=0;

update_disbuf(0x50,"time_ ",hour,min,sec);

pro_display();

lcd_pos(0x49);break; case 8:min++;

if (min>59) min=0;

update_disbuf(0x50,"time_ ",hour,min,sec);

pro_display();

lcd_pos(0x4c);break; case 9:sec++;

if (sec>59) sec=0;

update_disbuf(0x50,"time_ ",hour,min,sec);

pro_display();

lcd_pos(0x4f);break; default: break ; } }

//Timer1中断处理程序,产生报警的声音 void timer1() interrupt 3 {

TH1=0x80; TL1=0x00; SPK=~SPK; t++;

}

//外部中断INT1中断处理程序,停止报警声音void int1() interrupt 2 {

if(TR1)

TR1=0; }

相关推荐