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

180it 2022-04-06 PM 577℃ 0条

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

小型气象站

问题咨询及项目源码下载请加群:

群名:IT项目交流群

群号:245022761

20190119220732759.png

一、实验目的

  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传感器来监测环境中各项指标,通过传感器收集各类数据,发送给单片机实现了小型气象站的效果,学习传感器之路刚刚起步,还有很多不足,发布此文章仅希望广大码友互相学习。

附上本人在大学学习期间所有学习实现的demo,这些项目的实现都通过本人亲自测试运行,也是自己学习中知识的一点一点积累,整理不易,希望能帮到大家,不足之处大家多多交流。

QQ:1805523903

邮箱:1805523603@qq.com

昵称:忘尘

支付宝打赏支付宝打赏 微信打赏微信打赏

如果文章或资源对您有帮助,欢迎打赏作者。一路走来,感谢有您!

标签: none

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