您的当前位置:首页单片机电子时钟设计报告

单片机电子时钟设计报告

来源:小侦探旅游网
电子时钟实验报告

全部代码在文档末尾:51单片机,LCD1602液晶显示屏平台下编程实现,可直接编译运行目录:

一, 实验目的 ................................................. 2 二,实验要求 .................................................. 2 三,实验基本原理 .............................................. 3 四,实验设计分析 .............................................. 3 五,实验要求实现 .............................................. 4

A.电路设计 ................................................. 4

1. 整体设计 ............................................ 5 2. 分块设计 ............................................ 5

输入部分 ........................................... 5 输出部分 ........................................... 5 晶振与复位电路 ..................................... 6

B.程序设计 ................................................. 6

程序总体设计 .......................................... 6 程序主要模块 .......................................... 6

五.实验总结及感想 ............................................. 6 一, 实验目的

20世纪末,电子技术获得了飞速的发展,在其推动下,现代电子产品几乎渗透了社会的各个领域,有力地推动了社会生产力的发展和社会信息化程度的提高,同时也使现代电子产品性能进一步提高,产品更新换代的节奏也越来越快。

现代生活的人们越来越重视起了时间观念,可以说是时间和金钱划上了等号。对于那些对时间把握非常严格和准确的人或事来说,时间的不准确会带来非常大的麻烦,所以电子钟是以其小巧,价格低廉,走时精度高,使用方便,功能多,便于集成化而受广大消费的喜爱,得到了广泛的使用。

1. 学习8051定时器时间计时处理、按键扫描及LCD液晶显示的设计方法。 2. 设计任务及要求 利用实验平台上LCD1602液晶显示屏, 设计带有闹铃功能的数字时钟 二,实验要求 A.基本要求:

1. 在LCD1602液晶显示屏上显示当前日期,时间。

2. 利用按键可对时间及闹玲进行设置,并可显示设置闹玲的时间。闹玲时间到蜂鸣

器发出声响,一分钟后闹铃停止。 B.扩展部分:

1.日历功能(能对年,月,日,星期进行显示,分辨平年,闰年以及各月天数,并调整)实现年月日时分秒的调整,星期准确的随着日期改变而改变进行显示。 2.定时功能(设定一段时间长度,定时到后,闹铃提示) C.可扩展部分:

1.闹铃重响功能(闹铃被停止后,以停止时刻开始,一段时间后闹铃重响,且重响时间的间隔可调)

2.可进行备忘录提示,按照年月日,可在设定的某年某月进行闹铃提示。 三,实验基本原理

利用单片机定时器完成计时功能,定时器0计时中断程序每隔中断一次并当作一个计数,设定定时1秒的中断计数初值为20,每中断一次中断计数初值加1,当减到20时,则表示1s到了,秒变量加1,同理再判断是否1min钟到了,再判断是否1h到了,是否一天到了,是否一个月到了,是否一年到了。

将时间在LCD液晶屏上显示,降低了程序的编写难度。LCD的固定显示特性是我们省去了数码管的动态扫描显示。 四,实验设计分析

针对要实现的功能,采用AT89S52单片机进行设计,AT89S52 单片机是一款低功耗,高性能CMOS8位单片机,片内含4KB在线可编程(ISP)的可反复擦写1000次的Flash只读程序存储器,器件采用高密度、非易失性存储技术制造,兼容标准MCS- 51指令系统及80C51引脚结构。这样,既能做到经济合理又能实现预期的功能。

在程序方面,采用分块设计的方法,这样既减小了编程难度、使程序易于理解,又能便于添加各项功能。程序可分为闹钟的声音程序、时间显示程序、日期显示程序,秒表显示程序,时间调整程序、闹钟调整程序、定时调整程序,延时程序等。运用这种方法,关键在于各模块的兼容和配合,若各模块不匹配会出现意想不到的错误。

