CH32V203操作DHT11的应用
1、关于DHT11产品介绍
DHT11是一款数字温湿度传感器,单线制串行接口,信号传输距离可达20米以上。湿度测量范围为20—90%RH,精度为±5%RH;温度测量范围为0-50℃,精度为±2℃。通常应用在湿度调节器、暖通空调、测试及检测设备等应用领域。
2、关于DHT11应用连接
DHT11具有4个引脚,分别为VDD引脚,单串行总线引脚DATA引脚,NC引脚,GND引脚。其中关于DATA引脚,建议连接长度小于20米时接5K上拉电阻,大于20米时适当降低上拉电阻的阻值。VDD引脚供电范围为3—5.5V,注意传感器上电后需要等待1s度过不稳定状态时间,且在此期间不需要发送任何指令,因此VDD引脚需要接一个100nF的电容,用以去耦滤波。
3、关于DHT11应用程序
DHT11信号线为单线双向串行接口总线,用于与DHT11与MCU之间的通讯和同步。程序中仅需配置一个引脚作为DHT11信号线引脚即可,此处选择PB0引脚。
DHT11一次完整的数据传输为40bit,高位先出。
数据格式:8bit湿度整数数据+8bit湿度小数数据+8bi温度整数数据+8bit温度小数数据+8bit校验和
数据传送正确时校验和数据等于“8bit湿度整数数据+8bit湿度小数数据+8bi温度整数数据+8bit温度小数数据” 所得结果的末8位。
DHT11的通讯过程如下图所示:
DHT11空闲状态下总线电平为高电平,因此在DHT11初始化时将该总线引脚配置为输出并拉高,具体程序如下:
//DHT11 初始化函数 void DHT11_Init ( void ) { DHT11_GPIO_Config (); //DHT11总线空闲状态为高电平,因此在DHT11初始化时将其拉高 DHT11_Dout_1; // 拉高PB0 } //DHT11_GPIO_Config static void DHT11_GPIO_Config ( void ) { /*定义一个GPIO_InitTypeDef类型的结构体*/ GPIO_InitTypeDef GPIO_InitStructure={0}; /*开启DHT11_Dout_GPIO_PORT的外设时钟*/ RCC_APB2PeriphClockCmd ( RCC_APB2Periph_GPIOB, ENABLE ); /*选择要控制的DHT11_Dout_GPIO_PORT引脚*/ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; /*设置引脚模式为通用推挽输出*/ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; /*设置引脚速率为50MHz */ GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; /*调用库函数,初始化DHT11_Dout_GPIO_PORT*/ GPIO_Init ( GPIOB, &GPIO_InitStructure ); }
当开始进行通讯获取DHT11温湿度时,MCU作为主机把DHT11模块总线引脚拉低且拉低时间必须大于等于18ms,方能保证DHT11模块能检测到起始信号并进行响应,起始信号具体程序如下:
/***********************开始信号**************************/ /*输出模式*/ DHT11_Mode_Out_PP(); /*主机拉低*/ DHT11_Dout_0; /*延时18ms*/ Delay_Ms(18); //延时时间需要大于等于18ms /***********************开始信号**************************/
DHT11接收到主机的开始信号后,等待主机开始信号结束 ,此时需要将信号线拉高并延时等待一段时间,大约20-40us,具体程序如下:
/******************等待开始信号结束**********************/ /*总线拉高 主机延时30us*/ DHT11_Dout_1; Delay_Us(30); //延时30us /******************等待开始信号结束**********************/
DHT11接收到主机的开始信号后,会向主机发送80us低电平响应信号,此时需要将信号线切换为输入模式接收响应信号,当接收到高电平时标志响应信号结束,具体程序如下:
/*主机设为输入 判断从机响应信号*/ DHT11_Mode_IPU(); /*判断从机是否有低电平响应信号 如不响应则跳出,响应则向下运行*/ if(DHT11_Dout_IN()==Bit_RESET) { /*轮询直到从机发出 的80us 低电平 响应信号结束*/ while(DHT11_Dout_IN()==Bit_RESET); /*轮询直到从机发出的 80us 高电平 标置信号结束*/ while(DHT11_Dout_IN()==Bit_SET);
由上图通讯过程波形图可以看出,当检测到响应信号高电平结束标志时,开始接收数据,根据上述数据格式,依次是湿度数据、温度数据、校验数据,具体程序如下:
/*开始接收数据*/ DHT11_Data->humi_int= DHT11_ReadByte(); DHT11_Data->humi_deci= DHT11_ReadByte(); DHT11_Data->temp_int= DHT11_ReadByte(); DHT11_Data->temp_deci= DHT11_ReadByte(); DHT11_Data->check_sum= DHT11_ReadByte();
接收数据完成之后,信号总线由输入切换为输出,主机将信号线拉高,表示一次通讯结束,最后根据得到的数据进行校验,具体程序如下:
/*读取结束,引脚改为输出模式*/ DHT11_Mode_Out_PP(); /*主机拉高*/ DHT11_Dout_1; /*检查读取的数据是否正确*/ if(DHT11_Data->check_sum == DHT11_Data->humi_int + DHT11_Data->humi_deci + DHT11_Data->temp_int+ DHT11_Data->temp_deci) return 1; else return 0;
以上就是DHT11获取数据的一个通讯过程,关于DHT11_ReadByte函数,温湿度以及校验和的数据都是由8位组成的,因此各获取一个字节即可。关于DHT11_ReadByte函数,具体程序如下:
//从DHT11读取一个字节,MSB先行 static uint8_t DHT11_ReadByte ( void ) { uint8_t i, temp=0; for(i=0;i<8;i++) { /*每bit以50us低电平标置开始,轮询直到从机发出 的50us 低电平 结束*/ while(DHT11_Dout_IN()==Bit_RESET); /*DHT11 以26~28us的高电平表示“0”,以70us高电平表示“1”, *通过检测 x us后的电平即可区别这两个状 ,x 即下面的延时 */ Delay_Us(40); //延时x us 这个延时需要大于数据0持续的时间即可 if(DHT11_Dout_IN()==Bit_SET)/* x us后仍为高电平表示数据“1” */ { /* 等待数据1的高电平结束 */ while(DHT11_Dout_IN()==Bit_SET); temp|=(uint8_t)(0x01<<(7-i)); //把第7-i位置1,MSB先行 } else // x us后为低电平表示数据“0” { temp&=(uint8_t)~(0x01<<(7-i)); //把第7-i位置0,MSB先行 } } return temp; }
DHT11发送响应信号结束后,会把总线拉高80us之后发送数据,如上述程序while查询注释。当开始发送数据之后,每一位数据都要以50us低电平间隙表示开始,因此在读取字节的函数中开始位置有一个while查询,查询总线电平是否为低电平。此外DHT11数据的高低(即0、1)是由高电平的持续时间决定的:
当50us的低电平之后,高电平的持续时间为26或28us,则表示数据0;
当50us的低电平之后,高电平的持续时间为70us,则表示数据1,但一般这个时间只要大于程序中定义的延时时间(大于数据0的时间)即可。
如程序中就是定义延时40us,当40us过后还是高电平,则表示1,否则表示0。
以上就是DHT11通讯的一个整个的流程。
4、下载运行结果以及整体代码:
dht11.h文件
#ifndef __DHT11_H #define __DHT11_H #include "ch32v20x.h" /************************** DHT11 数据类型定义********************************/ typedef struct { uint8_t humi_int; //湿度的整数部分 uint8_t humi_deci; //湿度的小数部分 uint8_t temp_int; //温度的整数部分 uint8_t temp_deci; //温度的小数部分 uint8_t check_sum; //校验和 } DHT11_Data_TypeDef; /************************** DHT11 函数宏定义********************************/ #define DHT11_Dout_0 GPIO_ResetBits ( GPIOB, GPIO_Pin_0 ) #define DHT11_Dout_1 GPIO_SetBits ( GPIOB, GPIO_Pin_0 ) #define DHT11_Dout_IN() GPIO_ReadInputDataBit ( GPIOB, GPIO_Pin_0 ) /************************** DHT11 函数声明 ********************************/ void DHT11_Init( void ); uint8_t DHT11_Read_TempAndHumidity( DHT11_Data_TypeDef * DHT11_Data ); #endif
dht11.c文件
#include "dht11.h" #include "debug.h" static void DHT11_GPIO_Config( void ); static void DHT11_Mode_IPU( void ); static void DHT11_Mode_Out_PP( void ); static uint8_t DHT11_ReadByte( void ); //DHT11 初始化函数 void DHT11_Init ( void ) { DHT11_GPIO_Config (); //DHT11总线空闲状态为高电平,因此在DHT11初始化时将其拉高 DHT11_Dout_1; // 拉高PB0 } //DHT11_GPIO_Config static void DHT11_GPIO_Config ( void ) { /*定义一个GPIO_InitTypeDef类型的结构体*/ GPIO_InitTypeDef GPIO_InitStructure={0}; /*开启DHT11_Dout_GPIO_PORT的外设时钟*/ RCC_APB2PeriphClockCmd ( RCC_APB2Periph_GPIOB, ENABLE ); /*选择要控制的DHT11_Dout_GPIO_PORT引脚*/ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; /*设置引脚模式为通用推挽输出*/ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; /*设置引脚速率为50MHz */ GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; /*调用库函数,初始化DHT11_Dout_GPIO_PORT*/ GPIO_Init ( GPIOB, &GPIO_InitStructure ); } //使DHT11-DATA引脚变为上拉输入模式 static void DHT11_Mode_IPU(void) { GPIO_InitTypeDef GPIO_InitStructure={0}; /*选择要控制的DHT11_Dout_GPIO_PORT引脚*/ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; /*设置引脚模式为浮空输入模式*/ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU ; /*调用库函数,初始化DHT11_Dout_GPIO_PORT*/ GPIO_Init(GPIOB, &GPIO_InitStructure); } //使DHT11-DATA引脚变为推挽输出模式 static void DHT11_Mode_Out_PP(void) { GPIO_InitTypeDef GPIO_InitStructure={0}; /*选择要控制的DHT11_Dout_GPIO_PORT引脚*/ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; /*设置引脚模式为通用推挽输出*/ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; /*设置引脚速率为50MHz */ GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; /*调用库函数,初始化DHT11_Dout_GPIO_PORT*/ GPIO_Init(GPIOB, &GPIO_InitStructure); } //从DHT11读取一个字节,MSB先行 static uint8_t DHT11_ReadByte ( void ) { uint8_t i, temp=0; for(i=0;i<8;i++) { /*每bit以50us低电平标置开始,轮询直到从机发出 的50us 低电平 结束*/ while(DHT11_Dout_IN()==Bit_RESET); /*DHT11 以26~28us的高电平表示“0”,以70us高电平表示“1”, *通过检测 x us后的电平即可区别这两个状 ,x 即下面的延时 */ Delay_Us(40); //延时x us 这个延时需要大于数据0持续的时间即可 if(DHT11_Dout_IN()==Bit_SET)/* x us后仍为高电平表示数据“1” */ { /* 等待数据1的高电平结束 */ while(DHT11_Dout_IN()==Bit_SET); temp|=(uint8_t)(0x01<<(7-i)); //把第7-i位置1,MSB先行 } else // x us后为低电平表示数据“0” { temp&=(uint8_t)~(0x01<<(7-i)); //把第7-i位置0,MSB先行 } } return temp; } /* * 一次完整的数据传输为40bit,高位先出 * 8bit 湿度整数 + 8bit 湿度小数 + 8bit 温度整数 + 8bit 温度小数 + 8bit 校验和 */ uint8_t DHT11_Read_TempAndHumidity(DHT11_Data_TypeDef *DHT11_Data) { /***********************开始信号**************************/ /*输出模式*/ DHT11_Mode_Out_PP(); /*主机拉低*/ DHT11_Dout_0; /*延时18ms*/ Delay_Ms(18); //延时时间需要大于等于18ms /***********************开始信号**************************/ /******************等待开始信号结束**********************/ /*总线拉高 主机延时30us*/ DHT11_Dout_1; Delay_Us(30); //延时30us /******************等待开始信号结束**********************/ /*主机设为输入 判断从机响应信号*/ DHT11_Mode_IPU(); /*判断从机是否有低电平响应信号 如不响应则跳出,响应则向下运行*/ if(DHT11_Dout_IN()==Bit_RESET) { /*轮询直到从机发出 的80us 低电平 响应信号结束*/ while(DHT11_Dout_IN()==Bit_RESET); /*轮询直到从机发出的 80us 高电平 标置信号结束*/ while(DHT11_Dout_IN()==Bit_SET); /*开始接收数据*/ DHT11_Data->humi_int= DHT11_ReadByte(); DHT11_Data->humi_deci= DHT11_ReadByte(); DHT11_Data->temp_int= DHT11_ReadByte(); DHT11_Data->temp_deci= DHT11_ReadByte(); DHT11_Data->check_sum= DHT11_ReadByte(); /*读取结束,引脚改为输出模式*/ DHT11_Mode_Out_PP(); /*主机拉高*/ DHT11_Dout_1; /*检查读取的数据是否正确*/ if(DHT11_Data->check_sum == DHT11_Data->humi_int + DHT11_Data->humi_deci + DHT11_Data->temp_int+ DHT11_Data->temp_deci) return 1; else return 0; } else return 0; }
main.c文件
/********************************** (C) COPYRIGHT ******************************* * File Name : main.c * Author : WCH * Version : V1.0.0 * Date : 2021/06/06 * Description : Main program body. ********************************************************************************* * Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd. * Attention: This software (modified or not) and binary are used for * microcontroller manufactured by Nanjing Qinheng Microelectronics. *******************************************************************************/ /* *@Note USART Print debugging routine: USART1_Tx(PA9). This example demonstrates using USART1(PA9) as a print debug port output. */ #include "debug.h" #include "dht11.h" /* Global typedef */ /* Global define */ /* Global Variable */ /********************************************************************* * @fn main * * @brief Main program. * * @return none */ int main(void) { DHT11_Data_TypeDef DHT11_Data; NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); Delay_Init(); USART_Printf_Init(115200); printf("SystemClk:%d\r\n", SystemCoreClock); printf("This is printf example\r\n"); /*初始化DTT11的引脚*/ DHT11_Init (); while(1) { /*调用DHT11_Read_TempAndHumidity读取温湿度,若成功则输出该信息*/ if( DHT11_Read_TempAndHumidity ( & DHT11_Data ) == 1) { printf("\r\n读取DHT11成功!\r\n\r\n湿度为%d.%d %RH ,温度为 %d.%d℃ \r\n",\ DHT11_Data.humi_int,DHT11_Data.humi_deci,DHT11_Data.temp_int,DHT11_Data.temp_deci); } else { printf("Read DHT11 ERROR!\r\n"); } Delay_Ms(2000); } }