DHT11模块驱动引脚接的是ESP8266模块的GPIO5引脚。
含有校准数字信号, 可测量环境温度, 相对湿度。 具有极高的可靠性与卓越的长期稳定性。 模块带有电源指示灯。 主要特点如下:
DHT11 数字温湿度传感器是一款含有已校准数字信号输出的温湿度复合传感器。 它应用专用的数字模块采集技术和温湿度传感技术, 确保产品具有枀高的可靠性与卓越的长期稳定性。 传感器包括一个电容式感湿元件和一个 NTC 测温元件, 并与一个高性能 8 位单片机相连接。
根据 DHT11 的特点, DHT11 模块可以应用在暖通空调、 除湿器、 农业、 冷链仓储、 测试及检测设备、 消费品、 汽车、 自动控制、 数据记录器、 气象站、 家电、 湿度调节器、 医疗、 其他相关湿度检测控制 等方面。
DHT11 传感器采用简化的单总线通信。 单总线即只有一根数据线, 系统中的数据交换、 控制均由单总线完成。 设备(主机或从机) 通过一个漏极开路或三态端口(单片机 IO 口) 连至该数据线, 以允许设备在不发送数据时能够释放总线(由上拉电阻控制, 把总线上拉成高电平) , 而让其它设备使用总线;单总线通常要求外接一个约 4.7kΩ 的上拉电阻, 这样, 当总线闲置时, 其状态为高电平。由于它们是主从结极, 只有主机呼叫从机时, 从机才能应答, 因此主机访问器件都必须严格遵循单总线序列,如果出现序列混乱, 器件将不响应主机。
DHT11 采用单总线通讯协议,与单片机(主机) 的通讯的过程:主机发送起始信号—>DHT11 检测到并发送响应信号—>DHT11 发送 40 位数据(高位先出) —>DHT11 发送结束信号。
接收到的 40 位数据为:湿度高 8 位,湿度低 8 位,温度高 8 位,温度低 8 位,8 位校验位。湿度的高 8位是测量湿度的整数, 湿度的低 8 位是测量湿度的小数(湿度的小数部分为 0);温度的高 8 位是测量温度的整数, 温度的低 8 位是测量温度的小数;8 位校验和 = 湿度高 8 位 + 湿度低 8 位 + 温度高 8 位 + 温度低 8位, 可参考下图:
- 数据通信协议
用户主机(MCU)发送一次开始信号后, DHT11 从低功耗模式转换到高速模式,待主机开始信号结束后, DHT11 发送响应信号,送出 40bit 的数据,并触发一次信采集。信号发送如图所示。
注:主机从 DHT11 读取的温湿度数据总是前一次的测量值,如两次测间隔时间很长,请连续读两次以第二次获得的值为实时温湿度值。
- 外设读取步骤
步骤一:
DHT11 上电后(DHT11 上电后要等待 1S 以越过不稳定状态在此期间不能发送任何指令),测试环境温湿度数据,并记录数据,同时 DHT11 的 DATA 数据线由上拉电阻拉高一直保持高电平;此时 DHT11 的DATA 引脚处于输入状态,时刻检测外部信号。
步骤二:
微处理器的 I/O 设置为输出同时输出低电平,且低电平保持时间不能小于 18ms(最大不得超过 30ms),然后微处理器的 I/O 设置为输入状态,由于上拉电阻,微处理器的 I/O 即 DHT11 的 DATA 数据线也随之变高,等待 DHT11 作出回答信号,发送信号如图所示:
步骤三:
DHT11 的 DATA 引脚检测到外部信号有低电平时,等待外部信号低电平结束,延迟后 DHT11 的 DATA引脚处于输出状态,输出 83 微秒的低电平作为应答信号,紧接着输出 87 微秒的高电平通知外设准备接收数据,微处理器的 I/O 此时处于输入状态,检测到 I/O 有低电平(DHT11 回应信号)后,等待 87 微秒的高电平后的数据接收,发送信号如图所示:
步骤四:
由 DHT11 的 DATA 引脚输出 40 位数据,微处理器根据 I/O 电平的变化接收 40 位数据,位数据“0”的格式为: 54 微秒的低电平和 23-27 微秒的高电平,位数据“1”的格式为: 54 微秒的低电平加 68-74微秒的高电平。位数据“0”、“1”格式信号如图所示:
结束信号:
DHT11 的 DATA 引脚输出 40 位数据后,继续输出低电平 54 微秒后转为输入状态,由于上拉电阻随之变为高电平。但 DHT11 内部重测环境温湿度数据,并记录数据,等待外部信号的到来。
/*
* dht11.c
*
* Created on: 2019年1月15日
* Author: Marshal
*/
#include "driver/dht11.h"
#include "driver/delay.h"
// 全局变量
//==================================================================================
// DHT11_Data_Array[0] == 湿度_整数_部分
// DHT11_Data_Array[1] == 湿度_小数_部分
// DHT11_Data_Array[2] == 温度_整数_部分
// DHT11_Data_Array[3] == 温度_小数_部分
// DHT11_Data_Array[4] == 校验字节
// DHT11_Data_Array[5] == 【1:温度>=0℃】【0:温度<0℃】
//-----------------------------------------------------
u8 DHT11_Data_Array[6] = { 0 }; // DHT11数据数组
u8 DHT11_Data_Char[2][10] = { 0 }; // DHT11数据字符串【行:湿/温度】【列:数据字符串】
// DHT11_Data_Char[0] == 【湿度字符串】
// DHT11_Data_Char[1] == 【温度字符串】
//==================================================================================
/*
*函数名称:void Dht11OutputConfig(bool val)
*函数功能:设置DHT11为输出模块
*函数形参:bool val 高低电平
*返 回 值:无
*硬件连接:GPIO5
* */
void Dht11OutputConfig(bool val)
{
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO5_U, FUNC_GPIO5); // GPIO5设为IO口
GPIO_OUTPUT_SET(GPIO_ID_PIN(5), val);//主机发送高电平
}
/*
*函数名称:void Dht11InputConfig(void)
*函数功能:设置DHT11为输入模块
*函数形参:无
*返 回 值:无
*硬件连接:GPIO5
* */
void Dht11InputConfig(void)
{
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO5_U, FUNC_GPIO5); // GPIO5设为IO口
GPIO_DIS_OUTPUT(GPIO_ID_PIN(5));
}
/*
*函数名称:u8 Dht11Star(void)
*函数功能:判断DHT11模块释放存在
*函数形参:无
*返 回 值:
* 0:存在 非0:不存在
*硬件连接:GPIO5
* */
u8 Dht11Star(void)
{
u8 UsFlag = 0;
Dht11OutputConfig(1);//
DelayMs(1);
//起始信号 -- 主机把数据总线拉低 T(18ms < T < 30ms), 通知传感器准备数据
Dht11OutputConfig(0);
DelayMs(20);
//释放总线
Dht11OutputConfig(1);
DelayUs(5);
//接受响应信号
//设置为输入模式,才能接收信号
Dht11InputConfig();
//等待接收响应信号70US以上
while (GPIO_INPUT_GET(GPIO_ID_PIN(5)) == 1 && UsFlag < 70)
{
DelayUs(1);
UsFlag++;
}
if (UsFlag >= 70) return 1;//响应超时
UsFlag = 0;
//假如低电平时间太长,认为模块有问题
while (GPIO_INPUT_GET(GPIO_ID_PIN(5)) == 0 && UsFlag < 200)
{
DelayUs(1);
UsFlag++;
}
if (UsFlag >= 200)
return 1;//响应超时
else
return 0;
}
/*
*函数名称:u8 DHT11ReadBit(void)
*函数功能:DHT11模块独一位数据
*函数形参:无
*返 回 值:
* 0/1
*硬件连接:GPIO5
* */
u8 DHT11ReadBit(void)
{
u8 UsFlag = 0; // 延时计时
// 等待响应信息的低电平【最迟等150us】
while (GPIO_INPUT_GET(GPIO_ID_PIN(5)) == 1 && UsFlag<150)
{
DelayUs(1); // 1us计时
UsFlag++;
}
UsFlag = 0; // 低电平计时开始
// 数据位的低电平时长计时【最多200us】
while (GPIO_INPUT_GET(GPIO_ID_PIN(5)) == 0 && UsFlag<120)
{
DelayUs(1);
UsFlag++; // 低电平时长
}
// 数据位的低电平结束后,是数据位的高电平
// 数据"0"的高电平时长 == [23~27us]
// 数据"1"的高电平时长 == [68~74us]
DelayUs(45); // 跳过数据"0"的高电平部分
// 延时45us后,检测信号线电平 此时电平为高就输出数据1 电平为低就输出数据0
return GPIO_INPUT_GET(GPIO_ID_PIN(5));
}
/*
*函数名称:u8 DHT11ReadByte(void)
*函数功能:DHT11模块读一个字节数据
*函数形参:无
*返 回 值:
* 返回一个字节数据
*硬件连接:GPIO5
* */
u8 DHT11ReadByte(void)
{
u8 Bit = 0; // 位计数
u8 T_DHT11_Byte_Data = 0; // DHT11字节数据
for (Bit = 0; Bit<8; Bit++) // 读取DHT11一个字节
{
T_DHT11_Byte_Data <<= 1;
T_DHT11_Byte_Data |= DHT11ReadBit(); // 一位一位的读取
}
return T_DHT11_Byte_Data; // 返回读取字节
}
/*
*函数名称:u8 DHT11ReadDataComplete(void)
*函数功能:DHT11模块读取所有数据
*函数形参:无
*返 回 值:
* 0:数据读取成功
* 非0:数据读取失败
*硬件连接:GPIO5
* */
u8 DHT11ReadDataComplete(void)
{
u8 UsFlag = 0; // 延时计时
// 启动DHT11传输_成功
//------------------------------------------------------------------------
if (Dht11Star() == 0) // DHT11:输出起始信号->接收响应信号
{
DHT11_Data_Array[0] = DHT11ReadByte(); // 湿度_整数_部分
DHT11_Data_Array[1] = DHT11ReadByte(); // 湿度_小数_部分
DHT11_Data_Array[2] = DHT11ReadByte(); // 温度_整数_部分
DHT11_Data_Array[3] = DHT11ReadByte(); // 温度_小数_部分
DHT11_Data_Array[4] = DHT11ReadByte(); // 校验字节
// 如果此时是最后一位数据的高电平,则等待它结束
while (GPIO_INPUT_GET(GPIO_ID_PIN(5)) == 1 && UsFlag < 100)
{
DelayUs(1); // 1us计时
UsFlag++;
}
UsFlag = 0; // 低电平计时开始
// 结束信号的低电平时长计时
while (GPIO_INPUT_GET(GPIO_ID_PIN(5)) == 0 && UsFlag < 100)
{
DelayUs(1); // 1us计时
UsFlag++;
}
if (UsFlag >= 100)
return 1; // 返回1,表示:结束信号的低电平时长超时
// 数据校验
if (DHT11_Data_Array[4] ==
DHT11_Data_Array[0] + DHT11_Data_Array[1] +
DHT11_Data_Array[2] + DHT11_Data_Array[3])
{
// 读取DHT11数据结束,ESP8266接管DHT11信号线
//-----------------------------------------------------------
//os_delay_us(10);
//DHT11_Signal_Output(1); // DHT11信号线输出高(ESP8266驱动)
// 判断温度是否为 0℃以上
//----------------------------------------------
if ((DHT11_Data_Array[3] & 0x80) == 0)
{
DHT11_Data_Array[5] = 1; // >=0℃
}
else
{
DHT11_Data_Array[5] = 0; // <0℃
DHT11_Data_Array[3] &= 0x7F; // 更正温度小数部分
}
return 0; // 返回0,表示:温湿度读取成功
}
else return 3; // 返回3,表示:校验错误
}
else return 2; // 返回2,表示:启动DHT11传输,失败
}
/*
*函数名称:void DHT11NUMChar(void)
*函数功能:将DHT11读取的数据全部转为字符串
*函数形参:无
*返 回 值:无
* */
void DHT11NUMChar(void)
{
u8 C_char = 0; // 字符计数
// 湿度数据字符串
if(DHT11_Data_Array[0] / 100) // 湿度整数的百位
DHT11_Data_Char[0][C_char++] = DHT11_Data_Array[0] / 100 + 48;
if((DHT11_Data_Array[0] % 100) / 10) // 湿度整数的十位
DHT11_Data_Char[0][C_char++] = (DHT11_Data_Array[0] % 100) / 10 + 48;
// 湿度整数的个位
DHT11_Data_Char[0][C_char++] = DHT11_Data_Array[0] % 10 + 48;
DHT11_Data_Char[0][C_char++] = '.'; // 小数点
// 湿度整数的小数
DHT11_Data_Char[0][C_char++] = DHT11_Data_Array[1] % 10 + 48;
DHT11_Data_Char[0][C_char++] = ' '; // ' '
DHT11_Data_Char[0][C_char++] = '%'; // '%'
DHT11_Data_Char[0][C_char++] = 'R'; // 'R'
DHT11_Data_Char[0][C_char++] = 'H'; // 'H'
DHT11_Data_Char[0][C_char] = 0; // 添0
C_char = 0; // 重置
// 温度数据字符串
if(DHT11_Data_Array[5] == 0) // 温度 < 0℃
DHT11_Data_Char[1][C_char++] = '-';
if(DHT11_Data_Array[2] / 100) // 湿度整数的百位
DHT11_Data_Char[1][C_char++] = DHT11_Data_Array[2] / 100 + 48;
if((DHT11_Data_Array[2] % 100) / 10) // 湿度整数的十位
DHT11_Data_Char[1][C_char++] = (DHT11_Data_Array[2] % 100) / 10 + 48;
// 湿度整数的个位
DHT11_Data_Char[1][C_char++] = DHT11_Data_Array[2] % 10 + 48;
DHT11_Data_Char[1][C_char++] = '.'; // 小数点
// 湿度整数的小数
DHT11_Data_Char[1][C_char++] = DHT11_Data_Array[3] % 10 + 48;
DHT11_Data_Char[1][C_char++] = ' '; // ' '
DHT11_Data_Char[1][C_char++] = 'C'; // 'C'
DHT11_Data_Char[1][C_char] = 0; // 添0
}
/*
* dht11.h
*
* Created on: 2019年1月15日
* Author: Marshal
*/
#ifndef APP_INCLUDE_DRIVER_DHT11_H_
#define APP_INCLUDE_DRIVER_DHT11_H_
#include "ets_sys.h"
#include "osapi.h"
#include "user_interface.h"
#include "driver/uart.h"
#include "gpio.h"
#include "eagle_soc.h"
#include "driver/delay.h"
extern u8 DHT11_Data_Array[6]; // DHT11数据数组
extern u8 DHT11_Data_Char[2][10]; // DHT11数据字符串【行:湿/温度】【列:数据字符串】
void Dht11OutputConfig(bool val);
void Dht11InputConfig(void);
u8 Dht11Star(void);
u8 DHT11ReadBit(void);
u8 DHT11ReadByte(void);
u8 DHT11ReadDataComplete(void);
void DHT11NUMChar(void);
#endif /* APP_INCLUDE_DRIVER_DHT11_H_ */
/*
* timer.c
*
* Created on: 2018年8月14日
* Author: 小良哥
*/
#include "driver/delay.h"
#include "driver/led.h"
#include "driver/key.h"
#include "driver/uart.h"
#include "driver/timer.h"
#include "driver/dht11.h"
/*
* 函数名称:void TimerInitConfig(uint32_t DelayMs,bool repeat_flag)
* 函数功能:定时器初始化函数
* 函数形参:
* uint32_t DelayMs 延时时间
* bool repeat_flag 是否重复
* 1:重复 0:不重复
* 返回值:
* */
os_timer_t os_timer_500ms;//定义一个定时器名称
void TimerInitConfig(uint32_t DelayMs, bool repeat_flag)
{
os_timer_disarm(&os_timer_500ms);//关闭定时器
os_timer_setfn(&os_timer_500ms, (os_timer_func_t *)TimerBackFunction, NULL);//设置定时器回调函数
os_timer_arm(&os_timer_500ms, DelayMs, repeat_flag);
}
/*
* 函数名称:void TimerBackFunction(void)
* 函数功能:定时器回调函数
* 函数形参:无
* 返回值: 无
* */
void TimerBackFunction(void)
{
if(DHT11ReadDataComplete() == 0) // 读取DHT11温湿度值
{
//-------------------------------------------------
// DHT11_Data_Array[0] == 湿度_整数_部分
// DHT11_Data_Array[1] == 湿度_小数_部分
// DHT11_Data_Array[2] == 温度_整数_部分
// DHT11_Data_Array[3] == 温度_小数_部分
// DHT11_Data_Array[4] == 校验字节
// DHT11_Data_Array[5] == 【1:温度>=0】【0:温度<0】
//-------------------------------------------------
// 温度超过30℃,LED亮
//----------------------------------------------------
if(DHT11_Data_Array[5] == 1 && DHT11_Data_Array[2] >= 30)
GPIO_OUTPUT_SET(GPIO_ID_PIN(4), 0); // LED亮
else
GPIO_OUTPUT_SET(GPIO_ID_PIN(4), 1); // LED灭
// 串口输出温湿度
//---------------------------------------------------------------------------------
if(DHT11_Data_Array[5] == 1) // 温度 >= 0℃
{
os_printf("\r\n湿度 == %d.%d %RH\r\n", DHT11_Data_Array[0], DHT11_Data_Array[1]);
os_printf("\r\n温度 == %d.%d ℃\r\n", DHT11_Data_Array[2], DHT11_Data_Array[3]);
}
else // if(DHT11_Data_Array[5] == 0) // 温度 < 0℃
{
os_printf("\r\n湿度 == %d.%d %RH\r\n", DHT11_Data_Array[0], DHT11_Data_Array[1]);
os_printf("\r\n温度 == -%d.%d ℃\r\n", DHT11_Data_Array[2], DHT11_Data_Array[3]);
}
// OLED显示温湿度
// DHT11NUMChar(); // DHT11数据值转成字符串
}
}
void ICACHE_FLASH_ATTR user_init(void)
{
int i;
os_DelayMs(1000);
uart_init(115200, 115200);//设置串口波特率
while (Ds18b20InitConfig())
{
}
// LedInitConfig();//LED灯初始化函数
// KeyInitConfig();//按键初始化函数
// KeyExtiInitConfig();//配置按键外部中断
TimerInitConfig(500, 1);//通过定时器回调采集DHT数据
// hw_timer_set_func();
// HwTimerInitConfig();
os_printf("=============================================\r\n");
os_printf("\t SDK version:\t%s", system_get_sdk_version());
os_printf("\r\n嵌入式陈工个人编辑资料\r\n未经本人同意请勿私自传播\r\n");
// os_printf("\r\n温度数据为%d\r\n,Ds18b20ReadTemp()");
os_printf("\r\n带看门狗\r\n");
os_printf("\r\nSPI Flash 的 ID 号:%d\r\n", spi_flash_get_id());
os_printf("=============================================\r\n");
}
源代码参考:https://github.com/ChenJiliang00/ESP8266