////////////////////////////////////////////////
//////此系统的功能是在晚上而且有人时自动开灯////
//////白天和晚上无人以及晚上有人但睡觉时关灯////
////晚上睡觉时按一下睡眠开关就能关灯而不受光////
////控和人体红外控制的影响,再按一下又可以灯////
////灭,按了睡眠开关后那光和红外控制不起作用////
////所以我设置了早上八点后自动恢复功能来,让////
////此两种控制起作用,而避免了早上起床后要按////
////开关才能让其起作用的麻烦////////////////////
////////////////////////////////////////////////
#include<reg52.h>
#include<intrins.h>
#define uchar unsigned char
#define uint unsigned int
sbit DQ=P3^0; //温度传感的DQ脚
sbit lcden=P3^1; //LCD 的E脚
sbit lcdrs=P3^2; //LCD的RS脚
sbit cro_l=P3^3; //光控中断控制脚
sbit cro_h=P3^4; //人体红外中断控制脚
sbit lamp=P3^5;
sbit wr=P3^6; //AD转换的wr脚
sbit rd=P3^7; //AD转换的rd脚
sbit cro_t=P1^7; //时间控制中断脚
bit flag_init=0;
uchar code ascII[]={
0x30,0x31,0x32,0x33,
0x34,0x35,0x36,0x37,
0x38,0x39}; //0到9的ASCII码
uchar code tbweek[][9]={
{0x4d,0x6f,0x6e,0x64,0x61,0x79},
{0x54,0x75,0x65,0x73,0x64,0x61,0x79},
{0x57,0x65,0x64,0x6e,0x65,0x73,0x64,0x61,0x79},
{0x54,0x68,0x75,0x72,0x73,0x64,0x61,0x79},
{0x46,0x72,0x69,0x64,0x61,0x79},
{0x53,0x64,0x74,0x75,0x72,0x64,0x61,0x79},
{0x53,0x75,0x6e,0x64,0x61,0x79}}; //星期一到星期天在液晶中显示用的代码
uint tens,units,decile,tmp,un_guang;
uchar time,sec,min,hour,week;
float tmprt;
void time_init();
void lcd_init();
void ADC_init();
void write_com(uchar com);
void write_data(uchar date);
void DS18B20_init();
void Readtemperature();
void Writeonechar(uchar dat);
uchar Readonechar();
void displaytt(uchar sec1,uchar min1,uchar hour1);
void displaytemp();
void displaywe();
void keyscan();
void delay(uint z);
void Delay_uS(uint tt);
void main()
{
lcd_init(); //液晶初始化
time_init(); //定时器初始化
DS18B20_init(); //温度传感器初始化
hour=12; //初始时间设为12点
while(1)
{
Readtemperature(); //读DS18B20检测到的温度
displaytt(sec,min,hour); //显示时间
keyscan(); //按键扫描
displaytemp(); //显示温度
displaywe(); //显示星期
ADC_init();
if(un_guang>52) //(假设)夜晚光强少于150lx
cro_l=1;
else //白天光强大于150lx,AD转换后的数少于52,给中断口一个电平,使进入灯灭函数
cro_l=0;
if(cro_h==1)
lamp=0;
else //人体红外检测到有低电平(没人时),控制灯灭
lamp=1;
if(hour==8) //早上八点让被睡眠开关置零的P1^7口恢复高电平,传感器开始正常工作
cro_t=1;
delay(21);
while(time>=16) //用大于等于代替等于才能确保进去
{
time=0;
sec++;
while(sec==60)
{
sec=0;
min++;
while(min==60)
{
min=0;
hour++;
while(hour==24)
{
sec=0;
min=0;
hour=0;
week++;
while(week==7)
week=0;
}
}
}
}
}
}
void lcd_init()
{
lcden=0; //写状态,使能为高脉冲
write_com(0x38); //显示模式设置
write_com(0x01); //显示清屏
write_com(0x0c); //开显示,光标不显示
write_com(0x06); //光标移动设置
write_com(0x80+0x00); //设置数据地址指针
}
void write_com(uchar com) //往LCD写指令
{
lcdrs=0;
P2=com;
delay(2);
lcden=1;
delay(2);
lcden=0;
}
void write_data(uchar date) //往LCD写数据
{
lcdrs=1;
P2=date;
delay(2);
lcden=1;
delay(2);
lcden=0;
}
void displaytt(uchar sec1,uchar min1,uchar hour1) //LCD时间显示
{
uchar s1,s2,m1,m2,h1,h2;
write_com(0x80+0x00);
s2=sec1/10;
s1=sec1%10;
m2=min1/10;
m1=min1%10;
h2=hour1/10;
h1=hour1%10;
s1=ascII[s1];
s2=ascII[s2];
m1=ascII[m1];
m2=ascII[m2];
h1=ascII[h1];
h2=ascII[h2];
write_data(h2); //写入小时的高位代码
delay(5);
write_data(h1); //写入小时的地位代码
delay(5);
write_data(0x3a); //写入时分间的分号代码
delay(5);
write_data(m2);
delay(5);
write_data(m1);
delay(5);
write_data(0x3a);
delay(5);
write_data(s2);
delay(5);
write_data(s1);
delay(5);
}
void displaywe() //显示星期
{
uchar j,we;
write_com(0x80+0x40); //星期在LCD中的地址
we=tbweek[week][0];
for(j=0;j<=8;j++)
{
we=tbweek[week][j];
write_data(we); //写入星期的ASCII符号
delay(2);
}
}
void keyscan() //键盘扫描
{
uchar temp;
temp=P1;
temp=temp&0x0f;
while(temp!=0x0f)
{
temp=P1;
temp=temp&0x0f;
delay(3);
switch(temp)
{
case 0x07: cro_t=!cro_t; //每按一次改变一次灯的状态
break;
case 0x0b: min++;
if(min==60)
min=0;
break;
case 0x0d: hour++;
if(hour==24)
hour=0;
break;
case 0x0e: week++;
if(week==7)
week=0;
break;
}
while(temp!=0x0f) //按键松开检测
{
temp=P1;
temp=temp&0x0f;
}
}
}
void time_init() //定时器初始化
{
TMOD=0x01;
TH0=(65536-50000)/256; //定时设定为50ms
TL0=(65536-50000)%256;
EA=1;
ET0=1;
TR0=1;
}
void ADC_init() //ADC转换初始化
{
wr=1;
wr=0;
delay(2);
wr=1;
rd=0;
un_guang=P0; //AD转换后的电压值二进制数据赋给变量un_guang
delay(2);
rd=1;
}
void DS18B20_init() //温度传感ds18b20初始化
{
flag_init=1; //初始化成功标志位,0为成功
DQ=1; //DQ复位
_nop_();
DQ=0; //拉低DQ
Delay_uS(52); //延时484us(此信号要求480us~960us)
DQ=1; //释放DQ
Delay_uS(5); //延时61us (在检测到 I/O 引脚上的上升沿之后DS18B20等待15us~60us并且接着发送存在脉冲60-240us的低电平信号)
flag_init=DQ;
Delay_uS(25); //延时241us(DS18B20发送存在脉冲的低电平信号60~240us)
}
void displaytemp() //温度显示
{
uchar te1,te2,te3;
write_com(0x80+0x4a); //温度在LCD显示的地址
te3=ascII[tens];
write_data(te3); //显示十位
delay(5);
te2=ascII[units];
write_data(te2); //显示各位
delay(5);
write_data(0x2e); //显示小数点
delay(5);
te1=ascII[decile];
write_data(te1); //显示小数点后一位
delay(5);
write_data(0xdf); //显示摄氏度的符号圆圈
delay(5);
write_data(0x43); //显示摄氏度的符号C
delay(5);
}
uchar Readonechar() //读一个字节
{
unsigned char i,dat=0;
for(i=8;i;i--)
{
DQ=0;
dat>>=1;
DQ=1;
if(DQ)
dat|=0x80;
Delay_uS(5);//延时61us(所有读时间片的最短持续期限为60us,各个读时间片之间必须有最短为1us的恢复时间)
}
return dat;
}
void Writeonechar(unsigned char dat) //写一个字节
{
unsigned char i;
for(i=8;i;i--)
{
DQ=0;
DQ=dat&0x01;
Delay_uS(5); //延时61us(在I/O线由高电平变为低电平之后DS1820在15us~60us之间对 I/O 线采样)
DQ=1;
dat>>=1;
}
}
void Readtemperature() //读温度
{
tens=0;units=0;decile=0;tmp=0;
tmprt=0;
DS18B20_init();
Writeonechar(0xcc);// 跳过读序列号的操作
Writeonechar(0x44);// 启动温度转换
DS18B20_init();
Writeonechar(0xcc);//跳过读序号列号的操作
Writeonechar(0xbe);//读取温度寄存器
units=Readonechar();//读低八位
tens=Readonechar(); //读高八位
tens<<=8;
tens|=units; //高低位合并
tmprt=tens*0.0625; //计算温度值
tmp=tmprt*10+0.5; //放大10倍输出并四舍五入
tens=tmp/100; //十位
units=tmp/10%10; //个位
decile=tmp%10; //小数位
}
void timer0() interrupt 1 //定时器0中断
{
TH0=(65536-50000)/256;
TL0=(65536-50000)%256;
time++;
}
void delay(uint z)
{
uint x,y;
for(x=z;x>0;x--)
for(y=110;y>0;y--);
}
void Delay_uS(unsigned int tt) //延时时间为(tt*9+16)us.晶振为12MHz
{
while(tt--)
;
}