基于AT89C51单片机的小型气象站

目录

 小型气象站

 

一、实验目的

1. 了解温湿度传感器工作原理

2. 了解大气压力传感器工作原理

3. 了解激光颗粒物传感器工作原理

4. 学会使用SHT11温湿度传感器检测环境温湿度

5. 学会使用BMP085气压传感器检测大气压力

6. 学会使用激光颗粒物传感器检测空气质量

7. 会使用1602液晶显示模块显示气象参数

二、系统连接图

 

 

三、代码实现部分

1602代码段:

#include <1602.h>

sbit LCDEN=P3^4;
sbit RS=P3^5;
//RW直接接地,只允许写不允许读
sbit BF=P0^7;

//u8 DectectBusyBit(void)//状态判断函数(忙/闲?)
//{   
//	bit result;
//	P0 = 0xff;	//读状态前先置高电平,防止误判
//	RS = 0;
//	delay_ms(5);
//    RW = 1;
//	LCDEN = 1;
//	delay_ms(5);
//	result=BF; //若LCM忙,则反复测试,在此处原地踏步;当LCM闲时,才往下继续
//	LCDEN = 0;
//	return result;		      
//}

//u8 RdACAdr(void)//读当前光标地址
//{   
//	u8 result;
//	P0 = 0xff;	//读地址前先置高电平,防止误判
//	RS = 0;
//	delay_ms(5);
//    RW = 1;
//	LCDEN = 1;
//	delay_ms(5);
//	result=P0&0x7f; //去掉最高位忙闲标记,只保留低7位地址值
//	LCDEN = 0;
//	return result;		      
//} 

void WrComLCD(u8 ComVal)//写命令函数
{
//	while(DectectBusyBit()==1);         //先检测LCM是否空闲
	RS = 0;
	delay_ms(1);
//  RW = 0;
	LCDEN = 1;
	P0 = ComVal;
	delay_ms(1);
	LCDEN = 0;	
}

void WrDatLCD(u8 DatVal)//写数据函数
{
//	while(DectectBusyBit()==1); 
	RS = 1;
	delay_ms(1);
//  RW = 0;
	LCDEN = 1;
	P0 = DatVal;
	delay_ms(1);
	LCDEN = 0;	
}

void LCD1602_Init(void)//1602初始化函数
{ 
	WrComLCD(0x38);     // 功能设定:16*2行、5*7点阵、8位数据接口
	WrComLCD(0x38);
	WrComLCD(0x38);    
//多次重复设定功能指令,因为LCD启动后并不知道使用的是4位数据接口还是8位的,所以开始时总是默认为4位,这样刚开始写入功能设定指令时,低4位被忽略,为了可靠,最好多写几遍该指令 
	WrComLCD(0x01);    // 清屏 
	WrComLCD(0x06);    // 光标自增、屏幕不动  
	delay_ms(1);	      // 延时,等待上面的指令生效,下面再显示,防止出现乱码
	WrComLCD(0x0C);    // 开显示、关光标
	delay_ms(5);
}

void LCD1602pos(u8 x,u8 y)//1602显示坐标定位函数:x为行标,0:第一行,1:第二行;y为列标,0-15
{
	u8 t;
	t=x?0x40:0x00;
	WrComLCD(0x80+t+y);  
}

void LCD1602_disstr(u8 *p,u8 x,u8 y)//从指定坐标开始显示英文字符串(长度不超过32)
{	
	u8 i=0;
    LCD1602pos(x,y);
	while(p[i]!='\0')
	{  
	  	WrDatLCD(p[i]);
		i++;
		delay_ms(5);
	    if(y+i==16) {x=x^0x01;LCD1602pos(x,0);}//x=x^0x01;//如果第1行写完换行到第2行,如果第2行写完换行到第1行	
		
	}	
}
void LCD1602_disch(u8 ch,u8 x,u8 y)//显示一个英文字符
{	
   	LCD1602pos(x,y);
	WrDatLCD(ch);
	delay_ms(5);	
}

void LCD1602_clear(void)//1602清屏函数
{
 	WrComLCD(0x01);    // 清屏
} 

void LCD1602_backspace(void)//向左删除一个字符
{
 	WrComLCD(0x10);//光标左移
	WrDatLCD(' ');//输出空格
	WrComLCD(0x10);//光标左移
}
压强传感器bmp0851代码实现:

#include  <BMP085.h>
	  
sbit	  SCL=P1^0;      //IIC时钟引脚定义
sbit 	  SDA=P1^1;      //IIC数据引脚定义
#define	BMP085_SlaveAddress   0xee	  //定义器件在IIC总线中的从地址                             

#define OSS 0	// Oversampling Setting (note: code is not set up to use other OSS values)
							   
