设计了一款智能控制系统。
AT89C51+LCD1602+DS1302+按键+LED组成了这样一个完整的设计。
P2.0-P2.3 4个LED等代表庭院内的4盏灯
P1.3-P1.7 5个开关,第一个代表光敏电阻传感器,后四个都是热释电红外传感器
P3.2、P3.4、P3.5、P3.6为四个按键开关,可设置万年历时间。
人性化地加入了时间和温度的LCD显示,并可以通过按键去设置时间日期等。
智能灯控制分为自动和手动控制。
逻辑功能:
当P3.0为0,也就是按下的时候为手动控制,反之则为自动控制。
手动:
按下开关P3.1,所有的灯都打开,按下开关P3.2,所有的灯都关闭。
自动:
一个光敏电阻传感器和四个红外热释电传感器,实现逻辑为:
当时间为晚上18点-早上6点之间或者光敏电阻传感器为0时,一旦有人,则点亮灯,一个灯附近安装一个红外热释电传感器,
当走进灯时,灯亮。
仿真原理图如下(proteus仿真工程文件可到本帖附件中下载)
参考论文:
单片机源程序如下:
- #include<reg51.h>
- #include<intrins.h>
- #include<string.h>
- #define uchar unsigned char
- #define uint unsigned int
- #define delayNOP() {_nop_();_nop_();_nop_();_nop_();}
- sbit SDA=P1^0; // DS1302数据线
- sbit CLK=P1^1; // DS1302时钟线
- sbit RST=P1^2; // DS1302复位线
- //DS18B20数据端口定义
- sbit DQ=P2^4; //DS18B20数据端口
- sbit LCD_RS=P2^5; // LCD寄存器选择
- sbit LCD_RW=P2^6; // LCD读写/写控制
- sbit LCD_EN=P2^7; // LCD启用
- sbit K1=P3^2; // 选择
- sbit K2=P3^3; // 确定
- sbit K3=P3^5; // 加
- sbit K4=P3^6; // 减
-
- sbit key_1=P3^0; //自动手动切换
- sbit key_2=P3^1; //关灯,按一下,关一个
- sbit key_3=P3^4; //开灯,按一下,开一个
- uchar code zz[]={0x08,0x0c,0x04,0x06,0x02,0x03,0x01,0x09};
- uchar code fz[]={0x09,0x01,0x03,0x02,0x06,0x04,0x0c,0x08};
- sbit R_LED=P2^0; //红灯
- sbit Y_LED=P2^1; //黄灯
- sbit G_LED=P2^2; //绿灯
- sbit B_LED=P2^3; //蓝灯
- sbit guangmin=P1^3; //光敏电阻传感器
- sbit ren1=P1^4; //红外热释电传感器1
- sbit ren2=P1^5; //红外热释电传感器2
- sbit ren3=P1^6; //红外热释电传感器3
- sbit ren4=P1^7; //红外热释电传感器4
- //温度字符
- uchar code Temperature_Char[8]={0x0c,0x12,0x12,0x0c,0x00,0x00,0x00,0x00};
- //温度小数对照表
- uchar code df_Table[]={0,1,1,2,3,3,4,4,5,6,6,7,8,8,9,9};
- uchar CurrentT=0; //当前读取温度的整数部分
- uchar Temp_Value[]={0x00,0x00}; //从DS18B20读取的温度值
- uchar Display_Digit[]={0,0,0,0};//待显示的各温度数位
- bit DS18B20_IS_OK=1;//传感器正常标志
- uchar tCount=0;
- //一年中每个月的天数,2月的天数由年份决定
- uchar MonthsDays[]={0,31,0,31,30,31,30,31,31,30,31,30,31};
- //周日,每周一到周六(0,1-6)【读取DS1302时分别是1-7】
- uchar *WEEK[]={"SUN","MON","TUS","WEN","THU","FRI","SAT"};
- //LCD显示缓冲
- uchar LCD_DSY_BUFFER1[]={"00-00-00 "};
- uchar LCD_DSY_BUFFER2[]={"00-00-00 "};
- uchar DateTime[7]; //所读取的日期时间
- uchar Adjust_flag=0; //当前调节的时间对像:秒,分,时,日,月,年(1,2,3,4,5,6)
- uchar Change_Flag[]=" YMDHM";//(分,时,日,月,年)(不调节秒与周)
- //延时
- void DelayMS(uchar x)
- {uchar i;
- while(x--)
- for(i=0;i<120;i++);
- }
- //向DS1302写入一个字节
- void Write_A_Byte_TO_DS1302(uchar x)
- {uchar i;
- for(i=0;i<8;i++)
- {SDA=x&1; CLK=1; CLK=0; x>>=1;
- }
- }
- //从DS11302读取一个字节
- uchar Get_A_Byte_FROM_DS1302()
- {uchar i,b,t;
- for(i=0;i<8;i++)
- {b>>=1; t=SDA; b|=t<<7; CLK=1;CLK=0;
- }
- //BCD码转换
- return b/16*10+b%16;
- }
- //从DS1302指定位置读数据
- uchar Read_Data(uchar addr)
- {uchar dat;
- RST=0; CLK=0; RST=1;
- Write_A_Byte_TO_DS1302(addr);
- dat=Get_A_Byte_FROM_DS1302();
- CLK=1;RST=0;
- return dat;
- }
- //向DS1302某地址写入数据
- void Write_DS1302(uchar addr,uchar dat)
- {CLK=0;RST=1;
- Write_A_Byte_TO_DS1302(addr);
- Write_A_Byte_TO_DS1302(dat);
- CLK=0;RST=0;
- }
- //设置时间
- void SET_DS1302()
- { uchar i;
- Write_DS1302(0x8e,0x00); //写控制字,取消写保护
- //分时日月年依次写入
- for(i=0;i<7;i++)
- {
- //分的起始地址10000010(0x82),后面连续依次是时,日,月,周,年,写入 地址每次增2
- Write_DS1302(0X80+2*i,(DateTime[i]/10<<4)|(DateTime[i]%10));
- }
- Write_DS1302(0x8e,0x80);//加保护
- }
- //读取当前的时间
- void GetTime()
- {
- uchar i;
- for(i=0;i<7;i++)
- {
- DateTime[i]=Read_Data(0x81+2*i);
- }
- }
- //LCD忙等待
- bit LCD_Busy_Check()
- {bit result;
- LCD_RS=0;
- LCD_RW=1;
- LCD_EN=1;
- delayNOP();
- result=(bit)(P0&0x80);
- LCD_EN=0;
- return result;
- }
- //写指令
- void Write_LCD_Command(uchar cmd)
- {while(LCD_Busy_Check());
- LCD_RS=0;LCD_RW=0;LCD_EN=0; _nop_(); _nop_();
- P0=cmd;
- delayNOP();
- LCD_EN=1;
- delayNOP();
- LCD_EN=0;
- }
- //写数据
- void Write_LCD_Data(uchar dat)
- {while(LCD_Busy_Check());
- LCD_RS=1;LCD_RW=0;LCD_EN=0;
- P0=dat;
- delayNOP();
- LCD_EN=1;
- delayNOP();
- LCD_EN=0;
- }
- //初始化
- void Init_LCD()
- {Write_LCD_Command(0x01);DelayMS(5);
- Write_LCD_Command(0x38);DelayMS(5);
- Write_LCD_Command(0x0c);DelayMS(5);
- Write_LCD_Command(0x06);DelayMS(5);
- }
- //设置显示位置
- void Set_LCD_POS(uchar pos)
- { Write_LCD_Command(pos|0x80);
- }
- //在LCD上显示字符串
- void Display_LCD_String(uchar p,uchar *s)
- {uchar i;
- Set_LCD_POS(p);
- for(i=0;i<16;i++)
- {Write_LCD_Data(s[i]);
- DelayMS(1);
- }
- }
- //日期与时间值转换为数字字符
- void Format_DateTime(uchar d,uchar *a)
- {a[0]=d/10+'0';
- a[1]=d%10+'0';
- }
- //判断是否为闰年
- uchar isLeapYear(uint y)
- {return (y%4==0&&y%100!=0)||(y%400==0);
- }
- //求自2000.1.1开始的任何一天是星期几
- void RefreshWeekDay()
- {uint i,d,w=5; //已知1999.12.31是星期五
- for(i=2000;i<2000+DateTime[6];i++)
- { d=isLeapYear(i)?366:365;
- w=(w+d)%7;
- }
- d=0;
- for(i=1;i<DateTime[4];i++)
- d+=MonthsDays[i];
- d+=DateTime[3];
- //保存星期,0~6表示星期日,星期一,二.....六,为了与DS1302的星期格式匹配,返回值需要加1
- DateTime[5]=(w+d)%7+1;
- }
- //延时函数2
- void Delay(uint x)
- {while(--x);
- }
- /***************初始化18B20温度函数******************/
- uchar Init_DS18B20()
- {
- uchar status;
- DQ=1;
- Delay(8);
- DQ=0;
- Delay(90);
- DQ=1;
- Delay(8);
- status=DQ;
- Delay(100);
- DQ=1;
- return status;
- }
-
- //读一字节
- uchar ReadOneByte()
- {uchar i,dat=0;
- DQ=1;_nop_();
- for(i=0;i<8;i++)
- {DQ=0;dat>>=1;DQ=1;_nop_();_nop_();
- if(DQ)dat|=0x80;
- Delay(30);
- DQ=1;
- }
- return dat;
- }
- //写一字节
- void WriteOneByte(uchar dat)
- {uchar i;
- for(i=0;i<8;i++)
- { DQ=0;DQ=dat&0x01;Delay(5);DQ=1;dat>>=1;
- }
- }
- //读取温度值
- void Read_Temperature()
- {
- //延时值与负数标识
- uchar t=150,ng=0;
- //高5位全为1(0xf8)则为负数,为负数时取反加1,并设置负数标识
- if(Init_DS18B20()==1) //DS18B20故障
- DS18B20_IS_OK=0;
- else
- {WriteOneByte(0xcc); //跳过序列号
- WriteOneByte(0x44);//启动温度转换
- Init_DS18B20();
- WriteOneByte(0xcc); //跳过序列号
- WriteOneByte(0xBE);//读取温度寄存器
- Temp_Value[0]=ReadOneByte(); //温度低8位
- Temp_Value[1]=ReadOneByte(); //温度高8位
- DS18B20_IS_OK=1;
- if((Temp_Value[1]&0xf8)==0xf8)
- {
- Temp_Value[1]=~Temp_Value[1];
- Temp_Value[0]=~Temp_Value[0]+1;
- if(Temp_Value[0]==0x00)
- Temp_Value[1]++;
- //负数标志置1
- ng=1;
- }
- //查表得到温度的小数部分
- Display_Digit[0]=df_Table[Temp_Value[0]&0x0f];
- //获取温度整数部分(高字节中的低三位与低字节中的高4位,无符号)
- CurrentT=((Temp_Value[0]&0xf0)>>4)|((Temp_Value[1]&0X07)<<4);
- //将整数部分分解为3位待显示数字
- Display_Digit[3]=CurrentT/100;
- Display_Digit[2]=CurrentT%100/10;
- Display_Digit[1]=CurrentT%10;
- //刷新LCD显示缓冲
- LCD_DSY_BUFFER2[13]= Display_Digit[0]+'0';
- LCD_DSY_BUFFER2[12]='.' ;
- LCD_DSY_BUFFER2[11]=Display_Digit[1]+'0';
- LCD_DSY_BUFFER2[10]=Display_Digit[2]+'0';
- LCD_DSY_BUFFER2[9]=Display_Digit[3]+'0';
- //高位为0时不显示
- if(Display_Digit[3]==0) LCD_DSY_BUFFER2[9]=' ';
- //高位为0,且次高位为0时,次高位不显示
- if(Display_Digit[2]==0&&Display_Digit[3]==0)
- LCD_DSY_BUFFER2[10]=' ';
- //负数符号显示在恰当的位置
- if(ng)
- {if(LCD_DSY_BUFFER2[10]==' ')
- LCD_DSY_BUFFER2[10]='-';
- else
- if(LCD_DSY_BUFFER2[9]==' ')
- LCD_DSY_BUFFER2[9]='-';
- else
- LCD_DSY_BUFFER2[8]='-';
- }
- }
- }
- //键盘中断 0
- void EX_INT0() interrupt 0
- {if(K1==0) //选择调整对象(Y M D H M)
- { while(K1==0);
- Adjust_flag++;
- LCD_DSY_BUFFER1[13]='[';
- LCD_DSY_BUFFER1[14]=Change_Flag[Adjust_flag];
- LCD_DSY_BUFFER1[15]=']';
- if(Adjust_flag==6)
- {Adjust_flag=0;
- LCD_DSY_BUFFER1[13]=' ';
- LCD_DSY_BUFFER1[14]=' ';
- LCD_DSY_BUFFER1[15]=' ';
-
- }
- }
- }
- //键盘中断1
- void EX_INT1() interrupt 2
- {
- while(K2==0);
- SET_DS1302();//将调整后的时间写入DS1302
- LCD_DSY_BUFFER1[13]=' ';
- LCD_DSY_BUFFER1[14]=' ';
- LCD_DSY_BUFFER1[15]=' ';
- Adjust_flag=0;//操作索引重设为-1,时间继续正常显示
- }
- //定时器0每秒刷新LCD显示
- void T0_INT() interrupt 1
- { uchar i;
- TH0=-50000/256;
- TL0=-50000%256;
- Set_LCD_POS(0X4e);Write_LCD_Data(0Xdf);
- Set_LCD_POS(0X4f);Write_LCD_Data('C');
- if(++tCount!=2) return;
- tCount=0;
- Read_Temperature();
- //按指定的格式生成待显示的日期时间串
- Format_DateTime(DateTime[6],LCD_DSY_BUFFER1);
- Format_DateTime(DateTime[4],LCD_DSY_BUFFER1+3);
- Format_DateTime(DateTime[3],LCD_DSY_BUFFER1+6);
- //星期
- strcpy(LCD_DSY_BUFFER1+9,WEEK[DateTime[5]-1]);
- //时分秒
- Format_DateTime(DateTime[2],LCD_DSY_BUFFER2);
- Format_DateTime(DateTime[1],LCD_DSY_BUFFER2+3);
- Format_DateTime(DateTime[0],LCD_DSY_BUFFER2+6);
- //显示年月日,星期,时分秒
- Display_LCD_String(0x00,LCD_DSY_BUFFER1);
- Set_LCD_POS(0x40);
- for(i=0;i<14;i++)
- {
- Write_LCD_Data(LCD_DSY_BUFFER2[i]);
- DelayMS(1);
- }
- }
- //手动按键控制灯函数
- void key_LED()
- {
- R_LED=1;
- Y_LED=1;
- G_LED=1;
- B_LED=1;
- if(key_2==0)
- {
- R_LED=0;
- Y_LED=0;
- G_LED=0;
- B_LED=0;
- }
- if(key_3==0)
- {
- R_LED=1;
- Y_LED=1;
- G_LED=1;
- B_LED=1;
- }
-
- }
- //主程序
- void main()
- {
- Init_LCD();//液晶初始化
- IE=0X87;//允许INT0,T0中断
- IP=0X05;
- IT0=0X01;
- IT1=0X01;
- TMOD=0X01;
- TH0=-50000/256;
- TL0=-50000%256;
- TR0=1;
- while(1)
- {
- R_LED=Y_LED=G_LED=B_LED=1;
- if(key_1==0) //手动模式
- {
- key_LED(); //按键控制灯函数
- }
- else
- {
- if(DateTime[2]>18||DateTime[2]<6||guangmin==0)
- {
- if(ren1==0)
- {
- R_LED=0;
- Y_LED=1;
- G_LED=1;
- B_LED=1;
- }
- if(ren2==0)
- {
- R_LED=0;
- Y_LED=0;
- G_LED=1;
- B_LED=1;
- }
- if(ren3==0)
- {
- R_LED=0;
- Y_LED=0;
- G_LED=0;
- B_LED=1;
- }
- if(ren4==0)
- {
- R_LED=0;
- Y_LED=0;
- G_LED=0;
- B_LED=0;
- }
- }
-
- }
- switch(Adjust_flag)
- {
- case 0:GetTime(); break;
- case 1:
- if(K3==0&&DateTime[6]<99)
- {
- while(K3==0);
- DateTime[6]++;}
- if(K4==0&&DateTime[6]>0)
- {
- while(K4==0);
- DateTime[6]--;
- }
- MonthsDays[2]=isLeapYear(2000+DateTime[6])?29:28;
- //如果年份变化后当前月份的天数大于上限则设为上限
- if(DateTime[3]>MonthsDays[DateTime[4]])
- DateTime[3]=MonthsDays[DateTime[4]];
- RefreshWeekDay();//刷新星期
- break;
-
- case 2:
- if(K3==0&&DateTime[4]<12)
- {
- while(K3==0);
- DateTime[4]++;}
- if(K4==0&&DateTime[4]>1)
- {
- while(K4==0);
- DateTime[4]--;
- }
- //获取2月份天数
- MonthsDays[2]=isLeapYear(2000+DateTime[6])?29:28;
- //如果变化后当前月份的天数大于上限则设为上限
- if(DateTime[3]>MonthsDays[DateTime[4]])
- DateTime[3]=MonthsDays[DateTime[4]];
- RefreshWeekDay();//刷新星期
- break;
- case 3://日00-28/29/30/31,调节之前首先根据年份得出该年中2月的天数
- MonthsDays[2]=isLeapYear(2000+DateTime[6])?29:28;
- //根据当前的月份决定调节日期的上限
- if(K3==0&&DateTime[3]<MonthsDays[DateTime[4]])
- {
- while(K3==0);
- ……………………
- …………限于本文篇幅 余下代码请从51黑下载附件…………
复制代码
所有资料51hei提供下载:
智能灯控制系统.rar
(1.28 MB, 下载次数: 212)
|