基于stm32的太空人温湿度时钟项目——DHT11(HAL库)_hal库dht11(1)

效果图:

引脚连接:

OLED:

VCC --> 3.3V

GND --> GND

SCL --> PB10

SDA --> PB11

DHT11:

DATA --> PA9

VCC --> 3.3V

GND --> GND

一、 DHT11简介

1.1 DTH11概念与介绍

DHT11 是广州奥松有限公司生产的一款湿温度一体化的数字传感器。该传感器包括一个电阻式测湿元件和一个 NTC 测温元件,并与一个高性能 8 位单片机相连接。通过单片机等微处理器简单的电路连接就能够实时的采集本地湿度和温度。DHT11 与单片机之间能采用简单的单总线进行通信****(DATA引脚),仅仅需要一个I/O 口。传感器内部湿度和温度数据 40Bit 的数据一次性传给单片机,数据采用校验和方式进行校验,有效的保证数据传输的准确性。DHT11 功耗很低,5V 电源电压下,工作平均最大电流 0.5mA。(正常使用3.3V即可)

工作电压范围:3.5V-5.5V
工作电流 :平均 0.5mA
湿度测量范围:20-90%RH
温度测量范围:0-50℃
湿度分辨率 :1%RH 8 位
温度分辨率 :1℃ 8 位
采样周期 :1S
单总线结构
与 TTL 兼容(5V)

**注意事项:**DTH11由于物理与化学结构性质,是比较容易损坏的,需要尽量远离恶劣坏境进行工作测量。如果实在不小心,对DHT1传感器造成了破坏,读者可以去网上搜索一下修复教程进行抢救一下。

实物图:

1.2 DTH11工作原理

前文有提到:DTH11传感器只具有3根引脚,所以是单总线进行通讯(DATA),为了方便读者对于通讯原理与编程的同步高效率理解。关于时序的通讯方式将在本文代码段部分逐个详细讲解。

单片机读取数据

DHT11数字湿温度传感器采用单总线数据格式。即,单个数据引脚端口完成输入输出双向传输。其数据包由5Byte(5个字节=40Bit)组成。数据分小数部分整数部分,具体格式在下面说明。一次完整的数据传输为40bit,高位先出。

**校验方式:**校验和数据为前四个字节相加。

传感器数据输出的是未编码的二进制数据。数据(湿度、温度、整数、小数)之间应该分开处理。如果,某次从传感器中读取如下5Byte数据:

由以上数据就可得到湿度和温度的值,计算方法:
        humi (湿度)= byte4 . byte3=45.0 (%RH)
        temp (温度)= byte2 . byte1=28.0 ( ℃)
        jiaoyan(校验)= byte4+ byte3+ byte2+ byte1=73(=humi+temp)(校验正确)
        注意:DHT11一次通讯时间最大3ms,主机连续采样间隔建议不小于100ms。(这一点在时序编程的时候也会经常体现出来)

if(data[0] + data[1] + data[2] + data[3] == data[4])
{
   //执行程序
}

时序部分是笔者认为DHT11最为重要的部分,结合代码与文字描述可能会让读者有更好的理解,读者可以调至下文代码段进行学习。

二、RTC简介

RTC(Real Time Clock)即实时时钟,RTC究其本质还是一个定时器(独立的定时器)。RTC模块拥有一个连续计数的计数器,在相应的软件配置下,可以提供时钟日历的功能。修改计数器的值可以重新设置当前时间和日期 RTC还包含用于管理低功耗模式自动唤醒单元

断电情况下 RTC仍可以独立运行 只要芯片的备用电源一直供电,RTC上的时间会一直走。(部分最小系统板是没有芯片备用电源的,所以不一定都可以掉电仍然计时)

其拥有的两个 32 位寄存器包含二进码十进数格式 (BCD) 的秒、分钟、小时( 12 或 24 小时制)、星期几、日期、月份和年份。此外,还可提供二进制格式的亚秒值。系统可以自动将月份的天数补偿为 28、29(闰年)、30 和 31 天。

RCT特征:

● 可编程的预分频系数:分频系数高为220
● 32位的可编程计数器,可用于较长时间段的测量。
● 2个分离的时钟:用于APB1接口的PCLK1和RTC时钟(RTC时钟的频率必须小于PCLK1时钟 频率的四分之一以上)。
● 可以选择以下三种RTC的时钟源
     ● HSE时钟除以128;
     ● LSE振荡器时钟;(比较常用的)
     ● LSI振荡器时钟

2个独立的复位类型:
     ● APB1接口由系统复位;
     ● RTC核心(预分频器、闹钟、计数器和分频器)只能由后备域复位

3个专门的可屏蔽中断
     ● 1.闹钟中断,用来产生一个软件可编程的闹钟中断。

● 2.秒中断,用来产生一个可编程的周期性中断信号(长可达1秒)。

