开发环境:
Proteus:是比较好的仿真单片机及外围器件的工具。
keil5:嵌入式开发的软件。
原理图和PCB图:
原理图:
功能和操作说明:
本系统以C51单片机为核心,通过软件在LCD1602上显示具体时间日期的系统。本质是一个简易电子时钟。同时可以通过4个按键来调整时间。
在仿真开始后,LCD显示屏会显示初始时间,同时随着定时器工作,时钟会走动。若按下“MODEL”按键,则定时器停止工作,光标定位到年,同时可按下“+”,“-”键来完成对时间的更改,按下两次“MODEL”键,光标定位到月,三次,光标定位到周,四次到日,五次到时,六次到分钟,七次到秒。最后按下“CONFIRM”键,光标消失,定时器重新开始工作,时钟走动。
代码:
#include <reg51.h>
#include <stdio.h>
#include <intrins.h>
#define uchar unsigned char
#define uint unsigned int
sbit RW=P2^1;//定义1602液晶RW端,
sbit RS=P2^0; //定义1602液晶RS端
sbit EN=P2^2;//定义1602液晶EN端
sbit s1=P1^0;
sbit s2=P1^1;
sbit s3=P1^2;
sbit s4=P1^3;
sbit LED=P2^5;
uchar count,s1_num,year,month,day,week,second,hour,minu;
uchar code OP[]=" 20 - - ";//定义初始上显示状态
void DELAY_1(uint k)
{
int j;
for(;k!=0;k--)
{
for(j=112;j>0;j--);
}
}
void write_com(uchar com)//写命令函数
{
RS=0;
RW=0;
EN=0;
P0=com;
DELAY_1(5);
EN=1;
DELAY_1(5);
EN=0;
}
void write_da(uchar dat)//写数据
{
RS=1;
RW=0;
EN=0;
P0=dat;
DELAY_1(5);
EN=1;
DELAY_1(5);
EN=0;//执行
}
void write_yea(uchar pla,uchar date1)//LCD第一行写入
{
uchar hour1,hour2;
hour1=date1/10;
hour2=date1%10;
write_com(0x80+pla);
write_da(0x30+hour1);
write_da(0x30+hour2);
}
void write_sec(uchar add,uchar date)//LCD第二行写入
{
uchar hour1,hour2;
hour1=date/10;
hour2=date%10;
write_com(0xC0+add);
write_da(0x30+hour1);
write_da(0x30+hour2);
}
void write_week(char week)//星期显示函数
{
write_com(0x80+13);//第一行第12个位置
switch(week)
{
case 1: write_da('M');DELAY_1(5);write_da('O');DELAY_1(5);write_da('N');break;
case 2: write_da('T');DELAY_1(5);write_da('U');DELAY_1(5);write_da('E');break;
case 3: write_da('W');DELAY_1(5);write_da('E');DELAY_1(5);write_da('D'); break;
case 4: write_da('T');DELAY_1(5);write_da('H');DELAY_1(5);write_da('U');break;
case 5: write_da('F');DELAY_1(5);write_da('R');DELAY_1(5); write_da('I');break;
case 6: write_da('S');DELAY_1(5); write_da('A');DELAY_1(5); write_da('T');break;
case 7: write_da('S');DELAY_1(5);write_da('U');DELAY_1(5);write_da('N');break;
default: break;
}
}
void writemon()//溢出判断
{
day=1;
month++;
if(month==13)
{
month=1;
year++;
if(year==100)
year=0;
write_yea(3,year);//年若变化则重新写入
}
write_yea(6,month);//月若变化则重新写入
}
void timeint()//定时器初始化
{
TMOD=0x01;
ET0=1;
EA=1;
TR0=1;
TH0=(65536-50000)/256;
TL0=(65536-50000)%256;
}
void init()//初始化LCD函数
{
uchar num;
EN=0;
RS=0;
RW=0;
year=22;//初始化种变量值
month=7;
day=4;
week=1;
hour=10;
minu=59;
second=50;
count=0;
s1_num=0;
LED=0;
write_com(0x38);//初始化LCD1602
write_com(0x0c);//开显示
write_com(0x06);//写读地址指针自动加一
write_com(0x01);
write_com(0x80);//设置显示第一行
for(num=0;num<15;num++)//显示年月日星期
{
write_da(OP[num]);
DELAY_1(5);
}
DELAY_1(5);
write_com(0xc4);//写出时间显示部分的两个":"
write_da(':');
DELAY_1(5);
write_com(0xc7);
write_da(':');
DELAY_1(5);
write_week(week);
write_yea(3,year);//显示
write_yea(6,month);
write_yea(9,day);
write_sec(8,second);//显示
write_sec(5,minu);
write_sec(2,hour);
}
void keyscan()//按键扫描函数
{
if(s1==0)
{
DELAY_1(5);
if(s1==0)//确认功能键被按下
{
s1_num++;//功能键按下次数记录
while(!s1);
if(s1_num==1)//第一次被按下时
{
TR0=0; //关闭定时器
write_com(0x84);//光标定位到年位置
write_com(0x0f); //光标开始闪烁
}
if(s1_num==2)//第二次按下光标闪烁定位到月位置
{
write_com(0x87);//第一行第六个
}
if(s1_num==3)//第三次按下光标闪烁定位到日位置
{
write_com(0x8a);
}
if(s1_num==4)//第四次按下光标闪烁定位到星期位置
{
write_com(0x8e);
}
if(s1_num==5)//第五次按下光标闪烁定位到时位置
{
write_com(0xC3);
}
if(s1_num==6)//第六次按下光标闪烁定位到分位置
{
write_com(0xc6);
}
if(s1_num==7)//第七次被按下时光标定位到秒位置
{
write_com(0xc9);
}
if(s1_num==8)
{
s1_num=0;//记录按键数清零
write_com(0x0c);//取消光标闪烁
TR0=1; //启动定时器使时钟开始走
}
}
}
if(s1_num!=0)//只有功能键被按下后,增加和减小键才有效
{
if(s4==0)//确认按键
{
DELAY_1(5);
if(s4==0)
{
while(!s4);//释放确认
s1_num=0;//记录按键数清零
write_com(0x0c);//取消光标闪烁
TR0=1; //启动定时器使时钟开始走
}
}
if(s2==0)//+键
{
DELAY_1(5);
if(s2==0)
{
while(!s2);//按键释放
if(s1_num==1)//若功能键第一次按下
{
year++; //则调整年加1
if(year==100)//若满100后将清零
year=0;
write_yea(3,year);//每调节一次送液晶显示一下
write_com(0x84);//显示位置重新回到调节处
}
if(s1_num==2)//若功能键第二次按下
{
month++;//则调整月加1
if(month==13)//若满12后将置一
month=1;
write_yea(6,month);//每调节一次送液晶显示一下
write_com(0x87);//显示位置重新回到调节处
}
if(s1_num==3)//若功能键第三次按下
{
day++;//则调整日加1
if(year%4==0&&month==2)
{
if(day==30)//若满29后将置一
day=1;
}
if(year%4!=0&&month==2)
{
if(day==29)//若满28后将置一
day=1;
}
if(month!=2&&month!=4&&month!=6&&month!=9&&month!=11)
{
if(day==32)//若满31后将置一
day=1;
}
if(month==4||month==6||month==9||month==11)
{
if(day==31)//若满30后将置一
day=1;
}
write_yea(9,day);;//每调节一次送液晶显示一下
write_com(0x8a);//显示位置重新回到调节处
}
if(s1_num==4)//若功能键第四次按下
{
week++;//则调整星期加1
if(week==8)//若满8后将置一
week=1;
write_week(week);//每调节一次送液晶显示一下
write_com(0x8d);//显示位置重新回到调节处
}
if(s1_num==5)//若功能键第五次按下
{
hour++;
if(hour==24)//若满24后将清零
{
hour=0;
}
write_sec(2,hour);
write_com(0xc3);
}
if(s1_num==6)//若功能键第二次按下
{
minu++;//则调整分钟加1
if(minu==60)//若满60后将清零
minu=0;
write_sec(5,minu);//每调节一次送液晶显示一下
write_com(0xc6);
}
if(s1_num==7)//若功能键第七次按下
{
second++; //整秒加1
if(second==60)//满60后将清零
second=0;
write_sec(8,second);//每调节一次送液晶显示一下
write_com(0xc9);//调整显示位置
}
}
}
}
if(s3==0)//-键
{
if(s3==0)//确认减小键被按下
{
while(!s3);//按键释放
if(s1_num==1)//若功能键第一次按下
{
year--;//则调整秒减1
if(year==-1)//若减到负数则将其重新设置为99
year=99;
write_yea(3,year);//每调节一次送液晶显示一下
write_com(0x84);//重新调整显示
}
if(s1_num==2)//若功能键第二次按下
{
month--;//则调整分钟减1
if(month==0)//若减到负数则将其重新设置为59
month=12;
write_yea(6,month);//每调节一次送液晶显示一下
write_com(0x87);//重新调整显示
}
if(s1_num==3)//若功能键第3次按下
{
day--;//则调整日加1
if(year%4==0&&month==2)
{
if(day==0)//若满29后将置一
day=29;
}
if(year%4!=0&&month==2)
{
if(day==0)//若满28后将置一
day=28;
}
if(month!=2&&month!=4&&month!=6&&month!=9&&month!=11)
{
if(day==0)//若满31后将置一
day=31;
}
if(month==4||month==6||month==9||month==11)
{
if(day==0)//若满30后将置一
day=30;
}
write_yea(9,day);;//每调节一次送液晶显示一下
write_com(0x8a);//重新调整显示
}
if(s1_num==4)//MODEL第4次按下
{
week--;//小时减1
if(week==0)//若减到负数则将其重新设置为23
week=7;
write_week(week);//每调节一次送液晶显示一下
write_com(0x8d);//重新调整显示
}
if(s1_num==5)//MODEL第5次按下
{
hour--;
if(hour==-1)//若满24后将清零
hour=23;
}
if(s1_num==6)//MODEL第6次按下
{
minu--;//则调整分钟减1
if(minu==-1)//若减到负数则将其重新设置为59
minu=59;
write_sec(5,minu);//每调节一次送液晶显示一下
write_com(0xc6);//重新调整显示
}
if(s1_num==7)//若功能键第7次按下
{
second--;//则调整秒减1
if(second==-1)//若减到负数则将其重新设置为59
second=59;
write_sec(8,second);//每调节一次送液晶显示一下
write_com(0xc9);//重新调整显示
}
}
}
}
void timer0() interrupt 1
{
TH0=(65536-50000)/256;//定时器初值
TL0=(65536-50000)%256;
count++; //中断次数累加
if(count==20) //20次50毫秒为1秒
{
LED=0;
if(minu==59 && second==59 ) LED=1;
count=0;
second++;
if(second==60)//秒加到60则进位分钟
{
second=0;//同时秒数清零
minu++;
if(minu==60)//分钟加到60则进位小时
{
minu=0;//同时分钟数清零
hour++;
if(hour==24)//小时加到24则小时清零
{
hour=0;
week++;
if(week==8)
{
week=1;
}
write_week(week);//星期若变化则重新写入
day++;
if(year%4==0&&month==2)//判断是否为闰年的2月
{
if(day==30)
{
writemon();
}
write_yea(9,day);//日若变化则重新写入
}
if(year%4!=0&&month==2)//判断是否为平年的2月
{
if(day==29)
{
writemon();
}
write_yea(9,day);//日若变化则重新写入
}
if(month!=2&&month!=4&&month!=6&&month!=9&&month!=11)
{
if(day==32)
{
writemon();
}
write_yea(9,day);//日若变化则重新写入
}
if(month==4||month==6||month==9||month==11)
{
if(day==31)
{
writemon();
}
write_yea(9,day);//日若变化
}
}
write_sec(2,hour);
}
write_sec(5,minu);//分钟若变化
}
write_sec(8,second);//秒若变化
}
}
/********************主函数*************************/
void main()
{
init();//初始化LCD160tr
timeint();//定时器初始化化
while(1)
{
keyscan();//不停的检测按键是否被按下
}
}