typedef u8  BYTE;
typedef unsigned short WORD;
//int  dis_data;                              //变量

short ac1;
short ac2; 
short ac3; 
unsigned short ac4;
unsigned short ac5;
unsigned short ac6;
short b1; 
short b2;
short mb;
short mc;
short md;
/*
void delay(unsigned int k);


void conversion(long temp_data);

void  Single_Write(uchar SlaveAddress,uchar REG_Address,uchar REG_data);   //单个写入数据
uchar Single_Read(uchar REG_Address);                                      //单个读取内部寄存器数据
void  Multiple_Read(uchar,uchar);                                          //连续的读取内部寄存器数据
//------------------------------------
void Delay5us();
void Delay5ms();
void BMP085_Start();
void BMP085_Stop();
void BMP085_SendACK(bit ack);
bit  BMP085_RecvACK();
void BMP085_SendByte(BYTE dat);
BYTE BMP085_RecvByte();
void BMP085_ReadPage();
void BMP085_WritePage();  */
//-----------------------------------

//*********************************************************

/*******************************/
void delay(u16 k)	
{						
u16 i,j;				
for(i=0;i<k;i++)
{			
for(j=0;j<121;j++)			
{;}}						
}
/*******************************/
						

/**************************************
延时5微秒(STC90C52RC@12M)
不同的工作环境,需要调整此函数,注意时钟过快时需要修改
当改用1T的MCU时,请调整此延时函数
**************************************/
void Delay5us()
{
    _nop_();_nop_();_nop_();_nop_();
    _nop_();_nop_();_nop_();_nop_();
	_nop_();_nop_();_nop_();_nop_();
	_nop_();_nop_();_nop_();_nop_();
	_nop_();_nop_();_nop_();_nop_();
	_nop_();_nop_();_nop_();_nop_();
}

/**************************************
延时5毫秒(STC90C52RC@12M)
不同的工作环境,需要调整此函数
当改用1T的MCU时,请调整此延时函数
**************************************/
void Delay5ms()
{
    WORD n = 560;

    while (n--);
}

/**************************************
起始信号
**************************************/
void BMP085_Start()
{
    SDA = 1;                    //拉高数据线
    SCL = 1;                    //拉高时钟线
    Delay5us();                 //延时
    SDA = 0;                    //产生下降沿
    Delay5us();                 //延时
    SCL = 0;                    //拉低时钟线
}

/**************************************
停止信号
**************************************/
void BMP085_Stop()
{
    SDA = 0;                    //拉低数据线
    SCL = 1;                    //拉高时钟线
    Delay5us();                 //延时
    SDA = 1;                    //产生上升沿
    Delay5us();                 //延时
}

/**************************************
发送应答信号
入口参数:ack (0:ACK 1:NAK)
**************************************/
void BMP085_SendACK(bit ack)
{
    SDA = ack;                  //写应答信号
    SCL = 1;                    //拉高时钟线
    Delay5us();                 //延时
    SCL = 0;                    //拉低时钟线
    Delay5us();                 //延时
}

/**************************************
接收应答信号
**************************************/
bit BMP085_RecvACK()
{
    SCL = 1;                    //拉高时钟线
    Delay5us();                 //延时
    CY = SDA;                   //读应答信号
    SCL = 0;                    //拉低时钟线
    Delay5us();                 //延时

    return CY;
}

/**************************************
向IIC总线发送一个字节数据
**************************************/
void BMP085_SendByte(BYTE dat)
{
    BYTE i;

    for (i=0; i<8; i++)         //8位计数器
    {
        dat <<= 1;              //移出数据的最高位
        SDA = CY;               //送数据口
        SCL = 1;                //拉高时钟线
        Delay5us();             //延时
        SCL = 0;                //拉低时钟线
        Delay5us();             //延时
    }
    BMP085_RecvACK();
}

