STM32F1与STM32CubeIDE编程实例-设备驱动-DHT11温度温度传感器驱动

DHT11温度温度传感器驱动

DHT11是一款有已校准数字信号输出的温湿度传感器。 其精度湿度±5%RH, 温度±2℃,量程湿度595%RH, 温度-20+60℃。DHT11通讯方式是单总线的。

本文将介绍在STMCubeIDE上如何实现DHT11驱动。

在前面的文章中,对DHT11做了详细的介绍,请参考:

1、DHT11配置

DHT11引脚配置如下:

在这里插入图片描述

DHT11的引脚接线图如下:

在这里插入图片描述

3、DHT11驱动实现

3.1 基本定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
/\*
\* DHT.h
\*
\* Created on: Apr 4, 2022
\* Author: jenson
\*/

#ifndef DHT\_H\_
#define DHT\_H\_

#include "main.h"

/\* Настройки \*/
#define DHT\_TIMEOUT 10000 // 超时
#define DHT\_POLLING\_CONTROL 1 // 是否开户轮询
#define DHT\_POLLING\_INTERVAL\_DHT11 2000 // DHT11 轮询间隔(根据数据表为 0.5 Hz)
#define DHT\_POLLING\_INTERVAL\_DHT22 1000 // DHT22 轮询间隔(根据数据表为 1 Hz)
#define DHT\_IRQ\_CONTROL // 与传感器通信时禁用中断
/\* 传感器返回的数据结构 \*/
typedef struct {
float hum;
float temp;
} DHT_data;

/\* 使用的传感器类型\*/
typedef enum {
DHT11,
DHT22
} DHT_type;

/\* 传感器对象结构 \*/
typedef struct {
GPIO_TypeDef \*DHT_Port; // 传感器端口(GPIOA、GPIOB 等)
uint16\_t DHT_Pin; // 传感器引脚号(GPIO\_PIN\_0、GPIO\_PIN\_1 等)
DHT_type type; // 传感器类型(DHT11 或 DHT22)
uint8\_t pullUp; // 需要上拉数据线供电(GPIO\_NOPULL - no, GPIO\_PULLUP - yes)

// 传感器轮询频率控制
#if DHT\_POLLING\_CONTROL == 1
uint32\_t lastPollingTime;// 上次传感器轮询的时间
float lastTemp; // 上次温度值
float lastHum; // 上次湿度值
#endif
} DHT_sensor;


/\*从传感器获取数据 \*/
DHT_data DHT\_getData(DHT_sensor \*sensor); //

#endif

3.2 驱动实现

1)定义一些工具函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/\*
\* DHT.c
\*
\* Created on: Apr 4, 2022
\* Author: jenson
\*/

#include "DHT.h"

#define lineDown() HAL\_GPIO\_WritePin(sensor->DHT\_Port, sensor->DHT\_Pin, GPIO\_PIN\_RESET)
#define lineUp() HAL\_GPIO\_WritePin(sensor->DHT\_Port, sensor->DHT\_Pin, GPIO\_PIN\_SET)
#define getLine() (HAL\_GPIO\_ReadPin(sensor->DHT\_Port, sensor->DHT\_Pin) == GPIO\_PIN\_SET)
#define Delay(d) HAL\_Delay(d)

2)GPIO引脚输入和输出功能切换

由于DHT11使用的是总线,因此控制命令发送和数据接收都在一个引脚上完成,所以需要对DHT11引脚进行输入和输出切换。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// DHT.c
static void goToOutput(DHT_sensor \*sensor) {
GPIO_InitTypeDef GPIO_InitStruct = {0};

// 该线默认为高
lineUp();

// 设置端口退出
GPIO_InitStruct.Pin = sensor->DHT_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD; // 开漏
GPIO_InitStruct.Pull = sensor->pullUp; // 上拉

GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; // 高速端口
HAL\_GPIO\_Init(sensor->DHT_Port, &GPIO_InitStruct);
}

static void goToInput(DHT_sensor \*sensor) {
GPIO_InitTypeDef GPIO_InitStruct = {0};

// 配置输入端口
GPIO_InitStruct.Pin = sensor->DHT_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = sensor->pullUp; // 上拉
HAL\_GPIO\_Init(sensor->DHT_Port, &GPIO_InitStruct);
}

注意,代码中的DHT_PinDHT_Port在STM32CubeIDE中定义。

3)DHT11数据获取

DHT11总通讯时序如下:

在这里插入图片描述

当 MCU 发出启动信号时,DHT11 从低功耗模式切换到运行模式,等待 MCU 完成启动信号。完成后,DHT11 给MCU发送 40 位数据的响应信号,其中包含相关的 湿度和温度信息。 用户可以选择采集(读取)一些数据。没有来自MCU的启动信号,DHT11不会给MCU响应信号。 采集一次数据后,DHT11 会切换到低功耗模式,直到收到来自 MCU 的启动信号。

MCU给DHT11发送初始信号时序如下

在这里插入图片描述

数据单总线空闲状态为高电平。当MCU和DHT11开始通信时,MCU程序将数据单总线电压电平从高电平设置为低电平,这个过程至少需要18ms才能保证DHT检测到 MCU 的信号,然后 MCU 将拉高电压并等待 20-40us 等待 DHT 的响应。

DHT11响应MCU的时序如下

接收数据**0**

在这里插入图片描述

接收数据*1*

在这里插入图片描述

