- 课程设计要求
1、检测大气、水源污染情况及废气含量。
2、远程检测各个站点的环境情况,超标时进行远程报警。
- 设计思想
使用MQ135传感器实现空气监测,xpt2046芯片用于ad转换;
对于水质监测,使用相应的PH检测模块;
对于远程报警,手机和PC端配合WiFi模块连接tlink物联网平台;
LCD1602或者LCD12864用于单片机端信息的显示。
主要代码:
12864.h
#ifndef __12864_H__
#define __12864_H__
#include <reg52.h>
void Check_Busy() ;
void Write_Cmd(unsigned char Cmd) ;
void Write_Data(unsigned char Data) ;
void Init_ST7920() ;
void LCD_PutString(unsigned char x,unsigned char y,unsigned char code *s) ;
void ClrScreen() ;
#endif
12864.c
#include <reg52.h>
#include <intrins.h>
#include "delay.h"
#include "12864.h"
sbit RS = P2^6;
sbit RW = P2^5;
sbit E = P2^7;
sbit PSB = P2^2;
sbit RES = P2^4;
#define DataPort P0 //MCU P0<------> LCM
/*------------------------------------------------
检测忙位
------------------------------------------------*/
void Check_Busy()
{
RS=0;
RW=1;
E=1;
DataPort=0xff;
while((DataPort&0x80)==0x80);//忙则等待
E=0;
}
/*------------------------------------------------
写命令
------------------------------------------------*/
void Write_Cmd(unsigned char Cmd)
{
Check_Busy();
RS=0;
RW=0;
E=1;
DataPort=Cmd;
DelayUs2x(5);
E=0;
DelayUs2x(5);
}
/*------------------------------------------------
写数据
------------------------------------------------*/
void Write_Data(unsigned char Data)
{
Check_Busy();
RS=1;
RW=0;
E=1;
DataPort=Data;
DelayUs2x(5);
E=0;
DelayUs2x(5);
}
/*------------------------------------------------
液晶屏初始化
------------------------------------------------*/
void Init_ST7920()
{
DelayMs(40); //大于40MS的延时程序
PSB=1; //设置为8BIT并口工作模式
DelayMs(1); //延时
RES=0; //复位
DelayMs(1); //延时
RES=1; //复位置高
DelayMs(10);
Write_Cmd(0x30); //选择基本指令集
DelayUs2x(50); //延时大于100us
Write_Cmd(0x30); //选择8bit数据流
DelayUs2x(20); //延时大于37us
Write_Cmd(0x0c); //开显示(无游标、不反白)
DelayUs2x(50); //延时大于100us
Write_Cmd(0x01); //清除显示,并且设定地址指针为00H
DelayMs(15); //延时大于10ms
Write_Cmd(0x06); //指定在资料的读取及写入时,设定游标的移动方向及指定显示的移位,光标从右向左加1位移动
DelayUs2x(50); //延时大于100us
}
/*------------------------------------------------
显示字符串
x:横坐标值,范围0~8
y:纵坐标值,范围1~4
------------------------------------------------*/
void LCD_PutString(unsigned char x,unsigned char y,unsigned char code *s)
{
switch(y)
{
case 1: Write_Cmd(0x80+x);break;
case 2: Write_Cmd(0x90+x);break;
case 3: Write_Cmd(0x88+x);break;
case 4: Write_Cmd(0x98+x);break;
default:break;
}
while(*s>0)
{
Write_Data(*s);
s++;
DelayUs2x(50);
}
}
/*------------------------------------------------
清屏
------------------------------------------------*/
void ClrScreen()
{
Write_Cmd(0x01);
DelayMs(15);
}
xpt2046.h:
#ifndef __xpt2046_H_
#define __xpt2046_H_
#include "reg52.h"
#include "intrins.h"
#define u8 unsigned char
#define u16 unsigned int
/**输出引脚*/
sbit DOUT = P3^7;
/**时钟引脚*/
sbit CLK = P3^6;
/**输入引脚*/
sbit DIN = P3^4;
/**片选引脚*/
sbit CS = P3^5;
u16 read_ad_data(u8 cmd);
u16 SPI_read();
void SPI_write(u8 dat);
#endif
xpt2046.c
#include "xpt2046.h"
/**
*@brief 编写xpt2046的写数据函数
*@details 写入的是采集模拟量的地址,数据进行串行输入
*@param dat 8位数据
*@retval 无
*/
void SPI_write(u8 dat)
{
u8 i = 0;
CLK = 0;
for(i = 0; i <= 7; i++)
{
DIN = dat>>7;
dat = dat<<1;
CLK = 0;
/**延迟一个机器周期以形成相应沿信号*/
_nop_();
CLK = 1;
}
}
/**
*@brief 编写xpt2046的读数据函数
*@details DOUT为串行数据输出,需要一位一位地进行读取
*@param 无
*@retval dat 16位处理后数据
*/
u16 SPI_read()
{
u8 i = 0;
u16 dat = 0;
for(i = 0; i <= 11; i++)
{
dat = dat<<1;
CLK = 1;
_nop_();
CLK = 0;
dat |= DOUT;
}
return dat;
}
/**
*@brief 根据xpt2046芯片的通信时序实例化输出数字信号
*@details 根据相应时序初始化相对应的标志位
*@param cmd 8位数据采集地址
*@retval value 16位输出数字量
*/
u16 read_ad_data(u8 cmd)//模拟AD模数转换过程
{
u8 i;
u16 value;
CLK = 0;
CS = 0;
SPI_write(cmd);
for(i = 6; i >0; i--);
CLK = 1;
_nop_();
_nop_();
CLK = 0;
_nop_();
_nop_();
value = SPI_read();
/**通信结束置CS为高电平*/
CS = 1;
return value;
}
delay.h
#ifndef __DELAY_H__
#define __DELAY_H__
/*------------------------------------------------
uS延时函数,含有输入参数 unsigned char t,无返回值
unsigned char 是定义无符号字符变量,其值的范围是
0~255 这里使用晶振12M,精确延时请使用汇编,大致延时
长度如下 T=tx2+5 uS
------------------------------------------------*/
void DelayUs2x(unsigned char t);
/*------------------------------------------------
mS延时函数,含有输入参数 unsigned char t,无返回值
unsigned char 是定义无符号字符变量,其值的范围是
0~255 这里使用晶振12M,精确延时请使用汇编
------------------------------------------------*/
void DelayMs(unsigned char t);
#endif
delay.c
#include "delay.h"
/*------------------------------------------------
uS延时函数,含有输入参数 unsigned char t,无返回值
unsigned char 是定义无符号字符变量,其值的范围是
0~255 这里使用晶振12M,精确延时请使用汇编,大致延时
长度如下 T=tx2+5 uS
------------------------------------------------*/
void DelayUs2x(unsigned char t)
{
while(--t);
}
/*------------------------------------------------
mS延时函数,含有输入参数 unsigned char t,无返回值
unsigned char 是定义无符号字符变量,其值的范围是
0~255 这里使用晶振12M,精确延时请使用汇编
------------------------------------------------*/
void DelayMs(unsigned char t)
{
while(t--)
{
//大致延时1mS
DelayUs2x(245);
DelayUs2x(245);
}
}
main.c
#include <reg52.h>
#include "12864.h"
#include "xpt2046.h"
#define FUN_MODE 0 //设置时的三种模式
#define AIR_MODE 1
#define WATER_MODE 2
#define u8 unsigned char
#define u16 unsigned int
sbit key1 = P3^1;
sbit key2 = P3^0;
sbit key3 = P3^2; //按键
sbit P0_1=P0^1; //用于GSM模块发送短信
sbit BUZZ = P1^5; //蜂鸣器
sbit MQ135_CS = P2^2; //空气质量传感器
sbit PH_CS = P2^1; //PH传感器
bit FlagStartRH=0; //定时器参数
float num1; //用于PH传感器数据的接受
u16 value1, value2; //分别为空气质量和PH结果
u8 warning1 = 80, warning2 = 7; //设置单片机报警上下限
u8 num, set = 0, mode = 0, getdata; //标志位
u8 code str1[] = "设置空气质量阈值:"; //用于显示的字符
u8 code str2[] = "设置PH阈值:";
u8 code str3[] = "PH值:";
u8 code str4[] = "空气质量:";
u8 code math[] = "0123456789";
u8 code cipstart[]="AT+CIPSTART=\"TCP\",\"tcp.tlink.io\",8647\r\n"; //远程连接
u8 code cipmode[]="AT+CIPMODE=1\r\n"; //透传
u8 code cipsend[]="AT+CIPSEND\r\n"; //发送
u8 code zhuce[]="5M77H8P7EN86AY67";
u8 code denglu[]="i%zhang%zhang\r\n";
u8 code *shuju="d%li%shuju\r\n";
#define SET_KEY key1 //三个按键
#define ADD_KEY key2
#define SUB_KEY key3
void timecontrol1() interrupt 1 //定时器0的中断服务函数
{
u8 RHCounter;
TL0 = 0xb0;
TH0 = 0x3c; //定时器赋予初值
RHCounter++;
//每 1 秒钟启动一次转换
if (RHCounter >= 20) //定时器初值是 50ms这个变量加 20 次就是 1000ms 也就是1s
{
FlagStartRH = 1; //转换标志位置 1启动转换
RHCounter = 0; //计时变量清零
}
}
void timecontrol() //定时器0的中断初始配置
{
EA = 1;
ET0 = 1;
TR0 = 0;
TMOD |= 0x01;
TH0 = 0xFC;
TL0 = 0x18;
}
void timecon() //定时器1的中断初始配置
{
EA = 1;
ET1 = 1;
TR1 = 0;
TMOD |= 0x10;
TH1 = 0xFC;
TL1 = 0x18;
}
void delay_ms_tcp(u16 ms) //延时子程序
{
u8 i;
while(ms--) for(i=0;i<120;i++);
}
void Print_Char(u8 ch) //发送单个字符
{
SBUF=ch; //送入缓冲区
while(TI==0); //等待发送完毕
TI=0; //软件清零
}
void Print_Str(u8 *str) //发送字符串
{
while(*str!='\0') Print_Char(*str++);
}
void Ini_UART(void) //串口初始化、定时器初始化
{
TMOD = 0x20; //T1方式2,用于UART波特率
TH1 = 0xFD; //UART波特率设置:9600
TL1 = 0xFD;
SCON = 0x50; //UART方式1:8位UART; REN=1:允许接收
PCON = 0x00;
TR1 = 1; // 启动定时器1
Print_Str(cipstart);
delay_ms_tcp(1000);
Print_Str(cipmode);
delay_ms_tcp(1000);
Print_Str(cipsend);
delay_ms_tcp(1000);
Print_Str(zhuce);
delay_ms_tcp(1000);
Print_Str(zhuce);
delay_ms_tcp(1000);
Print_Str(denglu);
delay_ms_tcp(1000);
ES=1; //启动串行口中断
EA=1;
}
void delay()
{
int i,j;
for(i=0; i<=10; i++)
for(j=0; j<=2; j++);
}
void delay_ms(u8 ms)
{
u16 i, j;
for(i=0;i<ms;i++)
for(j=0;j<110;j++);
}
void datadisplay() //正常的数据显示
{
LCD_PutString(0, 0, &str4); //空气质量
Write_Cmd(0x8A);
Write_Data(math[value1/100%10]);
Write_Data(math[value1/10%10]);
Write_Data(math[value1%10]);
LCD_PutString(1, 0, &str3); //PH
Write_Cmd(0xc3);
Write_Data(math[value2/100]);
Write_Data(math[value2/10%10]);
Write_Data(0x2e); //小数点
Write_Data(math[value2%10]);
}
void displayWarning(u8 setMode) //设置时的显示界面
{
if(setMode == AIR_MODE)
{
LCD_PutString(0, 0, &str1); //前两行显示提示内容
Write_Cmd(0x90); //显示设置数值
Write_Data(math[warning1/100%10]);
Write_Data(math[warning1/10%10]);
Write_Data(math[warning1%10]);
}
else if(setMode == WATER_MODE)
{
LCD_PutString(0, 0, &str2); //前两行显示提示内容
Write_Cmd(0x90); //显示设置数值
Write_Data(math[warning2/100%10]);
Write_Data(math[warning2/10%10]);
Write_Data(math[warning2%10]);
}
}
void key() //按键扫描函数
{
if(SET_KEY==0) //如果设置按键按下
{
delay_ms(20); //延时去抖
if(SET_KEY==0) //再次判断按键是否按下
{
BUZZ=0; //蜂鸣器响,就是按键音
if(mode == 0 || mode == 2) //切换模式时不改变设置状态
{
set=!set; //设置的变量取反等于 1 时进入设置状态
TR0=!set; //定时器 0 会在进入设置状态后关闭退出设置状态后打开
}
if(mode == FUN_MODE) mode = AIR_MODE; //选择设置模式
else if(mode == AIR_MODE) mode = WATER_MODE;
else if(mode == WATER_MODE) mode = FUN_MODE;
if(set == 1) displayWarning(mode); //等于 1 进入设置状态,显示设置数值
else datadisplay(); //不是设置状态时,打开显示 无光标 光标闪烁
BUZZ=1; //蜂鸣器关
while(SET_KEY==0); //等待按键释放
}
}
if(ADD_KEY==0 && set!=0) //在设置的状态下按下加
{
delay_ms(20); //延时去抖
if(ADD_KEY==0 && set!=0) //再次判断加按键按下
{
BUZZ=0; //蜂鸣器响
if(mode == AIR_MODE)
{
warning1 += 1;
if(warning1 >= 100) warning1 = 100; //如果报警值大于等于 100 报警值等于 100
}
else if(mode == WATER_MODE)
{
warning2 += 1;
if(warning2 >= 10) warning2 = 10;
}
displayWarning(mode);
BUZZ=1; //蜂鸣器关
}
while(ADD_KEY==0); //等待按键释放
}
if(SUB_KEY==0 && set!=0) //在设置的状态下按下减
{
delay_ms(20);
if(SUB_KEY==0 && set!=0)
{
BUZZ=0; //蜂鸣器响
if(mode == AIR_MODE)
{
warning1 -= 1;
if(warning1 <= 0) warning1 = 0; //如果报警值小于等于 0 报警值等于 0
}
else if(mode == WATER_MODE)
{
warning2 -= 1;
if(warning2 <= 4) warning2 = 4;
}
displayWarning(mode);
BUZZ=1; //蜂鸣器关
}
while(SUB_KEY==0); //等待按键释放
}
}
/*空气质量检测+PH检测 实现对外部模拟量测量值——>电压值——>气体浓度值的转换*/
void datadispose()
{
//0xE4是外部输入AD值的地址,value1为实际检测气体浓度值,value2为PH检测结果
MQ135_CS = 0; //空气检测片选信号
value1 = read_ad_data(0xE4); //利用ad转换芯片读取数据
value1 = (int)(0.236 * (value1 - 12.8623));
MQ135_CS = 1;
delay_ms(10);
//PH
PH_CS = 0; //PH传感器片选信号
getdata = read_ad_data(0xE4); //利用ad转换芯片读取数据
getdata = (int)(0.236 * (value1 - 12.8623));
num1=getdata*140/256; //计算PH数值
value2 = (unsigned int)num1; //类型转换
PH_CS = 1;
}
void sendData(u8 val1, u8 val2) //发送数据到远程tlink物联网平台
{
u8 str[15]; //定义要发送的数组
str[0] = '#'; //按照协议填好数组的每一位
str[1] = val1+'0';
str[2] = ',';
str[3] = val2+'0';
str[4] = '#';
Print_Str(str); //用串口发送
}
void warning() //单片机报警
{
if(value1 > warning1 || value2 < warning2) //如果空气质量太差或水质呈酸性
{
BUZZ = 1; //蜂鸣器响
if(value1 > warning1) //分情况讨论
{
Write_Cmd(0x87); //在lcd12864的相应位置上显示
Write_Data('!');
}
if(value2 < warning2)
{
Write_Cmd(0x97);
Write_Data('!');
}
}
else //恢复
{
BUZZ = 0;
Write_Cmd(0x87);
Write_Data(' ');
Write_Cmd(0x97);
Write_Data(' ');
}
}
void send_modem_string(u8 *modem_string) //指令字符串发送指令
{
while(*modem_string)
{
SBUF = *modem_string;
while(TI==0);
TI=0;
modem_string++;
}
}
void main()
{
u8 h, time = 0;
u16 sum1 = 0, sum2 = 0;
delay_ms_tcp(3000);
Ini_UART(); //串口初始化
timecontrol();
timecon();
Init_ST7920();
while(1)
{
if(FlagStartRH == 1 && set == 0)//每隔1s且不处于设置状态扫描一次检测值
{
TR0 = 0; //定时器关闭
for(h=0;h<50;h++) //读取 50 次 AD 数值
{
datadispose(); //实现对MQ135传感器和PH传感器数据的处理
sum1 += value1; //累加每次读取到的 ad 值
sum2 += value2;
delay_ms(100); //每100ms读取一次
key(); //扫描一次按键
}
value1 = sum1/50;
value2 = sum2/50;
sum1 = sum2 = 0;
if(set == 0) datadisplay(); //将相对应的数据显示在液晶屏上
sendData(value1, value2); //定时向云端发送数据
TR0 = 1; //恢复定时器
}
key(); //扫描按键选择模式
}
}