基于AT89C51单片机的小型气象站
小型气象站
问题咨询及项目源码下载请加群:
群名:IT项目交流群
群号:245022761
一、实验目的
- 了解温湿度传感器工作原理
- 了解大气压力传感器工作原理
- 了解激光颗粒物传感器工作原理
- 学会使用SHT11温湿度传感器检测环境温湿度
- 学会使用BMP085气压传感器检测大气压力
- 学会使用激光颗粒物传感器检测空气质量
- 会使用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
昵称:忘尘
如果文章或资源对您有帮助,欢迎打赏作者。一路走来,感谢有您!
txttool.com 说一段 esp56物联 查询128 IP查询