单片机万年历设计

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

单片机课程设计实验报告-基于STC89C52RC的电子万年历设计单片机实验报告
题目:基于STC89C52RC的电子万年历设计专业:通信工程
年级: 2008级
班级:
学生姓名:
学号:
指导老师:
一、设计任务与要求:
利用STC89C52RC单片机设计一个具有如下功能的电子万年历:
1、能够显示年、月、日、时、分、秒、星期。

2、能正确显示闰年日期。

3、可更改年、月、日、时、分、秒、星期。

4、可设定指定日期、时间的闹钟。

二、方案设计与论证
按照系统设计的要求和功能,将系统分为主控模块、时钟电路模块、按键扫描模块、LCD显示模块、蜂鸣器电路、电源电路、复位电路、晶振电路几个模块,系统框图如图1所示。

主控模块采用STC89C52RC单片机,按键模块用5个按键,用于调整时间和设定闹钟,显示模块采用LCD1602,时钟电路模块采用DS1302实时时钟实现对时间,日期的操作。

图1 基于AT89C52RC单片机的电子万年历系统框图
三、硬件单元电路设计与参数计算
1、主控模块
我们选用STC89C52RC单片机作为中央处理器,如图2所示。

图2 STC89C52RC主控模块
2、时钟振荡电路
时钟振荡电路图3所示,时钟振荡电路
用于产生单片机正常工作时所需要的时钟
信号,电路由两个30pF的瓷片电容和一个
12MHz的晶振组成,并接入到单片机的XTAL1
和XTAL2引脚处,使单片机工作于内部振荡
模式。

此电路在加电后延迟大约10ms振荡
器起振,在XTAL2引脚产生幅度为3V左右
的正弦波时钟信号,其振荡频率主要由石英
晶振的频率决定。

图3 STC89C52RC时钟振荡电路3、复位电路
复位电路由电阻和极性电容组成,如图4所示,通过高电平使单片机复位,在时钟电路开始工作后,当高电平的时间超过大约2us时,即可实现复位。

此复位电路同时具备了上电复位和手动复位的功能,上电复位发生在开机加电时,由系统自动完成,手动复位通过一个按键来实现,在程序运行时,若遇到死机,死循环或程序“跑飞”等情况,通过手动复位就可以实现重新启动的操作。

手动按钮复位需要人为在复位输入端RST上加入高电平。

一般采用的办法是在RST 端和正电源Vcc之间接一个按钮和一个电阻,如图所示,当人为按下按钮时,则Vcc 的+5V电平就会直接加到RST端,由于人的动作再快也会使按钮保持接通达数十毫秒,所以,完全能够满足复位的时间要求。

图4 STC89C52RC复位电路
4、DS1302时钟电路
时钟电路主要由时钟芯片DS1302、晶振等几部分组成,DS1302采用3线串行接口,占用引脚少,如图5所示。

图5 DS1302时钟电路
5、按键电路
按键电路由5个轻触开关组成,如图6所示。

按键用来调整时间和设定闹钟,其一端直接接到单片机的端口,另一端接地,当按下按键时,相应的端口变为低电平,通过检测这一低电平就可以判断是哪个键按下,从而作相应的操作。

图6 按键电路
6、显示电路
显示电路采用LCD1602液晶显示,图中只画出了其相应的接口,如图7所示。

3脚用于调节LCD1602的背光,4、5、6为LCD1602的控制口,用于控制其写入或是读出指令,7至14脚为LCD1602的数据口,将数传送到LCD1602中。

图7 LCD1602接口电路
7、蜂鸣器电路
蜂鸣器电路如图8所示。

通过控制三极管的导通和截止来实现蜂鸣器的响与不响。

图8 蜂鸣器电路
四、软件设计与流程图
1、程序主流程图
主程序首先初始化定时器、LCD1602及DS1302,然后就开始查询按键,有键按下则开始调整时间和设置闹钟,若没有按下,则执行下面的时间、日期及闹钟时间的显示,最后依次循环这些相同的操作,相应流程图如图9所示:
图9 程序主流程图
按键的检测主要是通过查询的办法来实现,利用按键进行间调整及闹钟设置,闹钟开关键是开启和关闭闹钟功能;设置1键按下则开始设置时间及日期,同时被选择的时间和日期开始闪烁,第一次按下设置1键时,设置年,第二次按下设置月,后面依次设置日、星期、时、分、秒;闹钟设置键按下则开始设置闹钟时间及日期,同时被选择的时间和日期开始闪烁,第一次按下闹钟设置键时,设置年,第二次按下设置月,后面依次设置日、星期、时、分、秒;若按下下调键是减1操作,按下上调键是加1操作,无论是对时钟和闹钟的年份或月份进行加减,都会使日期归1。

