c8t6+DHT11测量温湿度+串口显示

本文介绍了如何使用STM32F10x单片机通过CH340模块与DHT11温湿度传感器通信,包括硬件连接、软件实现(包括时序处理和代码解读)以及在main.c中的应用实例。
摘要由CSDN通过智能技术生成

功能简介

        最小系统和温湿度传感器DHT11连接,并通过CH340模块在电脑串口助手上打印返回消息。

硬件连接

        DHT11有四根引脚,分别是:

        VDD:供电3.3~5.5V DC

        DATA:串行数据,单总线

        NC:空脚

        GND接地,电源负极

        在实际使用中,NC引脚实际上没有任何用处,因此也会有厂家对DHT11进行改进,将NC脚去掉。除此之外,VDD我们一般接3.3V就可以,而DATA引脚应该随便找一个引脚连接就行,这里本人选择PB0。

        具体连接如下:

        VDD —— 3.3V

        DATA —— PB0

        GND —— GND

软件实现

        关于DHT11的时序其他文章写的很清楚,这里就直接照着时序图边分析边引用野火的代码了。

        dht11.c会按我自己的理解分部分解析代码,顺序应该会和正常顺序不一样,不想看的可以直接C+V,dht11.c完整代码放文末了。

dht11.h

        宏定义没什么好说的,首先定义了结构体DHT11_Data_TypeDef,方便之后将数据存入其中;其次将引脚改成自己用的就行,宏的话喜欢就也改掉,但是要记得在.c 中替换掉。

#ifndef __DHT11_H
#define	__DHT11_H



#include "stm32f10x.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_SCK_APBxClock_FUN              RCC_APB2PeriphClockCmd
#define      DHT11_GPIO_CLK                       RCC_APB2Periph_GPIOB

#define      DHT11_GPIO_PORT                      GPIOB
#define      DHT11_GPIO_PIN                       GPIO_Pin_0



/************************** DHT11 函数宏定义********************************/
#define      DHT11_L	                            GPIO_ResetBits ( DHT11_GPIO_PORT, DHT11_GPIO_PIN ) 
#define      DHT11_H	                            GPIO_SetBits ( DHT11_GPIO_PORT, DHT11_GPIO_PIN ) 

#define      DHT11_IN()	                          GPIO_ReadInputDataBit ( DHT11_GPIO_PORT, DHT11_GPIO_PIN ) 



/************************** DHT11 函数声明 ********************************/
void                     DHT11_Init                      ( void );
uint8_t                  DHT11_Read_TempAndHumidity      ( DHT11_Data_TypeDef * DHT11_Data );

;

#endif /* __DHT11_H */

dht11.c

        这些是对dht11及其相关引脚的初始化以及其他一些配置。需要注意的是单片机与dht11相连的引脚要先拉高。

 /* 初始化函数 */
void DHT11_Init ( void )
{
	DHT11_GPIO_Config ();
	
	DHT11_H;               // 拉高GPIOB10
}

/* 配置DHT11的GPIO */
static void DHT11_GPIO_Config ( void )
{		
	/*定义一个GPIO_InitTypeDef类型的结构体*/
	GPIO_InitTypeDef GPIO_InitStructure; 

	/*开启DHT11_Dout_GPIO_PORT的外设时钟*/
  DHT11_SCK_APBxClock_FUN ( DHT11_GPIO_CLK, ENABLE );	
 
	/*选择要控制的DHT11_Dout_GPIO_PORT引脚*/															   
  	GPIO_InitStructure.GPIO_Pin = DHT11_GPIO_PIN;	

	/*设置引脚模式为通用推挽输出*/
  	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;   

	/*设置引脚速率为50MHz */   
  	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; 

	/*调用库函数,初始化DHT11_Dout_GPIO_PORT*/
  	GPIO_Init ( DHT11_GPIO_PORT, &GPIO_InitStructure );		  
	
}

/* 使DHT11-DATA引脚变为上拉输入模式 */
static void DHT11_Mode_IPU(void)
{
 	  GPIO_InitTypeDef GPIO_InitStructure;

	  	/*选择要控制的DHT11_Dout_GPIO_PORT引脚*/	
	  GPIO_InitStructure.GPIO_Pin = DHT11_GPIO_PIN;

	   /*设置引脚模式为上拉输入模式*/ 
	  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU ; 

	  /*调用库函数,初始化DHT11_Dout_GPIO_PORT*/
	  GPIO_Init(DHT11_GPIO_PORT, &GPIO_InitStructure);	 
	
}


