esp8266和dht11通讯_8266dh11-爱代码爱编程
文章目录
DHT11介绍
参考文献《DHT11温湿度模块学习手册》
DHT11传感器采用简化的单总线通信。单总线即只有一根数据线,系统中的数据交换、控制均由单总线完成。设备(主机或从机)通过一个漏极开路或三态端口(单片机10口)连至该数据线,以允许设备在不发送数据时能够释放总线(由上拉电阻控制,把总线上拉成高电本) ,而让其它设备使用总线;单总线通常要求,外接一个约4.7k2的上拉电阻,这样,当总线闲置时,其状态为高电平(模块中已经集成4.7k上拉电阻)。由于它们是主从结极,只有主机呼叫从机时,从机才能应答,因此主机访问器件都必须严格遵循单总线序列,如果出现序列混乱,器件将不响应主机。
1.通讯与数据说明
DHT11采用单总线通讯协议,与单片机通讯过程:
接受到的40位数据为:湿度高8位,湿度低8位,温度高8位,温度低8位, 8位校验位。湿度的高8位是测量湿度的整数,湿度的低8位是测量湿度的小数(湿度的小数部分为0) ;温度的高8位是测量温度的整数,温度的低8位是测量温度的小数;8位校验和-湿度高8位+湿度低8位+温度高8位+温度低8位,可参考下图:
注意:当温度低于0℃是,温度数据低8位最高位位置位1.
温度高8位|温度低8位|湿度高8位|湿度低8位=校验位
2. DHT11通讯实现
主要介绍dht11.c 与dht11.h 文件。
- DHT11上电后需要等待1S,并且数据总线要保持高电平
- 主机从DHT11读取的温湿度数据总是前一次的测量值,如果测量间隔时间较长(超过45) ,请连续读取两次值,然后以第二次读取的值为实时温湿度值
- 当主机要对数据总线操作操作(拉高/拉低)时,应配置为输出模式; DHT11对数据总线操作(拉高/拉低)时,主机应配置为输入模式
- 单片机用延迟函数时,误差不能太大,否则无法准确读取温湿度的值
- 通讯协议的实现函数多次用到超时检测,这是为了避免程序运行时DHT11出现错误而卡在while中
通讯可按照以下的步骤进行(函数实现在dhtl1.c文件中) : -
- 主机发送起始信号并检测DHT11的响应信号。主机会发送一个时间T(18ms<T<30ms)的低电平作为起始信号(拉低后要释放总线) , DHT11检测到起始信号后会把信号总线拉低83us作为响应信号,然后拉高87us通知主机准备接收数据。
-
- 主机发送起始信号并检测DHT11的响应信号。主机会发送一个时间T(18ms<T<30ms)的低电平作为起始信号(拉低后要释放总线) , DHT11检测到起始信号后会把信号总线拉低83us作为响应信号,然后拉高87us通知主机准备接收数据。
- 2)主机接收DHT11发送的数据(高位先出) 。在DHT11发送87us高电平的通知信号后紧接着发送40Bit数据。数据0由54us的低电平与23-27us的高电平构成;数据1由54us的低电平与68-74us构成,时序图如下:
-
- 主机读取DHT11发送的40Bit数据并检测DHT11的结束信号。DHT11发送完40Bit数据后会继续输出54us的低电平作为结束信号,然后转为输入状态。这时主机在检测到DHT11的结束电平结束后应该配置为输出模式并输出高电。时序图如下:
- 4)数据时序图
3.官方程序
1:输出启动信号>>接受响应信号
2.读取一个位的数据
3.数据用结构体保存
读取的DHT11 数据是用一个结构体保存,这样可以把40Bit 数据分为湿度的高八位、湿度的低八位、温度
的高八位、温度的低八位、八位校验数据保存,结构体定义在dht11.h 文件中
4.ESP8266 SDK函数库
#include "driver/dht11.h" // 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℃】
//-----------------------------------------------------
u8 DHT11_Data_Array[6] = {0}; // DHT11数据数组
u8 DHT11_Data_Char[2][10]={0}; // DHT11数据字符串【行:湿/温度】【列:数据字符串】
// DHT11_Data_Char[0] == 【湿度字符串】
// DHT11_Data_Char[1] == 【温度字符串】
//==================================================================================
// 毫秒延时函数
//=================================================
void ICACHE_FLASH_ATTR Dht11_delay_ms(u32 C_time)
{ for(;C_time>0;C_time--)
os_delay_us(1000);
}
//=================================================
// GPIO_5(DHT11信号线)设为输出模式,并输出参数对应的电平
//===========================================================================
void ICACHE_FLASH_ATTR DHT11_Signal_Output(u8 Value_Vol)
{
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO5_U, FUNC_GPIO5); // GPIO5设为IO口
GPIO_OUTPUT_SET(GPIO_ID_PIN(5),Value_Vol); // IO5设为输出=X
}
//===========================================================================
// GPIO_5(DHT11信号线)设为输入模式
//===========================================================================
void ICACHE_FLASH_ATTR DHT11_Signal_Input(void)
{
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO5_U, FUNC_GPIO5); // GPIO5设为IO口
GPIO_DIS_OUTPUT(GPIO_ID_PIN(5)); // GPIO5失能输出(输入)
}
//===========================================================================
// DHT11:输出起始信号->接收响应信号
//-----------------------------------------------------
// 返回值: 0 成功
// 1 失败:规定时间内未接收到响应信号
// 2 失败:响应信号的低电平时长超时
//===========================================================================
u8 ICACHE_FLASH_ATTR DHT11_Start_Signal_JX(void)
{
u8 C_delay_time = 0; // 延时计时
// IO5抬高
//--------------------------------------------------------
DHT11_Signal_Output(1); // DHT11信号线(IO5) == 输出高
Dht11_delay_ms(1);
// IO5拉低(25ms):起始信号
//---------------------------------------------
GPIO_OUTPUT_SET(GPIO_ID_PIN(5),0); // IO5 = 0
Dht11_delay_ms(25);
// IO5抬高【注:起始信号结束后的约13us,DHT11开始输出信号】
//---------------------------------------------------------
GPIO_OUTPUT_SET(GPIO_ID_PIN(5),1); // IO5 = 1
os_delay_us(5); // 延时5us
// 接收响应信号
//……………………………………………………………………………………
// IO5设为输入:接收DHT11数据
//-------------------------------------------------
DHT11_Signal_Input(); // DHT11信号线(IO5) = 输入
// 等待响应信号的低电平【最迟等50us】
//-------------------------------------------------------------
while( GPIO_INPUT_GET(GPIO_ID_PIN(5))==1 && C_delay_time<50 )
{
os_delay_us(1); // 1us计时
C_delay_time++;
}
// 响应信号超时未收到
//--------------------------------------------------
if(C_delay_time >= 50)
return 1; // 失败:规定时间内未接收到响应信号
C_delay_time = 0 ; // 低电平计时开始
// 响应信息的低电平时长计时【最多170us】
//-------------------------------------------------------------
while( GPIO_INPUT_GET(GPIO_ID_PIN(5))==0 && C_delay_time<170 )
{
os_delay_us(1);
C_delay_time++; // 低电平时长
}
// 响应信号的低电平时长超时
//------------------------------------------------
if(C_delay_time >= 170)
return 2; // 失败:响应信号的低电平时长超时
// 响应信号的低电平成功接收
//--------------------------
else
return 0; // 成功
}
//===========================================================================
// 读取DHT11一位数据
//--------------------------------
// 返回值: 0 数据=="0"
// 1 数据=="1"
//======================================================================
u8 ICACHE_FLASH_ATTR DHT11_Read_Bit(void)
{
u8 C_delay_time = 0; // 延时计时
// 等待响应信息的低电平【最迟等150us】
//-------------------------------------------------------------
while( GPIO_INPUT_GET(GPIO_ID_PIN(5))==1 && C_delay_time<150 )
{
os_delay_us(1); // 1us计时
C_delay_time++;
}
C_delay_time = 0 ; // 低电平计时开始
// 数据位的低电平时长计时【最多200us】
//-------------------------------------------------------------
while( GPIO_INPUT_GET(GPIO_ID_PIN(5))==0 && C_delay_time<120 )
{
os_delay_us(1);
C_delay_time++; // 低电平时长
}
// 数据位的低电平结束后,是数据位的高电平
// 数据"0"的高电平时长 == [23~27us]
// 数据"1"的高电平时长 == [68~74us]
//------------------------------------------------
os_delay_us(45); // 跳过数据"0"的高电平部分
// 延时45us后,检测信号线电平
// 如果此时信号线电平==1 => 数据=="1"
// 如果此时信号线电平==0 => 数据=="0"
//-------------------------------------
return GPIO_INPUT_GET(GPIO_ID_PIN(5));
}
//======================================================================
// 读取DHT11一个字节
//======================================================================
u8 ICACHE_FLASH_ATTR DHT11_Read_Byte(void)
{
u8 C_Bit = 0; // 位计数
u8 T_DHT11_Byte_Data = 0; // DHT11字节数据
for(; C_Bit<8; C_Bit++) // 读取DHT11一个字节
{
T_DHT11_Byte_Data <<= 1;
T_DHT11_Byte_Data |= DHT11_Read_Bit(); // 一位一位的读取
}
return T_DHT11_Byte_Data; // 返回读取字节
}
//======================================================================
// 完整的读取DHT11数据
//-----------------------------------------------
// 返回值: 0 DHT11数据读取成功
// 1 结束信号的低电平时长超时
// 2 启动DHT11传输_失败
// 3 校验错误
//==============================================================================
u8 ICACHE_FLASH_ATTR DHT11_Read_Data_Complete(void)
{
u8 C_delay_time = 0; // 延时计时
// 启动DHT11传输_成功
//------------------------------------------------------------------------
if(DHT11_Start_Signal_JX() == 0) // DHT11:输出起始信号->接收响应信号
{
DHT11_Data_Array[0] = DHT11_Read_Byte(); // 湿度_整数_部分
DHT11_Data_Array[1] = DHT11_Read_Byte(); // 湿度_小数_部分
DHT11_Data_Array[2] = DHT11_Read_Byte(); // 温度_整数_部分
DHT11_Data_Array[3] = DHT11_Read_Byte(); // 温度_小数_部分
DHT11_Data_Array[4] = DHT11_Read_Byte(); // 校验字节
// 如果此时是最后一位数据的高电平,则等待它结束
//-----------------------------------------------------------
while(GPIO_INPUT_GET(GPIO_ID_PIN(5))==1 && C_delay_time<100)
{
os_delay_us(1); // 1us计时
C_delay_time++;
}
C_delay_time = 0 ; // 低电平计时开始
// 结束信号的低电平时长计时
//-----------------------------------------------------------
while(GPIO_INPUT_GET(GPIO_ID_PIN(5))==0 && C_delay_time<100)
{
os_delay_us(1); // 1us计时
C_delay_time++;
}
//-----------------------------------------------------------
if(C_delay_time >= 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传输,失败
}
//==============================================================================
// DHT11数据值转成字符串
//======================================================================
void ICACHE_FLASH_ATTR DHT11_NUM_Char(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
//…………………………………………………………………………………………
}
//======================================================================