前言
今天在这边新开一个系列,那就是硬件模块。
后续会把之前写过的硬件模块再写一遍,因为之前都是使用的STM32来驱动模块的,而现在我快速上手ESP32系列也快结束了,因此我在原本文章的基础上再加上使用ESP32来驱动的部分构成硬件模块这个系列。
当然了,也不是吃老本的,这个系列也会写一些之前都没用过的模块,今天就先以DHT11这个之前都没写过的模块来开个头。
DHT11
DHT11是一款数字式温湿度传感器,它采用了已校准的数字信号输出方式,使得测量结果更加准确可靠。该传感器集成了温度测量、湿度测量和数字信号输出等功能,可以实时监测环境的温度和湿度变化。
DHT11传感器内部包含一个湿度检测电容和一个NTC温度测量元件,以及一个高性能的8位微控制器来处理信号。当传感器暴露在环境中时,温度传感器和湿度传感器能够感知到环境的温度和湿度,并将其转换为电信号。随后,微控制器将这些电信号转换为数字信号,并通过单总线通信协议输出给微处理器。
DHT11传感器具有多种优点。首先,它采用数字信号输出,使得读取数据变得简单方便,只需通过一根数据线即可完成温湿度读取。其次,DHT11的价格相对较低,适合大规模应用和低成本项目。此外,它还支持多种平台和开发板,如Arduino、Raspberry Pi等,可以广泛应用于各种嵌入式系统和物联网设备。
然而,DHT11传感器也存在一些缺点。首先,其精度相对较低,温度精度为±2°C,湿度精度为±5%RH,可能无法满足一些高精度测量的需求。其次,DHT11的响应时间较慢,通常需要2秒以上才能完成一次温湿度测量。此外,其测量范围也有限制,温度测量范围为0°C~50°C,湿度测量范围为20%~90%RH。
在实际应用中,DHT11传感器常用于室内环境监测、气象站和气象监测、温湿度控制系统以及物联网项目等领域。例如,在家庭、办公室、实验室等室内环境中,DHT11可以帮助维持舒适的生活和工作环境,并及时采取调节措施。在构建小型气象站时,DHT11可用于监测当地的温度和湿度,为农业、气象学研究以及天气预报等提供重要数据。同时,在温室、温室大棚和养殖场等环境中,DHT11传感器可以实现温湿度的自动控制,提供适宜的环境条件。此外,在物联网项目中,DHT11也是常用的传感器之一,可以实时监测和传输温湿度数据。
综上所述,DHT11是一款功能强大、简单易用且成本较低的温湿度传感器,适用于多种应用场景。然而,在选择使用时需要根据具体需求考虑其精度、响应时间以及测量范围等因素。
以上介绍来自文心一言。
简单来说就是DHT11这个温湿度传感器相当便宜,但是代价就是精度很差。
引脚
其中1号引脚是VCC,2号引脚是数据线,3号引脚没有用,4号引脚是GND。
供电电压的范围是3.3~5.5V
使用的通信协议是1-Wire,所以只需要2号引脚一根数据线即可。
时序
第一步是DHT11上电后需要等待1s,然后处于一个上拉输入的状态。
对于我们的单片机(主机)来说,我们要做的就是等DHT11一秒钟,其他不用操作。
第二步就是我们主机发送起始信号告诉DHT11开始采集数据了。
我们先拉低数据线18~30ms,可以拉个20ms。
之后再拉高延时一会等待DTH11反应,然后改为上拉输入模式。
第三步是DHT11收到信号之后等待数据线的低电平结束后会再拉低数据线83us表示收到,然后再拉高数据线87us通知我们的主机接收数据。
对于我们主机来说,我们发送完上面的起始信号检测数据线是不是被拉低了,如果是,那么我们再等待数据线拉高回去。
DHT11一共会发送40位数据,这40位数据分别代表什么含义这个稍后再说。
我们先看看数据0和1的区别,它们一开始都是拉低54us,不一样的是数据0后面拉高23~27us,而数据1后面拉高了66~74us。
也就是说不管是什么数据,都是先拉低后拉高,不一样的是后面拉高的持续时间。
因此我们可以先等待数据线拉高,拉高之后延时30us,如果是数据0,那么我们读出的将会是低电平,如果是数据1,那么我们读出的是高电平。这样就算是读出了一个bit。
接下来为了时序能够接上,我们再等待数据线拉低(如果是数据0则不用等待,数据线就是拉低的状态)。
重复上面的步骤8次,我们就读出了一个Byte。
最后读完40个bit(5个Byte)之后,我们再来个结束信号。
实际上我们不需要什么操作,上面都是DTH11需要执行的。
我们只要在读出数据之后等待60us即可。
数据
现在我们来看看五个Byte都是些什么含义。
从头到尾依次是湿度的整数,湿度的小数(一直都是0,不需要管它),温度的整数,温度的小数,校验位。
一共有两个数据,湿度和温度。
其中湿度的小数一直都是0。温度的小数对应的值乘上0.1度就是对应的温度。
最后一个校验位是前四位加起来的值。如果前四位加起来不等于校验位,那么表示数据出错了。
需要注意的是前四位数相加的值需要强转成8bit的类型才能和校验位进行比较。
STM32驱动代码
下面的代码里涉及OLED显示和Delay延时函数的部分需要自己实现。
OLED只是为了展示读取的数据,没有的小伙伴用串口通信打印到电脑上也可以。
延时函数网上随便一搜就有,也可以翻一下我之前的文章,直接复制粘贴也行。
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#define DHT11_DATE_PROT GPIO_Pin_0
void Z_DHT11_Init(void);
void Z_DHT11_Output(void);
void Z_DHT11_Input(void);
void Z_DHT11_SetDate(BitAction val);
uint8_t Z_DHT11_GetDate(void);
uint8_t Z_DHT11_GetByte(void);
void Z_DHT11_GetTemperatureAndHumidity(void);
//初始化DHT11
void Z_DHT11_Init(void){
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
Z_DHT11_Output();
Z_DHT11_SetDate(1);
Delay_s(1);
}
//将数据线配置为输出模式
void Z_DHT11_Output(void){
GPIO_InitTypeDef itd;
itd.GPIO_Mode=GPIO_Mode_Out_PP; //推挽输出
itd.GPIO_Pin=DHT11_DATE_PROT;
itd.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&itd);
}
//将数据线配置为输入模式
void Z_DHT11_Input(void){
GPIO_InitTypeDef itd;
itd.GPIO_Mode=GPIO_Mode_IPU; //上拉输入
itd.GPIO_Pin=DHT11_DATE_PROT;
itd.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&itd);
}
void Z_DHT11_SetDate(BitAction val){
GPIO_WriteBit(GPIOA,DHT11_DATE_PROT,val);
}
uint8_t Z_DHT11_GetDate(void){
return GPIO_ReadInputDataBit(GPIOA,DHT11_DATE_PROT);
}
//读取一个Byte
uint8_t Z_DHT11_GetByte(void){
uint8_t data;
for(uint8_t i=0;i<8;i++){
while(Z_DHT11_GetDate()==0);
Delay_us(30);
data<<=1;
if(Z_DHT11_GetDate()==1) data|=1;
while(Z_DHT11_GetDate()==1);
}
return data;
}
uint8_t DTH11_Data[5];
//获取温湿度
void Z_DHT11_GetTemperatureAndHumidity(void){
Z_DHT11_Output();
Z_DHT11_SetDate(0);
Delay_ms(20);
Z_DHT11_SetDate(1);
Delay_us(30);
Z_DHT11_Input();
if(Z_DHT11_GetDate()!=0) return;
while(Z_DHT11_GetDate()==0);
while(Z_DHT11_GetDate()==1);
DTH11_Data[0]=Z_DHT11_GetByte();
DTH11_Data[1]=Z_DHT11_GetByte();
DTH11_Data[2]=Z_DHT11_GetByte();
DTH11_Data[3]=Z_DHT11_GetByte();
DTH11_Data[4]=Z_DHT11_GetByte();
Delay_us(60);
}
int main(void){
OLED_Init();
Z_DHT11_Init();
while(1){
Z_DHT11_GetTemperatureAndHumidity();
if((uint8_t)(DTH11_Data[0]+DTH11_Data[2]+DTH11_Data[3])!=DTH11_Data[4]) continue;
OLED_ShowNum(1,1,DTH11_Data[0],2);
OLED_ShowNum(1,4,DTH11_Data[1],2);
OLED_ShowNum(2,1,DTH11_Data[2],2);
OLED_ShowNum(2,4,DTH11_Data[3],2);
OLED_ShowNum(3,1,DTH11_Data[4],2);
Delay_ms(500);
}
}
数据可以正常读取,就是精度确实有点低。
ESP32驱动代码
其实只需要把STM32的代码改改就行了,差别不大。
ESP32的代码相比较STM32会更简洁一些。
因为ESP32可以将GPIO口初始化成既可以输入也可以输出,因此不需要像STM32那样切换GPIO口的模式。我们只需要初始化一次GPIO口的模式即可,记得加上上拉电阻。
#include <stdio.h>
#include <unistd.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#define DHT11_DATE_PROT 18
void Z_DHT11_Init(void);
void Z_DHT11_SetDate(uint8_t val);
uint8_t Z_DHT11_GetDate(void);
uint8_t Z_DHT11_GetByte(void);
void Z_DHT11_GetTemperatureAndHumidity(void);
// 初始化DHT11
void Z_DHT11_Init(void){
gpio_config_t init = {
.intr_type = GPIO_INTR_DISABLE, // 失能中断;
.mode = GPIO_MODE_INPUT_OUTPUT, // 输出模式
.pin_bit_mask = (1ULL << DHT11_DATE_PROT),
.pull_down_en = GPIO_PULLDOWN_DISABLE, // 失能下拉模式
.pull_up_en = GPIO_PULLUP_ENABLE, // 使能上拉模式
};
gpio_config(&init);
Z_DHT11_SetDate(1);
vTaskDelay(1000/portTICK_PERIOD_MS);
}
void Z_DHT11_SetDate(uint8_t val){
gpio_set_level(DHT11_DATE_PROT,val);
}
uint8_t Z_DHT11_GetDate(void){
return gpio_get_level(DHT11_DATE_PROT);
}
// 读取一个Byte
uint8_t Z_DHT11_GetByte(void){
uint8_t data = 0;
for (uint8_t i = 0; i < 8; i++){
while (Z_DHT11_GetDate() == 0);
usleep(30);
data <<= 1;
if (Z_DHT11_GetDate() == 1) data |= 1;
while (Z_DHT11_GetDate() == 1);
}
return data;
}
uint8_t DTH11_Data[5];
// 获取温湿度
void Z_DHT11_GetTemperatureAndHumidity(void){
Z_DHT11_SetDate(0);
vTaskDelay(20/portTICK_PERIOD_MS);
Z_DHT11_SetDate(1);
usleep(30);
if (Z_DHT11_GetDate() != 0) return;
while (Z_DHT11_GetDate() == 0);
while (Z_DHT11_GetDate() == 1);
DTH11_Data[0] = Z_DHT11_GetByte();
DTH11_Data[1] = Z_DHT11_GetByte();
DTH11_Data[2] = Z_DHT11_GetByte();
DTH11_Data[3] = Z_DHT11_GetByte();
DTH11_Data[4] = Z_DHT11_GetByte();
usleep(60);
}
void app_main(void){
Z_DHT11_Init();
while (1){
Z_DHT11_GetTemperatureAndHumidity();
if ((uint8_t)(DTH11_Data[0] + DTH11_Data[2] + DTH11_Data[3]) != DTH11_Data[4]) continue;
printf("%d,%d,%d,%d,%d\r\n",DTH11_Data[0],DTH11_Data[1],DTH11_Data[2],DTH11_Data[3],DTH11_Data[4]);
vTaskDelay(1000/portTICK_PERIOD_MS);
}
}
ESP32移植STM32代码成功,并且还可以直接通过printf打印到电脑上方便我们查看数据。
说好听一点叫移植,说不好听一点那就是把代码抄过来改改能跑就行。
我们在改代码的时候,函数声明什么的都可以不改,只需要把每个函数里的逻辑适配成我们要移植的芯片的逻辑就行。