● 3.溢出中断,指示内部可编程计数器溢出并回转为0的状态。

RTC时钟源:

三种不同的时钟源可被用来驱动系统时钟(SYSCLK):

● HSI振荡器时钟
        ● HSE振荡器时钟
        ● PLL时钟

CubexMX时钟树下的选择:

三、OLED简介与使用

关于OLED的使用与原理不熟悉的笔者欢迎去笔者另一篇文章学习【强烈推荐】基于stm32的OLED各种显示实现(含动态图)_混分巨兽龙某某的博客-CSDN博客

四、CubexMX配置

1、RCC配置外部高速晶振(精度更高)——HSE;RTC时钟选择:LSE;

2、SYS配置:Debug设置成Serial Wire否则可能导致芯片自锁);

3、GPIO配置:PA9设置为普通输出(DHT11的DATA接线引脚);

4、RTC配置:年月日,时分秒;

5、TIM2配置:由上面可知DHT11的使用需要us级的延迟函数,HAL库自带只有ms的,所以需要自己设计一个定时器;

6、I2C2配置:作为OLED的通讯方式;

7、时钟树配置

8、工程配置

五、代码部分

5.1 DTH11代码:

要编写出DHT11温湿度传感器的程序代码,需要从DHT11时序图来分析:

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

总结:DHT11的代码实现总共分为6个部分

1、DTH11——DATA引脚配置

2、DHT11复位函数(即MCU发出起始信号)

3、DHT11检查函数

4、读取DHT11一位数据(返回值0/1)

5、读取一个字节(返回值:读到的数据)

6、读取显示温湿度传感器数据

5.1.1 配置输入输出

由于DHT11温湿度传感器采用了单总线通讯的方式,所以DATA引脚连接的PB9必须既要设置成输入也要设置成输出。

/**
  * @brief  设置引脚模式
  * @param  mode: 0->out, 1->in
  * @retval None
  */
static void DHT11_GPIO_MODE_SET(uint8_t mode)
{
    if(mode)
    {
        /*  输入  */
        GPIO_InitTypeDef GPIO_InitStruct;
        GPIO_InitStruct.Pin = GPIO_PIN_9;                   //  9号引脚
        GPIO_InitStruct.Mode = GPIO_MODE_INPUT;             //  输入模式
        GPIO_InitStruct.Pull = GPIO_PULLUP;                 //  上拉输入
        HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
    }
    else 
    {
        /*  输出  */
        GPIO_InitTypeDef GPIO_InitStructure;
        GPIO_InitStructure.Pin = GPIO_PIN_9;                //  9号引脚
        GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;      //  Push Pull 推挽输出模式
        GPIO_InitStructure.Pull = GPIO_PULLUP;              //  上拉输出
        GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH;    //  高速
        HAL_GPIO_Init(GPIOB,&GPIO_InitStructure);
    }
}

5.1.2 DHT11复位函数

复位DHT11就是发送DHT11起始信号,告诉传感器通讯开始。起始信号要求主机MCU先将总线PB9拉为低电平持续至少18ms(这里设置20ms),随后主机MCU将总线PB9设置为高电平持续20~40us(这里取中间值30us,HAL库中没有us,这里需要TIM2定时器制作一个延迟函数)

us延迟函数:

/**
  * @brief  定时器延时us,Prescaler -> 32-1
  * @param  us: <= 65535
  * @retval None
  */
void Tims_delay_us(uint16_t nus)
{
	__HAL_TIM_SET_COUNTER(DLY_TIM_Handle, 0);
	__HAL_TIM_ENABLE(DLY_TIM_Handle);
	while (__HAL_TIM_GET_COUNTER(DLY_TIM_Handle) < nus)
	{
	}
	__HAL_TIM_DISABLE(DLY_TIM_Handle);
}

DTH11复位函数(起始):
/**
  * @brief  温湿度传感器启动信号发送
  * @param  void
  * @retval None
  */
void DHT11_START(void)
{
    DHT11_GPIO_MODE_SET(0);                         //  主机设置为输出模式
    
    DHT11_PIN_RESET;                                //  主机拉低电平
    
    HAL_Delay(20);                                  //  主机等待 18 < ms > 30
    
    DHT11_GPIO_MODE_SET(1);                         //  主机设置为输入模式,等待DHT11答应
}                                                   //  因为设置了上拉输入,GPIO -> 1

5.1.3 DHT11检查函数

检查DHT11是否正常,正常的话会在单片机发送起始信号完成后,传感器返回80us低电平然后发送80us高电平。即证明DHT11工作正常,该函数工作正常返回0,否则返回1,该函数中利用了while循环检测在一定时间内的电平变化,此类用法在后面也会经常用到。(笔者这里采用了另一种检查DHT11是否正常的方式,就是直接延迟40us,虽然读取PB9引脚的数值,为1则正常,为0则重启DHT11即可)