/* 使DHT11-DATA引脚变为推挽输出模式 */
static void DHT11_Mode_Out_PP(void)
{
 	GPIO_InitTypeDef GPIO_InitStructure;

	 	/*选择要控制的DHT11_Dout_GPIO_PORT引脚*/															   
  	GPIO_InitStructure.GPIO_Pin = DHT11_GPIO_PIN;	

	/*设置引脚模式为通用推挽输出*/
  	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;   

	/*设置引脚速率为50MHz */   
  	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

	/*调用库函数,初始化DHT11_Dout_GPIO_PORT*/
  	GPIO_Init(DHT11_GPIO_PORT, &GPIO_InitStructure);	 	 
	
}

        首先单片机需要将对应DATA的引脚设置为推挽输出模式,向dht11发送信号。先将电平拉低至少18ms再拉高20~40us,这里一般取30us。之后,将引脚模式改为上拉输入,判断dht11是否响应。dht11检测到信号之后,会先将电平拉低80us再拉高80us,之后dht11开始发送数据,同时单片机将数据存入我们定义的结构体变量中。

        在所有的40bit发送完之后,将总线设置为输出模式并将电平拉高,准备下一次的数据传输;同时检查单片机读取的数据是否有错误。 

/*
 * 一次完整的数据传输为40bit,高位先出
 * 8bit 湿度整数 + 8bit 湿度小数 + 8bit 温度整数 + 8bit 温度小数 + 8bit 校验和 
 */
uint8_t DHT11_Read_TempAndHumidity(DHT11_Data_TypeDef *DHT11_Data)
{  
	/*输出模式*/
	DHT11_Mode_Out_PP();
	/*主机拉低*/
	DHT11_L;
	/*延时18ms*/
	DHT11_DELAY_MS(18);

	/*总线拉高 主机延时30us*/
	DHT11_H; 

	DHT11_DELAY_US(30);   //延时30us

	/*主机设为输入 判断从机响应信号*/ 
	DHT11_Mode_IPU();

	/*判断从机是否有低电平响应信号 如不响应则跳出,响应则向下运行*/   
	if(DHT11_IN()==Bit_RESET)     
	{
		/*轮询直到从机发出 的80us 低电平 响应信号结束*/  
		while(DHT11_IN()==Bit_RESET);

		/*轮询直到从机发出的 80us 高电平 标置信号结束*/
		while(DHT11_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_H;

		/*检查读取的数据是否正确*/
		if(DHT11_Data->check_sum == DHT11_Data->humi_int + DHT11_Data->humi_deci + DHT11_Data->temp_int+ DHT11_Data->temp_deci)
			return SUCCESS;
		else 
			return ERROR;
	}
	
	else
		return ERROR;
	
}

        dht11完整地发送一次数据的大小为40bit,高位(即MSB)先行,也就是先传入高位的数据。dht11每发送一个bit都会间隔50us,同样,在传感器完成响应后,会先进入50us的低电平。
        dht11的0和1数据的区别在于高电平的持续时长,其以26~28us的高电平表示“0”,以70us高电平表示“1”。因此只要检测一段时间后是什么电平就可以判断数据是0还是1,这个段时间只要大于表示"0"的高电平持续时间即可,一般取40us。

        因为dht11的数据格式是8*5bit,所以每次执行函数需要循环8次才能读完一个部分。

/* 
 * 从DHT11读取一个字节,MSB先行
 */
static uint8_t DHT11_ReadByte ( void )
{
	uint8_t i, temp=0;
	

	for(i=0;i<8;i++)    
	{	 
		/* 等待50us低电平结束 */  
		while(DHT11_IN()==Bit_RESET);

		/* 26~28us的高电平表示“0”,以70us高电平表示“1” */
		DHT11_DELAY_US(40); //延时大于0持续的时间即可	   	  

		if(DHT11_IN()==Bit_SET)/* x us后仍为高电平表示数据“1” */
		{
			/* 等待数据1的高电平结束 */
			while(DHT11_IN()==Bit_SET);

			temp|=(uint8_t)(0x01<<(7-i));  //把第7-i位置1,MSB先行 
		}
		else	 // 延时结束后为低电平表示数据“0”
		{			   
			temp&=(uint8_t)~(0x01<<(7-i)); //把第7-i位置0,MSB先行
		}
	}
	
	return temp;
	
}

高电平

低电平 

main.c  

      之后在main.c中函数进行结构体变量定义、初始化和串口打印,就万事大吉了。

#include "stm32f10x.h"
#include "SysTick.h"
#include "dht11.h"
#include "usart.h"

int main(void)
{
	DHT11_Data_TypeDef DHT11_Data;
	
	SysTick_Init();

	USART_Config();//初始化串口1

	DHT11_Init ();
	
	while(1)
	{	
			/*调用DHT11_Read_TempAndHumidity读取温湿度,若成功则输出该信息*/
			if( DHT11_Read_TempAndHumidity ( & DHT11_Data ) == SUCCESS)
			{
				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);
	}	
}

         需要注意的是,在串口配置文件中记得将printf重定向。

效果展示

        之前测试的时候忘记截图了,有机会在将串口打印的结果发出来。 

dht11.c完整代码

#include "dht11.h"
#include "core_delay.h" 

/* 可以在下面的宏定义中把后面的延时函数替换换SysTick的延时函数,就是想用那个就换成那个的 */

#define DHT11_DELAY_US(us)  CPU_TS_Tmr_Delay_US(us)
#define DHT11_DELAY_MS(ms)  CPU_TS_Tmr_Delay_MS(ms)

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 );



 /**
  * @brief  DHT11 初始化函数
  * @param  无
  * @retval 无
  */
