【单片机课程设计】环境监测控制系统

  • 课程设计要求

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();					//扫描按键选择模式 	  	
	}
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

为梦而生~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值