/**
  * @brief  检测温湿度传感器是否存在(检测DHT11的应答信号)
  * @param  void
  * @retval 0/1
  */
unsigned char DHT11_Check(void)
{
    Tims_delay_us(40);
    if(DHT11_READ_IO == 0)                          //  检测到DHT11应答
    {
        return 1;
    }
    else                                            //  检测到DHT11不应答
    {
        return 0;
    }
}

5.1.4 读取DHT11一位数据

这里延时40us后判断引脚电平,来判断该位数据为1或0。之所以是40微秒是因为传感器数字0的信号持续时间为**26-28us。**同时,从笔者日常实验中发现,DHT11中最好不要频繁使用延迟函数取可以检验DHT11是否正常工作,很有可能造成死机或者数据为0。

/**
  * @brief  读取一位数据 1bit
  * @param  void
  * @retval 0/1
  */
unsigned char DHT11_READ_BIT(void)
{
    while(!DHT11_READ_IO);                          //  过度数据的低电平 
    
    Tims_delay_us(40);                              //  过度数据的高电平
    
    if(DHT11_READ_IO)                               //  此时如果还为高电平则数据为 1
    {
        while(DHT11_READ_IO);                       //  过度数据的高电平
        return 1;
    }   
    else                                            //  若此时为低则为 0
    {
        return 0;
    }
}

5.1.5 读取一个字节

循环读入一个字节的数据,并将每一步新加入的数据放置在最低位。

/**
  * @brief  读取一个字节数据 1byte / 8bit
  * @param  void
  * @retval temp
  */
unsigned char DHT11_READ_BYTE(void)
{
    uint8_t i,temp = 0;                             //  暂时存储数据
    
    for(i=0; i<8 ;i++)
    {
        temp <<= 1;                                 
        if(DHT11_READ_BIT())                        //  1byte -> 8bit
        {
            temp |= 1;                              //  0000 0001
        }
    }
    return temp;
}
5.1.6 读取显示温湿度传感器数据

读取数据将数据存入数组,这里仅保留了温度数据的整数位,注意数据较验方法,校验和数据等于“8bit湿度整数数据+8bit湿度小数数据 +8bi温度整数数据+8bit温度小数数据”所得结果的末8位

/**
  * @brief  读取温湿度传感器数据 5byte / 40bit
  * @param  void
  * @retval 0/1/2
  */
unsigned char DHT11_READ_DATA(void)
{
    uint8_t i;
    uint8_t data[5] = {0};
    
    DHT11_START();                                  //  主机发送启动信号
    
    if(DHT11_Check())                               //  如果DHT11应答     
    {  
        while(!DHT11_READ_IO);                      //  过度DHT11答复信号的低电平
        while(DHT11_READ_IO);                       //  过度DHT11答复信号的高电平
        
        for(i=0; i<5; i++)
        {                        
            data[i] = DHT11_READ_BYTE();            //  读取 5byte
        }
        
        if(data[0] + data[1] + data[2] + data[3] == data[4])
        {
            //显示温度
			OLED_ShowCN_STR(0,4,0,2);
			OLED_ShowStr(32,4,":",2);
            OLED_ShowNum(40,4,data[2],2,16);
			OLED_ShowCN_STR(59,4,4,1);
					
			//显示湿度
			OLED_ShowCN_STR(0,6,2,2);
			OLED_ShowStr(32,6,":",2);
			OLED_ShowNum(40,6,data[0],2,16);
			OLED_ShowStr(59,6,"HR",2);
            return 1;                               //  数据校验通过
        }
        else
        {
            return 0;                               //  数据校验失败
        }
    }
    else                                            //  如果DHT11不应答
    {
        return 2;
    }
}

DHT11.C总代码:

#include "dht11.h"
#include "oled.h" 
 
/**
  * @brief  温湿度传感器主函数
  * @param  void
  * @retval None
  */
void DHT11(void)
{
    DHT11_READ_DATA();
    HAL_Delay(50);  		             // 预设一定缓冲
}
 
