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

简介: 【单片机课程设计】环境监测控制系统
  • 课程设计要求

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();          //扫描按键选择模式      
  }
}
相关文章
|
7月前
|
传感器 C语言 智能硬件
基于单片机的温度控制系统
基于单片机的温度控制系统
130 0
|
2月前
|
传感器 数据采集 存储
基于51单片机的大棚环境检测系统设计
基于51单片机的大棚环境检测系统设计
185 0
|
2月前
|
传感器 编解码 人机交互
基于51单片机的温室大棚环境检测系统
基于51单片机的温室大棚环境检测系统
71 0
|
5月前
单片机课程设计——PWM电机调速
单片机课程设计——PWM电机调速
|
6月前
|
数据安全/隐私保护
单片机课程设计——基于C51电子密码锁(源代码)
单片机课程设计——基于C51电子密码锁(源代码)
|
7月前
基于51单片机的模拟交通灯控制系统
该文档描述了一个基于51单片机的交通灯控制系统的设计要求和实现。系统应用于十字路口,控制主干道(东西方向)和支干道(南北方向)的交通流量。主干道绿灯时间为15秒,支干道为10秒,转换时黄灯闪烁3秒。用户可以通过按键设置通行时间和进行交通管制。系统包括四个状态:主干道绿灯、主干道黄灯、支干道绿灯和支干道黄灯,循环运行。此外,还提供了仿真电路图、原理图和实物照片,以及C代码示例。
191 1
|
7月前
|
IDE 开发工具
基于单片机的简易步进电机控制系统
基于单片机的简易步进电机控制系统
101 0
|
7月前
|
传感器 人工智能 前端开发
单片机毕业设计|农家菜园自动灌溉控制系统设计
单片机毕业设计|农家菜园自动灌溉控制系统设计
120 0
|
7月前
|
传感器 数据采集 监控
毕业设计|基于51单片机的配电室远程监控系统设计环境检测GSM环境报警设计
毕业设计|基于51单片机的配电室远程监控系统设计环境检测GSM环境报警设计
113 0
|
7月前
|
传感器 存储 芯片
毕业设计 基于51单片机环境监测设计 光照 PM2.5粉尘 温湿度 2.4G无线通信
毕业设计 基于51单片机环境监测设计 光照 PM2.5粉尘 温湿度 2.4G无线通信
133 0