当时看书看到lcd那章,就想利用定时器做一个电子时钟,当时也不知道有时钟模块的,以为都是定时器定时产生的时间,也是一个main文件从头到尾的版本,注释倒不少,不要说什么模块化编程和代码优化,想到什么就写什么,业余的就是可以随便写,所以思路肯定比别人的清楚明白,随便谁都看得懂,当然后来就把自己写的例程都自己做了个c51的库,傻瓜型的库,以后有空想到就慢慢记录下来
#include <reg52.h>
#include <string.h>
#define uchar unsigned char
#define uint unsigned int
//sbit u1la=P2^6; //U1锁存器 为1则打开锁存器 为0则关闭锁存器
//sbit u2la=P2^7; //U2锁存器
sbit lcdrs=P2^5; //lcd数据命令选择端 rs为L(ledrs=0)则写命令 H(ledrs=1)则写数据
sbit lcdrw=P2^6;
sbit lcden=P2^7; //lcd使能 lcden=1 则写入
//sbit key1=P3^0; //矩阵键盘中第4列中从上到下 第一个按键轮流3种状态 按一下小时闪烁 分钟闪烁 不闪烁则继续加秒数
//sbit key2=P3^1; // 第二个按键增加当前选中的秒 小时或者 分钟 等
//sbit key3=P3^2; // 第三个按键减少当前选中的数字
//sbit rd=P3^7; //置低电平后分离出第4列四个独立按键
sbit key1=P3^6; //独立 按键 由于P34 P35端口已经被lcd占用 所以只有两个独立按键可以用
sbit key2=P3^7;
sbit sbeep=P2^3; //蜂鸣器
uchar code table1[]=" 2019-08-05";
uchar code table2[]=" 00:00:00";
int code month[12]={31,28,31,30,31,30,31,31,30,31,30,31}; //每个月天数 第二月有两种情况 运用时闰年判断下后处理
char code *week[7]={"SUN","MON","TUE","WED","THU","FRI","SAT"}; //每周一天英文缩写 元素从0 星期天开始
void delayms(uint ms) ; //延迟子函数
void display(uchar *table,uchar line); //显示字符串子函数 table为字符串,最大16字符 line为显示的行数,1为第一行 2为第二行 函数目前不容错
void write_com(uchar cmd); //lcd写命令子函数 一次写入一个命令 例如0x80
void write_data1(uchar); //lcd写数据子函数 一次写入一个字符
void write_data(uchar ,uchar , uchar ); //写数据 第几行 第几个开始 数据 第三个参数必须是1-2位数
void init(); //初始化函数
void day_add(); //增加一天后处理函数
void calweek(); //计算某一天星期几并刷新显示
void mybeep(); //蜂鸣器
void keyscan(); //检测 处理按键
void settime_add(); //按键修改时间后刷新时间
void settime_minus(); //修改时间
void cursor_blink(); //判断blink状态 更新光标闪烁
uchar num0; //num0为定时器0中断次数
char months,weeks, days, hours,minutes,seconds,comparemonth,m; //月 周几 日 小时 分钟 秒 每月天数 m用来公式计算周几
char blink; //第一个设置按键状态 循环7中状态 秒闪烁 分钟闪烁 小时闪烁 月 日 年 不闪烁
unsigned int years; //年份超过255数字
void main()
{
init();
display(table1,1);
display(table2,2);
while(1)
{
keyscan();
delayms(5);
cursor_blink();
}
}
void init()
{
TMOD = 0x01; //定时器1工作方式1 定时器0工作方式1 定时器1在高四位
TH0=(65536-45872)/256; //定时器0 16位计数器高8位初始计数
TL0=(65536-45872)%256; //定时器0 16位计数器低8位初始计数
EA=1; //中端使能 中端总开关
ET0=1; //定时器0 中断允许位
TR0=1; //初始中断开始启动
// rd=0; //初始化rd******此处用来分离矩阵键盘独立按键第四列*******
// u1la=0; //关闭U1
// u2la=0; //关闭U2
lcden=0; //lcd关闭写入
lcdrw=0;
years=2019; //初始化显示 2019/08/05 程序开始测试的日子
months=8;
days=5;
weeks=1;
blink=0;
write_com(0x38); //设置16x2显示 5x7点阵 8位数据接口
write_com(0x0c); //开显示 不显示光标
write_com(0x06); //写一个字符后地址指针加1
write_com(0x01); //显示清零 数据指针清零 要写入数据先写入命令 write_cmd(0x80) 第一行第一个字符地址 0x80+0x40 第二行开头地址写入
write_com(0x80+0x40+6);
write_data1(':');
write_com(0x80+0x40+9);
write_data1(':');
calweek();
}
void T0_time() interrupt 1 //*******定时器0的中断序号是1*********
{
TH0=(65536-45872)/256; //进入中断再次初始化计数 TRO=1 所以计数会不停 下次中断仍然是50ms后
TL0=(65536-45872)%256; //16位计数器低8位初始计数
num0++;
if(num0 == 20) //中断20次后 50ms20次为1s
{
num0=0;
seconds++;
if(seconds==60) //60秒加1分钟
{
seconds=0;
minutes++;
if(minutes==60) //60分钟加1小时
{
minutes=0;
hours++;
if(hours==24) //超过24小时增加一天
{
hours=0;
day_add();
}
write_data(2,4,hours);
}
write_data(2,7,minutes);
}
write_data(2,10,seconds);
}
}
void keyscan()
{
if(key1==0) //第一个按键轮流7种状态 按一下秒闪烁 分钟闪烁 小时闪烁 月 日 年 不闪烁则继续加秒数 0 1 2 3 4 5
{
delayms(10);
if(key1==0)
{
mybeep();
blink++; //循环blink状态
if(blink ==7)
{
blink=0;
write_com(0x0c); //取消光标闪烁
TR0=1; //继续计时
}
if(blink != 0)
{
TR0=0; //暂停计时
// cursor_blink();
}
}
while(!key1); //等待按键弹起
}
if(key2==0) //第二个按键在闪烁时增加 小时、分钟等数字 各数字不联动
{
delayms(10);
if(key2==0) //确认按下按键
{
mybeep();
switch (blink)
{
case 1: seconds++; //秒
settime_add();
break;
case 2: minutes++; //分
settime_add();
break;
case 3: hours++; //时
settime_add();
break;
case 4: days++; //天
settime_add();
break;
case 5: months++; //月
settime_add();
break;
case 6: years++; //年
settime_add();
break;
default : ; //光标不闪烁的时候按增加键 不处理
}
}
while(!key2); //按键弹起后继续
}
// if(key3==0)
// {
// delayms(10);
// if(key3==0) //按键3 减小数字
// {
// mybeep();
// switch (blink)
// {
// case 1: seconds--; //秒
// settime_minus();
// break;
// case 2: minutes--; //分
// settime_minus();
// break;
// case 3: hours--; //时
// settime_minus();
// break;
// case 4: days--; //天
// settime_minus();
// break;
// case 5: months--; //月
// settime_minus();
// break;
// case 6: years--; //年
// settime_minus();
// break;
// default : ; //光标不闪烁的时候按减小键 不处理
// }
// }
// while(!key3);
// }
}
void settime_add() //增大时间时,不进位 只处理溢出情况
{
if(seconds==60) seconds=0;
if(minutes==60) minutes=0;
if(hours==24) hours=0;
comparemonth=month[months-1]; //comparemonth除了2月不确定,其他每月天数都是数组中的数字
if(months==2) { if(years%4==0) comparemonth=29; } //闰年二月29天
if(days>comparemonth) days=1;
if(months>12) months=1;
if(years%100==0) write_data(1,3,years/100); //年份后两位进位就增加年份前两位更新
write_data(1,5,years%100); //年份后两位没进位,只要更新后两位
write_data(2,4,hours);
write_data(2,7,minutes);
write_data(2,10,seconds);
write_data(1,8,months); //更新月份
write_data(1,11,days); //更新天数
calweek(); //计算周几并更新
}
void settime_minus() //减小时间时,不退位 只处理溢出情况
{
if(seconds<0) seconds=59;
if(minutes<0) minutes=59;
if(hours<0) hours=23;
comparemonth=month[months-1]; //comparemonth除了2月不确定,其他每月天数都是数组中的数字
if(months==2) { if(years%4==0) comparemonth=29; } //闰年二月29天
if(days==0) days=comparemonth;
if(months==0) months=12;
if(years%100==0) write_data(1,3,years/100); //年份后两位进位就增加年份前两位更新
write_data(1,5,years%100); //年份后两位没进位,只要更新后两位
write_data(2,4,hours);
write_data(2,7,minutes);
write_data(2,10,seconds);
write_data(1,8,months); //更新月份
write_data(1,11,days); //更新天数
calweek(); //计算周几并更新
}
void calweek()
{
char numi; //显示字符串for 循环用
if(months==1 || months==2) //如果是1 2月份,要看成前一年的13 14月
{
m=months+12;
years--;
}
else m=months;
weeks=(days+2*m+3*(m+1)/5+years+years/4-years/100+years/400+1)%7; //基姆拉尔森公式算星期几
write_com(0x80+0x40+13);
for(numi=0;numi<3;numi++) //for循环写入字符串每一个字符
{
write_data1(week[weeks][numi]);
delayms(5);
}
}
void cursor_blink()
{
switch (blink)
{
case 1: write_com(0x80+0x40+11); //秒 光标定位到秒
write_com(0x0f); //光标开始闪烁
break;
case 2: write_com(0x80+0x40+8); //分
write_com(0x0f);
break;
case 3: write_com(0x80+0x40+5); //时
write_com(0x0f);
break;
case 4: write_com(0x80+12); //天
write_com(0x0f);
break;
case 5: write_com(0x80+9); //月
write_com(0x0f);
break;
case 6: write_com(0x80+6); //年
write_com(0x0f);
break;
default : ; //光标不闪烁
}
}
void day_add()
{
comparemonth=month[months-1];
if(months==2) //闰年二月29天
{
if(years%4==0)
comparemonth=29;
}
days++;
if(days>comparemonth) //days超过当月天数 回归1日 月份加一
{
days=1;
months++;
if(months>12) //加一年
{
months=1;
years++;
if(years%100==0) //加一个世纪
{
write_data(1,3,years/100); //年份后两位近位就前两位更新
}
write_data(1,5,years%100); //年份后两位没进位,只要更新后两位
}
write_data(1,8,months); //更新月份
}
write_data(1,11,days); //更新天数
calweek(); //计算周几
}
void write_com(uchar cmd)
{
lcdrs=0; //lcd数据命令选择端 rs为L(ledrs=0)则写命令 H(ledrs=1)则写数据
P0=cmd; //P0数据与lcd数据口相连
delayms(1);
lcden=1; //写入命令
delayms(1);
lcden=0; //关闭写入
}
void write_data1(uchar data2)
{
lcdrs=1; //lcd数据命令选择端 rs为L(ledrs=0)则写命令 H(ledrs=1)则写数据
P0=data2;
delayms(1);
lcden=1;
delayms(1);
lcden=0;
}
void write_data(uchar line,uchar add, uchar data1) //写数据 第几行 第几个开始 数据 data1必须是1-2位数 此程序中不会溢出
{
uchar address,shi,ge;
shi=data1/10;
ge=data1%10;
address=0x80+(line-1)*0x40+add; //根据行数和add参数定位写数据位置
write_com(address);
write_data1(0x30+shi); 输出十位字符****写完后光标自动下移一个,无需再定位地址****
write_data1(0x30+ge); 输出个位字符
}
void display(uchar *table,uchar line) //显示字符串子函数 table为字符串,最大16字符 line为显示的行数,1为第一行 2为第二行 函数目前容错
{
uchar strlen1;
uchar num;
strlen1=strlen(table); //strlen函数得到当前字符串长度
if(strlen1>16) strlen1=16; //超出16字符后只取前16字符
if(line==1) //第一行写入地址0x80
write_com(0x80);
else if(line==2) //第二行写入地址0xc0
write_com(0x80+0x40);
if(line==1 || line==2) //line参数1或者2才操作,不然不处理
{
for(num=0;num<strlen1;num++) //for循环写入字符串每一个字符
{
write_data1(table[num]);
delayms(1);
}
}
else //行数溢出 bibi两声
{
mybeep();
mybeep();
}
}
void delayms(uint ms) //延迟毫秒函数 参数为需要延迟的毫秒数
{
uint i,j;
for(i=ms;i>0;i--)
for(j=110;j>0;j--);
}
void mybeep() //蜂鸣器发声
{
sbeep=0;
delayms(100);
sbeep=1;
}