工程介绍
本工程使用标准库和HAL库实现STM32F103读取DHT11数据,并将数据显示在OLED上。
所需项目器材有:
STMF103C8T6最小系统板
DHT11温湿度传感器
0.96寸4针脚IIC OLED屏幕
实物演示
OLED显示Temp为当前环境温度值,Humi为当前环境湿度
工程接线
DHT11温湿度传感器 | STM32F103 |
DATA | PB12 |
VCC | 3.3V |
GND | GND |
OLED屏幕 | STM32F103 |
SCK | PB6 |
SDA | PB7 |
VCC | 3.3V |
GND | GND |
模块介绍
DHT11介绍
DHT11数字温湿度传感器是一款含有已校准数字信号输出的温湿度复合传感器。传感器包括一个电阻式感湿元件和一个NTC测温元件,并与一个高性能8位单片机相连接。具有品质卓越、超快响应、抗干扰能力强、性价比极高等优点。
DHT11数据位
DHT11采用单线双向的串行接口,DHT11之间的通讯和同步,采用单总线数据格式,一次通讯时间4ms左右,数据分小数部分和整数部分。
一次发送40bit数据,采用高位先行的策略
发送数据格式:
8bit湿度整数数据+8bit湿度小数数据+8bit温度整数数据+8bit温度小数数据+8bit校验数据
数据正确时,校验数据 = 8bit湿度整数数据+8bit湿度小数数据+8bit温度整数数据+8bit温度小数数据
DHT11时序
通信总时序:
总线空闲状态为高电平,主机要想想DHT11通信首先要拉低电平,然后再拉高电平等待DHT11响应。DHT11收到通信信号后会先拉低总线,随后再拉高总线用来回复主机。
接下DHT11进入数据发送。
启动DHT11时序详解:
启动时序逻辑如下:
主机发送模式
主机拉低信号线至少18ms
主机拉高信号线20-40us
主机接收模式
数据位0和1时序详解:
数据0时序
数据1时序
判断数据位0和1的方法:
0和1数据位的区别主要在高电平时间的长短,所以我们可以利用延迟读取电平的方法判断数据位。数据位开始前等待低电平出现,检测到低电平则证明数据位开始。然后等待高电平出现,出现高电平证明数据位已经产生。随后我们延迟40us再检测电平,如果此时检测到是高电平则证明该数据位是1,否则检测到低电平则该数据位是0。
以下是伪代码流程:
// 等待数据位开始(检测低电平)
while (电平状态 == 高):
// 空循环,持续检测电平
do_nothing()
// 检测到低电平,数据位开始
// 等待高电平出现(数据位已产生)
while (电平状态 == 低):
// 空循环,持续检测电平
do_nothing()
// 高电平出现,数据位已产生
// 延迟40微秒后检测电平状态
delay(40 us)
if (当前电平 == 高):
return 1 // 高电平代表数据位1
else:
return 0 // 低电平代表数据位0
代码
DHT11.h
#ifndef __DHT11_H
#define __DHT11_H
#define DHT11_PROT GPIOB
#define DHT11_PIN GPIO_Pin_12
uint8_t DHT11_ReadData(uint8_t *Temp,uint8_t *Humi);
uint8_t DHT11_ReadByte(void);
void DHT11_Rst(void);
void Master_OutputMode(void);
void Master_InputMode(void);
void DHT11_Init(void);
#endif
DHT11.c
#include "stm32f10x.h" // Device header
#include "DHT11.h"
#include "Delay.h"
uint8_t DHT11_ReadData(uint8_t *Temp,uint8_t *Humi)
{
DHT11_Rst(); //发送信号
GPIO_SetBits(DHT11_PROT,DHT11_PIN); //拉高电平
if(GPIO_ReadInputDataBit(DHT11_PROT,DHT11_PIN) == 0) //判断DHT11是否有响应
{
while(GPIO_ReadInputDataBit(DHT11_PROT,DHT11_PIN) == 0); //等待低电平结束
while(GPIO_ReadInputDataBit(DHT11_PROT,DHT11_PIN) == 1); //等待高电平结束
uint8_t Data[5] = {0};
for(int i=0;i<5;i++) //循环读取5次
{
Data[i] = DHT11_ReadByte();
}
GPIO_ResetBits(DHT11_PROT,DHT11_PIN); //拉低电平
Delay_us(55); //延迟等待
GPIO_SetBits(DHT11_PROT,DHT11_PIN); //拉高电平
if(Data[0]+Data[1]+Data[2]+Data[3] == Data[4]) //数据校验
{
*Humi = Data[0];
*Temp = Data[2];
}
return 1;
}
return 2;
}
uint8_t DHT11_ReadByte(void)
{
uint8_t Byte=0;
for(int i=0;i<8;i++) //循环读取8次 一个字节
{
while(GPIO_ReadInputDataBit(DHT11_PROT,DHT11_PIN) == 0); //等待上一个低电平结束
Delay_us(30); //延迟30us 再判断
Byte <<= 1; //数据左移1位 准备接收新的bit
if(GPIO_ReadInputDataBit(DHT11_PROT,DHT11_PIN) == 1) //如果此时是高电平 证明发送的是1 否则是0
{
Byte |= 1; //或上新的bit
}
while(GPIO_ReadInputDataBit(DHT11_PROT,DHT11_PIN) == 1); //等待高电平结束
}
return Byte;
}
void DHT11_Rst(void)
{
Master_OutputMode(); //主机切换为输出模式
GPIO_ResetBits(DHT11_PROT,DHT11_PIN); //输出低电平
Delay_ms(20); //至少20ms
GPIO_SetBits(DHT11_PROT,DHT11_PIN); //输出高电平
Delay_us(30); //拉高20-40us
Master_InputMode(); //主机切换为输出模式
}
void Master_OutputMode(void) //主机输出模式 用于发送指令
{
GPIO_InitTypeDef GPIO_InitStrcuture;
GPIO_InitStrcuture.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStrcuture.GPIO_Pin = DHT11_PIN;
GPIO_InitStrcuture.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(DHT11_PROT,&GPIO_InitStrcuture);
}
void Master_InputMode(void) //主机输入模式 用于接收数据
{
GPIO_InitTypeDef GPIO_InitStrcuture;
GPIO_InitStrcuture.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStrcuture.GPIO_Pin = DHT11_PIN;
GPIO_InitStrcuture.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(DHT11_PROT,&GPIO_InitStrcuture);
}
void DHT11_Init(void)
{
//开启GPIOB时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
//开启默认输入模式
GPIO_InitTypeDef GPIO_InitStrcuture;
GPIO_InitStrcuture.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStrcuture.GPIO_Pin = DHT11_PIN;
GPIO_InitStrcuture.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(DHT11_PROT,&GPIO_InitStrcuture);
}
Main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "DHT11.h"
uint8_t Temp=0,Humi=0;
int main(void)
{
OLED_Init();
DHT11_Init();
DHT11_Rst(); //DHT11复位
OLED_ShowString(1,1,"Temp:");
OLED_ShowString(2,1,"Humi:");
while (1)
{
DHT11_ReadData(&Temp,&Humi); //获取DHT11数据
OLED_ShowNum(1,6,Temp,2);
OLED_ShowNum(2,6,Humi,2);
Delay_ms(1000);
}
}