/**************************************
从IIC总线接收一个字节数据
**************************************/
BYTE BMP085_RecvByte()
{
    BYTE i;
    BYTE dat = 0;

    SDA = 1;                    //使能内部上拉,准备读取数据,
    for (i=0; i<8; i++)         //8位计数器
    {
        dat <<= 1;
        SCL = 1;                //拉高时钟线
        Delay5us();             //延时
        dat |= SDA;             //读数据               
        SCL = 0;                //拉低时钟线
        Delay5us();             //延时
    }
    return dat;
}
//*********************************************************
//读出BMP085内部数据,连续两个
//*********************************************************
short Multiple_read(u8 ST_Address)
{   
	u8 msb, lsb;
	short _data;
    BMP085_Start();                          //起始信号
    BMP085_SendByte(BMP085_SlaveAddress);    //发送设备地址+写信号
    BMP085_SendByte(ST_Address);             //发送存储单元地址
    BMP085_Start();                          //起始信号
    BMP085_SendByte(BMP085_SlaveAddress+1);         //发送设备地址+读信号

    msb = BMP085_RecvByte();                 //BUF[0]存储
    BMP085_SendACK(0);                       //回应ACK
    lsb = BMP085_RecvByte();     
	BMP085_SendACK(1);                       //最后一个数据需要回NOACK

    BMP085_Stop();                           //停止信号
    Delay5ms();
    _data = msb << 8;
	_data |= lsb;	
	return _data;
}
//********************************************************************
long bmp085ReadTemp(void)
{

    BMP085_Start();                  //起始信号
    BMP085_SendByte(BMP085_SlaveAddress);   //发送设备地址+写信号
    BMP085_SendByte(0xF4);	          // write register address
    BMP085_SendByte(0x2E);       	// write register data for temp
    BMP085_Stop();                   //发送停止信号
	delay(10);	// max time is 4.5ms
	
	return (long) Multiple_read(0xF6);

}
//*************************************************************
long bmp085ReadPressure(void)
{
	long pressure = 0;

    BMP085_Start();                   //起始信号
    BMP085_SendByte(BMP085_SlaveAddress);   //发送设备地址+写信号
    BMP085_SendByte(0xF4);	          // write register address
    BMP085_SendByte(0x34);       	  // write register data for pressure
    BMP085_Stop();                    //发送停止信号
	delay(10);    	                  // max time is 4.5ms
	
	pressure = Multiple_read(0xF6);
	pressure &= 0x0000FFFF;
	
	return pressure;	

	//return (long) bmp085ReadShort(0xF6);
}

//**************************************************************

//初始化BMP085,根据需要请参考pdf进行修改**************
void Init_BMP085()
{
	ac1 = Multiple_read(0xAA);
	ac2 = Multiple_read(0xAC);
	ac3 = Multiple_read(0xAE);
	ac4 = Multiple_read(0xB0);
	ac5 = Multiple_read(0xB2);
	ac6 = Multiple_read(0xB4);
	b1 =  Multiple_read(0xB6);
	b2 =  Multiple_read(0xB8);
	mb =  Multiple_read(0xBA);
	mc =  Multiple_read(0xBC);
	md =  Multiple_read(0xBE);
}
//***********************************************************************



long bmp085Convert()
{
	u16 ut;
	u32 up;
	long x1, x2, b5, b6, x3, b3, p;
	u32 b4, b7;
	long  temperature;
	long  pressure;
	ut = bmp085ReadTemp();	   // 读取温度

	up = bmp085ReadPressure();  // 读取压强

	x1 = (((long)ut - (long)ac6)*(long)ac5) >> 15;
	x2 = ((long) mc << 11) / (x1 + md);
	b5 = x1 + x2;
	temperature = ((b5 + 8) >> 4);


   b6 = b5 - 4000;
   // Calculate B3
   x1 = (b2 * (b6 * b6)>>12)>>11;
   x2 = (ac2 * b6)>>11;
   x3 = x1 + x2;
   b3 = (((((long)ac1)*4 + x3)<<OSS) + 2)>>2;
  
   // Calculate B4
   x1 = (ac3 * b6)>>13;
   x2 = (b1 * ((b6 * b6)>>12))>>16;
   x3 = ((x1 + x2) + 2)>>2;
   b4 = (ac4 * (u32)(x3 + 32768))>>15;
  
   b7 = ((u32)(up - b3) * (50000>>OSS));
   if (b7 < 0x80000000)
     p = (b7<<1)/b4;
   else
     p = (b7/b4)<<1;
    
   x1 = (p>>8) * (p>>8);
   x1 = (x1 * 3038)>>16;
   x2 = (-7357 * p)>>16;
  pressure = p+((x1 + x2 + 3791)>>4);

  return pressure;

}

温度传感器DHT11代码段:

#include<dht11.h>

sbit Data=P0^0;   //dht11数据线
void delay_us(u8 n)//微秒延时
{
    while(--n);
} 

void DHT11_start()//启动
{
   Data=1;
   delay_us(2);
   Data=0;
   delay_ms(20);   //延时18ms以上
   Data=1;
   delay_us(30);
}

u8 DHT11_rec_byte()      //接收一个字节
{
  u8 i,dat=0;
  for(i=0;i<8;i++)    //从高到低依次接收8位数据
   {          
      while(!Data);   ////等待50us低电平过去
      delay_us(8);     //延时60us,如果还为高则数据为1,否则为0 
      dat<<=1;           //移位使正确接收8位数据,数据为0时直接移位
      if(Data==1)    //数据为1时,使dat加1来接收数据1
         dat+=1;
      while(Data);  //等待数据线拉低    
    }  
    return dat;
}

