温湿度模块DHT22详解二:编程篇

前一篇的DHT22基础篇差不多是一年前写的了,这一年间发生挺多事情,编程篇拖到现在,废话不多说进入正题。

从前面的基础篇也很详细讲了如何去驱动DHT22的步骤(需了解请移步至:https://blog.csdn.net/k1ang/article/details/98789397),这里我们就将这些步骤进行代码化。

1、起始信号:

void DHT22_Start(void)
{
	Set_IO_Output(); //设置IO为输出模式
	DHT_DQ_OUT_L();  //输出低电平1ms
	SysTick_Delay_Ms(1);
	DHT_DQ_OUT_H();  //释放总线,即输出高电平30us
	SysTick_Delay_Us(30);
}

2、应答信号:

应答信号并不是单纯的按照时序编写,中间增加了应答超时判断,详细见以下代码:

uint8_t DHT22_Ack(void)
{
	uint8_t cnt = 0;
	Set_IO_Input();//设置IO为输入模式
	if (DHT_DQ_IN() == Bit_RESET) //检测是否有低电平
	{
		while((!DHT_DQ_IN()) && (cnt <= 85))//计算低电平持续时间,判断低电平是否应答超时,手册最大应答时间是85us
		{
			cnt ++; //每1us自加一次
			SysTick_Delay_Us(1);
		}
		if(cnt > 85) //大于85us则应答超时
		{
			return ACK_OVER_TIME;//响应超时
		}
		else //应答正常
		{
			cnt = 0;
		}
		/*低电平应答正常后到高电平                       */
		while(DHT_DQ_IN() && (cnt <= 85))计算高电平持续时间,判断高电平是否应答超时,最大应答时间也是85us
		{
			cnt ++;
			SysTick_Delay_Us(1);
		}
		if(cnt > 85) //响应超时
		{
			return ACK_OVER_TIME;
		}
		else
		{
			cnt = 0;
			return ACK_SUCCESS;
		}
	}
	else
	{
		return ACK_ERROR;//应答错误,起始信号后没有应答信号返回
	}
}

3、温湿度数据读取:

应答信号过后,DHT22会连续输出40位数据,这40位数据可分为5个字节数据,5个字节数据分别表示湿度高8位、湿度低8位、温度高8位、温度低8位和校验位。校验位等于湿度高8位+湿度低8位+温度高8位+温度低8位,由于校验位也是8位的,4个8位数据加起来基本会溢出了,如果代码是根据校验结果来更新数显温湿度,如果溢出就会导致数据不更新。为了避免这种情况,可以先将4个温湿度数据相加后强制转换为8位,再和DHT22返回的校验位比较,如果相等则表示数据无误,可以更新显示,详细代码如下:

uint8_t Read_DHT22_Data(DHT22_Data_TypeDef *Data)
{
	
	DHT22_Start();//发送起始信号
	if(DHT22_Ack() == ACK_SUCCESS) //接收应答成功
	{
		Data->RH_int = DHT22_Read_Byte();    //读湿度数据高8位
		Data->RH_deci = DHT22_Read_Byte();   //读湿度数据低8位
		Data->Temp_int = DHT22_Read_Byte();  //读温度数据高8位
		Data->Temp_deci = DHT22_Read_Byte(); //读温度数据低8位
		Data->check_sum = DHT22_Read_Byte(); //读校验位
	}
	
	Set_IO_Output();//设置IO为输出模式
	GPIO_SetBits(DHT_DQ_Port,DHT_DQ_Pin);//释放总线
	
	Data->sum = (uint8_t)(Data->RH_deci + Data->RH_int + Data->Temp_deci + Data->Temp_int);//将温湿度高低8位累加,并强制转换为uint8_t类型

	if(Data->check_sum == Data->sum)//校验正确
	{
		Data->RH = (float)((256 * Data->RH_int + Data->RH_deci) / 10);//转换为湿度数据,除以10是因为传感器出来的值是实际的10倍
		Data->Temp = (float)((256 * Data->Temp_int + Data->Temp_deci) / 10);//转换为温度数据
		
		return SUCCESS; //校验通过
	}
	else
		return ERROR;//校验不过
}

4、读一个字节函数

在第3步用到一个读字节函数,下面从代码分析如果根据“0”和“1”时序读取,详细代码如下:

uint8_t DHT22_Read_Byte(void)
{
	uint8_t i;
	uint8_t DHT22_Byte = 0;
	for(i = 0; i < 8; i++)
	{
		while(DHT_DQ_IN() == Bit_RESET);//等待低电平结束
		SysTick_Delay_Us(40); //等待40us,再判断IO口电平状态
		if(DHT_DQ_IN() == Bit_SET)// 40 us后仍为高电平则表示数据“1” 
		{
			/* 等待数据1的高电平结束 */
			while(DHT_DQ_IN() == Bit_SET);

			DHT22_Byte |= (uint8_t)(0x01 << (7-i));  //把第7-i位置1,MSB先行 
		}
		else	 // 40 us后为低电平则表示数据“0”
		{			   
			DHT22_Byte &= (uint8_t)~(0x01 << (7-i)); //把第7-i位置0,MSB先行
		}
	}
	
	return DHT22_Byte;//返回当前读取到的字节
}

这里解释下为什么以40us来判断1和0信号:

先看下手册中“0”和“1”信号的时序

从上图中可以看到信号“0”和信号“1”的低电平时间都是相同的,信号“0”的高电平时间最大是30us,信号“1”的高电平时间最大是75us,要区分出信号“0”和信号“1”只能通过高电平的时间来判断。从30us和75us之间取一个时间点,我以40us作为临界点,当低电平结束后,经过40us如果还是高电平则说明是信号“1”,如果40us后是低电平则说明是信号“0”。

最后附上张测试结果与小米温湿度计对比的图片:

  • 19
    点赞
  • 116
    收藏
    觉得还不错? 一键收藏
  • 19
    评论
评论 19
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值