#include <REGX52.H>
#include <define.h>
#include <LCD1602.h>
#include <Buzzer.h>
#include <MatrixKey.h>
sbit Buzzer=P2^5;
sbit LCD_RS=P2^6;
sbit LCD_RW=P2^5;
sbit LCD_EN=P2^7;
#define LCD_DataPort P0
bit countdown = 0; // 倒计时状态:0=未开始,1=正在倒计时
uchar cd_shi = 0, cd_fen = 0, cd_miao = 0; // 倒计时剩余时长(时/分/秒
uchar count=0; //调时标志:1为秒,2为分,3为时
void delay(uint z) //延时函数
{
uint x,y;
for(x=z;x>0;x--)
for(y=110;y>0;y--);
}
void write_com(uchar com) //写液晶命令函数
{
LCD_RS=0;
LCD_EN=0;
P0=com;
delay(5);
LCD_EN=1;
delay(5);
LCD_EN=0;
}
void write_date(uchar date) //写液晶数据函数
{
LCD_RS=1;
LCD_EN=0;
P0=date;
delay(5);
LCD_EN=1;
delay(5);
LCD_EN=0;
}
void init() //初始化函数
{
// uchar num;
// 中断初始化
EA=1; // 使能总中断
EX1=1; // 使能外部中断1
IT1=1; // 外部中断1设置为下降沿触发
TMOD |= 0x01; // 定时器0工作模式1(16位) //定时器0初始化(倒计时1秒计时)
TH0 = (65536 - 10000) / 256; // 初值:10ms中断一次(12MHz晶振)
TL0 = (65536 - 10000) % 256;
ET0 = 1; // 使能定时器0中断
TR0 = 1; // 启动定时器0
flag1=0; // 闹钟设置标志位清零
t0_num=0; // 定时器0计数变量清零
week=1; // 星期初始化为1(周一)
LCD_EN=0;
rd=0;
write_ds(0x0A,0x20); //打开震荡器
write_ds(0x0B,0X26); //设置24小时模式,数据二进制格式,开启闹铃中断
}
void write_sfm(uchar add,uchar date) //液晶刷新时分秒函数:4为时,7为分,10为秒
{
char shi,ge;
shi=date/10; // 计算十位数字
ge=date%10; // 计算个位数字
write_com(0x80+0x40+add); // 设置显示位置(第二行)
write_date(0x30+shi); // 显示十位(0x30是字符'0'的ASCII码)
write_date(0x30+ge); // 显示个位
}
void write_ds(uchar add,uchar date) //写DS12C887函数
{
dscs=0; // 片选使能
dsas=1; // 地址锁存准备
dsds=1; // 数据锁存准备
dsrw=1; // 读写控制准备
P0=add; // 写入地址
dsas=0; // 锁存地址
dsrw=0; // 写使能
P0=date; // 写入数据
dsrw=1; // 结束写操作
dsas=1; // 释放地址锁存
dscs=1; // 释放片选
}
uchar read_ds(uchar add) //读DS12C887函数
{
uchar ds_date;
dsas=1; // 地址锁存准备
dsds=1; // 数据锁存准备
dsrw=1; // 读写控制准备
dscs=0; // 片选使能
P0=add; // 写入要读取的地址
dsas=0; // 锁存地址
dsds=0; // 读数据使能
P0=0Xff; // 端口置高阻,准备读取
ds_date=P0; // 读取数据
dsds=1; // 结束读操作
dsas=1; // 释放地址锁存
dscs=1; // 释放片选
return ds_date; // 返回读取的数据
}
void set_time() //首次上电初始化时间函数
{
write_ds(0,0); // 秒初始化为0
write_ds(1,0); // 闹钟秒初始化为0
write_ds(2,0); // 分初始化为0
write_ds(3,0); // 闹钟分初始化为0
write_ds(4,0); // 时初始化为0
write_ds(5,0); // 闹钟时初始化为0
write_ds(6,0); // 星期初始化为0
write_ds(7,0); // 日初始化为0
write_ds(8,0); // 月初始化为0
write_ds(9,0); // 年初始化为0
}
void keyscan()
{
uchar KeyNum;
KeyNum=MatrixKey();
if(KeyNum==1)
{
Buzzer_Time(100); //计算标志:1为秒,2为分,3为时,4为退出调时模式
count++;
}
switch(count)
{
case 1: // 第1次按S1:设置"秒"
write_com(0x80+0x40+7); // 光标定位到第2行第10列(秒的位置)
write_com(0x0f); // 打开光标闪烁(提示当前正在设置该位置)
break;
case 2: // 第2次按S1:设置"分"
write_com(0x80+0x40+4); // 光标定位到第2行第7列(分的位置)
break;
case 3: // 第3次按S1:设置"时"
write_com(0x80+0x40+1); // 光标定位到第2行第4列(时的位置)
break;
case 4: count=0; //关闭调时模式
;
write_com(0x0c); // 关闭光标闪烁(退出设置状态)
write_ds(0,miao); // 保存秒 // 将当前设置的时间写入DS12C887芯片(保存到硬件时钟)
write_ds(2,fen); // 保存分
write_ds(4,shi); // 保存时
break;
}
if(count) //当S1被按下(即进入调整时间模式)
{
if(KeyNum==2)
{
Buzzer_Time(100);
switch(count)
{ case 1: // 调节"秒"
miao++; // 秒加1
if(miao==60) // 秒满60归零
miao=0;
write_sfm(10,miao); // 更新液晶上的秒显示
write_com(0x80+0x40+10); // 保持光标在秒的位置
break;
case 2: // 调节"分"
fen++; // 分加1
if(fen==60) // 分满60归零
fen=0;
write_sfm(7,fen); // 更新液晶上的分显示
write_com(0x80+0x40+7); // 保持光标在分的位置
break;
case 3: // 调节"时"
shi++; // 时加1
if(shi==24) // 时满24归零
shi=0;
write_sfm(4,shi); // 更新液晶上的时显示
write_com(0x80+0x40+4); // 保持光标在时的位置
break;
}
}
if(KeyNum==3)
{
Buzzer_Time(100);
switch(count)
{ case 1: // 调节"秒"
miao--; // 秒减1
if(miao==-1) // 秒减到-1时跳为59
miao=59;
write_sfm(10,miao); // 更新液晶上的秒显示
write_com(0x80+0x40+10); // 保持光标在秒的位置
break;
case 2: // 调节"分"
fen--; // 分减1
if(fen==-1) // 分减到-1时跳为59
fen=59;
write_sfm(7,fen); // 更新液晶上的分显示
write_com(0x80+0x40+7); // 保持光标在分的位置
break;
case 3: // 调节"时"
shi--; // 时减1
if(shi==-1) // 时减到-1时跳为23
shi=23;
write_sfm(4,shi); // 更新液晶上的时显示
write_com(0x80+0x40+4); // 保持光标在时的位置
break;
}
}
}
if(KeyNum==4) //切换倒计时模式
{ Buzzer_Time(100); // 按键反馈音
countdown=~countdown;
if(countdown==1)
{
cd_shi = shi; // 将当前调时的shi/fen/miao设为倒计时目标时长
cd_fen = fen;
cd_miao = miao;
write_sfm(4, cd_shi); // 显示初始倒计时时长
write_sfm(7, cd_fen);
write_sfm(10, cd_miao);
}
}
}
void Timer0_ISR() interrupt 1
{
// 重装定时器初值(12MHz晶振,10ms中断一次)
TH0 = (65536 - 10000) / 256;
TL0 = (65536 - 10000) % 256;
t0_num++; // 累计中断次数(每10ms+1)
// 累计100次=1000ms=1秒,触发倒计时递减
if (t0_num >= 100)
{
t0_num = 0; // 重置计数
// 仅在倒计时模式下执行递减
if (countdown == 1)
{
// 秒递减:秒>0时直接减1
if (cd_miao > 0)
{
cd_miao--;
}
// 秒=0但分>0:分减1,秒重置为59
else if (cd_fen > 0)
{
cd_fen--;
cd_miao = 59;
}
// 分=0但时>0:时减1,分和秒重置为59
else if (cd_shi > 0)
{
cd_shi--;
cd_fen = 59;
cd_miao = 59;
}
// 时=分=秒=0:倒计时结束
else
{
countdown = 0; // 关闭倒计时模式
Buzzer_Time(500); // 蜂鸣器长鸣1秒提醒
// 切回正常时间:读取DS12C887的真实时间并显示
shi = read_ds(4);
fen = read_ds(2);
miao = read_ds(0);
write_sfm(4, shi); // 更新LCD为正常时间
write_sfm(7, fen);
write_sfm(10, miao);
return; // 结束本次中断处理
}
}
write_sfm(4, cd_shi); // 显示剩余时(第2行第4列)
write_sfm(7, cd_fen); // 显示剩余分(第2行第7列)
write_sfm(10, cd_miao); // 显示剩余秒(第2行第10列)
}
}
void main()
{
init();
LCD_Init(); //调用初始化函数
LCD_ShowString(1,1,"count");
LCD_ShowString(1,11,"down");
LCD_ShowChar(1,6,':');
LCD_ShowChar(1,15,':');
LCD_ShowChar(2,7,'-');
LCD_ShowChar(2,10,'-');
while(1)
{
keyscan(); //按键扫描
LCD_ShowNum(1,7,count,1); // 在第一行第7列显示count(1位数)
LCD_ShowNum(1,16,countdown,1); // 在第一行第16列显示countdown(1位数)
if(countdown==0)
{ shi=read_ds(4);
fen=read_ds(2);
miao=read_ds(0);
write_sfm(10,miao); // 更新LCD显示
write_sfm(7,fen);
write_sfm(4,shi);
}
else
{
write_sfm(10,cd_miao); // 更新LCD显示
write_sfm(7,cd_fen);
write_sfm(4,cd_shi);
}
}
}
void exter()interrupt 2 // 外部中断1服务程序(DS12C887闹钟中断)
{
uchar temp;
// 正确读取C寄存器(地址0x0C),清除中断标志(必须严格时序)
dscs = 0; // 片选使能
dsas = 1; // 地址锁存准备
dsrw = 1; // 读写准备
dsds = 1; // 数据锁存准备
P0 = 0x0C; // 写入C寄存器地址
dsas = 0; // 锁存地址(至少100ns,延时确保稳定)
delay(1); // 延时1ms(兼容不同晶振)
dsds = 0; // 读数据使能
delay(1); // 等待数据稳定
temp = P0; // 读取数据(清除中断标志的关键步骤)
dsds = 1; // 关闭数据读取
dsas = 1; // 释放地址锁存
dscs = 1; // 释放片选
}
按照你的方法修改后为什么第2行的内容不断增加,最后LCD不显示任何东西