首先,在编程之前必须了解硬件结构尤其是各引脚的用法,以及内部寄存器、存储单元的用法,否则,编程无从下手,电路也无法设计。这是前期准备工作。第二部分是硬件部分:依据想要的功能分块设计设计,比如输入需要开关电路,输出需要显示驱动电路和数码管电路等。第三部分是软件部分:先学习理解C语言的编程方法再根据设计的硬件电路进行分块的编程调试,最终完成程序设计。第四部分是软件画图部分:设计好电路后进行画图,包括电路图和仿真图的绘制。第五部分是软件仿真部分:软硬件设计好后将软件载入芯片中进行仿真,仿真无法完成时检查软件程序和硬件电路并进行修改直到仿真成功。第六部分是硬件实现部分:连接电路并导入程序检查电路,若与设计的完全一样一般能实现想要的功能。最后进行功能扩展,在已经正确的设计基础上,添加额外的功能! 五,实验要求实现

A.电路设计

1. 整体设计

此次设计主要是应用单片机来设计电子时钟,硬件部分主要分以下电路模块:显示电路用lcd1602显示,年份,月份,星期,小时、分钟和秒(日),使电路更加简单。单片机采用AT89S51系列,这种单片机应用简单,适合电子钟设计。

电路的总体设计框架如下:

2. 分块设计 单片机输出部分模块电路主要分为:输入部分、输出部分、复位和晶振电路。 输入部分 晶振和复位 输入部分 输入信号主要是各种模式选择和调整信号,由按键开关提供。以下为输入部

分样例:

在本实验中主要用用P3口输入按键信号,还用到了特殊的P0口。对于P0口,由于其存在高阻状态,为了实现开关功能,给其添加上拉电阻,具体如下图所示: 输出部分

本电路的输出信号LCD的写入命令和写入数据,闹铃脉冲信号。 闹铃由端输出,模块如下:

日期时间由LCD1602输出,模块如下:

晶振与复位电路

本实验单片机时钟用内部时钟,模块如下: 复位电路为手动复位构成,模块如下: B.程序设计 程序总体设计 本实验用汇编程序完成. 程序总的流程图如下: 程序主要模块 中断服务程序:

/******************一秒定时中断函数*****************/ void timer0() interrupt 1 using 1 {

ET0=0;

验总结及感想

一分耕耘,一分收获。只有亲自用实践来验证这句话,在能得其要领。经过这次单片机课程设计,我从一个单片机实践的门外汉,已经越升为略知一二的新手。虽然还有很多有关单片机的应用有待学习,但万变不离其宗,只要深入了解单片的原理,全部知识点,各个细节,一切设计皆有可能。

在实验的开始几天,基本上没有收获,不知何从下手,不知所措。为了看得更远,不妨站在前人的肩膀上,我在整体思路模糊的情况下,在网上大量招资粮,各种与电子时钟相关的文章,我阅读了不少。随着涉猎的点滴积累,我对电子时钟的设计方案已经慢慢酝酿而成。有了方向和不少知识储备后,在接下来的几天,几乎每天都有突破,虽然有时只是一句程序的修改或诞生,但那种收获的感觉很暖人心。

实验中遇到了不少问题,接下来总结一下,共同探讨。

1, P0口开关问题。P0口比较特殊,它存在高阻态,要使其输入不是高电平就是低电平,就要接上拉电阻,给其高电平输入。

以上就是实验中遇到的主要问题,基本上都找到了相应的解决之道。整个实验的过程就是一个解决问题的过程,每天都解决一些问题,我的实验也就解决了,当然结果不重要,功利化的追求结果,会使人浮躁,还是享受那份疑难迎刃而解的快感吧!

全部完整代码:

此代码未经本人优化,可以根据自己的需求优化代码。盗版不究,可直接引用。 /****************************************************************/ 基于STC89S52单片机以及液晶1602显示的万年历项目

/***************************************************************/ #include<>

#include<>

#define uchar unsigned char #define uint unsigned int

/***************液晶1602相关参数***************/ #define data_port P0 sbit rs=P1^0;

//液晶屏指令、数据通信接口

//液晶屏寄存器选择接口(rs=0时选择

指令寄存器,rs=1时选择数据寄存器) sbit rw=P1^1;

//液晶屏读写选择接口(rw=0时选择写

入,rw=1时选择读出) sbit en=P1^2;

//液晶屏使能接口(en=0时通信接口中

断,en=1时通信接口接通)

