
基于内部定时器的走时、调时、整点报时(蜂鸣器、LED)、3个闹钟设置,LCD显示时、分、秒、年、月、日、5个按键操作,添加纪念日功能。
题 目: 单片机电子钟
院 (系):
专 业:
学 号:
姓 名:
2011年 11 月 11 日
摘 要
...
关键词:单片机 ATC51 1602液晶显示器
2.实验内容
2.1电子钟的仿真及原理图
原理图如下:
2.2电子钟的PCB封装图
2.3 ATC51的工作原理
ATC51是美国ATMEL公司生产的低电压,高性能CMOS8位单片机,片内含4k bytes的可反复擦写的只读程序存
储器(PEROM)和128 bytes的随机存取数据存储器(RAM),器件采用ATMEL公司的高密度、非易失性存储技术生产,
兼容标准MCS-51指令系统,片内置通用8位处理器(CPU)和Flash存储单元,功能强大ATC51单片机可为您提
供许多高性价比的应用场合,可灵活应用于各种控制领域。
3、功能特性概述:
ATC51 提供以下标准功能:4k 字节Flash 闪速存储器,128字节内部RAM,32 个I/O 口线,两个16位定时/计
数器,一个5向量两级中断结构,一个全双工串行通信口,片内振荡器及时钟电路。同时,ATC51可降至0Hz的静态逻
辑操作,并支持两种软件可选的节电工作模式。空闲方式停止CPU的工作,但允许RAM,定时/计数器,串行通信口及
中断系统继续工作。掉电方式保存RAM中的内容,但振荡器停止工作并禁止其它所有部件工作直到下一个硬件复位。
3.1 主要性能参数:
·与MCS-51产品指令系统完全兼容
·4k字节可重擦写Flash闪速存储器
·1000次擦写周期
·全静态操作:0Hz-24MHz
·三级加密程序存储器
·128×8字节内部RAM
·32个可编程I/O口线
·2个16位定时/计数器
·6个中断源
·可编程串行UART通道
·低功耗空闲和掉电模式
4、电子钟工作原理:
利用51单片机内部定时计数器功能产生1S的计时,通过设定好的变量及函数分别按一定进制累加到时、分、秒、日、月、年上,通过按键扫描完成时间修改及闹钟设置,最后通过LCD1602显示时间,通过蜂鸣器及LED进行整点报时和闹钟报时。
5、通用1602液晶介绍:
6、电子钟源程序程序
/*********************************************************************
使用说明:
K1键为菜单选项:单击→进入位选择修改;双击:修改;
K2键为上调键;K3键为下调键;K4键为退出修改;
K5键为闹钟时间设置键;按一次:闹钟一;两次:闹钟二;三次:闹钟三;四次:重置 五次:纪念日
**********************************************************************/
#include /******************************************************************** 位定义 *********************************************************************/ sbit RS=P2^0; //功能数据选择位 sbit RW=P2^1; //读写选择位 sbit E=P2^2; //使能位 0 sbit busy=P0^7; //lcd忙标位 sbit menu=P1^0; //菜单及确定 sbit inc=P1^1; //增一 sbit dec=P1^2; //减一 sbit quit=P1^3; //退出 sbit sound=P1^4; //闹钟控制 /********************************************************************* 全局变量 **********************************************************************/ unsigned char data sec20,sec,min,hour,amin1,ahour1,amin2,ahour2,amin3,ahour3,temp,year1,year2,month,day,amonth,aday; //计秒,秒,分,时 unsigned char data timebuf[]={0,0,0x3a,0,0,0x3a,0,0,0,0,0,0,0x2d,0,0,0x2d,0,0}; //存放时间 unsigned char key; //按键标志 unsigned char time=0; //记录按menu键的次数 unsigned char place=0x84; //记录光标的位置 unsigned char signal=0; //光标与时间值增减标志位 //0光标 1时间值 /********************************************************************* 函数申明 **********************************************************************/ void delay(unsigned char ms); //延时函数 void cbusy(); //测试lcd忙碌状态函数 void wrcom(unsigned char cmd); //写指令函数 void wrdat(unsigned char dat); //写数据函数 void lcdinit(); //lcd初始化函数 void protime(); //时间处理函数 void display(); //时间显示函数 unsigned char scankey(); //按键扫描 void keywork(); // 按键功能处理函数 void naoling1(); void naoling2(); void naoling3(); /********************************************************************* 延时函数 **********************************************************************/ void delay(unsigned char ms) { unsigned char i; unsigned char m=4; while(ms--) { for(i=0;i<20;i++) { do {}while(m--); } } } /********************************************************************* 测试lcd忙碌状态函数 **********************************************************************/ void cbusy() { do { P0=0xff; RS=0; RW=1; E=0; E=0; busy=P0&0x80; E=1; }while(busy!=0); } /********************************************************************* 写指令函数 **********************************************************************/ void wrcom(unsigned char cmd) { P0=cmd; RS=0; RW=0; E=0; cbusy(); E=1; } /********************************************************************* 写数据函数 **********************************************************************/ void wrdat(unsigned char dat) { P0=dat; RS=1; RW=0; E=0; cbusy(); E=1; } /********************************************************************* lcd初始化函数 **********************************************************************/ void lcdinit() { wrcom(0x01); //清屏 wrcom(0x38); //8位总线,5*7点阵 wrcom(0x14); //文字不动光标自动右移 wrcom(0x0c); //开显示光标不显不闪 } /********************************************************************* 定时器0中断产生秒 **********************************************************************/ void time0() interrupt 1 /*定时中断0*/ { TL0=0xb0; //定时50ms TH0=0x3c; sec20++; //计数到一秒 protime(); } /********************************************************************* 闹铃函数 **********************************************************************/ void naoling1() { amin1=min; ahour1=hour; } void naoling2() { amin2=min; ahour2=hour; } void naoling3() { amin3=min; ahour3=hour; } /********************************************************************* 时间处理函数 **********************************************************************/ void protime() { if(sec20>19) //一秒到 { sec20=0; sec++; } if(sec>59) { sec=0; min++; } if(min>59) { min=0; hour++; } if(hour>23) { hour=0; day++; } if(day>31) { day=0; month++; } if(month>12) { month=0; year2++;} timebuf[0]=sec%10+48; //时间数据更新 timebuf[1]=sec/10+48; timebuf[3]=min%10+48; timebuf[4]=min/10+48; timebuf[6]=hour%10+48; timebuf[7]=hour/10+48; timebuf[8]=year1/10+48; //时间数据更新 timebuf[9]=year1%10+48; timebuf[10]=year2/10+48; //时间数据更新 timebuf[11]=year2%10+48; timebuf[13]=month/10+48; timebuf[14]=month%10+48; timebuf[16]=day/10+48; timebuf[17]=day%10+48; display(); /* if(min==0&&sec==0){sound=0;delay(1);sound=1;delay(1); sound=0;delay(1);sound=1;delay(1);sound=0;delay(1);sound=1;delay(1); } */ if(min==0&&sec==0||amin1==min&&ahour1==hour&&sec==0||amin2==min&&ahour2==hour&&sec==0||amin3==min&&ahour3==hour&&sec==0||aday==day&&amonth==month&&min==0&&sec==0&&hour==0) {sound=0;} if(sec>5) { sound=1;} } /********************************************************************* 时间显示函数 **********************************************************************/ void display() { unsigned char t; //wrcom(0x80); //wrdat(0xfb); wrcom(0x84); for(t=8;t>=1;t--) { wrdat(timebuf[t-1]); } wrcom(0xc3); for(t=7;t<=16;t++) { wrdat(timebuf[t+1]); } if(amin1!=61) { wrcom(0xc1); wrdat(0x41); } } /********************************************************************* 按键扫描 **********************************************************************/ unsigned char scankey() { if(P1!=0xff) //判断是否有键按下 { delay(2); //延时消抖 if(P1!=0xff) { switch(P1) { case 0xfe:{P1=0xff; while(P1!=0xff); return(1);}break; //判断所按键值并等 case 0xfd:{P1=0xff; while(P1!=0xff); return(2);}break; // 等待按键松开,再 case 0xfb:{P1=0xff; while(P1!=0xff); return(3);}break; // 返回键值 case 0xf7:{P1=0xff; while(P1!=0xff); return(4);}break; case 0x7f:{P1=0xff; while(P1!=0xff); return(5);}break; } } } else return(0); } /********************************************************************* 按键功能处理函数 **********************************************************************/ void keywork() { key=scankey(); switch(key) { case 1: //按下menu键 { time++; //menu键按下次数 EA=0; //关中断停止计时产生秒 if((time%2)==0) //判断按下是否为修改时间的状态 { wrcom(place); wrcom(0x0f); //光标显、闪 signal=1; //修改时间模式 } else { wrcom(place); wrcom(0x0e); //光标显,不闪 signal=0; //选择修改位模式 } } break; case 2: //按下inc键 { if(signal==0) //修改位模式下 { place++; //显示位置循环移动 wrcom(place); if(place==0x8c) place=0xc2; if(place>=0xcd) place=0x83; } else switch(place) //修改时间值模式下 { case 0x84:{ wrcom(0x84); //保证光标闪烁位置和当前位置一至 wrcom(0x0f); timebuf[7]++; if(timebuf[7]>50) timebuf[7]=48; display(); wrcom(0x84); } break; case 0x85:{ wrcom(0x85); wrcom(0x0f); timebuf[6]++; if(timebuf[7]<50) { if(timebuf[6]>57) timebuf[6]=48; } else { if(timebuf[6]>51) timebuf[6]=48; } display(); wrcom(0x85); } break; case 0x86:break; case 0x87:{ wrcom(0x87); wrcom(0x0f); timebuf[4]++; if(timebuf[4]>53) timebuf[4]=48; display(); wrcom(0x87); }break; case 0x88:{ wrcom(0x88); wrcom(0x0f); timebuf[3]++; if(timebuf[3]>57) timebuf[3]=48; display(); wrcom(0x88); }break; case 0x:break; case 0x8a:{ wrcom(0x8a); wrcom(0x0f); timebuf[1]++; if(timebuf[1]>53) timebuf[1]=48; display(); wrcom(0x8a); }break; case 0x8b:{ wrcom(0x8b); wrcom(0x0f); timebuf[0]++; if(timebuf[0]>57) timebuf[0]=48; display(); wrcom(0x8b); }break; case 0xcc:{ wrcom(0xcc); wrcom(0x0f); timebuf[17]++; if(timebuf[17]>57) timebuf[17]=48; display(); wrcom(0xcc); }break; case 0xcb:{ wrcom(0xcb); wrcom(0x0f); timebuf[16]++; if(timebuf[16]>51) timebuf[16]=48; display(); wrcom(0xcb); }break; case 0xc9:{ wrcom(0xc9); wrcom(0x0f); timebuf[14]++; if(timebuf[14]>57) timebuf[14]=48; display(); wrcom(0xc9); }break; case 0xc8:{ wrcom(0xc8); wrcom(0x0f); timebuf[13]++; if(timebuf[13]>49) timebuf[13]=48; display(); wrcom(0xc8); }break; } }break; case 3: //按下dec键 { if(signal==0) { place--; wrcom(place); wrcom(0x0e); if(place==0xc2) place=0x8c; if(place==0x83) place=0xcd; if(place>=0xcd) place=0x83; } else switch(place) { case 0x84:{ wrcom(0x84); wrcom(0x0f); timebuf[7]--; if(timebuf[7]<48) timebuf[7]=50; display(); wrcom(0x84); } break; case 0x85:{ wrcom(0x85); wrcom(0x0f); timebuf[6]--; if(timebuf[7]<50) { if(timebuf[6]<48) timebuf[6]=57; } else { if(timebuf[6]<48) timebuf[6]=51; } display(); wrcom(0x85); } break; case 0x86:break; case 0x87:{ wrcom(0x87); wrcom(0x0f); timebuf[4]--; if(timebuf[4]<48) timebuf[4]=53; display(); wrcom(0x87); }break; case 0x88:{ wrcom(0x88); wrcom(0x0f); timebuf[3]--; if(timebuf[3]<48) timebuf[3]=57; display(); wrcom(0x88); }break; case 0x:break; case 0x8a:{ wrcom(0x8a); wrcom(0x0f); timebuf[1]--; if(timebuf[1]<48) timebuf[1]=53; display(); wrcom(0x8a); }break; case 0x8b:{ wrcom(0x8b); wrcom(0x0f); timebuf[0]--; if(timebuf[0]<48) timebuf[0]=57; display(); wrcom(0x8b); }break; case 0xcc:{ wrcom(0xcc); wrcom(0x0f); timebuf[17]--; if(timebuf[17]<48) timebuf[17]=57; display(); wrcom(0xcc); }break; case 0xcb:{ wrcom(0xcb); wrcom(0x0f); timebuf[16]--; if(timebuf[16]<48) timebuf[16]=51; display(); wrcom(0xcb); }break; case 0xc9:{ wrcom(0xc9); wrcom(0x0f); timebuf[14]--; if(timebuf[14]<48) timebuf[14]=57; display(); wrcom(0xc9); }break; case 0xc8:{ wrcom(0xc8); wrcom(0x0f); timebuf[13]--; if(timebuf[13]<48) timebuf[13]=49; display(); wrcom(0xc8); }break; } } break; case 4: //按quit键保存修改并退出调时状态 { sec=timebuf[0]-48+(timebuf[1]-48)*10; min=timebuf[3]-48+(timebuf[4]-48)*10; hour=timebuf[6]-48+(timebuf[7]-48)*10; year1=timebuf[9]-48+(timebuf[8]-48)*10; year2=timebuf[11]-48+(timebuf[10]-48)*10; month=timebuf[14]-48+(timebuf[13]-48)*10; day=timebuf[17]-48+(timebuf[16]-48)*10; EA=1; wrcom(0x0c); time=0; place=0x83; }break; //按quit键保存修改并退出调时状态 case 5: { temp++; if(temp==1) naoling1(); if(temp==2) naoling2(); if(temp==3) naoling3(); if(temp==4) { amin1=61; ahour1=0; amin2=61; ahour2=0; amin3=61; ahour3=0; amonth=13; aday=32; wrcom(0x01);} if(temp==5) {amonth=month; aday=day; temp=0; } }break; } } /********************************************************************* 主函数 **********************************************************************/ void main() { P1=0xff; sec20=0; hour=0; min=0; sec=1; year1=20; year2=11; month=12; day=31; amonth=13; aday=32; TMOD=0x01; TL0=0xb0; TH0=0x3c; TR0=1; ET0=1; EA=1; amin1=61; ahour1=0; amin2=61; ahour2=0; amin3=61; ahour3=0; temp=0; lcdinit(); delay(15); while(1) {keywork();} } 7.实现功能 基于内部定时器的走时、调时、整点报时(蜂鸣器、LED)、3个闹钟设置,LCD显示时、分、秒、年、月、日、5个按键操作,添加纪念日功能。 8.系统调试 (1)焊接电路板后,用万用表检测线路连接是否正常,工作电压是否正常,及各引脚信号。 (2)LCD与c51的初始化时间不同,会造成短暂的LCD显示异常,在main函数中加入延时程序。 (3)LCD的对比度通过滑动变阻控制LCD工作电压来调节。 (4)若LCD显示为黑色方块,证明软件或硬件有错误,须认真检查修改。 9.总结: 改进功能展望: 闹铃数量可由函数任意设定,可设定夜间模式,原理图外引出的排针引脚可连接温度检测模块和音乐报时模块,通过单片机处理统一显示在LCD上。 10.参考文献 1.《例说51单片机》 张义和著 2.LCD1602中文资料 3.百度网站 www.baidu.com
