【HAL库STM32CubeMX+DHT11温湿度传感器串口显示模块化封装】
本文介绍DHT11温湿度传感器原理,包括单总线通讯、数据格式等。利用STM32CubeMX配置STM32F103ZET6开发板的RCC、TIM定时器、USART串口及GPIO,通过Keil 5编写代码,对DHT11进行模块化封装,实现数据读取。借助串口助手显示温湿度数据,完成从硬件配置到软件代码的完整开发流程,适用于学习交流,可帮助掌握DHT11传感器应用。
HAL库STM32CubeMX+DHT11温湿度传感器串口显示模块化封装
一、工具原理部分
1.DHT11温湿度传感器基本原理
1.1 DHT11传感器概述
DHT11是一款低成本、数字输出的温湿度复合传感器,适用于对精度要求不高的场景。传感器包括一个电阻式感湿元件和一个NTC测温元件,每个DHT11传感器都在极为精确的湿度校验室中进行校准。校准系数以程序的形式储存在OTP内存中,传感器内部在检测信号的处理过程中要调用这些校准系数。单线制串行接口,使系统集成变得简易快捷。超小的体积、极低的功耗,信号传输距离可达20米以上,使其成为各类应用甚至最为苛刻的应用场合的最佳选则。
1.2 DHT11接口说明
在单片机简单应用中,DHT11的供电电压为3-5.5V,本文接线使用5V供电,连接线长度低于20m时采用5k上拉电阻,在实际使用情景超过20m的环境中需要根据实际需求调整上拉电阻。下图为其典型应用电路:
1.3 DHT11通讯过程
DHT11共有三根线,VCC、GND和DATA,其中DATA采用单总线数据模式,一次完整的数据传输为40bit,高位先出。得到的数据格式:(湿度高8位即第1字节即湿度的整数部分)+(湿度低8位即第2字节即湿度的小数部分)+(温度高8位即第3字节即温度的整数部分)+(温度低8位即第4字节即温度的小数部分)+(校验和即第5字节即前4字节的和的低8位),其中校验和=(湿度高8位+湿度低8位+温度高8位+温度低8位)mod256。若接收到的第5字节(校验和)等于前4字节的和的低8位,则数据有效;否则需重新读取。
1.主机发送起始信号:
主机将单总线拉低至少18ms,确保DHT11检测到起始信号。随后主机拉高总线,保持20−40μs,进入接收状态。
2.DHT11响应:
DHT11检测到起始信号后,先将总线拉低80μs作为响应信号,告知主机已准备好。接着DHT11再将总线拉高80μs,完成响应并准备输出数据。
3.数据传输:
DHT11开始传输数据(如湿度、温度等)。每个数据位通过高低电平的持续时间区分:
数据 “0”:拉低总线后,高电平持续时间较短。
数据 “1”:拉低总线后,高电平持续时间较长。
主机通过检测高电平持续时间解析数据位。在整个通讯过程中,主机通过起始信号触发交互,DHT11响应后按约定格式输出数据,主机完成数据接收与解析,实现通讯。
通讯过程如图所示:
2.所需工具和具体配置
2.1 STM32CubeMX
STM32CubeMX 是意法半导体(STMicroelectronics)推出的一款图形化配置工具,专为简化 STM32 微控制器的嵌入式软件开发而设计。本次采用正点原子STM32F103ZET6开发板(其他开发板如典型的C8T6也可以使用),基于HAL库封装。首先打开CubeMX软件进行配置。
2.1.1SYS配置
调试接口配置(Debug):如截图中选择 Serial Wire,用于设定芯片调试方式,方便后续代码调试、下载程序。
2.1.2RCC时钟配置
首先,开启RCC时钟源,使用外部晶体或陶瓷谐振器作为高速、低速时钟源,为系统提供高频、低频时钟信号。
在时钟配置处,选择 72 MHZ 输出。
2.1.3 TIM定时器设置
由于DHT11需要定时采集数据,需要开启基本定时器,这里我开启了TIM3标准定时器(可以自行选择定时器,高级定时器这种简单情况下应该用不到)。
选择TIM3并将时钟源(Clock Source )开启为内部时钟(Internal Clock),预分频系数(Prescaler)设为 72-1 ,计数周期(Counter Period)默认设置为 65536 ,默认采用向上计数。
2.1.4 USART串口配置
通过串口通信显示在电脑窗口上,由于DHT11单总线模式通信,仅通过一根数据线与主机通信,无法像同步通信(如 SPI)那样提供额外的时钟线。所以在USART模块开启异步通信(Asynchronous),无需共享时钟信号,双方依据约定的时序规则即可完成数据交互。波特率可默认设置为115200(其它也可,需要匹配串口助手),数据长度、停止位、校验位默认。
2.1.5 GPIO配置
这里我采用PA7作为DHT11的DATA引脚(用户可以自定修改引脚),双击PA7可以设置为输出(GPIO_Output)模式,另外将PA7设置了用户标签(User Label),方便在代码里引用该标签,所以建议学习者可以跟我一样配置,编译显示串口效果后可以自行修改。
2.1.6 工程命名和开发环境
保存文件名、保存路径。选择完成后,STM32CubeMX 将生成适用于 Keil MDK - ARM 开发环境的工程代码,方便后续在 Keil 中进行编译、调试等操作。
2.1.7 管理工程并生成代码
2.2 Keil 5
点击魔术棒-Target,这里我是用的是version6,勾选Use Micro LIB(直接使用 Micro LIB 可以方便地实现基本的 C 语言功能,如输入输出、字符串处理等)。
点击小扳手,选择GB2312,方便显示汉字等。
2.3 串口助手
串口助手小软件可以选择江协科技,正点原子等等,都可以达到效果,本文使用后者。
以上配置完成后就是代码部分模块化封装。
二、代码封装部分
1.添加DHT11.c和DHT11.h文件
在Keil中添加生成.c和.h文件:
2.DHT11.c
/* DHT11.c */
#include "DHT11.h"
#include "tim.h"
extern TIM_HandleTypeDef DHT11_TIM_HANDLE;
// 私有函数声明
static void DHT11_IO_Out(void);
static void DHT11_IO_In(void);
static void DHT11_StartSignal(void);
static uint8_t DHT11_CheckResponse(void);
static uint8_t DHT11_ReadBit(void);
static uint8_t DHT11_ReadByte(void);
// 微秒级延时函数
static void Delay_us(uint16_t delay)
{
__HAL_TIM_SET_COUNTER(&DHT11_TIM_HANDLE, 0);
HAL_TIM_Base_Start(&DHT11_TIM_HANDLE);
while(__HAL_TIM_GET_COUNTER(&DHT11_TIM_HANDLE) < delay);
HAL_TIM_Base_Stop(&DHT11_TIM_HANDLE);
}
// 设置IO为输出模式
static void DHT11_IO_Out(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = DHT11_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(DHT11_PORT, &GPIO_InitStruct);
}
// 设置IO为输入模式
static void DHT11_IO_In(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = DHT11_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(DHT11_PORT, &GPIO_InitStruct);
}
// 发送起始信号
static void DHT11_StartSignal(void)
{
DHT11_IO_Out();
HAL_GPIO_WritePin(DHT11_PORT, DHT11_PIN, GPIO_PIN_RESET);
HAL_Delay(18);
HAL_GPIO_WritePin(DHT11_PORT, DHT11_PIN, GPIO_PIN_SET);
Delay_us(30);
}
// 检测DHT11响应
static uint8_t DHT11_CheckResponse(void)
{
uint8_t retry = 0;
DHT11_IO_In();
// 等待DHT11拉低总线
while(HAL_GPIO_ReadPin(DHT11_PORT, DHT11_PIN) && retry < 100)
{
retry++;
Delay_us(1);
}
if(retry >= 100) return DHT11_ERROR_NO_RESPONSE;
retry = 0;
// 等待DHT11拉高总线
while(!HAL_GPIO_ReadPin(DHT11_PORT, DHT11_PIN) && retry < 100)
{
retry++;
Delay_us(1);
}
if(retry >= 100) return DHT11_ERROR_NO_RESPONSE;
return DHT11_OK;
}
// 读取单个bit
static uint8_t DHT11_ReadBit(void)
{
uint8_t retry = 0;
while(HAL_GPIO_ReadPin(DHT11_PORT, DHT11_PIN) && retry < 100)
{
retry++;
Delay_us(1);
}
retry = 0;
while(!HAL_GPIO_ReadPin(DHT11_PORT, DHT11_PIN) && retry < 100)
{
retry++;
Delay_us(1);
}
Delay_us(40);
return HAL_GPIO_ReadPin(DHT11_PORT, DHT11_PIN);
}
// 读取一个字节
static uint8_t DHT11_ReadByte(void)
{
uint8_t value = 0;
for(uint8_t i = 0; i < 8; i++)
{
value <<= 1;
value |= DHT11_ReadBit();
}
return value;
}
// 主读取函数
DHT11_Status DHT11_ReadData(uint8_t *temperature, uint8_t *humidity)
{
uint8_t data[5] = {0};
DHT11_StartSignal();
if(DHT11_CheckResponse() != DHT11_OK)
return DHT11_ERROR_NO_RESPONSE;
for(uint8_t i = 0; i < 5; i++)
{
data[i] = DHT11_ReadByte();
}
// 校验数据
if(data[4] != (data[0] + data[1] + data[2] + data[3]))
return DHT11_ERROR_CHECKSUM;
*humidity = data[0];
*temperature = data[2];
return DHT11_OK;
}
3.DHT11.h
/* dht11.h */
#ifndef __DHT11_H
#define __DHT11_H
#include "stm32f1xx_hal.h"
// 硬件配置宏(用户根据实际硬件修改)
#define DHT11_PORT GPIOA
#define DHT11_PIN GPIO_PIN_7
#define DHT11_TIM_HANDLE htim3
// 错误代码定义
typedef enum {
DHT11_OK = 0,
DHT11_ERROR_NO_RESPONSE,
DHT11_ERROR_CHECKSUM
} DHT11_Status;
// 函数声明
DHT11_Status DHT11_ReadData(uint8_t *temperature, uint8_t *humidity);
#endif /* __DHT11_H */
4.在tim.c中添加
/* USER CODE BEGIN 1 */
void Delay_us(uint16_t delay)
{
__HAL_TIM_DISABLE(&htim3);
__HAL_TIM_SET_COUNTER(&htim3,0);
__HAL_TIM_ENABLE(&htim3);
uint16_t curCnt=0;
while(1)
{
curCnt=__HAL_TIM_GET_COUNTER(&htim3);
if(curCnt>=delay)
break;
}
__HAL_TIM_DISABLE(&htim3);
}
/* USER CODE END 1 */
5.在main.c中添加
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "DHT11.h"
#include "stdio.h"
#include "string.h"
/* USER CODE END Includes */
/* USER CODE BEGIN 1 */
// 系统初始化...
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_TIM3_Init();
MX_USART1_UART_Init();
uint8_t temp = 0, humi = 0;
char buffer[50];
/* USER CODE END 1 */
/* USER CODE BEGIN WHILE */
while (1)
{
DHT11_Status status = DHT11_ReadData(&temp, &humi);
if(status == DHT11_OK)
{
sprintf(buffer, "Temp: %d℃, Humi: %d%%\r\n", temp, humi);
HAL_UART_Transmit(&huart1, (uint8_t*)buffer, strlen(buffer), 100);
}
else
{
sprintf(buffer, "Error: %d\r\n", status);
HAL_UART_Transmit(&huart1, (uint8_t*)buffer, strlen(buffer), 100);
}
HAL_Delay(2000);
/* USER CODE END WHILE */
最后,编译下载,打开串口工具。
三、接线图和串口效果
1.实物接线图
2.串口显示效果
3.写在最后
————本文仅供学习交流使用,有不足之处欢迎大家批评指正,代码有部分参考@cl777_,谢谢大家!————
更多推荐
所有评论(0)