前言
基于stm32f103C6T6核心板+STM32CubeMX的DHT11的使用。其实吧,要不是网上关于stm32调用DHT11的代码大多数用不了或者乱七八糟的,我也不想推时序的,嘎嘎麻烦。写着写着就发现,本章的篇幅有点长了,所以本文就先介绍DHT11的时序流程,和我写的关键库,和经验。在详细的程序应用介绍和工程文件的话在写另一篇。
常见问题
-
STM32像DHT11发出控制指令,DHT11无响应(低电平响应),并且在示波器或逻辑分析仪中看到,没有后续时序。
答:这种情况大概率是STM32引脚的输入,输出模式没有切换。引脚还强制在输出模式下,锁定在某个电平。
-
STM32像DHT11发出控制指令,检测不到DHT11的响应(低电平响应),并且在示波器或逻辑分析仪中看到,可以看到有后续时序。
答:可能是控制指令的时序错了,拉低的时间是ms级,等待反应的时间是us级。注意检测的时间。并且在STM32中,一般函数只提供ms级的函数延迟,us级的话,要自己通过主频+NOP()的调用,反推出来。
-
STM32读取DHT11的数据时,只能读取到一次,即使是放在主循环中也没用,也已经确保不是DHT中的判断时序的循环卡住。
答:这个应该还是比较少见的。但是我遇见了,就顺带讲了。我出现这个问题是STM32的引脚切换函数中的引脚的配置结构,有一个引脚速率空缺了,以为没事,但是确出问题了。所以说啊,大家一定要养成好习惯,配置结构一定要配置完啊。
DHT11
-
基本参数
简介:DHT11数字温湿度传感器是一款含有已校准数字信号输出的温湿度复合传感器。它应用专用的数字模块采集技术和温湿度传感技术,确保产品具有可靠性与卓越的长期稳定性,成本低、相对湿度和温度测量、快响应、抗干扰能力强、信号传输距离长、数字信号输出、精确校准。传感器包括一个电容式感湿元件和一个NTC 测温元件。
下面是网上常见的DHT11形状和引脚介绍,一般商家制作成模块时只会引出三个引脚,NC脚不引出,并且在模块上已经会有DATA引脚的上拉电阻和电源指示灯了,不过没有防反接保护,所以说接线时一定要注意。
型号 工作电压 温度检测范围 湿度检测范围 DHT11 3.3-5.5V 0°C~50°C 20%RH-90%RH(2℃) - 温度分辨率:1℃ 温度精度:±2℃
- 湿度分辨率:1%RH 湿度精度: ±5%RH(0-50)℃
-
时序介绍
主要的三大块时序:DHT11触发采集时序,DHT11高电平时序,DHT11低电平时序
-
文字流程:
读取DHT11数据的文字流程,使用STM32作为主机:
- 主机将DHT11数据引脚拉为低电平,持续至少18ms。
- 主机将DHT11数据引脚拉为高电平,持续20~40us,等待DHT11响应。
- 如果DHT11存在,则会有80us的响应信号,此时数据引脚会被拉为低电平,否则数据引脚始终为高电平。
- 接下来控制权转移给DHT11,DHT11会继续把数据引脚拉高80us,然后开始返回数据。
- 首先,DHT11会拉低数据引脚50us,表示开始传输数据位。
- 接下来便是DHT11发送数据位,数据位的高低电平取决于数据位拉高的时间:如果数据位高电平的时间为26us~28us,则代表数据位是低电平 ‘0’;如果数据位高电平的时间为70us,则代表数据位是高电平 ‘1’。
-
数据结构:
40位数据=8bit湿度整数数据+8bit湿度小数数据+8bit温度整数数据+8bit温度小数数据+8bit校验位
-
-
DHT11触发采集时序
主机将DHT11数据引脚拉为低电平,持续至少18ms。主机将DHT11数据引脚拉为高电平,持续20~40us,等待DHT11响应。接下来控制权转移给DHT11,如果DHT11存在,则会有80us的响应信号,此时数据引脚会被DHT11拉为低电平,否则数据引脚始终为高电平。DHT11会继续把数据引脚拉高80us,然后开始返回数据。
-
DHT11高电平时序
DHT11将数据引脚拉低50us,表示要发送数据位。DHT11将数据引脚拉高,持续70us,表示数据位是1。
-
DHT11低电平时序
DHT11将数据引脚拉低50us,表示要发送数据位。DHT11将数据引脚拉高,持续26~28us,表示数据位是0。
-
DHT11总时序
当DHT11开始发送数据时,主机就要切换到输入模式,并对信号判断是’0’还是’1’。
在强调一下,经过对上述文件描述和图片的分析可以发现,DHT11传输数据时,并不是通过数据引脚的高低电平差来表示数据信息。而是,通过数据位高电平的时长来表示数据的信息。
我的代码
下面我用逻辑分析仪,OLED,计算器调试的时候,可以看到DHT11有响应数据,而且经过计算也符合校验的。OLED上的,第一行是第一个八位数据和第二个八位数据(也就是湿度数据),第二行是第三个八位数据和第四个八位数据(也就是温度数据),第三行是校验和。第四行的不用管。
代码部分我也经量用了宏定义来调用,可能方便移植到其它单片机使用。花了傍晚和早上写的可能还有点粗糙哈。也欢迎大家提建议和说看法。
实践环境:代码是基于STM32CubeMX生成的,主频是72MHz,DHT11的DATA引脚接到PA1。
-
DHT.C
#include "main.h" #include "DHT11.h" //控制接口 #define DHT11_DataPinx GPIO_PIN_1 #define DHT11_DataGPIOx GPIOA #define DHT11_DataH() HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_SET) #define DHT11_DataL() HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET) #define DHT11_DataR() HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_1) #define DHT11_5NOP(); __NOP;__NOP;__NOP;__NOP;__NOP; //数据存放数组 unsigned char DHT11_DATA[5]; //DHT11毫秒延时 //参数1: Xms延时 void DHT11_DelayMs(int ms) { HAL_Delay(ms); } //DHT11的10微秒延时 void DHT11_Delay10Us() { for(char i=100;i>0;i--) { DHT11_5NOP(); } } //DHT11数据引脚设置为输入模式 void DHT11_DataSetIn() { GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitStruct.Pin = DHT11_DataPinx; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_PULLUP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(DHT11_DataGPIOx, &GPIO_InitStruct); } //DHT11数据引脚设置为输出模式 void DHT11_DataSetOut() { GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitStruct.Pin = GPIO_PIN_1; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(DHT11_DataGPIOx, &GPIO_InitStruct); } //判断数据位 //返回值: char DHT11_IfBit() { char Start; char StartNum=0; do{ DHT11_Delay10Us(); Start=HAL_GPIO_ReadPin(DHT11_DataGPIOx,DHT11_DataPinx); StartNum++; }while(Start); //电平判断 if(StartNum<5) { Start=0; } else { Start=1; } return Start; } //等待收集数据 void DHT11_ReadData() { char Start; DHT11_Init(); DHT11_DataSetIn(); //拉高判断 do{ Start=HAL_GPIO_ReadPin(DHT11_DataGPIOx,DHT11_DataPinx); }while(!Start); //拉低判断 do{ Start=HAL_GPIO_ReadPin(DHT11_DataGPIOx,DHT11_DataPinx); }while(Start); //拉高判断 do{ Start=HAL_GPIO_ReadPin(DHT11_DataGPIOx,DHT11_DataPinx); }while(!Start); //第一个数据开始传输 //数组索引0~4 for(char i=0;i<5;i++) { for(char a=0;a<8;a++) { Start=DHT11_IfBit(); DHT11_DATA[i]=DHT11_DATA[i]|Start; //拉高判断 do{ Start=HAL_GPIO_ReadPin(DHT11_DataGPIOx,DHT11_DataPinx); }while(!Start); if(a>=7) break; DHT11_DATA[i]=DHT11_DATA[i]<<1; } } //设置为输出模式 DHT11_DataSetOut(); } //DHT11初始化 //返回值: 0.有响应 1.无响应 char DHT11_Init(void) { char Start=1; //主机拉低至少20ms DHT11_DataL(); DHT11_DelayMs(20); //主机拉高至少20us-40us DHT11_DataH(); DHT11_Delay10Us(); DHT11_Delay10Us(); DHT11_Delay10Us(); //设置为输入模式 DHT11_DataSetIn(); Start=HAL_GPIO_ReadPin(DHT11_DataGPIOx,DHT11_DataPinx); //设置为输出模式 DHT11_DataSetOut(); return Start; } //DHT11获取数据 //参数: 0:返回湿度 1:返回温度 float DHT11_GetData(char Mode) { float ReData; switch(Mode) { case 0: ReData=DHT11_DATA[0]+DHT11_DATA[1]/255.0f; break; case 1: ReData=DHT11_DATA[2]+DHT11_DATA[3]/255.0f; break; } return ReData; }
-
DHT11.H
#ifndef __DHT11_H__ #define __DHT11_H__ char DHT11_Init(void); void DHT11_ReadData(void); float DHT11_GetData(char Mode); #endif