476 lines
10 KiB
C
476 lines
10 KiB
C
#include<reg51.h>
|
|
#include<intrins.h>
|
|
#include"lcd.h"
|
|
#include"ds1302.h"
|
|
#include"ds18b20.h"
|
|
|
|
/* 端口定义 */
|
|
sbit K1=P3^1; /* 模式/确认键 */
|
|
sbit K2=P3^0; /* 切换/返回键 */
|
|
sbit K3=P3^2; /* 加/开始暂停 */
|
|
sbit K4=P3^3; /* 减/复位清0 */
|
|
|
|
|
|
/* 全局变量 */
|
|
unsigned char mode = 0; /* 模式: 0=时钟, 2=秒表 */
|
|
unsigned char last_mode = 0xFF; /* 上一模式,用于检测模式切换 */
|
|
int temperature = 0; /* 温度值(10倍) */
|
|
unsigned char temp_cnt = 0; /* 温度采集计数 */
|
|
unsigned char alarm_hour=6, alarm_min=0, alarm_on=0; /* 闹钟参数 */
|
|
|
|
|
|
/* 秒表变量 */
|
|
unsigned int sw_sec = 0; /* 秒表秒数 */
|
|
unsigned char sw_csec = 0; /* 秒表百分秒 */
|
|
bit sw_running = 0; /* 秒表运行状态 */
|
|
|
|
/* 宏定义 */
|
|
#define BCD2DEC(bcd) (((bcd)>>4)*10 + ((bcd)&0x0f))
|
|
#define DEC2BCD(dec) (((dec)/10<<4) | ((dec)%10))
|
|
|
|
/* 函数声明 */
|
|
void Delay10ms(void);
|
|
void DelayMs(unsigned int ms);
|
|
void Timer1Init(void);
|
|
void TempPros(void);
|
|
void AlarmJudge(void);
|
|
void AlarmBuzPro(void);
|
|
void SetAlarm(void);
|
|
void SetTime(void);
|
|
void LcdFramework(void);
|
|
void LcdUpdate(void);
|
|
void KeyScan(void);
|
|
|
|
/* K1按键检测:去抖 + 确认释放后返回1 */
|
|
bit K1_Pressed(void)
|
|
{
|
|
if(K1) return 0;
|
|
Delay10ms();
|
|
if(K1) return 0;
|
|
while(!K1);
|
|
return 1;
|
|
}
|
|
|
|
/* K2按键检测:去抖 + 确认释放后返回1 */
|
|
bit K2_Pressed(void)
|
|
{
|
|
if(K2) return 0;
|
|
Delay10ms();
|
|
if(K2) return 0;
|
|
while(!K2);
|
|
return 1;
|
|
}
|
|
|
|
/* K3按键检测:去抖 + 确认释放后返回1 */
|
|
bit K3_Pressed(void)
|
|
{
|
|
if(K3) return 0;
|
|
Delay10ms();
|
|
if(K3) return 0;
|
|
while(!K3);
|
|
return 1;
|
|
}
|
|
|
|
/* K4按键检测:去抖 + 确认释放后返回1 */
|
|
bit K4_Pressed(void)
|
|
{
|
|
if(K4) return 0;
|
|
Delay10ms();
|
|
if(K4) return 0;
|
|
while(!K4);
|
|
return 1;
|
|
}
|
|
|
|
/* LCD写字符串 */
|
|
void LcdWriteStr(unsigned char code *str)
|
|
{
|
|
while(*str) LcdWriteData(*str++);
|
|
}
|
|
|
|
/* LCD定位 + 2位十进制数显示 */
|
|
void LcdShowNumAt(unsigned char addr, unsigned char num)
|
|
{
|
|
LcdWriteCom(addr);
|
|
LcdWriteData('0' + num / 10);
|
|
LcdWriteData('0' + num % 10);
|
|
}
|
|
|
|
/* 通用参数调整函数:循环检测K3/K4加减,K1确认退出 */
|
|
void AdjustValue(unsigned char *val, unsigned char maxVal,
|
|
unsigned char dispAddr, unsigned char cursorAddr)
|
|
{
|
|
LcdWriteCom(cursorAddr); LcdWriteCom(0x0f);
|
|
while(1)
|
|
{
|
|
if(K3_Pressed())
|
|
{
|
|
if(*val < maxVal) (*val)++; else *val = 0;
|
|
LcdShowNumAt(dispAddr, *val);
|
|
LcdWriteCom(cursorAddr); LcdWriteCom(0x0f);
|
|
}
|
|
if(K4_Pressed())
|
|
{
|
|
if(*val > 0) (*val)--; else *val = maxVal;
|
|
LcdShowNumAt(dispAddr, *val);
|
|
LcdWriteCom(cursorAddr); LcdWriteCom(0x0f);
|
|
}
|
|
if(K1_Pressed()) { LcdWriteCom(0x0c); return; }
|
|
}
|
|
}
|
|
|
|
/* 主函数 */
|
|
void main()
|
|
{
|
|
Timer1Init(); /* 初始化定时器1 */
|
|
LcdInit(); /* 初始化LCD1602 */
|
|
|
|
|
|
|
|
Ds1302Init(); /* 初始化DS1302 */
|
|
DS18B20_SetResolution(0x3F); /* 设置DS18B20分辨率10位 */
|
|
DS18B20_StartConvert(); /* 开始温度转换 */
|
|
|
|
/* 从DS1302 RAM读取闹钟参数 */
|
|
alarm_hour = Ds1302Read(0xC1);
|
|
if(alarm_hour > 23) alarm_hour = 6;
|
|
alarm_min = Ds1302Read(0xC3);
|
|
if(alarm_min > 59) alarm_min = 0;
|
|
alarm_on = Ds1302Read(0xC5);
|
|
if(alarm_on > 1) alarm_on = 0;
|
|
|
|
/* 主循环 */
|
|
while(1)
|
|
{
|
|
Ds1302ReadTime(); /* 读取DS1302时间 */
|
|
|
|
/* 每5次循环采集一次温度 */
|
|
temp_cnt++;
|
|
if(temp_cnt >= 5)
|
|
{
|
|
temp_cnt = 0;
|
|
TempPros();
|
|
}
|
|
|
|
/* 模式切换时刷新LCD框架 */
|
|
if(mode != last_mode)
|
|
{
|
|
LcdFramework();
|
|
last_mode = mode;
|
|
}
|
|
|
|
AlarmJudge(); /* 闹钟判断 */
|
|
AlarmBuzPro(); /* 闹钟蜂鸣器处理 */
|
|
KeyScan(); /* 按键扫描 */
|
|
LcdUpdate(); /* LCD显示更新 */
|
|
}
|
|
}
|
|
|
|
/* LCD写2位十进制数(当前位置) */
|
|
void ShowLcdNum(unsigned char num)
|
|
{
|
|
LcdWriteData('0' + num / 10);
|
|
LcdWriteData('0' + num % 10);
|
|
}
|
|
|
|
/* LCD显示星期写入 */
|
|
void ShowLcdWeek(unsigned char week)
|
|
{
|
|
switch(week)
|
|
{
|
|
case 1: LcdWriteData('S'); LcdWriteData('u'); LcdWriteData('n'); break; /* Sunday */
|
|
case 2: LcdWriteData('M'); LcdWriteData('o'); LcdWriteData('n'); break; /* Monday */
|
|
case 3: LcdWriteData('T'); LcdWriteData('u'); LcdWriteData('e'); break; /* Tuesday */
|
|
case 4: LcdWriteData('W'); LcdWriteData('e'); LcdWriteData('d'); break; /* Wednesday */
|
|
case 5: LcdWriteData('T'); LcdWriteData('h'); LcdWriteData('u'); break; /* Thursday */
|
|
case 6: LcdWriteData('F'); LcdWriteData('r'); LcdWriteData('i'); break; /* Friday */
|
|
case 7: LcdWriteData('S'); LcdWriteData('a'); LcdWriteData('t'); break; /* Saturday */
|
|
}
|
|
}
|
|
|
|
/* LCD框架:刷新静态结构(按模式) */
|
|
void LcdFramework()
|
|
{
|
|
LcdWriteCom(0x0c); /* 关闭显示光标 */
|
|
|
|
if(mode == 0) /* 时钟模式框架 */
|
|
{
|
|
LcdWriteCom(0x80);
|
|
LcdWriteStr("20 - - ");
|
|
LcdWriteCom(0xC0);
|
|
LcdWriteStr(" : : ");
|
|
}
|
|
else if(mode == 2) /* 秒表模式框架 */
|
|
{
|
|
LcdWriteCom(0x80);
|
|
LcdWriteStr(" Stop Watch ");
|
|
LcdWriteCom(0xC0);
|
|
LcdWriteStr(" : : . ");
|
|
}
|
|
}
|
|
|
|
/* LCD:刷新动态数据 */
|
|
void LcdUpdate()
|
|
{
|
|
int temp_disp;
|
|
|
|
if(mode == 0) /* 时钟模式 */
|
|
{
|
|
LcdShowNumAt(0x82, BCD2DEC(TIME[6])); /* 年 */
|
|
LcdShowNumAt(0x85, BCD2DEC(TIME[4])); /* 月 */
|
|
LcdShowNumAt(0x88, BCD2DEC(TIME[3])); /* 日 */
|
|
LcdWriteCom(0x8C);
|
|
ShowLcdWeek(TIME[5] & 0x07); /* 星期 */
|
|
LcdShowNumAt(0xC0, BCD2DEC(TIME[2])); /* 时 */
|
|
LcdShowNumAt(0xC3, BCD2DEC(TIME[1])); /* 分 */
|
|
LcdShowNumAt(0xC6, BCD2DEC(TIME[0])); /* 秒 */
|
|
|
|
/* 显示温度:-12.3C */
|
|
LcdWriteCom(0xC9);
|
|
temp_disp = temperature;
|
|
if(temp_disp < 0) { LcdWriteData('-'); temp_disp = -temp_disp; }
|
|
else { LcdWriteData(' '); }
|
|
LcdWriteData('0' + temp_disp / 100);
|
|
LcdWriteData('0' + (temp_disp / 10) % 10);
|
|
LcdWriteData('.');
|
|
LcdWriteData('0' + temp_disp % 10);
|
|
LcdWriteData('C');
|
|
}
|
|
else if(mode == 2) /* 秒表模式 */
|
|
{
|
|
LcdShowNumAt(0xC0, sw_sec / 3600); /* 时 */
|
|
LcdShowNumAt(0xC3, (sw_sec % 3600) / 60); /* 分 */
|
|
LcdShowNumAt(0xC6, sw_sec % 60); /* 秒 */
|
|
LcdShowNumAt(0xC9, sw_csec); /* 百分秒 */
|
|
}
|
|
}
|
|
|
|
/* 按键扫描 */
|
|
void KeyScan()
|
|
{
|
|
if(mode == 0) /* 时钟模式按键 */
|
|
{
|
|
if(K1_Pressed())
|
|
{
|
|
SetTime();
|
|
last_mode = 0xFF; /* 强制刷新显示 */
|
|
}
|
|
if(K2_Pressed())
|
|
{
|
|
mode = 2;
|
|
sw_running = 0;
|
|
sw_sec = 0;
|
|
sw_csec = 0;
|
|
LcdFramework();
|
|
last_mode = mode;
|
|
LcdUpdate();
|
|
}
|
|
}
|
|
else if(mode == 2) /* 秒表模式按键 */
|
|
{
|
|
if(K3_Pressed())
|
|
{
|
|
sw_running = !sw_running; /* 切换运行状态 */
|
|
TR1 = sw_running; /* 控制定时器启停 */
|
|
}
|
|
if(K4_Pressed())
|
|
{
|
|
if(!sw_running) { sw_sec = 0; sw_csec = 0; } /* 停止时清零 */
|
|
}
|
|
if(K2_Pressed())
|
|
{
|
|
mode = 0;
|
|
TR1 = 0;
|
|
sw_running = 0;
|
|
sw_sec = 0;
|
|
sw_csec = 0;
|
|
LcdFramework();
|
|
last_mode = mode;
|
|
LcdUpdate();
|
|
}
|
|
}
|
|
}
|
|
|
|
/* 时间设置:K1切换字段,K3/K4加/减,最后一个字段后自动写入DS1302 */
|
|
void SetTime()
|
|
{
|
|
unsigned char y,mo,d,w,h,mi,s;
|
|
|
|
/* BCD转十进制 */
|
|
y = BCD2DEC(TIME[6]);
|
|
mo = BCD2DEC(TIME[4]);
|
|
d = BCD2DEC(TIME[3]);
|
|
w = TIME[5] & 0x07;
|
|
h = BCD2DEC(TIME[2]);
|
|
mi = BCD2DEC(TIME[1]);
|
|
s = BCD2DEC(TIME[0]);
|
|
|
|
/* 清除显示 */
|
|
LcdWriteCom(0x80);
|
|
LcdWriteStr("20 - - ");
|
|
LcdWriteCom(0xC0);
|
|
LcdWriteStr(" : : ");
|
|
|
|
/* 显示当前时间 */
|
|
LcdShowNumAt(0x82, y);
|
|
LcdShowNumAt(0x85, mo);
|
|
LcdShowNumAt(0x88, d);
|
|
LcdWriteCom(0x8C); ShowLcdWeek(w);
|
|
LcdShowNumAt(0xC0, h);
|
|
LcdShowNumAt(0xC3, mi);
|
|
LcdShowNumAt(0xC6, s);
|
|
|
|
/* 日期调节 */
|
|
AdjustValue(&y, 99, 0x82, 0x82);
|
|
AdjustValue(&mo, 12, 0x85, 0x85);
|
|
AdjustValue(&d, 31, 0x88, 0x88);
|
|
AdjustValue(&h, 23, 0xC0, 0xC0);
|
|
AdjustValue(&mi, 59, 0xC3, 0xC3);
|
|
AdjustValue(&s, 59, 0xC6, 0xC6);
|
|
|
|
/* 星期调节(手动) */
|
|
LcdWriteCom(0x8C); LcdWriteCom(0x0f);
|
|
while(1)
|
|
{
|
|
if(K3_Pressed()) { if(w<7) w++; else w=1; LcdWriteCom(0x8C); ShowLcdWeek(w); LcdWriteCom(0x8C); LcdWriteCom(0x0f); }
|
|
if(K4_Pressed()) { if(w>1) w--; else w=7; LcdWriteCom(0x8C); ShowLcdWeek(w); LcdWriteCom(0x8C); LcdWriteCom(0x0f); }
|
|
if(K1_Pressed()) { break; }
|
|
}
|
|
|
|
/* 转换为BCD存入TIME数组 */
|
|
TIME[6] = DEC2BCD(y);
|
|
TIME[4] = DEC2BCD(mo);
|
|
TIME[3] = DEC2BCD(d);
|
|
TIME[5] = w;
|
|
TIME[2] = DEC2BCD(h);
|
|
TIME[1] = DEC2BCD(mi);
|
|
TIME[0] = DEC2BCD(s);
|
|
|
|
/* 写入DS1302 */
|
|
Ds1302Write(0x8E, 0x00);
|
|
Ds1302Write(0x80, TIME[0]);
|
|
Ds1302Write(0x82, TIME[1]);
|
|
Ds1302Write(0x84, TIME[2]);
|
|
Ds1302Write(0x86, TIME[3]);
|
|
Ds1302Write(0x88, TIME[4]);
|
|
Ds1302Write(0x8a, TIME[5]);
|
|
Ds1302Write(0x8c, TIME[6]);
|
|
Ds1302Write(0x8E, 0x80);
|
|
|
|
/* 恢复显示 */
|
|
LcdWriteCom(0x0c);
|
|
last_mode = 0xFF;
|
|
}
|
|
|
|
/* 闹钟设置:使用AdjustValue重复利用代码 */
|
|
void SetAlarm()
|
|
{
|
|
LcdWriteCom(0x80);
|
|
LcdWriteStr("Alarm Clock Set ");
|
|
LcdWriteCom(0xC0);
|
|
LcdWriteStr(" : ");
|
|
|
|
LcdShowNumAt(0xC3, alarm_hour);
|
|
LcdShowNumAt(0xC6, alarm_min);
|
|
|
|
LcdWriteCom(0xCA);
|
|
LcdWriteStr(alarm_on ? " ON" : "OFF");
|
|
|
|
AdjustValue(&alarm_hour, 23, 0xC3, 0xC4); /* 调整小时 */
|
|
AdjustValue(&alarm_min, 59, 0xC6, 0xC7); /* 调整分钟 */
|
|
|
|
LcdWriteCom(0xCC); LcdWriteCom(0x0f);
|
|
while(1) /* 闹钟开关 */
|
|
{
|
|
if(K3_Pressed()) { alarm_on = 1; LcdWriteCom(0xCA); LcdWriteStr(" ON"); LcdWriteCom(0xCC); LcdWriteCom(0x0f); }
|
|
if(K4_Pressed()) { alarm_on = 0; LcdWriteCom(0xCA); LcdWriteStr("OFF"); LcdWriteCom(0xCC); LcdWriteCom(0x0f); }
|
|
if(K1_Pressed()) { break; }
|
|
}
|
|
|
|
/* 保存闹钟参数到DS1302 RAM */
|
|
LcdWriteCom(0x0c); /* 关闭光标 */
|
|
Ds1302Write(0x8E, 0x00); /* 允许写DS1302 */
|
|
Ds1302Write(0xC0, alarm_hour); /* 存小时到RAM */
|
|
Ds1302Write(0xC2, alarm_min); /* 存分钟到RAM */
|
|
Ds1302Write(0xC4, alarm_on); /* 存开关状态到RAM */
|
|
Ds1302Write(0x8E, 0x80); /* 禁止写DS1302 */
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 定时器1初始化(供秒表计时) */
|
|
void Timer1Init()
|
|
{
|
|
TMOD &= 0x0F; /* 清T1位 */
|
|
TMOD |= 0x10; /* 设置T1为模式1(16位定时器) */
|
|
ET1 = 1; /* 使能T1中断 */
|
|
EA = 1; /* 总中断使能 */
|
|
}
|
|
|
|
/* 定时器1中断服务(约10ms中断一次) */
|
|
void Timer1() interrupt 3
|
|
{
|
|
TH1 = 0xD8; /* 装载初值 */
|
|
TL1 = 0xF0;
|
|
|
|
if(sw_running) /* 秒表运行时 */
|
|
{
|
|
sw_csec++;
|
|
if(sw_csec >= 100) /* 百分秒满100进1秒 */
|
|
{
|
|
sw_csec = 0;
|
|
sw_sec++;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* 温度处理:读取DS18B20并转换为10倍温度值 */
|
|
void TempPros()
|
|
{
|
|
int raw;
|
|
unsigned char sign = 0;
|
|
|
|
if(!DS18B20_GetTemp(&raw)) /* 获取原始温度 */
|
|
return;
|
|
|
|
if(raw & 0x8000) /* 负温度处理 */
|
|
{
|
|
raw = ~raw + 1;
|
|
sign = 1;
|
|
}
|
|
|
|
temperature = raw * 10 / 16; /* 转换为10倍温度值 */
|
|
if(sign)
|
|
temperature = -temperature;
|
|
|
|
DS18B20_StartConvert(); /* 启动下一次温度转换 */
|
|
}
|
|
|
|
/* 闹钟判断(空函数) */
|
|
void AlarmJudge(void)
|
|
{
|
|
}
|
|
|
|
/* 闹钟蜂鸣器处理(蜂鸣器已坏) */
|
|
void AlarmBuzPro(void)
|
|
{
|
|
}
|
|
|
|
/* 毫秒延时 @12MHz */
|
|
void DelayMs(unsigned int ms)
|
|
{
|
|
unsigned int i, j;
|
|
for(i=0; i<ms; i++)
|
|
for(j=0; j<112; j++);
|
|
}
|
|
|
|
|
|
|
|
/* 10ms延时 @12MHz */
|
|
void Delay10ms(void)
|
|
{
|
|
unsigned char a,b,c;
|
|
for(c=1;c>0;c--)
|
|
for(b=38;b>0;b--)
|
|
for(a=130;a>0;a--);
|
|
} |