DHT11编程

一、参数

通信协议:单总线
测量范围:20-90%,0-50℃
精度:±5%,±2℃

二、串行接口(单线双向)

DATA 数据引脚用于微处理器与DHT11之间的通讯和同步,采用单总线数据格式,一次通讯时间4ms左右,数据分小数部分和整数部分,具体格式在下面说明,当前小数部分用于以后扩展,现读出为零。

操作流程如下:一次完整的数据传输为40bit,高位先出。

数据格式:8bit湿度整数数据+8bit湿度小数数据+8bi温度整数数据+8bit温度小数数据+8bit校验和

数据传送正确时校验和数据等于“8bit湿度整数数据+8bit湿度小数数据+8bi温度整数数据+8bit温度小数数据”所得结果的末8位。

用户MCU发送一次开始信号后, DHT11从低功耗模式转换到高速模式,等待主机开始信号结束后, DHT11发送响应信号,送出40bit的数据,并触发一次信号采集,用户可选择读取部分数据.从模式下, DHT11接收到开始信号触发一次温湿度采集,如果没有接收到主机发送开始信号, DHT11不会主动进行温湿度采集.采集数据后转换到低速模式。

通信可以分为三个过程:
1.通信的开始
2.通信的内容
3.通信的结束
在这里插入图片描述
时序图
(1)PG9引脚变为输出模式,PG9引脚变为低电平,持续至少18s,推荐20us,PG9引脚变为高电平,持续20us-40us,推荐30us
(2)PG9引脚变为输入模式检查DFT11的响应信号,低电平持续80us,高电平持续80us,也就是说DFT11有响应,如果没有响应,就一直为高电平。

在这里插入图片描述
数字0信号表示

PG9引脚保持输入模式,识别低电平50us,高电平26-28us,就为bit0

在这里插入图片描述
数字1信号表示

PG9引脚保持输入模式,识别低电平50us,高电平70us,就为bit1

在这里插入图片描述
通信的结束

PG9引脚输入模式识别低电平50us,PG9引脚输出模式输出高电平

在这里插入图片描述

三、代码

1.配置引脚的输入输出模式,记得在打开时钟RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG, ENABLE);

void dht11_outputmode(void)
{
    
    

	GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_9;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
	GPIO_Init(GPIOG, &GPIO_InitStructure);	
	
}
void dht11_inputmode(void)
{
    
    
	GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_9;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
	GPIO_Init(GPIOG, &GPIO_InitStructure);	
}

2.编写
(1)开始信号函数,
(2)读取一个字节函数,
(3)读取所有数据函数,用一个指针来保存数据

开始信号的函数
int dht11_start(void)
{
    
    
	uint32_t i;

	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG, ENABLE);
	
	//PG9变为输出模式
	dht11_outputmode();

	//PG9引脚输出低电平
	PGout(9) = 0;

	//延时20ms
	delay_ms(20);

	//PG9引脚输出高电平
	PGout(9) = 1;
	
	//延时30us
	delay_us(30);

	//切换到输入模式,检查DHT11温湿度模块是否有响应
	dht11_inputmode();
	
	//检测是否有低电平
	i=0;
	while(i<100)
	{
    
    
		if(PGin(9)==0)//如果变为高电平,则说明读取成功跳出while
			break;
		delay_us(1);
		i++;
	}
	
	//设置100,是为了证明已经超过80us时间,超时返回1
	if(i >=100)
		return 1;
	
	
	//检测是否有高电平
	i=0;
	while(i<100)
	{
    
    
		if(PGin(9))
			break;
		delay_us(1);
		i++;
	}
	
	//超时
	if(i >=100)
		return 1;
	
	//没有错误,正确的返回 
	return 0;
}
读取一个字节的数据,并返回
uint8_t dht11_read_byte(void)
{
    
    
	uint8_t d=0;//读取一个字节,新建一个变量进行保存
	
	uint32_t i=0;//控制八次循环读取八位数据
	
	//等待高电平持续完毕
	while(PGin(9));//在读取一个位之前,需要等高电平变为低电平,!!!这是易漏点
	
	for(i=0; i<8; i++)
	{
    
    
		//检测低电平
		while(PGin(9)==0);//因为数据1和数据0前半部分都是低电平,可以一直等待,等待变为高电平
		
		//延时40us
		delay_us(40);//如果超过28us后还是高电平就是数据1,结合时序图来看
		
		//判断PG9引脚的电平,若为高电平就是bit1
		if(PGin(9))
		{
    
    
		/*数据是从高往低获取的,获取的是第7位的数据,
		所以要把这个数据从第0位送到第7位,采用左移的方法最为简单,
		如果是高电平则将第七位置1,直到这一位数据发送完,电平才翻转,进行下一位的传输,持续八次
		*/
			d|=1<<(7-i);
			
			//等待高电平持续完毕
			while(PGin(9));//!!!这里也是易漏点		
		}
	}
		
	return d;
}

下面把整个通信过程进行组合:
uint32_t dht11_read_data(uint8_t *pbuf)
{
    
    
	uint32_t i=0;
	uint8_t  check_sum=0;
	
	//建立启动信号
	while(dht11_start()==1);
	
	//连续读取5个字节
	for(i=0; i<5; i++)
	{
    
    
		pbuf[i] = dht11_read_byte();
	
	}

	//计算校验和
	check_sum = pbuf[0]+pbuf[1]+pbuf[2]+pbuf[3];
	
	//检验和判断
	if(check_sum != pbuf[4])
		return 1;//不正确返回1
	
	//结束部分
	//PG9变为输出模式
	dht11_outputmode();
	//PG9引脚输出高电平,这两句可以不加,因为默认上拉电阻会自动上拉总线
	PGout(9) = 1;

	return 0;//正确返回0
}

注意需要将system_stm32f4xx.c中的PLL_M 修改正确
以及stm32f4xx.h中的HSE_VALUE修改正确,外部晶振的值

在主函数部分需要注意的几个点

创建一个数组用于保存变量的值
uint8_t dht11_data[4]={
    
    0};
if(dht11_read_data(dht_data)==0)
{
    
    
	printf("temp=%d.%d\r\n",dht_data[2],dht_data[3]);
	printf("humi=%d.%d\r\n",dht_data[0],dht_data[1]);
	printf("\r\n");
}


如果出现没有数值的情况,可以使用Printf在校验和那里查看是否输出正确。

猜你喜欢

转载自blog.csdn.net/ABCisCOOL/article/details/115052256