/**
**自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。**

**深知大多数嵌入式工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!**

**因此收集整理了一份《2024年嵌入式&物联网开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。**

![img](https://img-blog.csdnimg.cn/img_convert/90002678263bf564e6c762c7f86056be.png)

![img](https://img-blog.csdnimg.cn/img_convert/e325227423a95b6de43f0b91b6557501.jpeg)

![img](https://img-blog.csdnimg.cn/img_convert/8846a002ae6bd2281075c85d09de1a7c.png)

 **既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上嵌入式&物联网开发知识点,真正体系化!**

![img](https://img-blog.csdnimg.cn/img_convert/0a1d03f05c59b365f3c1e3351ec6f3f7.png)

![img](https://img-blog.csdnimg.cn/img_convert/cb3f01d0c6aa18299e3928ccb4bcfbac.png)

 

**由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新**

**如果你觉得这些内容对你有帮助,可以+V:Vip1104z获取!!! (备注:嵌入式)**

<img src="https://img-community.csdnimg.cn/images/73bb5de17851459088c6af944156ee24.jpg" alt="img" style="zoom: 67%;" />



# 最后

**资料整理不易,觉得有帮助的朋友可以帮忙点赞分享支持一下小编~**

**你的支持,我的动力;祝各位前程似锦,offer不断,步步高升!!!**

875)]

[外链图片转存中...(img-4FZ0eKol-1712384859877)]

[外链图片转存中...(img-8DuILRDz-1712384859878)]

 **既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上嵌入式&物联网开发知识点,真正体系化!**

[外链图片转存中...(img-Dek79tH5-1712384859878)]

[外链图片转存中...(img-hIexjzFP-1712384859879)]

 

**由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新**

**如果你觉得这些内容对你有帮助,可以+V:Vip1104z获取!!! (备注:嵌入式)**

<img src="https://img-community.csdnimg.cn/images/73bb5de17851459088c6af944156ee24.jpg" alt="img" style="zoom: 67%;" />



# 最后

**资料整理不易,觉得有帮助的朋友可以帮忙点赞分享支持一下小编~**

**你的支持,我的动力;祝各位前程似锦,offer不断,步步高升!!!**

**[更多资料点击此处获qu!!](https://bbs.csdn.net/topics/618376385)**
  • 13
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,您需要了解关于如何使用STM32 HAL库读取DHT11温湿度传感器的数据。 首先,您需要按照DHT11的规格书连接DHT11传感器到您的STM32开发板上。一般来说,DHT11可以通过一个数据线连接到开发板的GPIO端口上。 接下来,您需要使用STM32 HAL库中的GPIO和延时函数来读取DHT11传感器的数据。以下是一个示例代码: ```c #include "stm32f1xx_hal.h" #include <stdbool.h> #define DHT11_GPIO_PORT GPIOA #define DHT11_GPIO_PIN GPIO_PIN_0 bool read_dht11(uint8_t *rh_int, uint8_t *rh_dec, uint8_t *t_int, uint8_t *t_dec) { uint8_t data[5] = {0}; // 发送起始信号 HAL_GPIO_WritePin(DHT11_GPIO_PORT, DHT11_GPIO_PIN, GPIO_PIN_RESET); HAL_Delay(18); // 最少拉低18ms HAL_GPIO_WritePin(DHT11_GPIO_PORT, DHT11_GPIO_PIN, GPIO_PIN_SET); HAL_Delay(20); // 拉高20~40us HAL_GPIO_WritePin(DHT11_GPIO_PORT, DHT11_GPIO_PIN, GPIO_PIN_RESET); HAL_Delay(20); // 拉低80us // 等待DHT11响应 HAL_GPIO_ReadPin(DHT11_GPIO_PORT, DHT11_GPIO_PIN); if (HAL_GPIO_ReadPin(DHT11_GPIO_PORT, DHT11_GPIO_PIN) == GPIO_PIN_RESET) { HAL_Delay(80); // 等待DHT11响应 if (HAL_GPIO_ReadPin(DHT11_GPIO_PORT, DHT11_GPIO_PIN) == GPIO_PIN_SET) { HAL_Delay(50); // 等待DHT11结束响应 // 读取40位数据 for (int i = 0; i < 40; i++) { // 等待数据线拉高 while (HAL_GPIO_ReadPin(DHT11_GPIO_PORT, DHT11_GPIO_PIN) == GPIO_PIN_RESET) {} uint32_t t = 0; while (HAL_GPIO_ReadPin(DHT11_GPIO_PORT, DHT11_GPIO_PIN) == GPIO_PIN_SET) { t++; if (t > 100) return false; // 超时 HAL_Delay(1); } data[i / 8] <<= 1; if (t > 50) data[i / 8] |= 1; } // 验证数据 if (data[4] == ((data[0] + data[1] + data[2] + data[3]) & 0xFF)) { *rh_int = data[0]; *rh_dec = data[1]; *t_int = data[2]; *t_dec = data[3]; return true; } } } return false; } ``` 这个函数的作用是读取DHT11传感器的湿度和温度数据,并将结果存储在传入的指针参数中。如果读取成功,函数返回true,否则返回false。 在函数内部,我们首先发送起始信号,然后等待DHT11的响应。如果DHT11正确响应,我们就读取40位数据,然后验证数据的正确性。最后,将湿度和温度数据存储在传入的指针参数中,并返回true。 请注意,这只是一个简单的示例代码,并且可能需要根据您的具体情况进行修改。例如,您可能需要调整延时时间以确保正确的数据读取。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值