目录
开发板为野火STM32F103RCT6min开发板
一、DHT11模块介绍
1.产品概述
DHT11数字温湿度传感器是一款含有已校准数字信号输出的温湿度复合传感器。 它应用专用的数字模块采集技术和温湿度传感技术,确保产品具有极高的可靠性与卓越的长期稳定性。传感器包括一个电阻式感湿元件和一个NTC测温元件,并与一个高性能8位单片机相连接。因此该产品具有品质卓越、超快响应、抗干扰能力强、性价比极高等优点。每个DHT11传感器都在极为精确的湿度校验室中进行校准。校准系数以程序的形式储存在OTP内存中,传感器内部在检测信号的处理过程中要调用这些校准系数。 单线制串行接口,使系统集成变得简易快捷。超小的体积、极低的功耗, 信号传输距离可达20米以上。
引脚说明:
2.传感器的参数
3.应用电路
4.时序介绍
DATA 用于微处理器与 DHT11之间的通讯和同步,采用单总线数据格式,一次通讯时间4ms左右,数据分小数部分和整数部分,具体格式在下面说明,当前小数部分用于以后扩展,现读出为零。
一次完整的数据传输为40bit,高位先出。数据格式:8bit湿度整数数据+8bit湿度小数数据+8bi温度整数数据+8bit温度小数数据+8bit校验和
数据传送正确时校验和数据等于“8bit湿度整数数据+8bit湿度小数数据+8bi温度整数数据+8bit温度小数数据” 所得结果的末8位。
用户MCU发送一次开始信号后,DHT11从低功耗模式转换到高速模式,等待主机开始信号结束后,DHT11发送响应信号,送出40bit的数据,并触发一次信号采集,用户可选择读取部分数据.从模式下,DHT11接收到开始信号触发一次温湿度采集,如果没有接收到主机发送开始信号,DHT11不会主动进行温湿度采集.采集数据后转换到低速模式。
总线空闲状态为高电平,主机把总线拉低等待DHT11响应,主机把总线拉低必须大于18毫秒,保证DHT11能检测到起始信号。 DHT11接收到主机的开始信号后,等待主机开始信号结束,然后发送80us低电平响应信号.主机发送开始信号结束后,延时等待20-40us后, 读取DHT11的响应信号,主机发送开始信号后,可以切换到输入模式,或者输出高电平均可, 总线由上拉电阻拉高。
5.模块
模块如图所示,
原理图如下
二、模块代码
DHT11.h
#ifndef __DHT11_H
#define __DHT11_H
#include "stm32f10x.h"
// DHT11 连接的 GPIO 端口和引脚
#define DHT11_GPIO_PORT GPIOB
#define DHT11_GPIO_PIN GPIO_Pin_0
#define DHT11_RCC RCC_APB2Periph_GPIOB
// DHT11 数据结构体
typedef struct {
uint8_t humidity_int; // 湿度整数部分
uint8_t humidity_dec; // 湿度小数部分
uint8_t temperature_int; // 温度整数部分
uint8_t temperature_dec; // 温度小数部分
uint8_t checksum; // 校验和
} DHT11_Data;
// 初始化 DHT11 传感器
void DHT11_Init(void);
// 读取 DHT11 数据,成功返回 0,失败返回非 0
uint8_t DHT11_ReadData(DHT11_Data *data);
#endif
DHT11.c
延时本来想用自己封装的延时函数,结果运行结果不显示,延时函数应该有误差,而DHT11对时间的要求非常精确,这一块调用延时函数时自己注意一下。
#include "DHT11.h"
// **精准延时函数**
static void Delay_us(uint32_t us) {
us *= 9; // 适配 72MHz 主频(1us 需要约 9 次空操作)
while (us--) __NOP(); // 执行空指令,达到延时效果
}
// **初始化 DHT11 传感器**
void DHT11_Init(void) {
GPIO_InitTypeDef GPIO_InitStructure;
// **使能 GPIO 端口时钟**
RCC_APB2PeriphClockCmd(DHT11_RCC, ENABLE);
// **配置 DHT11 数据线(初始为输出模式)**
GPIO_InitStructure.GPIO_Pin = DHT11_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; // 推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // 50MHz 速度
GPIO_Init(DHT11_GPIO_PORT, &GPIO_InitStructure);
GPIO_SetBits(DHT11_GPIO_PORT, DHT11_GPIO_PIN); // 默认拉高,准备通信
}
// **设置 GPIO 为输出模式**
static void DHT11_SetOutput(void) {
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = DHT11_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; // 设置为推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(DHT11_GPIO_PORT, &GPIO_InitStructure);
}
// **设置 GPIO 为输入模式**
static void DHT11_SetInput(void) {
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = DHT11_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // 设置为上拉输入
GPIO_Init(DHT11_GPIO_PORT, &GPIO_InitStructure);
}
// **读取 1 个字节(8 位数据)**
static uint8_t DHT11_ReadByte(void) {
uint8_t i, byte = 0;
uint32_t timeout;
for (i = 0; i < 8; i++) {
// **等待数据线拉低(DHT11 发送 0/1 的起始信号)**
timeout = 10000;
while (!GPIO_ReadInputDataBit(DHT11_GPIO_PORT, DHT11_GPIO_PIN)) {
if (--timeout == 0) return 0; // 超时返回 0
}
Delay_us(35); // **延时 35us 判断数据位**
if (GPIO_ReadInputDataBit(DHT11_GPIO_PORT, DHT11_GPIO_PIN)) {
byte |= (1 << (7 - i)); // **如果数据线仍然为高,记录 1**
// **等待数据线回到低电平**
timeout = 10000;
while (GPIO_ReadInputDataBit(DHT11_GPIO_PORT, DHT11_GPIO_PIN)) {
if (--timeout == 0) return 0;
}
}
}
return byte; // **返回读取到的 8 位数据**
}
// **读取 DHT11 传感器数据**
uint8_t DHT11_ReadData(DHT11_Data *data) {
uint8_t buffer[5] = {0}; // **存储 5 字节数据**
uint8_t i;
uint32_t timeout;
// **1. 发送起始信号**
DHT11_SetOutput(); // **配置 GPIO 为输出模式**
GPIO_ResetBits(DHT11_GPIO_PORT, DHT11_GPIO_PIN); // **拉低数据线**
Delay_us(18000); // **延时 18ms,唤醒 DHT11**
GPIO_SetBits(DHT11_GPIO_PORT, DHT11_GPIO_PIN); // **释放数据线**
Delay_us(30); // **延时 30us 等待响应**
DHT11_SetInput(); // **切换为输入模式**
// **2. 等待 DHT11 响应**
timeout = 10000;
while (GPIO_ReadInputDataBit(DHT11_GPIO_PORT, DHT11_GPIO_PIN)) {
if (--timeout == 0) return 1; // 超时返回 1(无响应)
}
timeout = 10000;
while (!GPIO_ReadInputDataBit(DHT11_GPIO_PORT, DHT11_GPIO_PIN)) {
if (--timeout == 0) return 1;
}
timeout = 10000;
while (GPIO_ReadInputDataBit(DHT11_GPIO_PORT, DHT11_GPIO_PIN)) {
if (--timeout == 0) return 1;
}
// **3. 读取 5 字节数据**
for (i = 0; i < 5; i++) {
buffer[i] = DHT11_ReadByte(); // **逐字节读取数据**
}
// **4. 校验数据**
if ((buffer[0] + buffer[1] + buffer[2] + buffer[3]) != buffer[4]) {
return 2; // **校验失败**
}
// **5. 存储数据**
data->humidity_int = buffer[0]; // 湿度整数部分
data->humidity_dec = buffer[1]; // 湿度小数部分(一般为 0)
data->temperature_int = buffer[2]; // 温度整数部分
data->temperature_dec = buffer[3]; // 温度小数部分(一般为 0)
return 0; // **读取成功**
}
main.c
int main(void)
{
DHT11_Data dht11_data;
char buffer[20];
uint8_t retries; // 读取重试次数
delay_init();
MyI2C1_Init();
DS3231_Init(); //DS3231初始化
LED_GPIO_Config();
Serial_Init();
OLED_Init();
DHT11_Init();
// Set_DS3231_Time(25,3,15,12,00,00,6);//设置时间为2024年10月29日15时00分00秒星期2,下载一次之后,需要注释掉重新下载
// Get_DS3231_Time(); //获取DS3231的时间
while(1)
{
retries = 5; // 允许最多 5 次读取尝试
while (retries--) {
if (DHT11_ReadData(&dht11_data) == 0) {
// 读取成功,显示数据
printf("\r\n读取DHT11成功!\r\n\r\n温度为 %d.%d℃ ,湿度为%d.%d%",\
dht11_data.temperature_int,dht11_data.temperature_dec,dht11_data.humidity_int,dht11_data.humidity_dec);
sprintf(buffer, "T: %d.%dC", dht11_data.temperature_int, dht11_data.temperature_dec);
OLED_ShowString(1, 2, buffer);
sprintf(buffer, "H: %d.%d%%", dht11_data.humidity_int, dht11_data.humidity_dec);
OLED_ShowString(3, 2, buffer);
break; // 读取成功,退出循环
}
}
if (retries == 0) {
// 读取 5 次都失败,显示错误
OLED_ShowString(4, 1, "Read Error");
}
// 延时 1 秒,等待下一次测量
for (uint32_t i = 0; i < 5000000; i++) __NOP();
}
}
运行结果显示
如果OLED想要显示温湿度汉字,需要自己用取模软件取字,然后加入到OLED字模库中,然后在主程序中调用即可。