项目三 基于DHT11和蓝牙的温湿度检测系统
DHT11温湿度传感器
工作流程
- 通过51单片机给DHT11发送指令。DHT11开始测温湿度。DHT11一次完整的数据传输为40bit,数据的高位先出。
- 数据格式:8bit湿度整数数据+8bit湿度小数数据 +8bi温度整数数据+8bit温度小数数据+8bit校验位
发送时序检测DHT11是否存在

检测的时序:
- 首先总线拉高
- 总线拉低
- 等待至少18ms
- 总线拉高
- 等待20或者40us后,如果有设备,设备会主动将总线拉低,无设备则继续保持高电平
- 注意:DHT11的供电电压为 3-5.5V。传感器上电后,要等待 1s 以越过不稳定状态在此 期间无需发送任何指令。
代码实现:
//检测dht11是否存在
void check_dht()
{
//- 首先总线拉高
dht = 1;
//- 总线拉低
dht = 0;
//- 等待至少18ms
//此处为了保险 延时30ms
Delay30ms();
//- 总线拉高
dht = 1;
//- 等待20或者40us后,如果有设备,设备会主动将总线拉低80us,无设备则继续保持高电平
//此处等待60us以后在检测dht11总线上的电平
Delay60us();
//如果有dht11设备,此时总线会被dht11拉低,LED灯亮
if(dht == 0)
{
led = 0;
}
}
void main()
{
led = 1;
//dht11 上电后为了稳定要先等待1s
Delay1000ms();
check_dht();
while(1);
}
读取DHT11数据的时序分析:
时序分析:
- a点高电平
- b点低电平
- 至少延时15ms
- 拉高总线
- c:用while(dht)卡住,如果总线被拉低(有设备存在),则跳出循环。
- d:用while(!dht)卡住,如果总线被拉高会跳出循环
- e:用while(dht)卡住,总线被拉低则跳出循环
- f:用while(!dht)卡住总共总线被拉高进入g点,开始接收数据。
- g:g点延时50us后读取总线的状态。
注意:当单片机给DHT11开始信号的时候,DHT11只会读取一次数据。即40bit数据,所以每一次读取数据都要给DHT11开始信号。
代码实现:
再代码中要实现的是整形数据和字符数据的转换(例如:1和字符型 ‘1’)的转换,整形的1,2,3,...加上十六进制的30既可以得到字符型的1,2,3,....
#include "reg52.h"
#include "intrins.h"
sbit led = P3^7;
//dht11数据总线
sbit dht = P3^4;
char datas[5];
void Delay30ms() //@11.0592MHz
{
unsigned char i, j;
i = 54;
j = 199;
do
{
while (--j);
} while (--i);
}
void Delay40us() //@11.0592MHz
{
unsigned char i;
_nop_();
i = 15;
while (--i);
}
void Delay1000ms() //@11.0592MHz
{
unsigned char i, j, k;
_nop_();
i = 8;
j = 1;
k = 243;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
//dht 初始化
void dht_init()
{
//- 首先总线拉高
dht = 1;
//- 总线拉低
dht = 0;
//- 等待至少18ms
//此处为了保险 延时30ms
Delay30ms();
//- 总线拉高
dht = 1;
//- 等待20或者40us后,如果有设备,设备会主动将总线拉低80us,无设备则继续保持高电平
//卡住 等待设备将其拉低
while(dht);
//卡住 等待设备将其拉高
while(!dht);
//卡住 等待设备将其拉低 此时进入f点
while(dht);
}
//读取dht11的数据 读5个数据 1个数据8位 要读40次
void read_data_dht()
{
int i;
int j;
char tmp;
char data_mag;
dht_init();
for(i=0 ; i<5 ; i++)
{
for(j=0 ; j<8 ; j++)
{
//卡住g点
while(!dht);
//等60us后读取总线电平
Delay40us();
if(dht == 1)
{
tmp = 1;
while(dht);
}
else
{
tmp = 0;
}
//将data_mag的数据左移动一位
data_mag = data_mag << 1;
data_mag |= tmp;
}
*(datas+i) = data_mag;
}
}
void UartInit(void) //9600bps@11.0592MHz
{
PCON &= 0x7F; //波特率不倍速
SCON = 0x50; //8位数据,可变波特率
TMOD &= 0x0F; //清除定时器1模式位
TMOD |= 0x20; //设定定时器1为8位自动重装方式
TL1 = 0xFD; //设定定时初值
TH1 = 0xFD; //设定定时器重装值
ET1 = 0; //禁止定时器1中断
TR1 = 1; //启动定时器1
}
void seadByte(char byte)
{
//再串口接收的时候只可以接收字符型的数据 所以要将datas中的数据转为字符型
char data_mag;
//定义一个静态局部变量
static char cdata = 0;
data_mag = byte/10;
//将数字转为字符类型
data_mag += 0x30;
SBUF = data_mag;
while(!TI);
TI = 0;
data_mag = byte%10;
data_mag += 0x30;
SBUF = data_mag;
while(!TI);
TI = 0;
cdata++;
if(cdata == 1)
{
SBUF = '.';
while(!TI);
TI = 0;
}
else
{
SBUF = ' ';
while(!TI);
TI = 0;
cdata = 0;
}
}
void sendStr()
{
int i;
int len;
len = sizeof(datas)/sizeof(datas[0]);
for(i=0 ; i<len-1 ; i++)
{
seadByte(*(datas+i));
}
}
void main()
{
led = 1;
UartInit();
Delay1000ms();
Delay1000ms();
while(1)
{
Delay1000ms();
read_data_dht();
//将数据传给串口
sendStr();
}
}
项目三 基于蓝牙的温度检测系统
功能介绍:
- 利用DHT11检测温湿度
- 利用蓝牙模块将温湿度发送给手机
- LCD显示温湿度
- 利用继电器控制电风扇,当温度大于25度,开启风扇
代码
#include "reg52.h"
#include "intrins.h"
/*
p1^0 RS
p1^1 R/W
p1^2 E
P0 DATA
P1^3 Switch
*/
//dht11数据总线
sbit dht = P3^4;
sbit RS = P1^0;
sbit RW = P1^1;
sbit E = P1^2;
sbit Switch = P1^3;
char datas[5];
void Delay30ms() //@11.0592MHz
{
unsigned char i, j;
i = 54;
j = 199;
do
{
while (--j);
} while (--i);
}
void Delay40us() //@11.0592MHz
{
unsigned char i;
_nop_();
i = 15;
while (--i);
}
void Delay1000ms() //@11.0592MHz
{
unsigned char i, j, k;
_nop_();
i = 8;
j = 1;
k = 243;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
void Delay5ms() //@11.0592MHz
{
unsigned char i, j;
i = 9;
j = 244;
do
{
while (--j);
} while (--i);
}
void Delay15ms() //@11.0592MHz
{
unsigned char i, j;
i = 27;
j = 226;
do
{
while (--j);
} while (--i);
}
//dht 初始化
void dht_init()
{
//- 首先总线拉高
dht = 1;
//- 总线拉低
dht = 0;
//- 等待至少18ms
//此处为了保险 延时30ms
Delay30ms();
//- 总线拉高
dht = 1;
//- 等待20或者40us后,如果有设备,设备会主动将总线拉低80us,无设备则继续保持高电平
//卡住 等待设备将其拉低
while(dht);
//卡住 等待设备将其拉高
while(!dht);
//卡住 等待设备将其拉低 此时进入f点
while(dht);
}
//读取dht11的数据 读5个数据 1个数据8位 要读40次
void read_data_dht()
{
int i;
int j;
char tmp;
char data_mag;
dht_init();
for(i=0 ; i<5 ; i++)
{
for(j=0 ; j<8 ; j++)
{
//卡住g点
while(!dht);
//等60us后读取总线电平
Delay40us();
if(dht == 1)
{
tmp = 1;
while(dht);
}
else
{
tmp = 0;
}
//将data_mag的数据左移动一位
data_mag = data_mag << 1;
data_mag |= tmp;
}
*(datas+i) = data_mag;
}
}
void UartInit(void) //9600bps@11.0592MHz
{
PCON &= 0x7F; //波特率不倍速
SCON = 0x50; //8位数据,可变波特率
TMOD &= 0x0F; //清除定时器1模式位
TMOD |= 0x20; //设定定时器1为8位自动重装方式
TL1 = 0xFD; //设定定时初值
TH1 = 0xFD; //设定定时器重装值
ET1 = 0; //禁止定时器1中断
TR1 = 1; //启动定时器1
}
void seadByte(char byte)
{
//再串口接收的时候只可以接收字符型的数据 所以要将datas中的数据转为字符型
char data_mag;
//定义一个静态局部变量
static char cdata = 0;
static int tOrh = 0;
tOrh++;
cdata++;
if(tOrh == 1)
{
SBUF = 'H';
while(!TI);
TI = 0;
SBUF = ':';
while(!TI);
TI = 0;
}
if(tOrh == 3)
{
SBUF = 'T';
while(!TI);
TI = 0;
SBUF = ':';
while(!TI);
TI = 0;
tOrh = -1;
}
data_mag = byte/10;
//将数字转为字符类型
data_mag += 0x30;
SBUF = data_mag;
while(!TI);
TI = 0;
data_mag = byte%10;
data_mag += 0x30;
SBUF = data_mag;
while(!TI);
TI = 0;
if(cdata == 1)
{
SBUF = '.';
while(!TI);
TI = 0;
}
else
{
//换行
SBUF = '\r';
while(!TI);
TI = 0;
SBUF = '\n';
while(!TI);
TI = 0;
cdata = 0;
}
}
void sendStr()
{
int i;
int len;
len = sizeof(datas)/sizeof(datas[0]);
for(i=0 ; i<len-1 ; i++)
{
seadByte(*(datas+i));
}
}
void LCD_busy()
{
char busy = 0x80;
P0 = 0xff;
while(busy & 0x80)
{
RS = 0;
RW = 1;
E = 0;
_nop_();
E = 1;
_nop_();
busy = P0;
_nop_();
E = 0;
_nop_();
}
}
void LCD_write_dir(char data_mag)
{
LCD_busy();
RS = 0;
RW = 0;
E = 0;
P0 = data_mag;
_nop_();
E = 1;
_nop_();
_nop_();
E = 0;
_nop_();
}
void LCD_write_data(char data_mag)
{
LCD_busy();
RS = 1;
RW = 0;
E = 0;
P0 = data_mag;
_nop_();
E = 1;
_nop_();
_nop_();
E = 0;
_nop_();
}
void LCD_init()
{
//(1)延时 15ms
Delay15ms();
//(2)写指令 38H(不检测忙信号)
LCD_write_dir(0x38);
//(3)延时 5ms
Delay5ms();
//(4)以后每次写指令,读/写数据操作均需要检测忙信号
//(5)写指令 38H:显示模式设置
LCD_write_dir(0x38);
//(6)写指令 08H:显示关闭
LCD_write_dir(0x08);
//(7)写指令 01H:显示清屏
LCD_write_dir(0x01);
//(8)写指令 06H:显示光标移动设置
LCD_write_dir(0x06);
//(9)写指令 0CH:显示开及光标设置
LCD_write_dir(0x0c);
}
void sendLCD()
{
//要显示的位置
LCD_write_dir(0x80+0x03);
//要显示的内容
LCD_write_data('H');
LCD_write_data(':');
LCD_write_data(*datas/10 + 0x30);
LCD_write_data(*datas%10 + 0x30);
LCD_write_data('.');
LCD_write_data(*(datas+1)/10 + 0x30);
LCD_write_data(*(datas+1)%10 + 0x30);
//要显示的位置
LCD_write_dir(0x80+0x03+0x40);
LCD_write_data('T');
LCD_write_data(':');
LCD_write_data(*(datas+2)/10 + 0x30);
LCD_write_data(*(datas+2)%10 + 0x30);
LCD_write_data('.');
LCD_write_data(*(datas+3)/10 + 0x30);
LCD_write_data(*(datas+3)%10 + 0x30);
}
void main()
{
Switch = 1;
UartInit();
LCD_init();
Delay1000ms();
while(1)
{
Delay1000ms();
read_data_dht();
//将数据传给串口
sendStr();
//将数据传给LCD
sendLCD();
if(*(datas+2) > 25)
{
Switch = 0;
}
else
{
Switch = 1;
}
}
}

浙公网安备 33010602011771号