void DHT11_receive(u8 rec_dat[])      //接收40位的数据
{
    u8 R_H,R_L,T_H,T_L,RH,RL,TH,TL,revise; 
    DHT11_start();
    if(Data==0)
    {
        while(Data==0);   //等待拉高     
        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(); //接收校正位

        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;
        } 
        /*数据处理,方便显示*/
				rec_dat[0]='R';
        rec_dat[1]='0'+(RH/10);
        rec_dat[2]='0'+(RH%10);
        rec_dat[3]=' ';
        rec_dat[4]='C';
        rec_dat[5]='0'+(TH/10);
        rec_dat[6]='0'+(TH%10);
      
			
    }
}
Main函数实现:

#include <config.h>
//#include <stdio.h>
//#include <uart.h>
#include <dht11.h>
#include <1602.h>
#include <bmp058.h>
//#include <ds18b20.h>
u8 print[9];//缓冲区
u8 print1[8];//缓冲区
u8 value[8];//缓冲区
//-------------------------------
u8 cal[7]={0};//pm2.5传感器数据存储

//cal[0]:起始位 0xAA
//cal[1]:Vout(H)
//cal[2]:Vout(L)
//cal[3]:Vref(H)
//cal[4]:Vref(L)
//cal[5]:检验位
//cal[6]:结束位 0xFF
u8 buf;
u8 sum;
int i=0;
float Vo;
u16 k;
long   pressure;
//----------------------------------
void delay_ms(int x)//毫秒延时函数
{int i,j;
for(i=0;i<x;i++)
    for(j=0;j<115;j++);
}
/*************************
GP2Y1051AU0F二代灰尘传感器数据传回电脑与处理
*************************/
void senddate(u8 dat)
{
	if(dat==170)//判断起始位 0xAA(0xAA=170)
	{
		i=0;
		cal[i]=dat;
	}
	else
	{
		i=i+1;
		cal[i]=dat;
		if(i==6)
		{
			sum=cal[1]+cal[2]+cal[3]+cal[4];//sum=Vout(H)+Vout(L)+Vref(H)+Vref(L)
			if(sum==cal[5]  &&  cal[6]==255)//sum与校验位相同&&最后一位是0xFF结束位
			{
				for(i=0;i<=6;i++)//此处for循环是显示所有数值,即cal[7]中的数据
				{
				   SBUF=cal[i];  
			       while(!TI);
				   TI=0;							
				}
	             Vo=(cal[1]*256+cal[2])/1024.0*5;
				 k=Vo*900;//系数800--1000
			}					   
		}
	}
}

void main(void)
{
	
	LCD1602_Init();
	//Init_COM(); //串口初始化
	Init_BMP085();
	//LCD1602_disstr("WEREW",0,0);

	SCON=0x50;
	PCON=0x00;				 
	TMOD=0x20;
	EA=1;
	ES=1;
	TL1=0xF4;
	TH1=0xF4;
	TR1=1;
	
	delay_ms(100);	    //延时100ms	
  
	while(1)
	{
			//********************************
		 DHT11_receive(value); //读取传感器传送过来的温湿度数据
			//Print_str_COM(value); //将数据送串口  
		 LCD1602_disstr(value,0,0); 
		 delay_ms(200); //输出到1602
		//*******************************
		
		 sprintf(print,"M:%d%d%d",k/100,(k/10)%10,k%10);//输出到缓冲区
	   LCD1602_disstr(print,1,8);
	    delay_ms(100);
	
	 //**************************************
    pressure=bmp085Convert();
		sprintf(print1,"p:%d.%d",(u16)pressure/1000+70,(u16)pressure/100%10);
		LCD1602_disstr(print1,1,0); 
		delay_ms(100);
	   
		//***********************************
		//Ds18b20_Tempchg();	//启动温度变换
   //  n=Ds18b20_Gettemp();  //读取温度
    // sprintf(value,"T:%.1f",n); 
		// LCD1602_disstr(value,1,0); delay_ms(800); //输出到1602
    
	
	
	
	} 
}
/******************
 串行中断服务函数
******************/
void serial() interrupt 4
{
	ES=0;			   //关闭串口中断
	RI=0;			   //清除串口接受标志位
	buf=SBUF;		   //从串口缓存区取得数据`
	senddate(buf);
	ES=1;//允许串口中断
}

四、效果实现总结

思路是通过使用压强、sht11温度传感器、以及PM2.5传感器来监测环境中各项指标,通过传感器收集各类数据,发送给单片机实现了小型气象站的效果,学习传感器之路刚刚起步,还有很多不足,发布此文章仅希望广大码友互相学习。

猜你喜欢

转载自blog.csdn.net/qq_37037348/article/details/86557814