一、项目介绍
当前文章介绍基于51单片机和SHT30传感器设计的环境温度与湿度检测设备。设备采用IIC模拟时序通信协议,能够实时监测环境的温度和湿度,并将数据通过LCD显示屏显示出来;可以广泛应用于室内环境监测、气象观测、农业温室监测等领域。
在本项目中,使用了51单片机作为主控芯片,SHT30传感器作为温湿度传感器,LCD显示屏作为数据显示模块。通过51单片机的GPIO口模拟IIC通信协议,实现了与SHT30传感器的数据通信。
二、硬件设计
2.1 硬件构成
本次设计所需的硬件主要包括以下部分:
- STC89C52单片机
- SHT30温湿度传感器
- 串口通信模块
- LCD1602显示屏
- 电源模块
- 杜邦线等连接线
2.2 硬件接口及信号
本次设计使用51单片机通过IIC总线与SHT30传感器进行通信,同时使用串口与上位机进行数据传输,并使用液晶显示屏显示当前温湿度值。
具体接口和信号定义如下:
(1) 51单片机与SHT30传感器之间的IIC接口:
端口 | 功能 | 说明 |
---|---|---|
P2.0 | SDA | 数据线 |
P2.1 | SCL | 时钟线 |
P2.2 | RESET | 复位线 |
(2) 51单片机与串口通信模块之间的接口:
端口 | 功能 | 说明 |
---|---|---|
P3.0 | TXD | 发送线 |
P3.1 | RXD | 接收线 |
P3.2 | GND | 地线 |
(3) 51单片机与液晶屏之间的接口:
端口 | 功能 | 说明 |
---|---|---|
P1.0-P1.7 | DB0-DB7 | 数据线 |
P0.0 | RS | 指令/数据选择线 |
P0.1 | RW | 读/写选择线 |
P0.2 | E | 使能线 |
P0.3 | CS | 片选线 |
VCC | 电源正极 | 5V |
GND | 电源地 | 地 |
三、软件设计
3.1 SHT30传感器代码
下面代码读取SHT30传感器的值并通过串口打印。
#include <REG52.h>
#include <stdio.h>#define uchar unsigned char
#define uint unsigned intsbit SDA=P2^0;
sbit SCL=P2^1;void delay(int n)
{int i;while(n--){for(i=0; i<120; i++);}
}void start()
{SDA = 1;_nop_();SCL = 1;_nop_();SDA = 0;_nop_();SCL = 0;_nop_();
}void stop()
{SDA = 0;_nop_();SCL = 1;_nop_();SDA = 1;_nop_();
}void ack()
{SDA = 0;_nop_();SCL = 1;_nop_();SCL = 0;_nop_();SDA = 1;_nop_();
}void nack()
{SDA = 1;_nop_();SCL = 1;_nop_();SCL = 0;_nop_();
}void write_byte(uchar dat)
{uchar i;for(i=0; i<8; i++){SDA = dat & 0x80;_nop_();SCL = 1;_nop_();SCL = 0;_nop_();dat <<= 1;}ack();
}uchar read_byte()
{uchar i, dat;for(i=0; i<8; i++){dat <<= 1;SCL = 1;_nop_();dat |= SDA;SCL = 0;_nop_();}return dat;
}void init_sht30()
{start();write_byte(0x80);if(read_byte() != 0x5A){stop();return;}write_byte(0xBE);if(read_byte() != 0x08 || read_byte() != 0x00){stop();return;}stop();
}float measure_temp(void)
{uchar temp_h, temp_l, crc;float temp;start();write_byte(0x80); // 主机发送写地址write_byte(0x2C); // 选择开始温度测量命令write_byte(0x06);stop();delay(15); // 延时等待温度测量完成start();write_byte(0x81); // 主机发送读地址temp_h=read_byte(); ack();temp_l=read_byte(); ack();crc=read_byte();stop();temp = ((temp_h<<8)+temp_l)*175.0/0xffff - 45.0; // 温度值转换公式return temp;
}float measure_humi(void)
{uchar humi_h, humi_l, crc;float humi;start();write_byte(0x80); // 主机发送写地址write_byte(0x2C); // 选择开始湿度测量命令write_byte(0x06);stop();delay(15); // 延时等待湿度测量完成start();write_byte(0x81); // 主机发送读地址humi_h=read_byte(); ack();humi_l=read_byte(); ack();crc=read_byte();stop();humi = ((humi_h<<8)+humi_l)*100.0/0xffff; // 湿度值转换公式return humi;
}void main()
{ float temp, humi;init_sht30(); // SHT30 初始化TMOD=0x20; // 定时器0工作方式2,8位定时器,用于波特率设置TH1=0xfd; // 波特率9600TL1=0xfd;TR1=1; // 启动定时器0SCON=0x50; // 设置串口工作方式1,允许接收,允许接收中断ES=1; // 允许串口中断while(1){temp = measure_temp();humi = measure_humi();printf("Temperature: %.1fC, Humidity: %.1f%\n", temp, humi);delay(500); // 间隔时间500ms}
}void ser() interrupt 4 using 2
{if(RI) // 接收到数据{RI=0; // 清除标志位}if(TI) // 发送完毕{TI=0; // 清除标志位}
}
在上面的代码中,定义了两个函数 measure_temp
和 measure_humi
,分别用于测量温度和湿度值,并返回结果。在主函数中,利用这两个函数得到当前的温湿度值,然后通过串口打印出来。
3.2 LCD1602显示屏代码
下面代码是LCD1602驱动代码,完成数字字符显示。
#include <REG52.h>#define LCD1602_DB P0
sbit RS = P2^5;
sbit RW = P2^6;
sbit E = P2^7;void delay(int n)
{int i;while(n--){for(i=0; i<120; i++);}
}void main()
{ //LCD 初始化delay(1000);LCD1602_DB = 0x38;E = 1;delay(5);E = 0;delay(500);LCD1602_DB = 0x08;E = 1;delay(5);E = 0;delay(500);LCD1602_DB = 0x01;E = 1;delay(5);E = 0;delay(500);LCD1602_DB = 0x06;E = 1;delay(5);E = 0;delay(500);LCD1602_DB = 0x0C;E = 1;delay(5);E = 0;while(1){//向LCD中写入数字12345RS = 0; //选择指令寄存器LCD1602_DB = 0x80; //设置地址为第一行的第一个字符位置(0x80 + 0x00)E = 1;delay(5);E = 0;RS = 1; //选择数据寄存器LCD1602_DB = 0x31; //写入数字1E = 1;delay(5);E = 0;LCD1602_DB = 0x32; //写入数字2E = 1;delay(5);E = 0;LCD1602_DB = 0x33; //写入数字3E = 1;delay(5);E = 0;LCD1602_DB = 0x34; //写入数字4E = 1;delay(5);E = 0;LCD1602_DB = 0x35; //写入数字5E = 1;delay(5);E = 0;delay(500); //间隔时间为500ms}
}
在上面的代码中,定义了函数 delay
用于延时等待,并且实现了LCD1602的初始化和写入操作。在主函数中,执行LCD1602的初始化操作,然后循环不断向LCD中写入数字12345,并且间隔时间为500ms。
3.3 完整代码
#include<reg52.h>
#include<intrins.h>#define uchar unsigned char
#define uint unsigned intsbit SDA = P2^0; //定义SDA引脚
sbit SCL = P2^1; //定义SCL引脚
sbit CS = P0^3; //定义液晶屏片选引脚
sbit RW = P0^1; //定义液晶屏读/写引脚
sbit RS = P0^0; //定义液晶屏指令/数据引脚
sbit E = P0^2; //定义液晶屏使能引脚void delay(int n) //延时函数,n为延时时间
{int i;while(n--){for(i=0; i<120; i++);}
}void start() //开始信号
{SDA = 1; //数据线高电平_nop_();SCL = 1; //时钟线高电平_nop_();SDA = 0; //数据线低电平_nop_();SCL = 0; //时钟线低电平_nop_();
}void stop() //结束信号
{SDA = 0; //数据线低电平_nop_();SCL = 1; //时钟线高电平_nop_();SDA = 1; //数据线高电平_nop_();
}void ack() //应答信号
{SDA = 0; //数据线低电平_nop_();SCL = 1; //时钟线高电平_nop_();SCL = 0; //时钟线低电平_nop_();SDA = 1; //数据线高电平_nop_();
}void nack() //非应答信号
{SDA = 1; //数据线高电平_nop_();SCL = 1; //时钟线高电平_nop_();SCL = 0; //时钟线低电平_nop_();
}void write_byte(uchar dat) //写一个字节
{uchar i;for(i=0; i<8; i++){SDA = dat & 0x80;_nop_();SCL = 1;_nop_();SCL = 0;_nop_();dat <<= 1;}ack();
}uchar read_byte() //读一个字节
{uchar i, dat;for(i=0; i<8; i++){dat <<= 1;SCL = 1;_nop_();dat |= SDA;SCL = 0;_nop_();}return dat;
}void init_sht30() //SHT30初始化
{start();write_byte(0x80);if(read_byte() != 0x5A){stop();return;}write_byte(0xBE);if(read_byte() != 0x08 || read_byte() != 0x00){stop();return;}stop();
}void measure() //测量温湿度值
{float humi, temp;uint i;start();write_byte(0x80);read_byte();read_byte();read_byte();write_byte(0x2C);write_byte(0x06);for(i=0; i<40000; i++); //等待测量结果start();write_byte(0x80);read_byte();read_byte();read_byte();humi = read_byte() * 256;humi += read_byte();temp = read_byte() * 256;temp += read_byte();stop();temp = -45 + (175*temp)/65535; //转化温度humi = 100 * humi / 65535; //转化湿度//将温湿度值通过串口发送printf("Temperature: %.1fC\n", temp);printf("Humidity: %.1f%%RH\n", humi);
}void init_lcd() //液晶屏初始化
{RW = 0;RS = 0;E = 0;delay(15);write_byte(0x30);delay(15);write_byte(0x30);delay(5);write_byte(0x30);delay(5);write_byte(0x38);write_byte(0x08);write_byte(0x01);write_byte(0x06);write_byte(0x0c);
}void display(float temp, float humi) //显示温湿度值
{uchar i;uchar temp_str[5];uchar humi_str[5];//转化为字符串sprintf(temp_str, "%.1f", temp);sprintf(humi_str, "%.1f", humi);//显示温度RS = 0;E = 1;P1 = 0x80; //第一行第一个字符E = 0;RS = 1;for(i=0; i<5; i++){E = 1;P1 = temp_str[i];E = 0;}//显示湿度RS = 0;E = 1;P1 = 0xc0; //第二行第一个字符E = 0;RS = 1;for(i=0; i<5; i++){E = 1;P1 = humi_str[i];E = 0;}
}void main()
{init_sht30(); //SHT30初始化init_lcd(); //液晶屏初始化while(1){measure(); //测量温湿度值并通过串口发送delay(1000);display(temp, humi); //显示温湿度值}
}