#include < reg52.h >
#include < intrins.h > //用到nop()时使用
#define uchar unsigned char
#define uint unsigned int
sbit K5 = P1^4; //调节数位切换键
sbit K6 = P1^5; //数值增加键
sbit K7 = P1^6; //数值减小键
sbit K8 = P1^7; //确认键
uchar K5flag = 0;
sbit LCD_RS = P2^0; //LCD1602数据/命令选择端
sbit LCD_RW = P2^1; //LCD1602读/写选择端
sbit LCD_EN = P2^2; //LCD1602使能端
sbit DQ=P3^3; //DS18B20信号端
uint year=2015;
uchar month=2;
uchar day=27,daytemp;
uchar weekdayflag=5;
char H=12;
char M=0;
char S=0;
uchar displaybuf[9];
uchar code cdis1[20]= {"0123456789:.C- "};
uchar code cdis2[7][5]=
{"Mon",
"Tue",
"Wed",
"Thu",
"Fir",
"Sat",
"Sun"};
/**********************************************************
5us 延时子程序 ms延时函数
**********************************************************/
void delayNOP()
{
_nop_(); //一个机器周期1us延时
_nop_();
_nop_();
_nop_();
_nop_();
}
void delay(uint ms)
{
uint t;
while(ms--)
{
for(t = 0; t <100; t++);
}
}
/**********************************************************
1602液晶操作
**********************************************************/
//忙碌状态判断
bit lcd_busy() //RS/RW/EN=0/1/1为读状态
{
bit result;
LCD_RS = 0; //RS为1选择数据,RS为0选择命令
LCD_RW = 1; //RW为1选择读操作,RW为0选择写操作
LCD_EN = 1; //EN等于1可以读取信息,等于0可以执行命令
delayNOP(); //5us延时
result = (bit)(P0&0x80); //确保STA7为0,说明为“闲”状态,为1位“忙”操作;(bit)(P0&0x80)为强制类型转换
LCD_EN = 0; //读取数据结束之后,复位执行使能操作
return(result);
}
//写命令
void write_control(uchar control) //RS/RW/EN=0/0/高脉冲 为写指令状态
{
while(lcd_busy());
LCD_RS=0; //选择命令操作
LCD_RW=0; //RW为0时写数据;
LCD_EN=0; //1602执行命令
delayNOP();
P0=control; //发送高四位给1602数据P0口
LCD_EN = 1; //EN高脉冲
delayNOP();
LCD_EN = 0; //高脉冲之后恢复EN=0
delayNOP();
}
//写地址
void write_address(uchar pos) // 写入显示地址命令
{
pos=(pos|0x80); //数据指针=80+地址变量
write_control(pos);
}
//写数据
void write_data(uchar temp) // RS/RW/EN=1/0/高脉冲 为写数据状态
{
while(lcd_busy());
LCD_RS=1; //选择命令操作
LCD_RW=0; //RW为0时写数据;
LCD_EN=0; //1602执行命令
delayNOP();
P0=temp; //发送数据给1602数据P0口
LCD_EN = 1; //EN高脉冲
delayNOP();
LCD_EN = 0; //高脉冲之后恢复EN=0
delayNOP();
}
//液晶初始化
void lcd_init() //1602液晶初始化函数
{
delay(5);
write_control(0x38); //16*2显示,5*7点阵,8位数据
delay(1);
write_control(0x38); //重复写入,确保成功
delay(1);
write_control(0x38); //重复写入,确保成功
delay(1);
write_control(0x0f); //光标移动
delay(1);
write_control(0x04); //光标移动
delay(1);
write_control(0x01); //清除LCD的显示内容
delay(5); //延时
}
//万年历时间判断显示函数
void date_display()
{
while(lcd_busy());
write_address(0x00);
write_data(cdis1[year/1000%10]);
write_data(cdis1[year/100%10]);
write_data(cdis1[year/10%10]);
write_data(cdis1[year%10]);
write_data(cdis1[13]);
write_data(cdis1[month/10]);
write_data(cdis1[month%10]);
write_data(cdis1[13]);
write_data(cdis1[day/10]);
write_data(cdis1[day%10]);
}
//weekday显示函数
void weekday_display()
{
int i=3;
while(lcd_busy());
write_address(0x0c);
write_control(0x0c);
for(i=0;i<3;i++)
{
write_data(cdis2[weekdayflag-1][i]);
}
}
//时间程序显示函数
void TimeDisplay(jj) //时间程序显示子函数
{
uchar temp;
displaybuf[0]=jj%10;
displaybuf[1]=jj/10;
displaybuf[3]=M%10;
displaybuf[4]=M/10;
displaybuf[6]=H%10;
displaybuf[7]=H/10;
while(lcd_busy());
write_address(0x48); //写入时十位
temp=displaybuf[7];
write_data(cdis1[temp]);
temp=displaybuf[6]; //写入时个位
write_data(cdis1[temp]);
write_data(cdis1[10]); //写入":"
temp=displaybuf[4]; //写入分十位
write_data(cdis1[temp]);
temp=displaybuf[3]; //写入分个位
write_data(cdis1[temp]);
write_data(cdis1[10]); //:
temp=displaybuf[1]; //写入秒十位
write_data(cdis1[temp]);
temp=displaybuf[0]; //写入秒个位
write_data(cdis1[temp]);
}
//DS18B20操作函数
//初始化函数读取应答信号
unsigned char time; //设置全局变量,专门用于严格延时
bit Init_DS18B20(void)
{
bit flag; //储存DS18B20是否存在的标志,flag=0,表示存在;flag=1,表示不存在
DQ = 1; //先将数据线拉高
for(time=0;time<2;time++) //略微延时约6微秒
;
DQ = 0; //再将数据线从高拉低,要求保持480~960us
for(time=0;time<200;time++) //略微延时约600微秒
;
DQ = 1; //释放数据线(将数据线拉高)
for(time=0;time<10;time++)
; //延时约30us(释放总线后需等待15~60us让DS18B20输出存在脉冲)
flag=DQ; //让单片机检测是否输出了存在脉冲(DQ=0表示存在)
for(time=0;time<200;time++) //延时足够长时间,等待存在脉冲输出完毕
;
return (flag); //返回检测成功标志,返回1表示存在,返回0表示不存在
}
//从DS18B20中读取数据 ,读取的数据从最低位开始
unsigned char ReadOneChar(void)
{
unsigned char i=0;
unsigned char dat; //储存读出的一个字节数据
for (i=0;i<8;i++)
{
DQ =1; // 先将数据线拉高
_nop_(); //等待一个机器周期
DQ = 0; //单片机从DS18B20读书据时,将数据线从高拉低即启动读时序
dat>>=1; //复合运算符,等于:dat=dat>>1;
_nop_(); //等待一个机器周期
DQ = 1; //将数据线"人为"拉高,为单片机检测DS18B20的输出电平作准备
for(time=0;time<2;time++)
; //延时约6us,使主机在15us内采样
if(DQ==1)
dat|=0x80; //如果读到的数据是1,则将1存入dat
else
dat|=0x00; //如果读到的数据是0,则将0存入dat
//将单片机检测到的电平信号DQ存入r[i]
for(time=0;time<8;time++)
; //延时3us,两个读时序之间必须有大于1us的恢复期
}
return(dat); //返回读出的十进制数据
}
//向DS18B20中写入数据
void WriteOneChar(unsigned char dat)
{
unsigned char i=0;
for (i=0; i<8; i++)
{
DQ =1; // 先将数据线拉高
_nop_(); //等待一个机器周期
DQ=0; //将数据线从高拉低时即启动写时序
DQ=dat&0x01; //利用与运算取出要写的某位二进制数据, 并将其送到数据线上等待DS18B20采样
for(time=0;time<10;time++)
; //延时约30us,DS18B20在拉低后的约15~60us期间从数据线上采样
DQ=1; //释放数据线
for(time=0;time<1;time++)
; //延时3us,两个写时序间至少需要1us的恢复期
dat>>=1; //将dat中的各二进制位数据右移1位
}
for(time=0;time<4;time++)
; //写完数据之后稍作延时,给硬件一点反应时间
}
//温度值计算及显示函数
void Tdisplay()
{
uchar TL; //储存暂存器的温度低位
uchar TH; //储存暂存器的温度高位
uchar TN; //储存温度的整数部分
uchar TD; //储存温度的小数部分
uchar i,j,k; //计算温度整数部分的个位,十位,百位
Init_DS18B20();
WriteOneChar(0xCC); // 单线单DS18B20可以跳过读序号列号的操作,节省时间
WriteOneChar(0x44); // 启动温度转换命令
for(time=0;time<100;time++)
; //温度转换需要一点时间
Init_DS18B20(); //将DS18B20初始化
WriteOneChar(0xCC); //跳过读序号列号的操作
WriteOneChar(0xBE); //读取温度寄存器,前两个分别是温度的低位和高位
TL=ReadOneChar(); //先读的是温度值低位
TH=ReadOneChar(); //接着读的是温度值高位
TN=TH*16+TL/16; //实际温度值=(TH*256+TL)/16,即:TH*16+TL/16
//这样得出的是温度的整数部分,小数部分被丢弃了
k=TN/100%10; //取温度值的百位
j=TN/10%10; //取温度值的十位
i=TN%10; //取温度值的个位
TD=(TL%16)*10/16; //计算温度的小数部分,将余数乘以10再除以16取整,
//这样得到的是温度小数部分的第一位数字(保留1位小数)
while(lcd_busy());
write_address(0x41); //写入液晶开始显示温度的位置
//write_data(cdis1[k]); //写温度值的百位
write_data(cdis1[j]); //写温度值的十位
write_data(cdis1[i]); //写温度值的个位
write_data(cdis1[11]); //写小数点
write_data(cdis1[TD]); //写温度值的个位
write_data(0xdf);
write_data(cdis1[12]); //写温度单位
delay(10);
}
//按键扫描程序
void key_scan(void)
{
switch(month)
{
case 1:daytemp=31;break;
case 2:if((year%4==0)&&(year%100!=0)||(year%400==0)) //闰年:年份能被4整除且不能被100整除,或者能被400整除
{
daytemp=29;
}
else
daytemp=28;break;
case 3:daytemp=31;break;
case 4:daytemp=30;break;
case 5:daytemp=31;break;
case 6:daytemp=30;break;
case 7:daytemp=31;break;
case 8:daytemp=31;break;
case 9:daytemp=30;break;
case 10:daytemp=31;break;
case 11:daytemp=30;break;
case 12:daytemp=31;break;
default:break;
}
P1=0xf0; //将P1口高4位置高电平“1”
if((P1&0xf0)!=0xf0) //有键按下
{
delay(10); //延时消抖延时60ms再检测
if((P1&0xf0)!=0xf0) //确实有键按下
{
if(K5==0) //如果是K5键按下,切换到调节模式,按下一次是,年代数跳动
{
K5flag++;
if(K5flag>=7)
K5flag=0;
switch(K5flag)
{
case 1:write_address(0x03);write_control(0x0f);break; //光标跳动命令于"year"调节模式
case 2:write_address(0x06);write_control(0x0f);break; //光标跳动命令于"month"调节模式
case 3:write_address(0x09);write_control(0x0f);break; //光标跳动命令"day"调节模式
case 4:write_address(0x0e);write_control(0x0f);break; //光标跳动命令"weekday"调节模式
case 5:write_address(0x49);write_control(0x0f);break; //光标跳动命令"H"调节模式
case 6:write_address(0x4C);write_control(0x0f);break; //光标跳动命令"M"调节模式
default:break;
}
while(!K5); //等待K5释放
}
if(K6==0) //如果是k6键按下数值增加
{
switch(K5flag)
{
case 1:year++; break;
case 2:month++;if((month-1)>=12)month=1;break;
case 3:day++;if((day-1)>=daytemp)day=1;break;
case 4:weekdayflag++;if((weekdayflag-1)==7)weekdayflag=1;break;
case 5:H++;if(H>=24)H=0;break;
case 6:M++;if(M>=60)M=0;break;
default:break;
}
while(!K6); //等待K6释放
}
if(K7==0)
{
switch(K5flag)
{
case 1:year--; break;
case 2:month--;if((month+1)==1)month=12;break;
case 3:day--;if((day+1)==1)day=daytemp;break;
case 4:weekdayflag--;if((weekdayflag+1)==7)weekdayflag=1;break;
case 5:H--;if(H<0)H=23;break;
case 6:M--;if(M<0)M=59;break;
default:break;
}
while(!K7); //等待K7释放
}
if(K8==0) //如果是k8键确认键按下,光标停止闪烁,结束设置
{
write_control(0x0c);
K5flag=0;
}
}
}
}
/*
//串口通信函数
unsigned char tmp;
void send_char(unsigned char txd) // 传送一个字符
{
SBUF = txd;
while(!TI); // 等特数据传送
TI = 0; // 清除数据传送标志
}
void interrupt1_init() //串口初始化程序
{
TMOD=0x20; // 定时器1工作于8位自动重载模式, 用于产生波特率
TH1=TL1=0XFD; // 波特率9600,定时器装初值
SCON=0X50; // 设定串行口工作方式1 ,REN置1允许数据接收
PCON=0X00; // 波特率不倍增
TR1=1; // 启动定时器1,不用开定时器中断
}
main()
{
interrupt1_init();
while(1)
{
if(RI==1) // 允许接收标志位,RI=1有数据到来,允许接收,接收完一个字节数据后,自动置1
{
RI = 0; //RI置0,暂停接收,等待数据处理
tmp = SBUF; // 暂存接收到的数据
P0 = tmp; // 数据传送到P0口
send_char(tmp); // 回传接收到的数据,在电脑里显示
}
}
}
*/
/**********************************************************/
/* 主函数 */
void main()
{
lcd_init(); //LCD1602初始化
TMOD=0x01; //定时器T0设为16位定时器
EA=1;
ET0=1;
TR0=1;
while(1)
{
key_scan();
date_display(); //显示万年历部分
weekday_display(); //显示Weekday信息
Tdisplay(); //显示温度部分
TimeDisplay(S); //显示时钟部分
}
}
/* 中断函数 */
void interserve(void) interrupt 1 using 1 //定时器T0的中断编号为1,using 1寄存器组
{
uchar i;
TR0=0; //调用T0中断程序时先要关闭定时器T0
i++; //每来一次中断,中断次数int_time自加1
if(i==20) //够20次中断,即1秒钟进行一次检测结果采样
{
i=0; //中断次数清0
S++; //秒加1
if(S==60)
{
S=0;
M++;
if(M==60)
{
M=0;
H++;
if(H==24)
{
H=0;
day++;
if((day-1)==daytemp)
{
day=1;
month++;
if((month-1)==12)
{
month=1;
year++;
}
}
weekdayflag++;
if((weekdayflag-1)==7)
{
weekdayflag=1;
}
}
}
}
}
TH0=(65536-46080)/256; //重新给计数器T0赋初值50MS
TL0=(65536-46080)%256;
TR0=1; //启动定时器T0
}
|