基于51单片机的物联网安防系统使用Proteus进行仿真,LCD12864进行数据显示,集成了温湿度传感器、烟雾、甲烷传感器,执行器件是风扇,采用L298进行驱动,按键实现用户交互,蜂鸣器报警,红外检测人员状态,同时虚拟串口连接到我们的Python脚本,实现数据上传。
一、硬件设计
1、TLC549
TLC549是德州仪器(Texas Instruments)推出的一款高性能、低功耗的8位逐次逼近型模拟数字转换器(A/D Converter)。它采用串行接口,与微控制器的连接简单,特别适用于需要节省引脚数量和成本的单片机系统。TLC549以其卓越的转换速度、优秀的分辨率和低功耗特性,成为众多电子设计中的理想选择。
2、LCD12864
CD12864是一种常见的液晶显示屏(Liquid Crystal Display)模块,具有128列和64行的点阵显示能力,总点数达到8192点。这种显示屏通常采用超扭曲向列(Super Twisted Nematic, STN)技术,适用于显示文本、图形以及简单动画。LCD12864模块通常内置有汉字和ASCII字符的字符库,支持多种接口方式,包括4位或8位并行接口以及2线或3线串行接口,使得它可以轻松地与不同的微控制器或其他数字系统连接。
3、温湿度传感器
DHT11是一款常用的数字温湿度传感器,它能够提供经过校准的数字信号输出,适用于各种环境监测应用。DHT11传感器包含一个电阻式湿度传感元件和一个NTC温度传感元件,这些元件与一个高性能的8位单片机相连,确保了产品的可靠性和长期稳定性。
二、软件设计
1、TLC549驱动代码
cpp
#include "TLC549.h"
#include <REGX52.H>
#include <intrins.H>
sbit DI = P2^4;
sbit CS = P2^5;
sbit CLK = P2^6;
sbit DI2 = P2^0;
sbit CS2 = P2^1;
sbit CLK2 = P2^2;
//-----------------------------------------
// TLC549模数转换
//-----------------------------------------
uchar TLC549_ADC(void)
{
uchar n, tmp;
CS = 1; //CS置高,片选无效
CLK = 0;
CS = 0; //CS置低,片选有效,同时DO输出高位
_nop_();
_nop_(); //适当延迟时间1.4us Setup Time
for(n = 0; n < 8; n++) //串行数据移位输入
{
tmp <<= 1;
tmp |= DI;
CLK = 1; //0.4us
_nop_(); //延迟0.1us
CLK = 0; //0.4us
}
CS = 1; //CS置高,片选无效
for(n = 17; n != 0; n--) _nop_(); //下一次转换需要延迟17us
return (tmp);
}
//-----------------------------------------
// TLC549模数转换
//-----------------------------------------
uchar TLC549_ADC2(void)
{
uchar n, tmp;
CS2 = 1; //CS置高,片选无效
CLK2 = 0;
CS2 = 0; //CS置低,片选有效,同时DO输出高位
_nop_();
_nop_(); //适当延迟时间1.4us Setup Time
for(n = 0; n < 8; n++) //串行数据移位输入
{
tmp <<= 1;
tmp |= DI2;
CLK2 = 1; //0.4us
_nop_(); //延迟0.1us
CLK2 = 0; //0.4us
}
CS2 = 1; //CS置高,片选无效
for(n = 17; n != 0; n--) _nop_(); //下一次转换需要延迟17us
return (tmp);
}
2、LCD12864驱动代码
cpp
#include "12864.h"
#include "ziku.h"
void Delay_ms(unsigned int xms) //@11.0592MHz
{
unsigned int i,j;
for(i=xms;i>0;i--)
for(j=112;j>0;j--);
}
static void LCD12864_Write(bit COM_Data,unsigned char dat) //12864操作函数
{
LCD12864_RS=COM_Data; //指令 0 数据 1
LCDData_Pro=dat; //数据传输至P0扣
LCD12864_EN=1; //置1
_nop_();
LCD12864_EN=0; //置0 产生下降沿
}
void LCD12864_Init(void) //12864初始化
{
Delay_ms(300); //等待系统上电稳定
/******************************************************************************/
LCD12864_EN=1;
LCD12864_RS=1;
LCD12864_CS1=1; //仿真 0 实物 1
LCD12864_CS2=1; //仿真 0 实物 1
Delay_ms(10);
/******************************************************************************/
LCD12864_Write(LCD12864_COM,0x3f); //开显示
/*
LCD12864_Write(LCD12864_COM,0xc0); //这段不写也可以正常显示
LCD12864_Write(LCD12864_COM,0xb8);
LCD12864_Write(LCD12864_COM,0x40);
*/
LCD12864_Write(LCD12864_COM,0x30); //打开基本指令
LCD_Clear(); //清屏
}
/*****************************************************************************
函数功能:清平函数
入口参数:空
说 明:清空整个屏幕数据
版 本:V1.0
时 间:2020年5月9日
*****************************************************************************/
void LCD_Clear(void)
{
unsigned char i,j;
#if LCD_Mode
LCD12864_CS1=1; //选中左半屏
LCD12864_CS2=1; //选中右半屏
#else
LCD12864_CS1=0; //选中左半屏
LCD12864_CS2=0; //选中右半屏
#endif
LCD12864_Write(LCD12864_COM,0xc0); //行
for(i=0;i<8;i++)
{
LCD12864_Write(LCD12864_COM,0xb8+i); //页
for(j=0;j<64;j++)
{
LCD12864_Write(LCD12864_COM,0x40+j); //列
LCD12864_Write(LCD12864_DATA,0x00);
Delay_ms(5); //方便看清屏效果
}
}
}
/*****************************************************************************
函数功能:地址写入函数
入口参数:X,Y
说 明:根据地址自动切换左、右屏
版 本:V1.0
时 间:2020年5月9日
*****************************************************************************/
void LCD12864_X_Y(unsigned char X,unsigned char Y)
{
Y=Y&0x7f; //限定范围,列不能超过127
X=X&0x07; //限定范围,行不能超过7
if(Y<64)
{
#if LCD_Mode
LCD12864_CS1=1; //选中左半屏
LCD12864_CS2=0; //关闭右半屏
#else
LCD12864_CS1=0; //选中左半屏
LCD12864_CS2=1; //关闭右半屏
#endif
LCD12864_Write(LCD12864_COM,0x40+Y); //选择列基地址+Y 一共64行
if(Y==63) Y=64;
}
else
{
#if LCD_Mode
LCD12864_CS1=0; //关闭左半屏
LCD12864_CS2=1; //选中右半屏
#else
LCD12864_CS1=1; //关闭左半屏
LCD12864_CS2=0; //选中右半屏
#endif
Y&=0x3f;
LCD12864_Write(LCD12864_COM,0x40+Y); //选择列基地址+Y 一共64列
}
LCD12864_Write(LCD12864_COM,0xb8+X); //选择页基地址+X 一共7页
}
/*****************************************************************************
函数功能:6*16数值写入函数
入口参数:X,Y,num,*dat
说 明:X_横坐标 Y_纵坐标 num_数组的第num个数 *day_需要显示的数组
版 本:V1.5
修改时间:2022年7月10日
新 增:可实现跨左右屏
*****************************************************************************/
void LCD12864_Write_Number(unsigned char X,unsigned char Y,unsigned char num,unsigned char *dat)
{
unsigned char i;
for(i=0;i<6;i++)
{
LCD12864_X_Y(X,Y+i); //每写一个字节 更新一次Y 防止不能出现跨屏
LCD12864_Write(LCD12864_DATA,dat[num*12+i]); //写上半字 共写6个字节
}
for(i=0;i<6;i++)
{
LCD12864_X_Y(X+1,Y+i); //同上
LCD12864_Write(LCD12864_DATA,dat[num*12+6+i]); //写下半字 共写6个字节
}
}
/*****************************************************************************
函数功能:变量显示函数
入口参数:X,Y,num,len
说 明:在第X行的第Y个位置显示len个num数值
版 本:V1.2
修改时间:2022年7月10日
新 增:自动计算变量长度 最大计数99999
*****************************************************************************/
void LCD12864_Num(unsigned char X,unsigned char Y,unsigned int num)
{
unsigned char i;
unsigned char Len;
unsigned char Dis[5];
unsigned char Buf[5];
sprintf(Buf,"%d",num); //将数值转换成字符串
Len=strlen(Buf); //计算字符串长度
if(Len>4)Dis[Len-5]=num/10000 ; //若数据更大 按照规律写即可
if(Len>3)Dis[Len-4]=num%10000/1000;
if(Len>2)Dis[Len-3]=num%1000/100 ;
if(Len>1)Dis[Len-2]=num%100/10 ;
if(Len>0)Dis[Len-1]=num%10 ;
for(i=0;i<Len;i++) LCD12864_Write_Number(X,Y+(i*6),Dis[i],Num);
}
/*****************************************************************************
函数功能:12*16汉字写入函数
入口参数:X,Y,num,*dat
说 明:X_横坐标 Y_纵坐标 num_数组的第num个数 *day_需要显示的数组
版 本:V1.5
修改时间:2022年7月10日
新 增:左右跨屏
*****************************************************************************/
void LCD12864_Write_Chinese(unsigned char X,unsigned char Y,unsigned char num,unsigned char *dat)
{
unsigned char i;
for(i=0;i<12;i++)
{
LCD12864_X_Y(X,Y+i); //跨屏重要函数 勿删除
LCD12864_Write(LCD12864_DATA,dat[num*24+i]); //写上半字 共写12个字节
}
for(i=0;i<12;i++)
{
LCD12864_X_Y(X+1,Y+i); //同上
LCD12864_Write(LCD12864_DATA,dat[num*24+12+i]); //写下半字 共写12个字节
}
}
/*****************************************************************************
函数功能:英文字母显示函数
入口参数:X,Y,*dat
说 明:X_横坐标 Y_纵坐标 *dat_需要显示的英文字母
版 本:V1.0
修改时间:2022年7月10日
注 明:带字库索引功能
*****************************************************************************/
void LCD12864_Write_English(unsigned char X,unsigned char Y,unsigned char *dat)
{
unsigned char i,j;
unsigned char Len;
Len=strlen(dat); //获取数据长度
for(j=0;j<Len;j++)
{
for(i=0; ;i++) if(English_Buf[i]==*dat) {dat++; break;} //查找字库索引列表
LCD12864_Write_Number(X,Y+(j*6),i,English); //写数据
}
}
///*****************************************************************************
//函数功能:汉字显示函数
//入口参数:X,Y,*dat
//说 明:X_横坐标 Y_纵坐标 *dat_需要显示的汉字
//版 本:V1.0
//修改时间:2022年7月10日
//注 明:带字库索引功能
//注意:存在Bug 暂未修复
//*****************************************************************************/
//void LCD12864_Write_String(unsigned char X,unsigned char Y,unsigned char *dat)
//{
// unsigned char i,j;
// unsigned char Len;
//
// Len=(strlen(dat))/2; //1个汉字2个字节 所以要除以2
//
// for(j=0;j<Len;j++)
// {
// for(i=0; ;i++) if(Chinese_Buf[i]==*dat) {dat++; break;} //存在Bug不能显示第4个汉字
// LCD12864_Write_Chinese(X,Y+(j*12),i,Chinese);
// }
//}
3、DHT11驱动代码
cpp
#include "DHT11.h"
sbit Data=P3^2; //数据线
uchar rec_dat[9]; //储存数据
void DHT11_delay_us(uchar n)
{
while(--n);
}
void DHT11_delay_ms(uint z)
{
uint i,j;
for(i=z;i>0;i--)
for(j=110;j>0;j--);
}
/*
主机(单片机)发送起始信号:
1.主机先拉高data。
2.拉低data延迟18ms。
3.拉高data并延迟等待(通过此操作将单片机引脚设置为输入)。
*/
void DHT11_start()
{
Data=1;
DHT11_delay_us(2);
Data=0;
DHT11_delay_ms(25); //拉低延时18ms以上
Data=1;
DHT11_delay_us(30); //拉高 延时 20~40us,取中间值 30us
}
/*------------------------------------------------
接收八位二进制
------------------------------------------------*/
uchar DHT11_rec_byte() //接收一个字节
{
unsigned char i,dat=0;
for(i=0;i<8;i++) //从高到低依次接收8位数据
{
while(Data); //等待进入低电平
while(!Data); //等待50us低电平过去
DHT11_delay_us(8); //延时60us,如果还为高则数据为1,否则为0
dat<<=1;//移位(低位补零)使正确接收8位数据,数据为0时直接移位
if(Data==1) //数据为1时,使dat加1来接收数据1
dat+=1;
while(Data); //等待数据线拉低
}
return dat;
}
/*------------------------------------------------
接收40bit数据(具体的温湿度)
1.主机先把data线拉高(io设置为输入)。
2.从机把data线拉低,主机读取data线电平,直到低电平结束(大约50us)
从机拉高data线后,延迟40us左右(28~70us之间)主机再次读取data线电平,如果为低电平,则为"0",如果为高电平,则为"1"。
3.继续重复上述1,2步骤累计40次。
------------------------------------------------*/
uchar T_H;
int tem, hum;
void DHT11_receive() //接收40位的数据
{
uchar R_H,R_L,T_L,RH,RL,TH,TL,revise;
DHT11_start();//发送起始信号:
if(Data==0)
{
while(Data==0); //等待拉高
DHT11_delay_us(40); //拉高后延时80us
R_H=DHT11_rec_byte(); //接收湿度高八位
R_L=DHT11_rec_byte(); //接收湿度低八位
T_H=DHT11_rec_byte(); //接收温度高八位
T_L=DHT11_rec_byte(); //接收温度低八位
revise=DHT11_rec_byte(); //接收校正位
DHT11_delay_us(25); //结束
if((R_H+R_L+T_H+T_L)==revise) //最后一字节为校验位,校验是否正确
{
RH=R_H;
RL=R_L;
TH=T_H;
TL=T_L;
}
tem = TH;
hum = RH;
/*数据处理,转换为字符,方便显示*/
//湿度
rec_dat[0]=(RH/10);
rec_dat[1]=(RH%10);
rec_dat[2]=' ';
rec_dat[3]=' ';
//温度
rec_dat[4]=(TH/10);
rec_dat[5]=(TH%10);
rec_dat[6]=' ';
}
}
三、项目演示
(1) 首先运行仿真的Python脚本,可以看到如下:
此时设备已经在线
(2)按下布防按键,布防指示灯亮起,此时如果红外检测到人,报警灯亮起,否则报警灯熄灭,如下:
(3)按下调整切换,调整位置的数据持续闪烁,此时按加减可以操作数据,如下:
(4)当湿度超过阈值的时候,风扇转动,进行降湿,当Mq02超过阈值时,表示发生了火灾,此时蜂鸣器进行报警。
(5)同时,数据会上传到阿里云平台,如图:
四、项目总结
本次项目使用温湿度、烟雾、甲烷、红外传感器,结合阿里云物联网平台设计了一个安防系统,系统通过我们的python脚本,利用电脑的网络资源,将仿真数据上传到阿里云平台,实现仿真上实现物联网安防系统。