五、仿真电路图
基于STC89C52RC单片机的电子万年历硬件仿真电路图如图10所示,系统由STC89C52RC单片机、按键扫描电路、显示电路、时钟电路、晶振电路、复位电路、蜂鸣器电路组成。

图10 电子万年历仿真图
六、结论与心得
通过这次课程设计,我对单片机的理论知识有了进一步的掌握,对单片机的原理和实际应用有了更多地理解和掌握。

我对单片机的C语言编程有了更深刻的认识和更扎实的掌握,通过不断修改,不断尝试,对源代码各个模块的函数都有了一个非常深入的掌握。

在收获知识的同时,还收获了阅历,收获了成熟。

在此过程中,不仅培养了独立思考、动手制作的能力,在各种其它能力上也都有了提高。

更重要的是,在课程序设计里,我们学会了很多学习的方法。

而这是以后最实用的,真的是受益匪浅。

要面对社会的挑战,只有不断的学习、实践,再学习、再实践。

七、源程序
1、键盘扫描程序头文件:
/*-----------------------------------------------
矩阵键盘实验程序
通过反转法循环扫描矩阵键盘
------------------------------------------------*/
#include<> //包含头文件,一般情况不需要改动,头文件包含特殊功能寄存器的定义
#define uchar unsigned char
#define uint unsigned int
/*------------------------------------------------
函数声明
------------------------------------------------*/
uchar keyscan(void);//键盘扫描
void delay(uint i); //演示程序
/*------------------------------------------------
主函数
------------------------------------------------*/
/*------------------------------------------------
延时程序
------------------------------------------------*/
void delay1(uint i) //延时函数
{
while(i--);
}
/*------------------------------------------------
键盘扫描程序
------------------------------------------------*/
uchar keyboard(void) //键盘扫描函数,使用行列反转扫描法
{
uchar cord_h,cord_l;//行列值中间变量
P1=0x0f; //行线输出全为0
cord_h=P1&0x0f; //读入列线值
if(cord_h!=0x0f) //先检测有无按键按下
{
delay1(100); //去抖
if(cord_h!=0x0f)
{
cord_h=P1&0x0f; //读入列线值
P1=cord_h|0xf0; //输出当前列线值
cord_l=P1&0xf0; //读入行线值
P1=0X0F;
delay1(20);
while(P1!=0x0f);
//return(cord_h+cord_l);//键盘最后组合码值
switch(cord_h+cord_l)
{
/*case 0xee:return(12);break;//0按下相应的键显示相对应的码值case 0xde:return(8);break;//1 按下相应的键显示相对应的码值
case 0xbe:return(4);break;//2
case 0x7e:return(0);break;//3
case 0xed:return(13);break;//4
case 0xdd:return(9);break;//5
case 0xbd:return(5);break;//6
case 0x7d:return(1);break;//7
case 0xeb:return(14);break;//8
case 0xdb:return(10);break;//9
case 0xbb:return(6);break;//a
case 0x7b:return(2);break;//b
case 0xe7:return(15);break;//c
case 0xd7:return(11);break;//d
case 0xb7:return(7);break;//e
case 0x77:return(3);break;//f
default:return(0xff);break; */
case 0xee:return(0);break;//0按下相应的键显示相对应的码值case 0xde:return(1);break;//1 按下相应的键显示相对应的码值case 0xbe:return(2);break;//2
case 0x7e:return(3);break;//3
case 0xed:return(4);break;//4
case 0xdd:return(5);break;//5
case 0xbd:return(6);break;//6
case 0x7d:return(7);break;//7
case 0xeb:return(8);break;//8
case 0xdb:return(9);break;//9
case 0xbb:return(10);break;//a
case 0x7b:return(11);break;//b
case 0xe7:return(12);break;//c
case 0xd7:return(13);break;//d
case 0xb7:return(14);break;//e
case 0x77:return(15);break;//f
default:return(0xff);break;
}
}
}return(0xff); //返回该值
}
2、主程序如下:
#include<>
#include ""
#define uint unsigned int
#define uchar unsigned char
uchar
a,miao,shi,fen,ri=1,yue=1,nian,week=1,key1n=0,key2n=0,run,runl,bai,mstcnt,miao 1=0,shi1=0,fen1=0,ril=1,yuel=1,nianl,weekl=1,Wl,Rl,Yl,Nl,W,R,Y,N;
uint count1=0,count2=0,clk_set=0,clk_switch=0;
uchar MSB_reload_value=(65536-100)/256,LSB_reload_value=(65536-100)%256;
uchar key=0xff;
#define yh 0x80+16 //LCD第一行的初始位置,因为LCD1602字符地址首位D7恒定为1(0=80)
#define er 0x80+0x40+16 //LCD第二行初始位置(因为第二行第一个字符位置地址是0x40)#define yhl 0x80
#define er1 0x80+0x40
//液晶屏的与C51之间的引脚连接定义(显示数据线接C51的P0口)
sbit rs=P2^0;
sbit en=P2^2;
sbit rw=P2^1; //如果硬件上rw接地,就不用写这句和后面的rw=0了
sbit ACC0=ACC^0;
sbit ACC7=ACC^7;
/************************************************************
ACC累加器=A
=E0H
就是ACC的第0位。

Acc可以位寻址。

累加器ACC是一个8位的存储单元,是用来放数据的。

但是,这个存储单元有其特殊的地位,
是单片机中一个非常关键的单元,很多运算都要通过ACC来进行。

以后在学习指令时,
常用A来表示累加器。

但有一些地方例外,比如在PUSH指令中,就必须用ACC这样的名字。

一般的说法,A代表了累加器中的内容、而ACC代表的是累加器的地址。

***************************************************************/ sbit buzzer=P3^6;//蜂鸣器,通过三极管9012驱动,端口低电平响
/**************************************************************/ uchar code tab0[]=" Wang Zhe ";
uchar code tab1[]=" 01 ";
uchar code tab2[]="20 - - "; //年显示的固定字符
uchar code tab3[]=" : : CLK:OFF";//时间显示的固定字符
//延时函数,后面经常调用
void delay(uint xms)//延时函数,有参函数
{
uint x,y;
for(x=xms;x>0;x--)
for(y=110;y>0;y--);
}
void delay2(uint z) //延时
{
uint x,y;
for(x=z;x>0;x--)
for(y=500;y>0;y--);
}
/********液晶写入指令函数与写入数据函数,以后可调用**************/
write_1602com(uchar com)//****液晶写入指令函数****
{
rs=0;//数据/指令选择置为指令
rw=0; //读写选择置为写
P0=com;//送入数据
delay(1);
en=1;//拉高使能端,为制造有效的下降沿做准备
delay(1);
en=0;//en由高变低,产生下降沿,液晶执行命令
}
write_1602dat(uchar dat)//***液晶写入数据函数****
{
rs=1;//数据/指令选择置为数据
rw=0; //读写选择置为写
P0=dat;//送入数据
delay(1);
en=1; //en置高电平,为制造下降沿做准备
delay(1);
en=0; //en由高变低,产生下降沿,液晶执行命令
}
/****************屏幕移动*********************/
void zuoyi(uchar num3) //屏幕左移
{
uchar l;
for(l=0;l<num3;l++)
{
write_1602com(0x18);
delay2(5);
}
}
void youyi(uchar num4) //屏幕右移
{
uchar r;
for(r=0;r<num4;r++)
{
write_1602com(0x1c);
delay2(5);
}
}
lcd_init()//***液晶初始化函数****
{
write_1602com(0x38);//设置液晶工作模式,意思:16*2行显示,5*7点阵,8位数据write_1602com(0x0c);//开显示不显示光标
write_1602com(0x06);//整屏不移动,光标自动右移
write_1602com(0x01);//清显示
/*****************************************/
write_1602com(0x80);
for(a=0;a<15;a++)
write_1602dat(tab0[a]);
write_1602com(0x80+0x40);
for(a=0;a<15;a++)
write_1602dat(tab1[a]);
/*write_1602com(0x80+16);
for(a=0;a<17;a++)
write_1602dat(tab2[a]);
write_1602com(0x80+0x40+16);
for(a=0;a<15;a++)
write_1602dat(tab3[a]);*/
/*********************************************/
write_1602com(yh+1);//日历显示固定符号从第一行第1个位置之后开始显示
for(a=0;a<14;a++)
{
write_1602dat(tab2[a]);//向液晶屏写日历显示的固定符号部分
//delay(3);
}
write_1602com(er);//时间显示固定符号写入位置,从第2个位置后开始显示
for(a=0;a<16;a++)
{
write_1602dat(tab3[a]);//写显示时间固定符号,两个冒号
//delay(3);
}
delay2(600);
zuoyi(16);
}
/*********************over***********************/
//时分秒显示子函数
void write_sfm(uchar add,uchar dat)//向LCD写时分秒,有显示位置加、现示数据,两个参数
{
uchar gw,sw;
gw=dat%10;//取得个位数字
sw=dat/10;//取得十位数字
write_1602com(er+add-2);//er是头文件规定的值0x80+0x40
write_1602dat(0x30+sw);//数字+30得到该数字的LCD1602显示码
write_1602dat(0x30+gw);//数字+30得到该数字的LCD1602显示码
}
void write_sfm1(uchar add,uchar dat)//向LCD写时分秒,有显示位置加、现示数据,两个参数
{
uchar gw,sw;
gw=dat%10;//取得个位数字
sw=dat/10;//取得十位数字
write_1602com(er1+add);//er是头文件规定的值0x80+0x40
write_1602dat(0x30+sw);//数字+30得到该数字的LCD1602显示码
write_1602dat(0x30+gw);//数字+30得到该数字的LCD1602显示码
}
//-------------------------------------
//年月日显示子函数
void write_nyr(int add,int dat)//向LCD写年月日,有显示位置加数、显示数据,两个参数
{
int gw,sw;
gw=dat%10;//取得个位数字
sw=dat/10;//取得十位数字
write_1602com(yh+add);//设定显示位置为第一个位置+add
write_1602dat(0x30+sw);//数字+30得到该数字的LCD1602显示码
write_1602dat(0x30+gw);//数字+30得到该数字的LCD1602显示码
}
void write_nyrl(int add,int dat)//向LCD写年月日,有显示位置加数、显示数据,两个参数
{
int gw,sw;
gw=dat%10;//取得个位数字
sw=dat/10;//取得十位数字
write_1602com(yhl+add);//设定显示位置为第一个位置+add
write_1602dat(0x30+sw);//数字+30得到该数字的LCD1602显示码
write_1602dat(0x30+gw);//数字+30得到该数字的LCD1602显示码
}
//-------------------------------------------
void write_week(uchar week)//写星期函数
{
write_1602com(yh+0x0c);//星期字符的显示位置
switch(week)
{
case 1:write_1602dat('M');//星期数为1时,显示
write_1602dat('O');
write_1602dat('N');
break;
case 2:write_1602dat('T');//星期数据为2时显示
write_1602dat('U');
write_1602dat('E');
break;
case 3:write_1602dat('W');//星期数据为3时显示
write_1602dat('E');
write_1602dat('D');
break;
case 4:write_1602dat('T');//星期数据为4是显示
write_1602dat('H');
write_1602dat('U');
break;
case 5:write_1602dat('F');//星期数据为5时显示
write_1602dat('R');
write_1602dat('I');
break;
case 6:write_1602dat('S');//星期数据为6时显示
write_1602dat('T');
write_1602dat('A');
break;
case 7:write_1602dat('S');//星期数据为7时显示
write_1602dat('U');
write_1602dat('N');
break;
}
}
//****************键盘扫描有关函数**********************// void key16()
{
key=keyboard();
/************闹钟开关显示****************/
if(key==1)
{if(clk_switch==0)
{clk_switch=1;
TR0=0;
//write_1602com(0x0f);//设置光标为闪烁
write_1602com(er-0x02+0x0f);
write_1602dat('O');
write_1602com(er-0x01+0x0f);
write_1602dat('N');
write_1602com(er+0x0f);
write_1602dat(' ');
TR0=1;
}
else
{clk_switch=0;
TR0=0;
//write_1602com(0x0f);//设置光标为闪烁
write_1602com(er-0x02+0x0f);
write_1602dat('O');
write_1602com(er-0x01+0x0f);
write_1602dat('F');
write_1602com(er+0x0f);
write_1602dat('F');
TR0=1;}
}
if(key2n==0||key2n==8)
{
if(key==11)////---------------12为功能键(时间设置键)-------------------- {
buzzer=0;
delay(20);
buzzer=1;
key1n++;
if(key1n==9)key1n=1;//设置按键共有年、月、日、星期、时、分、秒、返回,8个功能循环
switch(key1n)
{
case 1: TR0=0;//关闭定时器
write_1602com(yh+0x04);//按动1次,nian
write_1602com(0x0f);//设置光标为闪烁
break;
case 2: write_1602com(yh+0x07);//按动2次,yue
break;
case 3: write_1602com(yh+0x0a);//按动3次,ri
break;
case 4: write_1602com(yh+0x0e);//按动4次,week
break;
case 5: write_1602com(er-0x02+0x03);//按动5次,shi
break;
case 6: write_1602com(er-0x02+0x06);//按6次fen位置显示光标
break;
case 7: write_1602com(er-0x02+0x09);//按7次,秒位置显示光标
break;
case 8: write_1602com(0x0c);//按动到第8次,设置光标不闪烁
TR0=1;break;//打开定时器
default:break;
}
}
if(key1n!=0)//当key1按下以下。

再按以下键才有效(按键次数不等于零){
if(key==3) //上调键
{
write_1602com(er-0x02+0x09);//因为设置液晶的模式是写入数据后,指针自动加一,在这里是写回原来的位置
buzzer=0;//蜂鸣器短响一次
delay(20);
buzzer=1;
switch(key1n)
{
case 1:nian++;ri=1;write_nyr(9,ri);
if(nian==100)
nian=0;
write_nyr(3,nian);//令LCD在正确的位置显示"加"设定好的年份数据 write_1602com(yh+4);//因为设置液晶的模式是写入数据后,指针自动加一,所以需要光标回位
break;
case 2:yue++;ri=1;write_nyr(9,ri);//令LCD在正确的位置显示"加"设定好的日期数据
if(yue==13)
yue=1;
write_1602com(yh+7);//因为设置液晶的模式是写入数据后,指针自动加一,所以需要光标回位
write_nyr(6,yue);//令LCD在正确的位置显示"加"设定好的月份数据
write_1602com(yh+7);//因为设置液晶的模式是写入数据后,指针自动加一,所以需要光标回位
break;
case 3:ri++;
run=nian%4;
if(run==0)
{}
else
run=1;
switch(yue)
{ case 1 : if(ri==32) ri=1;break;
case 2 : if(run==0)
{if(ri==30) ri=1;break;}
else
{if(ri==29) ri=1;break;}
case 3 : if(ri==32) ri=1;break;
case 4 : if(ri==31) ri=1;break;
case 5 : if(ri==32) ri=1;break;
case 6 : if(ri==31) ri=1;break;
case 7 : if(ri==32) ri=1;break;
case 8 : if(ri==32) ri=1;break;
case 9 : if(ri==31) ri=1;break;
case 10: if(ri==32) ri=1;break;
case 11:if(ri==31) ri=1;break;
case 12:if(ri==32) ri=1;break;
default:break;
}
write_nyr(9,ri);//令LCD在正确的位置显示"加"设定好的日期数据
write_1602com(yh+10);//因为设置液晶的模式是写入数据后,指针自动加一,所以需要光标回位
break;
case 4:week++;
if(week>=8)
week=1;
write_1602com(yh+0x0C);//指定'加'后的周数据显示位置
write_week(week);//指定周数据显示内容
write_1602com(yh+0x0e);//因为设置液晶的模式是写入数据后,指针自动加一,所以需要光标回位
break;
case 5:shi++;
if(shi==24)
{shi=0;}
write_sfm(2,shi);//令LCD在正确的位置显示"加"设定好的小时数据 write_1602com(er-2+3);//因为设置液晶的模式是写入数据后,指针自动加
一,所以需要光标回位
break;
case 6:fen++;
if(fen>=60)
fen=0;
write_sfm(0x05,fen);//令LCD在正确位置显示"加"设定好的分数据
write_1602com(er-2+6);//因为设置液晶的模式是写入数据后,指针自动加一,在这里是写回原来的位置
break;
case 7:miao++;
if(miao>=60)
miao=0;//秒超过59,再加1,就归零
write_sfm(0x08,miao);//令LCD在正确位置显示"加"设定好的秒数
write_1602com(er-0x02+0x09);//因为设置液晶的模式是写入数据后,指针自动加一,在这里是写回原来的位置
// write_1602com(0x0b);
break;
}
}
}
//------------------减键key3,各句功能参照'加键'注释--------------- if(key==7)
{
buzzer=0;//蜂鸣器短响一次
delay(20);
buzzer=1;
switch(key1n)
{
case 1:nian--;ri=1;write_nyr(9,ri);
if(nian==-1)
nian=99;
write_nyr(3,nian);
write_1602com(yh+4);//因为设置液晶的模式是写入数据后,指针自动加一,所以需要光标回位
break;
case 2:yue--;ri=1;write_nyr(9,ri);//令LCD在正确的位置显示"加"设定好的日期数据
if(yue==0)
yue=12;
write_1602com(yh+7);//因为设置液晶的模式是写入数据后,指针自动加一,所以需要光标回位
break;
case 3:ri--;
run=nian%4;
if(run==0)
{}
else {run=1;}
switch(yue)
{case 1 : if(ri==0) ri=31;break;
case 2 : if(run==0)
{if(ri==0) ri=29;break;}
else
{if(ri==0) ri=28;break;}
case 3 : if(ri==0) ri=31;break;
case 4 : if(ri==0) ri=30;break;
case 5 : if(ri==0) ri=31;break;
case 6 : if(ri==0) ri=30;break;
case 7 : if(ri==0) ri=31;break;
case 8 : if(ri==0) ri=31;break;
case 9 : if(ri==0) ri=30;break;
case 10: if(ri==0) ri=31;break;
case 11:if(ri==0) ri=30;break;
case 12:if(ri==0) ri=31;break;
default:break;
}
write_nyr(9,ri);
write_1602com(yh+10);//因为设置液晶的模式是写入数据后,指针自动加一,所以需要光标回位
break;
case 4:week--;
if(week==0)
week=7;
write_1602com(yh+0x0C);//指定'加'后的周数据显示位置
write_week(week);//指定周数据显示内容
write_1602com(yh+0x0e);//因为设置液晶的模式是写入数据后,指针自动加一,所以需要光标回位
break;
case 5:shi--;
if(shi==-1){ shi=23;}
write_1602com(er-2+3);//因为设置液晶的模式是写入数据后,指针自动加一,所以需要光标回位
break;
case 6:fen--;
if(fen==-1)
fen=59;
write_sfm(5,fen);
write_1602com(er-2+6);//因为设置液晶的模式是写入数据后,指针自动加一,在这里是写回原来的位置
break;
case 7:miao--;
if(miao==-1)
miao=59;//秒数据减到-1时自动变成59
write_sfm(0x08,miao);//在LCD的正确位置显示改变后新的秒数
write_1602com(er-0x02+0x09);//因为设置液晶的模式是写入数据后,指针自动加一,在这里是写回原来的位置
//write_1602com(0x0b);
break;
}
}
}
if(key1n==0||key1n==8)
{
if(key==5)////---------------12为功能键(闹钟设置键)-------------------- {
buzzer=0;
delay(20);
buzzer=1;
key2n++;
if(key2n==9)key2n=1;//设置按键共有年、月、日、星期、时、分、秒、返回,8个功能循环
switch(key2n)
{
case 1: clk_set=1;
write_1602com(yh+0x00);write_1602dat('!');
write_sfm(8,miao1);//秒,从第二行第8个字后开始显示(调用时分秒显示子函数)
write_sfm(5,fen1);//分,从第二行第5个字符后开始显示
write_sfm(2,shi1);//小时,从第二行第2个字符后开始显示
write_week(weekl);
write_nyr(9,ril);
write_nyr(6,yuel);
write_nyr(3,nianl);
write_1602com(yh+0x04);//按动1次,nian
write_1602com(0x0f);//设置光标为闪烁
break;
case 2: write_1602com(yh+0x07);//按动2次,yue
break;
case 3: write_1602com(yh+0x0a);//按动3次,ri
break;
case 4: write_1602com(yh+0x0e);//按动4次,week
break;
case 5: write_1602com(er-0x02+0x03);//按动5次,shi
break;
case 6: write_1602com(er-0x02+0x06);//按6次fen位置显示光标
break;
case 7: write_1602com(er-0x02+0x09);//按7次,秒位置显示光标
break;
case 8: write_1602com(0x0c);//按动到第8次,设置光标不闪烁
write_1602com(yh+0x00);write_1602dat(' ');
clk_set=0;
break;
default:break;
}
}
//-------------闹钟加-----------------//
if(key2n!=0)//当key1按下以下。

