基于 51 单片机 +SHT30 设计的环境温度与湿度检测设备 (IIC 模拟时序)
- 2024-09-09 重庆
本文字数:4688 字
阅读完需:约 15 分钟
一、项目介绍
当前文章介绍基于 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 接口:
(2) 51 单片机与串口通信模块之间的接口:
(3) 51 单片机与液晶屏之间的接口:
三、软件设计
3.1 SHT30 传感器代码
下面代码读取 SHT30 传感器的值并通过串口打印。
#include <REG52.h>
#include <stdio.h>
#define uchar unsigned char
#define uint unsigned int
sbit 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; // 波特率9600
TL1=0xfd;
TR1=1; // 启动定时器0
SCON=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中写入数字12345
RS = 0; //选择指令寄存器
LCD1602_DB = 0x80; //设置地址为第一行的第一个字符位置(0x80 + 0x00)
E = 1;
delay(5);
E = 0;
RS = 1; //选择数据寄存器
LCD1602_DB = 0x31; //写入数字1
E = 1;
delay(5);
E = 0;
LCD1602_DB = 0x32; //写入数字2
E = 1;
delay(5);
E = 0;
LCD1602_DB = 0x33; //写入数字3
E = 1;
delay(5);
E = 0;
LCD1602_DB = 0x34; //写入数字4
E = 1;
delay(5);
E = 0;
LCD1602_DB = 0x35; //写入数字5
E = 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 int
sbit 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); //显示温湿度值
}
}
版权声明: 本文为 InfoQ 作者【DS小龙哥】的原创文章。
原文链接:【http://xie.infoq.cn/article/07307ac215f2f9cf87bb2d12a】。文章转载请联系作者。
DS小龙哥
微信公众号:DS小龙哥嵌入式技术资讯 2022-01-06 加入
所有项目文章对应的工程源码,都可以在我的微信公众号:《DS小龙哥嵌入式技术资讯》 里下载。
评论