void DHT11_Init ( void )
{
	DHT11_GPIO_Config ();
	
	DHT11_H;               // 拉高GPIOB10
}


/*
 * 函数名:DHT11_GPIO_Config
 * 描述  :配置DHT11用到的I/O口
 * 输入  :无
 * 输出  :无
 */
static void DHT11_GPIO_Config ( void )
{		
	/*定义一个GPIO_InitTypeDef类型的结构体*/
	GPIO_InitTypeDef GPIO_InitStructure; 

	
	/*开启DHT11_Dout_GPIO_PORT的外设时钟*/
  DHT11_SCK_APBxClock_FUN ( DHT11_GPIO_CLK, ENABLE );	
 
	/*选择要控制的DHT11_Dout_GPIO_PORT引脚*/															   
  	GPIO_InitStructure.GPIO_Pin = DHT11_GPIO_PIN;	

	/*设置引脚模式为通用推挽输出*/
  	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;   

	/*设置引脚速率为50MHz */   
  	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; 

	/*调用库函数,初始化DHT11_Dout_GPIO_PORT*/
  	GPIO_Init ( DHT11_GPIO_PORT, &GPIO_InitStructure );		  
	
}


/*
 * 函数名:DHT11_Mode_IPU
 * 描述  :使DHT11-DATA引脚变为上拉输入模式
 * 输入  :无
 * 输出  :无
 */
static void DHT11_Mode_IPU(void)
{
 	  GPIO_InitTypeDef GPIO_InitStructure;

	  	/*选择要控制的DHT11_Dout_GPIO_PORT引脚*/	
	  GPIO_InitStructure.GPIO_Pin = DHT11_GPIO_PIN;

	   /*设置引脚模式为浮空输入模式*/ 
	  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU ; 

	  /*调用库函数,初始化DHT11_Dout_GPIO_PORT*/
	  GPIO_Init(DHT11_GPIO_PORT, &GPIO_InitStructure);	 
	
}


/*
 * 函数名:DHT11_Mode_Out_PP
 * 描述  :使DHT11-DATA引脚变为推挽输出模式
 * 输入  :无
 * 输出  :无
 */
static void DHT11_Mode_Out_PP(void)
{
 	GPIO_InitTypeDef GPIO_InitStructure;

	 	/*选择要控制的DHT11_Dout_GPIO_PORT引脚*/															   
  	GPIO_InitStructure.GPIO_Pin = DHT11_GPIO_PIN;	

	/*设置引脚模式为通用推挽输出*/
  	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;   

	/*设置引脚速率为50MHz */   
  	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

	/*调用库函数,初始化DHT11_Dout_GPIO_PORT*/
  	GPIO_Init(DHT11_GPIO_PORT, &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_IN()==Bit_RESET);

		/*DHT11 以26~28us的高电平表示“0”,以70us高电平表示“1”,
		 *通过检测 x us后的电平即可区别这两个状 ,x 即下面的延时 
		 */
		DHT11_DELAY_US(40); //延时x us 这个延时需要大于数据0持续的时间即可	   	  

		if(DHT11_IN()==Bit_SET)/* x us后仍为高电平表示数据“1” */
		{
			/* 等待数据1的高电平结束 */
			while(DHT11_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_L;
	/*延时18ms*/
	DHT11_DELAY_MS(18);

	/*总线拉高 主机延时30us*/
	DHT11_H; 

	DHT11_DELAY_US(30);   //延时30us

	/*主机设为输入 判断从机响应信号*/ 
	DHT11_Mode_IPU();

	/*判断从机是否有低电平响应信号 如不响应则跳出,响应则向下运行*/   
	if(DHT11_IN()==Bit_RESET)     
	{
		/*轮询直到从机发出 的80us 低电平 响应信号结束*/  
		while(DHT11_IN()==Bit_RESET);

		/*轮询直到从机发出的 80us 高电平 标置信号结束*/
		while(DHT11_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_H;

		/*检查读取的数据是否正确*/
		if(DHT11_Data->check_sum == DHT11_Data->humi_int + DHT11_Data->humi_deci + DHT11_Data->temp_int+ DHT11_Data->temp_deci)
			return SUCCESS;
		else 
			return ERROR;
	}
	
	else
		return ERROR;
	
}

   

         

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值