再按以下键才有效(按键次数不等于零)
{
if(key==3) //上调键
{
write_1602com(er-0x02+0x09);//因为设置液晶的模式是写入数据后,指针自动加一,在这里是写回原来的位置
switch(key2n)
{
case 1: nianl++;ril=1;write_nyr(9,ril);
if(nianl==100)
nianl=0;
write_nyr(3,nianl);//令LCD在正确的位置显示"加"设定好的年份数据 write_1602com(yh+4);//因为设置液晶的模式是写入数据后,指针自动加一,所以需要光标回位
break;
case 2:yuel++;ril=1;write_nyr(9,ril);//令LCD在正确的位置显示"加"设定好的日期数据
if(yuel==13)
yuel=1;
write_1602com(yh+7);//因为设置液晶的模式是写入数据后,指针自动加一,所以需要光标回位
write_nyr(6,yuel);//令LCD在正确的位置显示"加"设定好的月份数据
write_1602com(yh+7);//因为设置液晶的模式是写入数据后,指针自动加一,所以需要光标回位
break;
case 3:ril++;
runl=nianl%4;
if(runl==0)
{}
else
runl=1;
switch(yuel)
{ case 1 : if(ri==32) ril=1;break;
case 2 : if(runl==0)
{if(ril==30) ril=1;break;}
else
{if(ril==29) ril=1;break;}
case 3 : if(ril==32) ril=1;break;
case 4 : if(ril==31) ril=1;break;
case 5 : if(ril==32) ril=1;break;
case 6 : if(ril==31) ril=1;break;
case 7 : if(ril==32) ril=1;break;
case 8 : if(ril==32) ril=1;break;
case 9 : if(ril==31) ril=1;break;
case 10: if(ril==32) ril=1;break;
case 11: if(ril==31) ril=1;break;
case 12: if(ril==32) ril=1;break;
default:break;
}
write_nyr(9,ril);//令LCD在正确的位置显示"加"设定好的日期数据
write_1602com(yh+10);//因为设置液晶的模式是写入数据后,指针自动加一,所以需要光标回位
break;
case 4:weekl++;
if(weekl>=8)
weekl=1;
write_1602com(yh+0x0C);//指定'加'后的周数据显示位置
write_week(weekl);//指定周数据显示内容
write_1602com(yh+0x0e);//因为设置液晶的模式是写入数据后,指针自动加
一,所以需要光标回位
break;
case 5:shi1++;
if(shi1==24)
shi1=0;
write_sfm(2,shi1);//令LCD在正确的位置显示"加"设定好的小时数据
write_1602com(er-2+3);//因为设置液晶的模式是写入数据后,指针自动加一,所以需要光标回位
break;
case 6:fen1++;
if(fen1==60)
fen1=0;
write_sfm(0x05,fen1);//令LCD在正确位置显示"加"设定好的分数据
write_1602com(er-2+6);//因为设置液晶的模式是写入数据后,指针自动加一,在这里是写回原来的位置
break;
case 7:miao1++;
if(miao1==60)
miao1=0;//秒超过59,再加1,就归零
write_sfm(0x08,miao1);//令LCD在正确位置显示"加"设定好的秒数write_1602com(er-0x02+0x09);//因为设置液晶的模式是写入数据后,指针自动加一,在这里是写回原来的位置
break;
default:break;
}
}
}
//------------ 闹钟减--------------------//
if(key==7)
{
switch(key2n)
{
case 1:nianl--;ril=1;write_nyr(9,ril);
if(nianl==-1)
nianl=99;
write_nyr(3,nianl);
write_1602com(yh+4);//因为设置液晶的模式是写入数据后,指针自动加一,所以需要光标回位
break;
case 2:yuel--;ril=1;write_nyr(9,ril);//令LCD在正确的位置显示"加"设定好的日期数据
if(yuel==0)
yuel=12;
write_nyr(6,yuel);
write_1602com(yh+7);//因为设置液晶的模式是写入数据后,指针自动加一,所以需要光标回位
break;
case 3:ril--;
runl=nianl%4;
if(runl==0)
{}
else {runl=1;}
switch(yuel)
{case 1 : if(ril==0) ril=31;break;
case 2 : if(runl==0)
{if(ril==0) ril=29;break;}
else
{if(ril==0) ril=28;break;}
case 3 : if(ril==0) ril=31;break;
case 4 : if(ril==0) ril=30;break;
case 5 : if(ril==0) ril=31;break;
case 6 : if(ril==0) ril=30;break;
case 7 : if(ril==0) ril=31;break;
case 8 : if(ril==0) ril=31;break;
case 9 : if(ril==0) ril=30;break;
case 10: if(ril==0) ril=31;break;
case 11:if(ril==0) ril=30;break;
case 12:if(ril==0) ril=31;break;
default:break;
}
write_nyr(9,ril);
write_1602com(yh+10);//因为设置液晶的模式是写入数据后,指针自动加一,所以需要光标回位
break;
case 4:weekl--;
if(weekl==0)
weekl=7;
write_1602com(yh+0x0C);//指定'加'后的周数据显示位置
write_week(weekl);//指定周数据显示内容
write_1602com(yh+0x0e);//因为设置液晶的模式是写入数据后,指针自动加一,所以需要光标回位
break;
case 5:shi1--;
if(shi1==-1)
shi1=23;
write_sfm(2,shi1);
write_1602com(er-2+3);//因为设置液晶的模式是写入数据后,指针自动加一,所以需要光标回位
break;
case 6:fen1--;
if(fen1==-1)
fen1=59;
write_sfm(5,fen1);
write_1602com(er-2+6);//因为设置液晶的模式是写入数据后,指针自动加一,在这里是写回原来的位置
break;
case 7:miao1--;
if(miao1==-1)
miao1=59;//秒数据减到-1时自动变成59
write_sfm(0x08,miao1);//在LCD的正确位置显示改变后新的秒数
write_1602com(er-0x02+0x09);//因为设置液晶的模式是写入数据后,指针自动加一,在这里是写回原来的位置
break;
default:break;
}
}
} Wl=weekl;Rl=ril;Yl=yuel;Nl=nianl;W=week;R=ri;Y=yue;N=nian;
}
//---------------------------------------------//
//-------------------------------
void init() //定时器、计数器设置函数
{ /****************定时器2************************/
EA = 0; /*关中断,设置定时器2 */
T2MOD &= 0xFC; /* T2OE=0;定时器2输出不允许 */
T2MOD |= 0x01; /* DCEN=1;可以设置为向上/下计数:此程序中为向上达到0xff时溢出*/
EXF2=0; /* reset flag 定时器2外部标志位清除*/
TCLK=0;RCLK=0; /* disable baud rate generator 不用于波特率发生器*/
EXEN2=0; /* ignore events on T2EX 视T2EX端信号无效*/
TH2=MSB_reload_value; /* Init msb_value 填入初始化值*/
TL2=LSB_reload_value; /* Init lsb_value */
RCAP2H=MSB_reload_value; /* reload msb_value重载值 */
RCAP2L=LSB_reload_value; /* reload lsb_value */
C_T2=0; /* timer mode 定时模式*/
CP_RL2=0; /* reload mode 重载模式*/
/****************************************/
TMOD=0x11; //time0为定时器,方式1 TH0=0x3c; //预置计数初值
TL0=0xb0;
TH1=0xfc;
TL1=0x18;
EA=1;
ET0=1;
ET1=1;
ET2=1;
TR0=1;
TR1=0;
TR2=0;
}
void clock()
{
if(clk_switch==1)
{
if(shi==shi1)
{
if(fen==fen1)
{
if(miao==miao1)
{TR1=1;}
}
}
}
else{TR1=0;}
}
//*******************主函数**************************
//***************************************************
void main()
{ uchar keyfuck=0xff;
lcd_init(); //调用液晶屏初始化子函数
init(); //调用定时计数器的设置子函数
buzzer=0;//蜂鸣器长响一次
delay(80);
buzzer=1;
while(1) //无限循环下面的语句:
{
key16(); //调用键盘扫描子函数
/* keyfuck=keyboard();
if(keyfuck!=0xff)
{
switch(keyfuck)
{
case 10:write_1602com(er+0x0f);write_1602dat('A');break;
case 11:write_1602com(er+0x0f);write_1602dat('B');break;
case 12:write_1602com(er+0x0f);write_1602dat('C');break;
case 13:write_1602com(er+0x0f);write_1602dat('D');break;
case 14:write_1602com(er+0x0f);write_1602dat('E');break;
case 15:write_1602com(er+0x0f);write_1602dat('F');break;
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
case 8:
case 9:write_1602com(er+0x0f);write_1602dat(keyfuck+0x30);break; default:break;
}
}*/
}
}
void timer0() interrupt 1 //取得并显示日历和时间
{
//读取秒时分周日月年七个数据(DS1302的读寄存器与写寄存器不一样): TH0=0x3c;
TL0=0xb0;
mstcnt++;
if(mstcnt>=20 )
{
miao++;
mstcnt=0;
}
if(miao>=60)
{
fen++;
miao=0;
}
if(fen>=60)
{
shi++;
fen=0;
}
if(shi>=24)
{
week++;
if(week==8)
{
week=0;
}
else{}
ri++;
run=nian%4;
if(run==0)
{}
else
{run=1;}
switch(yue)
{
case 1 : if(ri==32) {ri=1;yue++;}break;
case 2 : if(run==0)
{if(ri==30) {ri=1;yue++;}break;}
else
{if(ri==29) {ri=1;yue++;}break;} case 3 : if(ri==32) {ri=1;yue++;}break;
case 4 : if(ri==31) {ri=1;yue++;}break;
case 5 : if(ri==32) {ri=1;yue++;}break;
case 6 : if(ri==31) {ri=1;yue++;}break;
case 7 : if(ri==32) {ri=1;yue++;}break;
case 8 : if(ri==32) {ri=1;yue++;}break;
case 9 : if(ri==31) {ri=1;yue++;}break;
case 10: if(ri==32) {ri=1;yue++;}break;
case 11 :if(ri==31) {ri=1;yue++;}break;
case 12 :if(ri==32) {ri=1;yue++;}break;
default:break;
}
shi=0;
百度文库- 好好学习,天天向上
}
if(yue>12)
{
nian++;
yue=1;
}
if(clk_set==0)
{
//显示、秒、时、分数据:
write_week(week);
write_sfm(8,miao);//秒,从第二行第8个字后开始显示(调用时分秒显示子函数)write_sfm(5,fen);//分,从第二行第5个字符后开始显示
write_sfm(2,shi);//小时,从第二行第2个字符后开始显示
//显示日、月、年数据:
write_nyr(9,ri);//日期,从第二行第9个字符后开始显示
write_nyr(6,yue);//月份,从第二行第6个字符后开始显示
write_nyr(3,nian);//年,从第二行第3个字符后开始显示
//write_1602com(er+0x09);//设置按键按动一次,秒位置显示光标
//keyscan( ); //按键扫描
clock(); //闹钟判断
}
}
void timer1() interrupt 3
{if(W==Wl&&R==Rl&&Y==Yl&&N==Nl)
{TH1=0xfc;
TL1=0x18;
count2++;
if(count2==20)
{
count1++;
count2=0;
if(count1==1)
{
buzzer=~buzzer;
count1=0;
count2=0;}
else buzzer=0;
}
}
}
-- 31 -。

相关文档
最新文档