uchar code line1_str[]={\"----Welcome!----\液晶屏第一行要显示的字符串 uchar code line2_str[]={\" made by: MCU \液晶屏第二行要显示的字符串 /***************按键设置的相关参数************/ sbit timer=P1^5; sbit sound=P3^4;

//蜂鸣器

//进入闹铃设置状态键

//闹铃设置标志位;闹铃工作标

uchar sflag=0,ssflag=0;

志位

sbit set_key=P3^7; sbit up_key=P3^6;

sbit cancle_key=P3^5; uchar set=0; uchar flag=0;

uchar year=0x00; uchar month=0x01; uchar day=0x01; uchar week=0x06; uchar hour; uchar minite; uchar second;

uchar jiamiao=0; uchar fen1,fen2; uchar shi1,shi2;

//进入设置 //加号键

//完成设置键

//是否进行了设置

//设置选择标志

//\"年\"寄存器 //\"月\"寄存器

//\"日\"寄存器

//\"星期\"寄存器 //\"时\"寄存器 //\"分\"寄存器

//\"秒\"寄存器

//加秒参数

//(数据交换)分定时寄存器 // (数据交换)时定时寄存器

uchar code num_tab[]={\"09\//液晶屏要显示的数字表

uchar dis_val[7]={0x00,0x01,0x01,0x06,0x00,0x00,0x00};//依次存储:年,月,日,星期,时,分,秒

/******************子函数部分*****************/ void lcd_int(); void welcome();

//初始化1602液晶 //欢迎界面

//向液晶屏输入命令

void in_command(uchar com); void in_data(uchar dat); void delay(uint ms); void key();

//向液晶屏输入数据 //延时函数 //按键设置函数

//星期计算函数

uchar calculate_week(); void week_disp(uchar x);

//LCD1602显示星期值子函数声明

void lcd_disp_string(uchar str[]); //液晶屏显示一串字符子函数声明 void brush_lcd(); void run();

//刷新液晶屏显示内容子函数声明 //万年历正常运行函数

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

void main() {

TMOD=0x01;

//0000 0001 使用定时器T0的模式1(16位模式)

TH0=(65536-50000)/256; //设定每次中断间隔50ms TL0=(65536-50000)%256; jiamiao=1;

//打开总中断允许

//打开定时器T0中断允许位 //启动定时器T0

EA=1;

ET0=1; TR0=1;

lcd_int(); welcome(); delay(1000); while(1) {

key();

if(jiamiao==20)

{

second++;

dis_val[6]=((second/10)<<4)|(second%10); //刷新\"秒\" jiamiao=0; run();

}

if(flag==0) {

if(dis_val[0]==0x00

&&

dis_val[1]==0x01

dis_val[2]==0x01)//2000年1月1日为星期六 {dis_val[3]=0x06;} brush_lcd();

//刷新液晶屏显示内容

}

if(hour==shi2 && minite==fen2 &&ssflag==1) {

timer=0;

&&

}

} else { }

timer=1;

}

/********延时子函数************/ void delay(uint ms) { }

uchar i; while(ms--) { }

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

/************LCD初始化*****************/ void lcd_int() { }

/**********液晶屏显示一串字符**********/ void lcd_disp_string(uchar str[]) {

uchar i=0;

while(str[i]!='\\0') {

in_data(str[i]);

//显示数据(字符)

delay(30);

//延时15MS,等待LCD初始化

in_command(0x01); //清显示屏

in_command(0x38); //8位通信,2行显示,5*7点阵 in_command(0x0c); //开显示,关光标,关闪烁

in_command(0x06); //字符不动,光标右移动,地址加一

}

}

i++;

/***************欢迎界面函数***************/ void welcome() {

uchar i;

in_command(0x80);

//设定第一行字符串起始显示位置

while(line1_str[i]!='\\0') { } i=0;

in_command(0xc0);

//设定第二行字符串起始显示位置

delay(100);

in_data(line1_str[i]); //显示数据(字符) i++;

}

while(line2_str[i]!='\\0') { }

delay(100);

in_data(line2_str[i]); //显示数据(字符) i++;

/********向液晶屏输入命令子函数************/ void in_command(uchar com) {

delay(2);

en=0;//关闭通信,为设置参数做准备 rs=0;//选择指令寄存器 rw=0;//写入液晶 _nop_(); data_port=com;

}

en=1;//开始通信 _nop_();

en=0;//关闭通信,为设置参数做准备

/********向液晶屏输入数据子函数************/ void in_data(uchar dat) {

delay(2);

en=0;//关闭通信,为设置参数做准备 rs=1;//选择数据寄存器 rw=0;//写入液晶 _nop_(); data_port=dat; _nop_();

en=1;//开始通信 _nop_();

}

en=0;//关闭通信,为设置参数做准备

/***********计算星期值子函数**************/ uchar calculate_week() {

uint days_number; //存储天数值 uchar i; if(year>=1) { } else { }

days_number=0;

days_number=(((year-1)/4)+1);

//从00年到year-1年的闰年个数

days_number=days_number+(year*365); //从00年到year-1年的总天数

for(i=1;iswitch(i) {

case 1: case 3: case 5: case 7: case 8: case 10:

case 12:days_number+=31;//大月31天

break;

case 4: case 6: case 9:

case 11:days_number+=30;//小月30天

}

}

}

break;

//闰年2月29天

case 2: if(year%4==0)

{days_number+=29;} else

//平年2月28天

{days_number+=28;} break;

days_number+=day; //加上本月日期

//计算现在的星期值,并返回其值

if((days_number+5)%7==0) {return(7);} else

{return((days_number+5)%7);}

/*********LCD1602显示星期值子函数************/ void week_disp(uchar x)

{

switch(x)

{

case 1: lcd_disp_string(\"MON\"); //显示\"星期一\"

break;

case 2: lcd_disp_string(\"TUE\"); //显示\"星期二\"

break;

case 3: lcd_disp_string(\"WED\"); //显示\"星期三\"

break;

case 4: lcd_disp_string(\"THU\"); //显示\"星期四\"

break;

case 5: lcd_disp_string(\"FRI\"); //显示\"星期五\"

break;

case 6: lcd_disp_string(\"SAT\"); //显示\"星期六\"

break;

case 7: lcd_disp_string(\"SUN\"); //显示\"星期日\"

}

break;

}

/*****************按键设置函数**************/ void key() {

if(set_key==0) //检测\"设置\"键是否按下 {

delay(5);

//延时消抖

if(set_key==0) {

ET0=0; TR0=0;

//打开定时器T0中断允许位 //启动定时器T0

if(flag<6) {flag++;} else

{flag=0;} switch(flag) { case 0: {

时\"

分\"

秒\"

}

if(set==1) //检测时间是否更改 set=0;

//时间更改标志复位

dis_val[0]=((year/10)<<4)|(year%10); //刷新\"年\" dis_val[1]=((month/10)<<4)|(month%10); //刷新\"月\" dis_val[2]=((day/10)<<4)|(day%10); //刷新\"日\"

dis_val[3]=week;

//刷新\"星期\"

dis_val[4]=((hour/10)<<4)|(hour%10);

//刷新\"

dis_val[5]=((minite/10)<<4)|(minite%10); //刷新\"

dis_val[6]=((second/10)<<4)|(second%10); //刷新\"

in_command(0x0c); //开显示,关光标,关闪烁 break;

case 1: in_command(0x83); //设定显示(光标)位置----\"年\"低

位闪烁

in_command(0x0d);

//开显示,关光标,开闪烁

year=((dis_val[0]>>4)*10)+(dis_val[0]&0x0f); //预设

调节变量初始值为当前时刻

month=((dis_val[1]>>4)*10)+(dis_val[1]&0x0f); day=((dis_val[2]>>4)*10)+(dis_val[2]&0x0f); hour=((dis_val[4]>>4)*10)+(dis_val[4]&0x0f); minite=((dis_val[5]>>4)*10)+(dis_val[5]&0x0f); second=((dis_val[6]>>4)*10)+(dis_val[6]&0x0f); break;

case 2: in_command(0x86); //设定显示(光标)位置----\"月\"低

位闪烁

break;

case 3: in_command(0x89); //设定显示(光标)位置----\"日\"低

位闪烁

break;

case 4: in_command(0xc5); //设定显示(光标)位置----\"时\"低

位闪烁

break;

case 5: in_command(0xc8); //设定显示(光标)位置----\"分\"低

位闪烁

break;

case 6: in_command(0xcb); //设定显示(光标)位置----\"秒\"低位

闪烁

}

if(up_key==0 && flag!=0)

//检测\"设置时间\"启动时,\"上升\"键是否

}

}

while(set_key==0);

break;

按下

{

delay(5);

//延时消抖

if(up_key==0) {

switch(flag) {

case 1: if(sflag==1)

{break;} if(year<99) {year++;} else {year=0;}

if(year%4!=0 && month==2)//平年的2月 {

if(day>28) {

----\"日\"高位

位闪烁

day=28;

in_command(0x88);

//设定显示(光标)位置

in_data(num_tab[day/10]); //修正\"日\" in_data(num_tab[day%10]);

}

}

week=calculate_week(); //计算星期值 in_command(0x8c); //设定显示(光标)位置----\"星期\" week_disp(week); //LCD1602显示星期值

in_command(0x82);

//设定显示(光标)位置----\"年\"高

in_data(num_tab[year/10]); //显示正在调节的\"年\"

in_data(num_tab[year%10]); in_command(0x83);

//设定显示(光标)位置----\"年\"低

break;

高位

case 2: if(sflag==1) {break;} if(month<12) {month++;} else {month=1;}

if(month==4 || month==6 || month==9 || month==11) { if(day>30) { day=30;

in_command(0x88);//设定显示(光标)位置----\"日\"

in_data(num_tab[day/10]); //修正\"日\" in_data(num_tab[day%10]);

}

}

if(month==2) {

----\"日\"高位

if(year%4!=0)

//平年

{ if(day>28) { day=28;

in_command(0x88); //设定显示(光标)位置

in_data(num_tab[day/10]); //修正\"日\" in_data(num_tab[day%10]);

}

} else

//闰年

{

----\"日\"高位

if(day>29) { day=29;

in_command(0x88); //设定显示(光标)位置

in_data(num_tab[day/10]); //修正\"日\" in_data(num_tab[day%10]);

}

}

}

week=calculate_week();//计算星期值

in_command(0x8c); //设定显示(光标)位置----\"星期\" week_disp(week); //LCD1602显示星期值

in_command(0x85); //设定显示(光标)位置----\"月\"高位 in_data(num_tab[month/10]); //显示正在调节的\"月\" in_data(num_tab[month%10]);

in_command(0x86); //设定显示(光标)位置----\"月\"低位

闪烁

break;

case 3: if(sflag==1)

{break;}

if(month==4 || month==6 || month==9 || month==11) { }

else if(month==2) {

if(year%4!=0) {

//平年

if(day<30) {day++;} else {day=1;}

if(day<28) {day++;} else {day=1;}

}

else { if(day<29) {day++;} else {day=1;}

}

}

else {

if(day<31)

//闰年

}

{day++;} else {day=1;}

week=calculate_week();//计算星期值

in_command(0x8c); //设定显示(光标)位置----\"星期\" week_disp(week); //LCD1602显示星期值

in_command(0x88); //设定显示(光标)位置----\"日\"高位 in_data(num_tab[day/10]); //显示正在调节的\"日\" in_data(num_tab[day%10]);

in_command(0x89); //设定显示(光标)位置----\"日\"低位

闪烁

break;

case 4: if(hour<23)

{

if(sflag==1)

//完成闹钟状态下的数值变换,并保

存设置前的状态

闪烁

{ shi1=hour; hour++; shi2=hour;

} else {hour++;}

} else {hour=0;}

in_command(0xc4); //设定显示(光标)位置----\"时\"高位 in_data(num_tab[hour/10]); //显示正在调节的\"时\"

in_data(num_tab[hour%10]);

in_command(0xc5); //设定显示(光标)位置----\"时\"低位

break;

case 5: if(minite<59)

设置前的状态

{

if(sflag==1)

//完成闹钟状态下的数值变换,并保存

{ fen1=minite; minite++; fen2=minite;

} else

{minite++;}

} else {minite=0;}

in_command(0xc7); //设定显示(光标)位置----\"分\"高位

闪烁

闪烁

}

in_data(num_tab[minite/10]); //显示正在调节的\"分\" in_data(num_tab[minite%10]);

in_command(0xc8); //设定显示(光标)位置----\"分\"低位

break;

case 6: if(second<59) {second++;} else {second=0;}

in_command(0xca); //设定显示(光标)位置----\"秒\"高位 in_data(num_tab[second/10]); //显示正在调节的\"秒\" in_data(num_tab[second%10]);

in_command(0xcb); //设定显示(光标)位置----\"秒\"低位

break;

}

}

set=1; //时间更改标志置位

while(up_key==0);

if(sound==0 && flag!=0)//启动定时状态标志 { }

if(cancle_key==0 && flag!=0)

//检测\"设置时间\"启动时,\"完成\"键是否

sflag=1;

按下

{

delay(5);

//延时消抖

if(cancle_key==0) {

set=0;

//时间更改标志复位

//设置按键状态值复位

flag=0;

}

}

}

in_command(0x0c); while(cancle_key==0); if(sflag==1) { } ET0=1; TR0=1;

minite=fen1; hour=shi1;

//开显示,关光标,关闪烁

//判断是否进行过闹钟定时设置

ssflag=1; //启动闹钟工作标志 sflag=0; //关闭闹钟定时状态标志

//打开定时器T0中断允许位 //启动定时器T0

/********刷新液晶屏显示内容子函数*********/ void brush_lcd()

{

static uchar old_second=0x01; if(dis_val[6]!=old_second) {

old_second=dis_val[6]; in_command(0x80);

//刷新原来的\"秒\"为新\"秒\"值

//设定第一行字符串起始显示位置

in_data('2'); //年的千位 in_data('0'); //年的百位 in_data(num_tab[dis_val[0]/16]); //显示\"年\" in_data(num_tab[dis_val[0]%16]); in_data('-');

//显示分隔符

in_data(num_tab[dis_val[1]/16]); //显示\"月\" in_data(num_tab[dis_val[1]%16]); in_data('-');

//显示分隔符

in_data(num_tab[dis_val[2]/16]); //显示\"日\" in_data(num_tab[dis_val[2]%16]);

in_data(' '); in_data('[');

//显示分隔符 //显示分隔符

//LCD1602显示星期值 //显示分隔符

//设定第二行字符串起始显示位置 //显示分隔符 //显示分隔符 //显示分隔符 //显示分隔符

week_disp(dis_val[3]); in_data(']');

in_command(0xc0);

in_data('-'); in_data('-'); in_data('-'); in_data('-');

in_data(num_tab[dis_val[4]/16]); //显示\"时\" in_data(num_tab[dis_val[4]%16]); in_data(':');

//显示分隔符

in_data(num_tab[dis_val[5]/16]); //显示\"分\" in_data(num_tab[dis_val[5]%16]); in_data(':');

//显示分隔符

in_data(num_tab[dis_val[6]/16]); //显示\"秒\"

}

}

in_data(num_tab[dis_val[6]%16]); in_data('-'); in_data('-'); in_data('-'); in_data('-');

//显示分隔符 //显示分隔符 //显示分隔符 //显示分隔符

/******************一秒定时中断函数*****************/ void timer0() interrupt 1 using 1 {

ET0=0; TR0=0;

//关闭定时器T0中断允许位 //关闭定时器T0

jiamiao++;

TH0=(65536-50000)/256; //设定每次中断间隔50ms TL0=(65536-50000)%256; TR0=1;

//启动定时器T0

}

ET0=1; //打开定时器T0中断允许位

/********************万年历正常运行函数********************/ void run() {

if(second>=59) { }

if(minite>=59) { }

hour++; minite=0x00; minite++; second=0x00;

if(hour>=23)

{ day++; hour=0x00;

}

if(month==4 || month==6 || month==9 || month==11){ if(day>=30)

{day=0x01;month++;} }

if(month==2) { if(year%4!=0)

//平年

{ if(day>=28)

{day=0x01;month++;}

}

}

else { }

//闰年

if(day>=29) { }

day=0x01; month++;

else { }

if(month>=12)

{month=0x01;year++;}

if(day>=31) {day=1;month++;}

}

dis_val[0]=((year/10)<<4)|(year%10); //刷新\"年\" dis_val[1]=((month/10)<<4)|(month%10); //刷新\"月\" dis_val[2]=((day/10)<<4)|(day%10); dis_val[3]=week;

//刷新\"日\"

//刷新\"星期\"

//刷新\"时\"

dis_val[4]=((hour/10)<<4)|(hour%10);

dis_val[5]=((minite/10)<<4)|(minite%10); //刷新\"分\" dis_val[6]=((second/10)<<4)|(second%10); //刷新\"秒\"

因篇幅问题不能全部显示,请点此查看更多更全内容