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_Pin和DHT_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
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!