STM32复习(三)— DHT11读取温湿度数据并显示在串口助手上
一、DHT11介绍
DHT11 数字温湿度传感器是一款具有已校准数字信号输出的温湿度复合传感器,内部由一个 8 位单片机控制一个电阻式感湿元件和一个 NTC 测温元件,其采用单总线协议,但与其他单总线协议略有不同,它既能测量温度又能测量湿度,温度测量范围为 0~50℃,误差在 ±2℃;湿度测量范围为 20%~90%RH,相对湿度误差为 ±5%RH。DHT11 的电路结构简单,只需将 DATA 引脚连接到单片机的 I/O 引脚,并在该引脚上拉一个 5K 电阻,供电电压为 3~5.5V。
二、DHT11接口说明
1.接线图
DHT11引脚说明:
Pin | 名称 | 注释 |
1 | VDD | 供电3~5.5v |
2 | GND | 接地,电源负极 |
3 | DATA | 串行数据,单总线 |
4 | NC | 空脚,悬空 |
注意:1.DHT11的供电电压为3~5.5 V。传感器上电后,要等待 1s 以越过不稳定状态,在此期间无需发送任何指令。
2.DATA 用于微处理器与 DHT11之间的通讯和同步,采用单总线数据格式,一次通讯时间4ms左右,数据分小数部分和整数部分。
三、DHT11数据格式
DHT11 数字湿温度传感器采用单总线数据格式,即单个数据引脚端口完成输入输出双向传输。其数据包由 5Byte(40Bit)组成。数据分小数部分和整数部分,一次完整的数据传输为40bit,高位先出。DHT11的数据格式为:
8bit湿度整数数据 + 8bit湿度小数数据 + 8bit温度整数数据 + 8bit温度小数数据 + 8bit校验和
其中8bit校验和的值为前四个字节相加。传感器数据输出的是未编码的二进制数据。数据(湿度、温度、整数、小数)之间应该分开处理。例如,某次从DHT11读到的数据如下表所示。
Byte4 | Byte3 | Byte2 | Byte1 | Byte0 |
0011 0101 | 0000 0000 | 0001 1000 | 0000 0000 | 0100 1101 |
湿度高八位(整数) | 湿度低八位(小数) | 温度高八位(整数) | 温度低八位(小数) | 检验位 |
湿度为:0011 0101(整数) + 0000 0000(小数) = 53%RH
温度为:0001 1000(整数) + 0000 0000(小数) = 24℃
校验位:Byte4+Byte3+Byte2+Byte1=Byte0=77 (数据正确)
四、操作时序
上图中每个信号的详细说明如下:t1时刻:主机(MCU)发送开始信号,即:拉低数据线,保持至少18ms;t2时刻:主机(MCU)拉高数据线20~40us,然后判断DHT11是否响应;t3时刻:DHT11拉低数据线40~50us,作为响应信号;t4时刻:DHT11 拉高数据线,保持40~50us后,开始输出数据。
如何判断输出的数据是0还是1?
当数据位之前的 12~14us 低电平时隙过后,总线肯定会拉高,此时延时 40us 后检测总线状态,如果为高,说明此时处于 116~118us 的时隙,则数据为“1”;如果为低,说明此时处于下一位数据 12~14us 的开始时隙,那么上一位数据肯定是“0”。
五、STM32例程
DHT11.h
#ifndef __DHT11_H__
#define __DHT11_H__
#define data1 GPIO_SetBits(GPIOA, GPIO_Pin_5)
#define data0 GPIO_ResetBits(GPIOA, GPIO_Pin_5)
#define read_data GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_5)
void DHT11_GPIO_Init(void);
void DHT11_GPIO_Init2(void);
void DHT11_Start(void);
u8 DHT11_Read_Data(u8 *temp,u8 *humi);
#endif
DHT11.c
#include "stm32f10x.h"
#include "dht11.h"
#include "delay.h"
#include "stdio.h"
static u8 DHT11_Check(void);
static uint8_t DHT11_Read_Bit(void);
static u8 DHT11_Read_Byte(void);
void DHT11_GPIO_Init(void) //对于STM32来说是输出状态(用来给DHT11发送信号)
{
GPIO_InitTypeDef gpio_initstruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
gpio_initstruct.GPIO_Mode = GPIO_Mode_Out_PP;
gpio_initstruct.GPIO_Pin = GPIO_Pin_5;
gpio_initstruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&gpio_initstruct);
}
void DHT11_GPIO_Init2(void) //对于STM32来说是输入状态(用来接收DHT11传回的信号以及数据)
{
GPIO_InitTypeDef gpio_initstruct;
gpio_initstruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
gpio_initstruct.GPIO_Pin = GPIO_Pin_5;
gpio_initstruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&gpio_initstruct);
}
void DHT11_Start(void) //DHT11初始化
{
DHT11_GPIO_Init(); //首先设置为输出状态
data0; //设置DATA引脚为低电平
delay_ms(18); //延时18ms来让DHT11检测
data1; //设置DATA引脚为高电平
delay_us(20); //延时20us
DHT11_GPIO_Init2(); //设置引脚为输入模式来检测DHT11是否工作
delay_us(20);
}
static u8 DHT11_Check(void) //超时机制来检测DHT11是否工作
{
u8 retry=0;
while (read_data==1 && retry<100)//DHT11会拉低40~50us(如果100us内引脚没有变为低电平则返回0,说明DHT11没有工作)
{
retry++;
delay_us(1);
}
if(retry >= 100) //超时未响应/未收到开始信号,退出检测
{
printf("20 \r\n");//while(1);
return 0;
}
else
retry = 0;
while (read_data==0 && retry<100)//DHT11拉低后会再次拉高40~50us(如果100us内引脚没有变为高电平则返回0,说明DHT11没有工作)
{
retry++;
delay_us(1);
}
if(retry >= 100)
{
printf("40 \r\n");
return 0;
} //超时,DHT11工作出错,退出检测
return 1; //设备正常响应,可以正常工作
}
static uint8_t DHT11_Read_Bit() //读取1bit的数据
{
u8 retry = 0; //DHT11的Bit开始信号为12-14us低电平
while(read_data==1 && retry < 100) //等待变为低电平(等待Bit开始信号)
{
retry++;
delay_us(1);
}
retry = 0;
while(read_data==0 && retry < 100) //等待变高电平(代表数据开始传输)
{
retry++;
delay_us(1);
}
delay_us(40);//等待40us
//0信号为26-28us,1信号则为116-118us,所以说超过30us去读取引脚状态就可以知道传输的值了
if(read_data==1) return 1;
else return 0;
}
static u8 DHT11_Read_Byte(void) //读取1byte数据
{
u8 i,dat;
dat=0;
for (i=0;i<8;i++)
{
dat <<= 1; //数据左移一位
dat |= DHT11_Read_Bit();
}
return dat;
}
u8 DHT11_Read_Data(u8 *temp,u8 *humi) 将读取的数据赋给*temp和*humi
{
u8 buf[5];
u8 i;
delay_s(1); //一定要先延时1s让DHT11度过不稳定状态
DHT11_Start();
if(DHT11_Check()==1) //设备响应正常
{
for(i=0;i<5;i++)//读取40位数据
{
buf[i]=DHT11_Read_Byte();
}
if((buf[0]+buf[1]+buf[2]+buf[3])==buf[4])//进行校验
{
*humi=buf[0];
*temp=buf[2];
}
}else return 0; //设备未成功响应,返回0
return 1; //读取数据成功返回1
}
main.c
#include "stm32f10x.h" // Device header
#include "stdio.h"
#include "dht11.h"
#include "delay.h"
#include "Usart.h"
#include "OLED.h"
int main(void)
{
OLED_Init();
my_Usart_Init();
my_Usart_Init2();
u8 tem1=0;
u8 hum1=0;
OLED_ShowString(1,1,"Airhumi:");
OLED_ShowString(2,1,"Airtemp:");
while(1)
{
if(DHT11_Read_Data(&tem1,&hum1)==1)
{
printf("\r\n temp: %d,humi: %d",tem1,hum1); //printf的函数在Usart中,这里不过多展示,作用就是将数据打印到串口上
OLED_ShowNum(1,9,hum1,2); //OLED屏显示湿度数据
OLED_ShowNum(2,9,tem1,2); //OLED屏显示温度数据
}
}
}
五、串口显示以及OLED显示数据
作者:do_while__