一、项目概述
1. 项目背景与意义
大一做学校“三小”,选题“智慧农业大棚应用系统”;市面上找不到源码,找到的好多都要money,我想做free的。如果有什么讲的不好,或者错误的地方,希望大家批评指正,一起为学习物联网做一点贡献。(本人目前是一名大二学生)
二、硬件系统设计
2.1 核心控制器选型(STC89C52/AT89C51)
51的板子STC89C52
2.2 传感器模块设计
·2.2.1 土壤湿度传感器(TDR/FDR原理)
左图左边的是双电压比较器,右边的是土壤湿度检测模块。按右图方式连接。
双电压比较器的VCC接5V,GND接地,AO输出的模拟信号接到51单片机自带的ADC模块上,自带的ADC模块一共有四个输入通道,其中三个已经被焊死了,只剩下IN3。如下图具体原理图去单片机查手册。
·2.2.2 DHT11温湿度传感器
VCC接5V,GND接地,Data我选择接P2.3。
·2.2.3 光敏电阻模块
选择51单片机自带的光敏电阻,用自带ADC即可得到光照强度值。
·2.2.4 SGP30二氧化碳传感器(也可以同时检测甲醛含量)
VCC接5V,GND接地,SCL我选择接P1.6,SDA我选择接P1.7。
注意:SGP30模块上电后,系统需要一段时间初始化,在此期间CO2值为400,TVOC值为0。
2.3 LCD1602
用自带的LCD1602,按上图所示插好。(在次感谢B站江协科技)
三、软件系统设计
3.1 开发环境搭建(Keil μVision5)
B站搜51单片机(江协科技)详细讲解了这部分,此处不再赘述。
3.2 系统主程序架构
void main()
{//定义变量名
u8 temp=0,humi=0;
u8 i=0;
unsigned long sgp30_dat;
u16 CO2Data;
Lig = ' ';
LCD_Init();
DHT11_Init();
SGP30_Init();
LCD_ShowString(1,1,"System_Init..");
//等待系统初始化
/*delay_ms(100);
SGP30_Write(0x20,0x08);
sgp30_dat = SGP30_Read();//读取SGP30的值
CO2Data = (sgp30_dat & 0xffff0000) >> 16;
while(CO2Data==400)
{
SGP30_Write(0x20,0x08);
sgp30_dat = SGP30_Read();//读取SGP30的值
CO2Data = (sgp30_dat & 0xffff0000) >> 16;//取出CO2浓度值
LCD_ShowString(1,1,"System_Init..");
delay_ms(100);
}*/
LCD_Init();
//循环开始
while(1)
{
i++;
if(i%200==0)//200次循环读取一次传感器数据,避免更新过快
{
//温湿度
DHT11_Read_Data(&temp,&humi);//读取DHT11输入的温湿度数据
//光照(AIN2)
Lig=XPT2046_ReadAD(XPT2046_VBAT_12); //读取AIN2,光敏电阻
//土壤湿度(AIN3)
Soi=XPT2046_ReadAD(XPT2046_AUX_12)/40.96;
//CO2
SGP30_Write(0x20,0x08);
(u32)sgp30_dat = (u32)SGP30_Read();
CO2Data = (sgp30_dat & 0xffff0000) >> 16;//LCD_ShowNum五项数据
/**/
}}
3.3 传感器驱动模块
3.3.1 土壤湿度传感器
Soi=XPT2046_ReadAD(XPT2046_AUX_12);//调用ADC模块函数即可
注意:此处Soi值与土壤湿度呈负相关(不确定是不是负线性)。
3.3.2 温湿度传感器
Dht11.h
#ifndef _dht11_H
#define _dht11_H
#include "Delay.h"
//管脚定义
sbit DHT11_DQ=P2^3;
//函数声明
u8 DHT11_Init(void);
void DHT11_Rst(void);
u8 DHT11_Check(void);
u8 DHT11_Read_Byte(void);
u8 DHT11_Read_Data(u8 *temp,u8 *humi);
#endif
DHT11_Read_Data(&temp,&humi);//读取DHT11输入的温湿度数据
调用Dht11.c中函数即可。
Dht11.c中部分代码截图。需要完整版私信我即可
3.3.3 ADC转换与光敏电阻
XPT2046.h
#ifndef __XPT2046_H__
#define __XPT2046_H__//8位方式,效果不明显
#define XPT2046_VBAT_8 0xAC
#define XPT2046_AUX_8 0xEC
#define XPT2046_XP_8 0x9C //0xBC
#define XPT2046_YP_8 0xDC//12位方式,效果明显,建议使用这种
#define XPT2046_VBAT_12 0xA4
#define XPT2046_AUX_12 0xE4
#define XPT2046_XP_12 0x94 //0xB4
#define XPT2046_YP_12 0xD4
unsigned int XPT2046_ReadAD(unsigned char Command);#endif
XPT2046.c
#include <REGX52.H>
#include <INTRINS.H>
#include "Delay.h"//引脚定义
sbit XPY2046_DIN=P3^4;
sbit XPY2046_CS=P3^5;
sbit XPY2046_DCLK=P3^6;
sbit XPY2046_DOUT=P3^7;/**
* @brief ZPT2046读取AD值
* @param Command 命令字,范围:头文件内定义的宏,结尾的数字表示转换的位数
* @retval AD转换后的数字量,范围:8位为0~255,12位为0~4095
*/
unsigned int XPT2046_ReadAD(unsigned char Command)
{
unsigned char i;
unsigned int Data=0;
XPY2046_DCLK=0;
XPY2046_CS=0;
for(i=0;i<8;i++)
{
XPY2046_DIN=Command&(0x80>>i);
XPY2046_DCLK=1;
XPY2046_DCLK=0;
}
for(i=0;i<16;i++)
{
XPY2046_DCLK=1;
Delay(1);//此处必须delay1ms,否则数据只会以2的幂次方形式改变
XPY2046_DCLK=0;
if(XPY2046_DOUT){Data|=(0x8000>>i);}
}
XPY2046_CS=1;
if(Command&0x08){return Data>>8;}
else return Data>>4;
}
Lig=XPT2046_ReadAD(XPT2046_VBAT_12); //读取AIN2,光敏电阻
主函数调用即可。
3.3.4 SGP30数据处理
//初始化IIC接口
void SGP30_Init(void)
{
SGP30_Write(0x20,0x03);
// SGP30_ad_write(0x20,0x61);
// SGP30_ad_write(0x01,0x00);
}void SGP30_Write(u8 a, u8 b)
{
I2CStart();
I2C_Write_Byte(SGP30_write); //发送器件地址+写指令
I2C_Write_Byte(a); //发送控制字节
I2C_Write_Byte(b);
I2CStop();
delay_ms(100);
}unsigned long SGP30_Read(void)
{
unsigned long dat;
int crc;
I2CStart();
I2C_Write_Byte(SGP30_read); //发送器件地址+读指令
dat = I2C_Read_Byte(ACK);
dat <<= 8;
dat += I2C_Read_Byte(ACK);
crc = I2C_Read_Byte(ACK); //check数据,舍去
crc = crc; //避免编译产生警告,这句可有可无
dat <<= 8;
dat += I2C_Read_Byte(ACK);
dat <<= 8;
dat += I2C_Read_Byte(NACK);
I2CStop();
return(dat);
}
在main函数中调用,3.2系统主程序架构中已写。
3.3.5通信协议设计(I2C)
void I2CDelay (u8 t)
{
while(t--);
}//I2C起始信号
void I2CStart(void)
{
SDA = 1; //发送起始条件的数据信号
SCL = 1;
I2CDelay(50); //起始条件建立时间大于4.7us,延时
SDA = 0; //发送起始信号
I2CDelay(50); //起始条件锁定时间大于4μs
SCL = 0; //钳住I2C总线,准备发送或接收数据
I2CDelay(50);
}//I2C停止信号
void I2CStop(void)
{
SDA = 0; //发送结束条件的数据信号
SCL = 0;
I2CDelay(50);
SCL = 1; //发送结束条件的时钟信号
I2CDelay(50); //结束条件建立时间大于4μs
SDA = 1; //发送I2C总线结束信号
I2CDelay(50);
}//I2C写一个字节数据,返回ACK或者NACK
u8 I2C_Write_Byte(u8 Write_Byte) //Sendbyte
{
u8 i;
SCL=0;
I2CDelay(10);
for(i=0; i<8; i++) //要传送的数据长度为8位
{
if(Write_Byte&0x80) //判断发送位
{
SDA = 1;
}
else
{
SDA = 0;
}
I2CDelay(5);
SCL=1; //输出SDA稳定后,拉高SCL给出上升沿,从机检测到后进行数据采样
I2CDelay(5); //保证时钟高电平周期大于4μs
SCL=0;
I2CDelay(5);
Write_Byte <<= 1;
}
I2CDelay(1);
SDA = 1; //8位发送完后释放数据线,准备接收应答位-ZLG
I2CDelay(40);
SCL = 1; //MCU告知SHT2X数据发送完毕,等待从机的应答信号
I2CDelay(40);
/*以下是判断I2C总线接收应到应答信号是ACK还是NACK*/
if(SDA==1) //SDA为高,收到NACK
{
I2CDelay(40);
SCL=0;
return NACK;
}
else //SDA为低,收到ACK
{
I2CDelay(40);
SCL=0;
return ACK;}
}//I2C读一个字节数据,入口参数用于控制应答状态,ACK或者NACK
u8 I2C_Read_Byte(u8 AckValue)//receivebyte
{
u8 i,RDByte=0;
SCL=0; //置时钟线为低,准备接收数据位
I2CDelay(40);
SDA = 1; //释放总线,置数据线为输入方式
for (i=0; i<8; i++)
{
SCL = 1; //SCL高电平期间,采集SDA信号,并作为有效数据 //置时钟线为高使数据线上数据有效
I2CDelay(20);
RDByte <<= 1; //移位
if(SDA==1) //采样获取数据
{
RDByte |= 0x01;
}
else
{
RDByte &= 0xfe;
}
I2CDelay(10);
SCL = 0; //下降沿,从机给出下一位值
I2CDelay(60);
}
/*以下是I2C总线发送应答信号ACK或者NACK*/
SDA = AckValue; //应答状态
I2CDelay(30);
SCL = 1;
I2CDelay(50); //时钟低电平周期大于4μs
SCL = 0; //清时钟线,钳住I2C总线以便继续接收
I2CDelay(150);
return RDByte;
}
四、结论与展望
后续会继续更新OLED屏,控制器(风扇、水泵、化肥播撒器),esp8266 WIFI模块。
有些讲得不好的地方还请各位多多指正多多包涵。
五、附录
元器件清单(BOM)
51单片机(STC89C52),土壤湿度传感器,温湿度传感器,SGP30,LCD1602,面包板、导线若干。
接口定义文档
1.土壤湿度传感器AO接IN3
2.OLED屏SCL接P1.4,SDA接P1.3
4.DHT11 Data接P2.3
7.CO2传感器SCL接P1.6,SDA接P1.7
参考文献
51单片机入门教程-2020版 程序全程纯手打 从零开始入门_哔哩哔哩_bilibili
淘宝各大店铺资料。
需要源码请私信。