- 课程设计要求
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(); //扫描按键选择模式 } }