#include<reg52.h>
#include<intrins.h>
#define uint unsigned int
#define uchar unsigned char
uchar a,b,miao,shi,fen,ri,yue,nian,week,key1n,temp,aa,flag;//flag用于读取头文件中的温度值
#define yh 0x80 //LCD 第一行的初始位置
#define er 0x80+0x40//LCD第二行初始位置(因为第二行第一个字符位置地址是0x40)
//液晶屏的与C51之间的引脚连接定义(显示数据线接C51的P0口)
sbit rs=P2^4;
sbit rw=P2^5;
sbit en=P2^6;
//DS1302时钟芯片与C51之间的引脚连接定义
sbit IO=P1^5;
sbit SCLK=P1^4;
sbit RST=P1^6;
sbit ACC0=ACC^0;
sbit ACC7=ACC^7;
/**********************************
ACC累加器=A
ACC.0=EOH
ACC.就是ACC的第0位。Acc可以位寻址。
累加器ACC是一个8位的存储单元,是用来放数据的。但是,这个存储单元有其特殊的地位,
是单片机中一个非常关键的单元,很多运算都要通过ACC来进行。以后在学习指令时,常用A来表示累加器。但有一些地方例外,比如在PUSH指令中,就必须用ACC这样的名字。
一般的说法,A代表了累加器中的内容、而ACC代表的是累加器的地址。
*****************************************/
//DS18B20的数据输出
sbit DQ=P1^3;
//校时按键与C51的引脚连接定义
sbit key1=P2^0;//设置键
sbit key2=P2^1;//加键
sbit key3=P2^2;//减键
//蜂鸣器
sbit buzzer=P1^0;//通过三极管8005驱动,端口低电平响
/*************************************/
//延时函数,后面经常调用
void delay(uint xms)//延时函数,有参函数
{
uint x,y;
for(x=xms;x>0;x--)
for(y=110;y>0;y--);
}
/*****液晶写入指令函数与写入数据函数,以后可调用**********/
/*在这个程序中,液晶写入有关函数会在DS1302的函数中调用,所以液晶程序要放在前面
*/
void write_1602com(uchar com)//******液晶写入指令函数
{
rs=0;//数据/指令选择置位置位指令
rw=0;//读写选择置位写
P0=com;//送入数据
delay(1);
en=1;//拉高使能端,为制造有效的下降沿做准备
delay(1);
en=0;//en由高变低,产生下降沿,液晶指示命令
}
void write_1602dat(uchar dat)//***液晶写入数据函数***
{
rs=1;//数据/指令选择置位数据
rw=0;//读写选择置位写
P0=dat;//送入数据
delay(1);
en=1;//en置高电平,为制造下降沿做准备
delay(1);
en=0;//en由高变低,产生下降沿,液晶执行命令
}
void lcd_init()//***液晶初始函数****
{
write_1602com(0x38); /*显示模式设置*/
delay(5);
write_1602com(0x01); /*显示清屏*/
write_1602com(0x06); /*显示光标移动设置*/
delay(5);
write_1602com(0x0C); /*显示开及光标设置*/
}
void print(uchar a3,uchar *str)//写字符串函数(没有延时)
{
write_1602com(a3|0x80);
while(*str!='\0')
{
//delay(100);//延时一下
write_1602dat(*str++);
}
*str=0;
}
/******************DS1302数***************/
void write_byte(uchar dat)//写入一个字节
{
ACC=dat;
RST=1;
for(a=8;a>0;a--)
{
IO=ACC0;
SCLK=1;
SCLK=0;
ACC=ACC>>1;
}
}
uchar read_byte()//读一个字节
{
RST=1;
for(a=8;a>0;a--)
{
ACC7=IO;
SCLK=1;
SCLK=0;
ACC=ACC>>1;
}
return(ACC);
}
//-------------------------
void write_1302(uchar add,uchar dat)//向1302芯片些函数,指定写入地址,数据
{
RST=0;
SCLK=0;
RST=1;
write_byte(add);
write_byte(dat);
SCLK=1;
RST=0;
}
uchar read_1302(uchar add)//从1302读数据函数,指定读取数据来源地址
{
uchar temp;
RST=0;
SCLK=0;
RST=1;
write_byte(add);
temp=read_byte();
SCLK=1;
RST=0;
return(temp);
}
uchar BCD_Decimal(uchar bcd)//BCD码转十进制函数,输入BCD,返回十进制
{
uchar Decimal;
Decimal=bcd>>4;
return(Decimal=Decimal*10+(bcd&=0x0f));//返回十进制数
}
//--------------------
void ds1302_init()//1302芯片初始化函数(2014/11/20 12:00:00 week4)
{
RST=0;
SCLK=0;
write_1302(0x8e,0x00);//允许写,禁止写保护
write_1302(0x8e,0x80);//打开写保护
}
/************DS18B20*************/
void delay2(uint s)//延时,用于温度程序部分
{
while(s--);//区分i,用s表示
}
void Init_DS18B20(void)//初始化DS18b20
{
uchar w=0;//注意是w
DQ=1;
delay2(8);
DQ=0;//拉低
delay2(29);//延时大于480us(此处延时时间不确定,实际时修改)
DQ=1;//拉高
delay2(3);//(时间不确定)
w=DQ;//稍作延时后如W=0则初始化成功
delay2(20);
}
uchar ReadOneChar(void)//DS18b20读一个字节函数
{
uchar i=0;
uchar dat=0;
for(i=8;i>0;i--)
{
DQ=0;//给脉冲信号
dat>>=1;//右移一位
DQ=1;//给脉冲信号
if(DQ)
dat|=0x80;
delay2(4);
}
return(dat);
}
void WriteOneChar(uchar dat)//写一个字节函数
{
uchar i=0;
for(i=8;i>0;i--)
{
DQ=0;
DQ=dat&0x01;
delay2(5);
DQ=1;
dat>>=1;
}
}
uint ReadTemperature(void)//读取温度
{
uchar a1=0;//用于合并
uchar b1=0;
uint t=0;
float tt=0;
Init_DS18B20();
WriteOneChar(0xCC);//跳过读取序列号的操作
WriteOneChar(0x44);//启动温度转换
Init_DS18B20();//再次初始化
WriteOneChar(0xCC);//跳过读取序列号
WriteOneChar(0xBE);//读取温度寄存器
a1=ReadOneChar();//先读取低位
b1=ReadOneChar();//后读取高位
t=b1;
t<<=8;
t=t|a1;
tt=t*0.0625;
t=tt*10+0.5;//放大10倍并四舍五入
return(t);
}
//-----------------
//温度显示子函数
void write_temp(uchar add,uchar dat)//向LCD写温度数据,并指定显示位置
{
uchar gw,sw,xw;
gw=dat%100/10;//取得个位数字
sw=dat/100;//取得十位数字
xw=dat%10;
write_1602com(er+add);//er是头文件规定的值0x80+0x40
write_1602dat(0x30+sw);//数字+30得倒该数字的LCD1602显示码
write_1602dat(0x30+gw);//数字+30得倒该数字的LCD1602显示码
write_1602dat(0x2e);//小数点
write_1602dat(0x30+xw);//小数位
write_1602dat(0xdf);//显示“°”(度)
write_1602dat(0x43);//显示“C”符号,为液晶字符的地址码
}
//-----------------------
//时分秒显示子函数
void write_sfm(uchar add,uchar dat)//向LCD写时分秒,有显示位置加、显示数据,两个参数
{
uchar gw,sw;
gw=dat%10;//取得个位数字
sw=dat/10;//取得十位数字
write_1602com(er+add);//er是头文件规定的值0x80+0x40
write_1602dat(0x30+sw);//数字+30得倒该数字的LCD1602显示码
write_1602dat(0x30+gw);//数字+30得倒该数字的LCD1602显示码
}
//-------------------
//年月日显示子函数
void write_nyr(uchar add,uchar dat)//向LCD写年月日,有显示位置加数、显示数据,两个参数
{
uchar gw,sw;
gw=dat%10;//取得个位数字
sw=dat/10;//取得十位数字
write_1602com(yh+add);//设定显示位置为第一个位置+add
write_1602dat(0x30+sw);//数字+30得倒该数字的LCD1602显示码
write_1602dat(0x30+gw);//数字+30得倒该数字的LCD1602显示码
}
//-------------------
void write_week(uchar week)//写星期函数
{
write_1602com(yh+0x0e);//星期字符的显示位置
switch(week)
{
case 1:write_1602dat('1');//括号内写入1 注意是单引号
break;
case 2:write_1602dat('2');
break;
case 3:write_1602dat('3');
break;
case 4:write_1602dat('4');
break;
case 5:write_1602dat('5');
break;
case 6:write_1602dat('6');
break;
case 7:write_1602dat('7');
break;
}
}
/**************键盘扫描有关函数*********/
void keyscan()
{
if(key1==0)//---------key1为功能键(设置键)------
{
delay(9);//延时,用于消除抖动
if(key1==0)//延时后再次确认按键按下
{
buzzer=0;//蜂鸣器短响一次
delay(20);
buzzer=1;
while(!key1);
key1n++;
if(key1n==9)
key1n=1;//设置按键共有秒、分、时、星期、日、月、年、返回,8个功能循环
switch(key1n)
{
case 1:TR0=0;//关闭定时器
write_1602com(er+0x07);//秒的位置
write_1602com(0x0f);//设置为光标闪烁
// temp=(miao)/10*16+(miao)%10;//秒化为bcd码
// write_1302(0x8e,0x00);
// write_1302(0x80,0x80|temp);//秒数据写入
// write_1302(0x8e,0x80);
break;
case 2:write_1602com(er+0x04);//分的位置
break;//不用再次设置为闪烁状态了
case 3:write_1602com(er+0x01);//时的位置
break;
case 4:write_1602com(yh+0x0e);//星期的位置
break;
case 5:write_1602com(yh+0x09);//日的位置
break;
case 6:write_1602com(yh+0x06);//月的位置
break;
case 7:write_1602com(yh+0x03);//年的位置
break;
case 8:write_1602com(0x0c);//第8次,光标不闪烁
TR0=1;//重新打开定时器
// temp=(miao)/10*16+(miao)%10;
// write_1302(0x8e,0x00);
// write_1302(0x80,0x00|temp);//写入秒
// write_1302(0x8e,0x80);
break;
}
}
}
/************加键key2****************/
if(key1n!=0)//当key1按下以后,再按一下键才有效(按键次数不等于零)
{
if(key2==0)//上调键
{
delay(10);
if(key2==0)
{
buzzer=0;
delay(20);
buzzer=1;
while(!key2);
switch(key1n)
{
case 1:miao++;//
if(miao==60) miao=0;
write_sfm(0x06,miao);/*在十位的位置写入,因为为两位数,个位数自动再后面显示*/
temp=(miao)/10*16+(miao)%10;//转换为bcd码
write_1302(0x8e,0x00);//允许写
write_1302(0x80,temp);// 写入秒
write_1302(0x8e,0x80);//打开保护
write_1602com(er+0x07);//液晶模式为写入后自动右移,在此返回原来位置
break;
case 2:fen++;
if(fen==60) fen=0;
write_sfm(0x03,fen);//在十位数位置开始写入
temp=(fen)/10*16+(fen)%10;//转换为bcd码
write_1302(0x8e,0x00);//允许写
write_1302(0x82,temp);//写入分
write_1302(0x8e,0x80);//打开保护
write_1602com(er+0x04);//返回个位数的位置
break;
case 3:shi++;
if(shi==24) shi=0;
write_sfm(0,shi);//在0位开始写入
temp=(shi)/10*16+(shi)%10;//转换为bcd码
write_1302(0x8e,0x00);//允许写
write_1302(0x84,temp);//写入时
write_1302(0x8e,0x80);//打开保护
write_1602com(er+0x01);//返回到个位位置
break;
case 4:week++;
if(week==8) week=1;
write_1602com(yh+0x0e);//显示位置
write_week(week);//写入星期
temp=(week)/10*16+(week)%10;
write_1302(0x8e,0x00);//允许写入
write_1302(0x8a,temp);//写入周
write_1302(0x8e,0x80);//打开保护
write_1602com(yh+0x0e);
break;
case 5:ri++;
if(ri==32) ri=1;
write_nyr(0x08,ri);//注意是在十位开始写入
temp=(ri)/10*16+(ri)%10;//转换为bcd码
write_1302(0x8e,0x00);//允许写
write_1302(0x86,temp);//写入日
write_1302(0x8e,0x80);//打开保护
write_1602com(yh+0x09);//返回个位数
break;
case 6:yue++;
if(yue==13) yue=1;
write_nyr(0x05,yue);//在十位开始写入
temp=(yue)/10*16+(yue)%10;//转换为bcd码
write_1302(0x8e,0x00);//允许写
write_1302(0x88,temp);//写入月
write_1302(0x8e,0x80);//打开保护
write_1602com(yh+0x06);//返回个位位置
break;
case 7:nian++;
if(nian==100) nian=0;
write_nyr(0x02,nian);//在第一行第三个字符开始写入
temp=(nian)/10*16+(nian)%10;//转换为bcd码
write_1302(0x8e,0x00);//允许写
write_1302(0x8c,temp);//写入年
write_1302(0x8e,0x80);//打开保护
write_1602com(yh+0x03);//返回个位位置
break;
}
}
}
}
/****************减键******************/
if(key3==0)
{
delay(10);
if(key3==0)
{
buzzer=0;
delay(20);
buzzer=1;
while(!key3);
switch(key1n)
{
case 1:miao--;/*此处有疑问:无符号数据是否要修改*/
if(miao==-1) miao=59;//减到-1返回59
write_sfm(0x06,miao);//在十位数写入
temp=(miao)/10*16+(miao)%10;//转换为bcd码
write_1302(0x8e,0x00);//允许写
write_1302(0x80,temp);//写入秒
write_1302(0x8e,0x80);//打开保护
write_1602com(er+0x07);//返回个位位置
break;
case 2:fen--;
if(fen==-1) fen=59;
write_sfm(0x03,fen);//在十位数位置写入
temp=(fen)/10*16+(fen)%10;//转换为bcd码
write_1302(0x8e,0x00);//允许写入
write_1302(0x82,temp);//写入分
write_1302(0x8e,0x80);//打开保护
write_1602com(er+0x04);//返回个位数位置
break;
case 3:shi--;
if(shi==-1) shi=23;
write_sfm(0,shi);//在0位开始写入
temp=(shi)/10*16+(shi)%10;//转换为bcd码
write_1302(0x8e,0x00);//允许写入
write_1302(0x84,temp);//写入时
write_1302(0x8e,0x80);//打开保护
write_1602com(er+0x01);//返回到个位位置
break;
case 4:week--;
if(week==0) week=7;
write_1602com(yh+0x0e);//显示位置
write_week(week);//写入星期
temp=(week)/10*16+(week)%10;//转换为bcd码
write_1302(0x8e,0x00);//允许写入
write_1302(0x8a,temp);//写入周
write_1302(0x8e,0x80);//打开保护
write_1602com(yh+0x0e);//返回原来位置
break;
case 5:ri--;
if(ri==0) ri=31;
write_nyr(0x08,ri);//在十位开始显示
temp=(ri)/10*16+(ri)%10;//转换为bcd码
write_1302(0x8e,0x00);//允许写入
write_1302(0x86,temp);//写入日
write_1302(0x8e,0x80);//打开保护
write_1602com(yh+0x09);//返回个位数
break;
case 6:yue--;
if(yue==0) yue=12;
write_nyr(0x05,yue);//在十位数位置开始写入
temp=(yue)/10*16+(yue)%10;//转换为bcd码
write_1302(0x8e,0x00);//允许写入
write_1302(0x88,temp);//写入月
write_1302(0x8e,0x80);//打开保护
write_1602com(yh+0x06);//返回到个位位置
break;
case 7:nian--;
if(nian==-1) nian=99;
write_nyr(0x02,nian);//第一行第三个字符开始写入
temp=(nian)/10*16+(nian)%10;//转换为bcd码
write_1302(0x8e,0x00);//允许写入
write_1302(0x8c,temp);//写入年
write_1302(0x8e,0x80);//打开保护
write_1602com(yh+0x03);//返回在年的尾数位置
break;
}
}
}
}
//-----------------
//按键初始化时间函数
void chushihua()//初始化时间函数
{
RST=0;
SCLK=0;
write_1302(0x8e,0x00);//允许写
write_1302(0x80,0x00);//秒:00
write_1302(0x82,0x00);//分:00
write_1302(0x84,0x12);//时:12
write_1302(0x8a,0x04);//周:4
write_1302(0x86,0x20);//日:20
write_1302(0x88,(11)/10*16+(11)%10);//月:11
write_1302(0x8c,(14)/10*16+(14)%10);//年:14
write_1302(0x8e,0x80);//打开保护
}
////}
//----------
//初始化的函数
void init_timer0(void)//定时器0初始化函数
{
TMOD|=0x01;//设置为定时器0的工作方式1
TH0=(65536-20000)/256;
TL0=(65536-20000)%256;
EA=1;
ET0=1;//允许T0中断
TR0=1;//启动中断
}
//void init_timer1(void)//定时器1初始化函数
//{
// TMOD|=0x01;//设置为定时器0的工作方式1
// TH1=(65536-20000)/256;
// TL1=(65536-20000)%256;
// EA=1;
// ET1=1;//允许T0中断
// TR1=1;//启动中断
//}
/*************************************************
主函数
*************************************************/
void main()
{
uint temp1;
lcd_init();
ds1302_init();
init_timer0();
buzzer=0;
delay(80);
buzzer=1;
chushihua();//初始化时间函数
while(1)
{
if(flag==1)
{
flag=0;
temp1=ReadTemperature();
}
keyscan();
print(0x40," : : ");
write_temp(10,temp1);//显示温度,从第二行第11个字符今后开始显示
write_sfm(6,miao);//秒,从第二行第7个字符开始显示
write_sfm(3,fen);//分,从第二行第6个字符开始显示
write_sfm(0,shi);//时,从第二行第1个字符开始显示
print(0x80,"20 / / W < >");
write_1602com(yh+0x0c);
write_1602dat(0x7e);//→右箭头
write_nyr(8,ri);//日期,从第一行第9个字符
write_nyr(5,yue);//月份,从第一行第6个字符
write_nyr(2,nian);//年,从第一行第3个字符
write_week(week);
}
}
void timer0_isr(void) interrupt 1
{
TH0=(65536-20000)/256;
TL0=(65536-20000)%256;
miao=BCD_Decimal(read_1302(0x81));
fen =BCD_Decimal(read_1302(0x83));
shi =BCD_Decimal(read_1302(0x85));
ri =BCD_Decimal(read_1302(0x87));
yue =BCD_Decimal(read_1302(0x89));
nian=BCD_Decimal(read_1302(0x8d));
aa++;
if(aa==30)
{
aa=0;
flag=1;
}
} |