具体代码实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
DHT_data DHT\_getData(DHT_sensor \*sensor) {
DHT_data data = {-128.0f, -128.0f};

#if DHT\_POLLING\_CONTROL == 1
/\* 对传感器轮询频率的限制 \*/
//根据传感器确定轮询间隔
uint16\_t pollingInterval;
if (sensor->type == DHT11) {
pollingInterval = DHT_POLLING_INTERVAL_DHT11;
} else {
pollingInterval = DHT_POLLING_INTERVAL_DHT22;
}

// 如果间隔很小,则返回最后一个已知的好值
if ((HAL\_GetTick() - sensor->lastPollingTime < pollingInterval) && sensor->lastPollingTime != 0) {
data.hum = sensor->lastHum;
data.temp = sensor->lastTemp;
return data;
}
sensor->lastPollingTime = HAL\_GetTick()+1;
#endif

/\* 从传感器请求数据 \*/
// 将端口切换为输出
goToOutput(sensor);

lineDown();
Delay(15);

lineUp();
goToInput(sensor);


#ifdef DHT\_IRQ\_CONTROL
// 关闭中断,以免干扰数据处理
\_\_disable\_irq();
#endif
/\*等待传感器的响应 \*/
uint16\_t timeout = 0;
// 等待退出
while(getLine()) {
timeout++;
if (timeout > DHT_TIMEOUT) {
#ifdef DHT\_IRQ\_CONTROL
\_\_enable\_irq();
#endif
//如果传感器没有响应,那么它肯定不存在。
//将最后已知的良好值重置为虚值
sensor->lastHum = -128.0f;
sensor->lastTemp = -128.0f;

return data;
}
}
timeout = 0;
//
while(!getLine()) {
timeout++;
if (timeout > DHT_TIMEOUT) {
#ifdef DHT\_IRQ\_CONTROL
\_\_enable\_irq();
#endif
sensor->lastHum = -128.0f;
sensor->lastTemp = -128.0f;

return data;
}
}
timeout = 0;
while(getLine()) {
timeout++;
if (timeout > DHT_TIMEOUT) {
#ifdef DHT\_IRQ\_CONTROL
\_\_enable\_irq();
#endif
return data;
}
}

/\* 读取传感器的响应 \*/
uint8\_t rawData[5] = {0,0,0,0,0};
for(uint8\_t a = 0; a < 5; a++) {
for(uint8\_t b = 7; b != 255; b--) {
uint16\_t hT = 0, lT = 0;
//当线路为低电平时,lT 变量递增
while(!getLine() && lT != 65535) lT++;
//当线为高时,hT 变量递增
timeout = 0;
while(getLine()&& hT != 65535) hT++;
//如果 hT 大于 lT,则已到达
if(hT > lT) rawData[a] |= (1<<b);
}
}

#ifdef DHT\_IRQ\_CONTROL
//接收数据后开启中断
\_\_enable\_irq();
#endif

/\* 数据完整性检查 \*/
if((uint8\_t)(rawData[0] + rawData[1] + rawData[2] + rawData[3]) == rawData[4]) {
//如果校验和匹配,则转换并返回结果值
if (sensor->type == DHT22) {
data.hum = (float)(((uint16\_t)rawData[0]<<8) | rawData[1])\*0.1f;
//温度负性测试
if(!(rawData[2] & (1<<7))) {
data.temp = (float)(((uint16\_t)rawData[2]<<8) | rawData[3])\*0.1f;
} else {
rawData[2] &= ~(1<<7);
data.temp = (float)(((uint16\_t)rawData[2]<<8) | rawData[3])\*-0.1f;
}
}
if (sensor->type == DHT11) {
data.hum = (float)rawData[0];
data.temp = (float)rawData[2];
}
}

#if DHT\_POLLING\_CONTROL == 1
sensor->lastHum = data.hum;
sensor->lastTemp = data.temp;
#endif

return data;
}

4)主程序代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
/\* USER CODE END Header \*/
/\* Includes ------------------------------------------------------------------\*/
#include "main.h"
#include "tim.h"
#include "usart.h"
#include "gpio.h"

/\* Private includes ----------------------------------------------------------\*/
/\* USER CODE BEGIN Includes \*/
#include <stdio.h>
#include "dht11/DHT.h"
/\* USER CODE END Includes \*/

int main(void) {
/\* USER CODE BEGIN 1 \*/

/\* USER CODE END 1 \*/

/\* MCU Configuration--------------------------------------------------------\*/

/\* Reset of all peripherals, Initializes the Flash interface and the Systick. \*/
HAL\_Init();

/\* USER CODE BEGIN Init \*/

/\* USER CODE END Init \*/

/\* Configure the system clock \*/
SystemClock\_Config();

/\* USER CODE BEGIN SysInit \*/

/\* USER CODE END SysInit \*/

/\* Initialize all configured peripherals \*/
MX\_GPIO\_Init();
MX\_USART1\_UART\_Init();
MX\_TIM1\_Init();
/\* USER CODE BEGIN 2 \*/
HAL\_TIM\_Base\_Start(&htim1);
// DHT11\_init(DHT11\_GPIO\_Port, DHT11\_Pin);
printf("\*\*\*\*dht11 demo\*\*\*\*\r\n");
/\* USER CODE END 2 \*/
DHT_sensor sensor;
sensor.DHT_Pin = DHT11_Pin;
sensor.DHT_Port = DHT11_GPIO_Port;
sensor.type = DHT11;
/\* Infinite loop \*/
/\* USER CODE BEGIN WHILE \*/
while (1) {
/\* USER CODE END WHILE \*/
DHT\_getData(&sensor);
printf("dht11:temp = %f,humidity=%f\r\n",sensor.lastTemp,sensor.lastHum);
HAL\_Delay(2000);
/\* USER CODE BEGIN 3 \*/
}
/\* USER CODE END 3 \*/
}

程序输出:

在这里插入图片描述

文章来源: https://iotsmart.blog.csdn.net/article/details/125242147