下图是proteus的仿真截图。2051的P1口用于1602的数据口;2051的P3.4用于1602的RS端;P3.5用于1602的RW端;P3.6用于1602的EN端;剩下的P3.2和P3.7用于调时和加一按钮。网上有些程序还有一个减一按钮,由于2051的I/O口就那么多,就省略了。如一定要加,可考虑做一个2X2的小键盘,并用扫描的方式,但是程序稍微复杂一点。
下面是详细的程序列表:
#include<reg52.h>
#define uchar unsigned char
#define uint unsigned int
#define datasend P1
//sbit LCD_RW = P3 ^ 5;
//sbit LCD_RS = P3 ^ 4;
//sbit LCD_E = P3 ^ 3;
sbit lcden="P3"^3; //LCD使能端
sbit lcdrs="P3"^4; //LCD 数据/指令(H/L)选择端
sbit lcdrw="P3"^5; //LCD 数据/指令(H/L)选择端
sbit s1=P3^2; //定义功能按键
sbit s2=P3^7; //定义时分秒 +1 按键
//sbit s3=P1^2; //定义时分秒 -1 按键
//uchar mytable0[]=" lwxkey ";
//uchar mytable1[]="A handsome boy!!";
uchar code line0[]=" 2001-01-01 "; //初始化显示
uchar code line1[]=" 00:00:00 ";
uchar code month0[]={31,28,31,30,31,30,31,31,30,31,30,31}; //平年月
uchar code month1[]={31,29,31,30,31,30,31,31,30,31,30,31}; //闰年月
uchar count,hour,minute,second,year=1,month=1,date=1;
uchar s1num; //s1num:定义功能键按下次数变量
void delay(unsigned int z) //延时子函数 z*1ms
{
unsigned int x,y;
for(x=z;x>0;x--)
for(y=110;y>0;y--);
}
void write_com(unsigned char c) //写命令子函数
{
lcdrs=0; //低电平选择为 写指令
lcdrw=0; //低电平选择为 写指令
lcden=0;
datasend=c; //把指令写入P1口
delay(5); //参考时序图
lcden=1; //开使能
delay(5); //读取指令
lcden=0; //关闭使能
}
void write_data(unsigned char d) //写数据子函数
{
lcdrs=1; //高电平选择为 写数据
lcdrw=0; //低电平选择为 写指令
datasend=d; //把数据写入P0口
delay(5); //参考时序图
lcden=1; //开使能
delay(5); //读取数据
lcden=0; //关闭使能
}
void write_hms(uchar add,uchar dat) //时分秒写函数 add:输入位置设置变量 dat:输入时分秒数据
{
uchar sw,gw; //定义十位,个位变量
sw=dat/10;
gw=dat%10;
write_com(0x80+0x40+add); //从第二行(add)位开始写数据
write_data(0x30+sw); //写入十位
write_data(0x30+gw); //写入个位
}
void write_ymd(uchar add,uchar dat) //年月日写函数 add:输入位置设置变量 dat:输入时分秒数据
{
uchar sw,gw; //定义十位,个位变量
sw=dat/10;
gw=dat%10;
write_com(0x80+add); //从第一行(add)位开始写数据
write_data(0x30+sw); //写入十位
write_data(0x30+gw); //写入个位
}
void t0() interrupt 1 //定时器0中断子程序
{
TH0=0x36;//(65536-50000)/256;
TL0=0x9d;//(65536-50000)%256;
count++;
}
void gethms() //时分秒处理子程序
{
second++;
if(second==60)
{
second=0;
minute++;
if(minute==60)
{
minute=0;
hour++;
if(hour==24)
{
hour=0;
date++;
}
write_hms(2,hour); //时输出显示
}
write_hms(5,minute); //分输出显示
}
write_hms(8,second); //秒输出显示
}
void getymd() //年月日处理子程序
{
if(year%4==0)
{
if(date==month1[month-1]+1)
{
date=1;
month++;
if(month==13)
{
month=1;
year++;
if(year==100)
{
year=1;
}
write_ymd(4,year);
}
write_ymd(7,month);
}
write_ymd(10,date);
}
else
{
if(date==month0[month-1]+1)
{
date=1;
month++;
if(month==13)
{
month=1;
year++;
if(year==100)
{
year=1;
}
write_ymd(4,year);
}
write_ymd(7,month);
}
write_ymd(10,date);
}
}
void keyscan() //按键扫描子程序
{
if(s1==0) //功能键 S1 功能设置
{
delay(5);
if(s1==0) //功能键按下
{
while(s1==0); //松手检测
s1num++; //功能键按下计数
TR0=0; //T0定时停止,进入时间调整
switch(s1num)
{
case 1: write_com(0x80+0x40+0x09); break; //光标闪烁位定为秒个位
case 2: write_com(0x80+0x40+0x06); break; //光标闪烁位定为分个位
case 3: write_com(0x80+0x40+0x03); break; //光标闪烁位定为时个位
case 4: write_com(0x80+0x0b); break; //光标闪烁位定为日个位
case 5: write_com(0x80+0x08); break; //光标闪烁位定为月个位
case 6: write_com(0x80+0x05); break; //光标闪烁位定为年个位
}
write_com(0x0f); //光标闪烁
if(s1num==7)
{
s1num=0; //按键计数复位
write_com(0x0c); //光标停止闪烁
TR0=1; //重新启动定时器
}
}
}
if(s2==0) //S2键 时分秒 +1 模块
{
delay(5);
if(s2==0)
{
while(s2==0);
switch(s1num)
{
case 1: second++; //秒+
if(second==60) second="0"; //复位
write_hms(8,second); //秒输出显示
write_com(0x80+0x40+0x09); //光标闪烁位定为秒个位
break;
case 2: minute++;
if(minute==60) minute="0"; //复位
write_hms(5,minute); //分输出显示
write_com(0x80+0x40+0x06); //光标闪烁位定为分个位
break;
case 3: hour++;
if(hour==24) hour="0"; //复位
write_hms(2,hour); //时输出显示
write_com(0x80+0x40+0x03); //光标闪烁位定为时个位
break;
case 4: date++; //日+
if(year%4==0) //平闰年判断
{
if(date==month1[month-1]+1)
{
date=1;
}
}
else
{
if(date==month0[month-1]+1)
{
date=1;
}
}
write_ymd(10,date); //日输出显示
write_com(0x80+0x0b); //光标闪烁位定为日个位
break;
case 5: month++; //月+
if(month==13) month="1";
if(year%4==0) //判断日和月的配对关系
{
if(date>month1[month-1]) date="month1"[month-1];
}
else
{
if(date>month0[month-1]) date="month0"[month-1];
}
write_ymd(10,date); //日输出显示
write_ymd(7,month); //月输出显示
write_com(0x80+0x08); //光标闪烁位定为月个位
break;
case 6: year++; //年+
if(year==100) year="1"; //复位
write_ymd(4,year); //年输出显示
write_com(0x80+0x05); //光标闪烁位定为年个位
break;
}
}
}
/*********************************************************************************
if(s3==0) //时分秒 -1 模块
{
delay(5);
if(s3==0)
{
while(s3==0);
switch(s1num)
{
case 1: second--; //秒-
if(second==255) second="59"; //复位
write_hms(8,second); //秒输出显示
write_com(0x80+0x40+0x09); //光标闪烁位定为秒个位
break;
case 2: minute--;
if(minute==255) minute="59"; //复位
write_hms(5,minute); //分输出显示
write_com(0x80+0x40+0x06); //光标闪烁位定为分个位
break;
case 3: hour--;
if(hour==255) hour="23"; //复位
write_hms(2,hour); //时输出显示
write_com(0x80+0x40+0x03); //光标闪烁位定为时个位
break;
case 4: date--; //日-
if(year%4==0)
{
if(date==0)
{
date=month1[month-1];
}
}
else
{
if(date==0)
{
date=month0[month-1];
}
}
write_ymd(10,date); //日输出显示
write_com(0x80+0x0b); //光标闪烁位定为日个位
break;
case 5: month--; //月-
if(month==0) month="12";
if(year%4==0) //判断日和月的配对关系
{
if(date>month1[month-1]) date="month1"[month-1];
}
else
{
if(date>month0[month-1]) date="month0"[month-1];
}
write_ymd(10,date); //日输出显示
write_ymd(7,month); //月输出显示
write_com(0x80+0x08); //光标闪烁位定为月个位
break;
case 6: year--; //年-
if(year==0) year="99"; //复位
write_ymd(4,year); //年输出显示
write_com(0x80+0x05); //光标闪烁位定为年个位
break;
}
}
}
*********************************************************************************/
}
void initialize() //LCD初始化函数
{
uchar num;
lcden=0;
write_com(0x38); //设置16x2显示,5x7点阵显示,8位数据接口
write_com(0x0c); //00001DCB,D(开关显示),C(是否显示光标),B(光标闪烁,光标不显示)
write_com(0x06); //000001N0,N(地址指针+-1)
write_com(0x01); //清屏指令 每次显示下一屏内容时,必须清屏
/*********************************************************************************
write_com(0x80+0x10); //第一行,前头空5格显示
for(num=0;num<11;num++) //显示6个字符
{
write_data(mytable0[num]);
delay(10);
}
write_com(0x80+0x50); //第二行,从第一格开始显示
for(num=0;num<16;num++) //显示16个字符
{
write_data(mytable1[num]);
delay(10);
}
for(num=0;num<16;num++)
{
write_com(0x1c); //0001(S/C)(R/L)**; S/C:高电平移动字符,低电平移动光标; R/L:高电平左移,低电平右移
delay(300);
}
delay(1000);
*********************************************************************************/
write_com(0x01); //清屏指令 每次显示下一屏内容时,必须清屏
write_com(0x80); //第一行,顶格显示
for(num=0;num<12;num++) //初始化第一行显示 2009-01-01
{
write_data(line0[num]);
delay(10);
}
write_com(0x80+0x40); //第二行,顶格显示
for(num=0;num<10;num++) //初始化第二行显示 00:00:00
{
write_data(line1[num]);
delay(10);
}
TMOD=0x01; //定时器0中断初值设置
TH0=0x36;//(65536-50000)/256;
TL0=0x9d;//(65536-50000)%256;
EA=1;
ET0=1;
TR0=1;
}
void main()
{
initialize(); //初始化
while(1)
{
keyscan(); //按键扫描
if(count==20) //定时1s 运行模块
{
count=0;
gethms(); //时分秒数据处理
getymd(); //年月日数据处理
}
}
}
用户507162 2010-1-13 07:27