配置环境:STM32CubeMX、IAR
硬件环境:STM32F405RGT6、DHT11
今天主要给大家分享一个比较简单的温湿度传感器(DHT11),只需要用到GPIO口就能实现。
DHT11读取过程比较简单,我们主要理解其工作时序就行了。
时序图:
这里需要注意的是,在唤醒之前是需要我们设置GPIO口以输出的方式进行唤醒的,然后再切换GPIO输入模式来获取响应信号和数据信号。
响应数据:
DHT11响应信号是收到主机的高电平信号后,输出一个低电平到高电频的转换实现响应信号。同时主机发送响应信号后需要延时等待,所以需要用到定时器实现一个us级别的延时。
每个数据信号开始前都是以一个12-14us的低电频信号开始,然后根据高电平的数据时间长短来区别数据'0'和数据'1'。
数据'0'
数据'1'
配置时钟:
定时器配置:
这里我们使用TIM3
IO配置:
代码实现timer3的us级别延时
//Time3us延时
void DHT11_DelayUs(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);
}
进行DHT11唤醒:
//设置IO口为输入
void DHT11_SetGpioInput()
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
//设置引脚9
GPIO_InitStruct.Pin = GPIO_PIN_9;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
}
//设置IO口为输出
void DHT11_SetGpioOutput()
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
//设置引脚9
GPIO_InitStruct.Pin = GPIO_PIN_9;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
}
//主机初始信号设置
void MasterResponseTest()
{
//设置当前IO口为输出
DHT11_SetGpioOutput();
//拉低当前信号
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_9, GPIO_PIN_RESET);
//拉低30us
HAL_Delay(30);
//再次拉高当前信号
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_9, GPIO_PIN_SET);
DHT11_DelayUs(40);
}
//dht11响应测试
Bool DHT11_ResponseSignalFeedback()
{
int temp = 0;
Bool ret = cFalse;
//主机初始信号测试
MasterResponseTest();
//设置当前IO为输入信号
DHT11_SetGpioInput();
//读取到低信号并且未超过响应时间
while(GPIO_PIN_RESET != HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_9) && (temp < RES_TIME_MAX))
{
temp ++;
DHT11_DelayUs(1);
}
//未超时 响应信号结束
if(temp < RES_TIME_MAX)
{
temp = 0;
}
//当读取高信号
while(GPIO_PIN_SET != HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_9) && (temp < RES_TIME_MAX))
{
temp ++;
DHT11_DelayUs(1);
}
//未超时
if(temp < RES_TIME_MAX)
{
temp = 0;
ret = cTrue;
}
return ret;
}
进行数据读取:
//bit位数据读取
Uint8 DHT11_BitDataRead()
{
int temp = 0;
//读取到低电频信号 :数据起始信号
while(GPIO_PIN_RESET != HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_9))
{
DHT11_DelayUs(1);
}
//读取到高电频信号 :数据信号
while(GPIO_PIN_SET != HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_9))
{
DHT11_DelayUs(1);
}
//延时一个数据起始信号
DHT11_DelayUs(30);
//读取到下起始信号 判断读取时间
if(GPIO_PIN_SET == HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_9))
{
temp = 1;
}
else
{
temp = 0;
}
return temp;
}
//数据读取函数
Uint8 DHT11_DataRead()
{
Uint8 Index = 0;
Uint8 data = 0;
Uint8 buff[7] = {0};
for(;Index < 8;Index++)
{
buff[Index] = DHT11_BitDataRead();
data |= (buff[Index] << (7 - Index));
}
return data;
}
//数据读取函数
void DHT11_ReadInfo(DHT11Info *dht11Info)
{
//空指针检查
if(NULL != dht11Info)
{
//发送起始信号
Bool temp = DHT11_ResponseSignalFeedback();
//起始信号响应成功
if(cTrue == temp)
{
dht11Info->humi_H = DHT11_DataRead();
dht11Info->humi_L = DHT11_DataRead();
dht11Info->temputer_H = DHT11_DataRead();
dht11Info->temputer_L = DHT11_DataRead();
dht11Info->check = DHT11_DataRead();
}
else
{
my_printf("DHT11 Err!!\r\n");
}
}
}
这里大家我喜欢把常见的数据类型重新定义一下,大家使用代码时记得在.h中添加上:
typedef unsigned char Uint8;
typedef signed char Int8;
typedef unsigned short Uint16;
typedef signed short Int16;
typedef unsigned long Uint32;
typedef signed long Int32;
typedef unsigned long long Uint64;
typedef signed long long Int64;
typedef float F32;
typedef float F64; /*TODO*/
typedef enum
{
cTrue = 0x55,
cFalse = 0xAA,
EnumReturnNull = 0x00
}Bool;
主函数调用:
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_DMA_Init();
MX_TIM3_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
DHT11_ResponseSignalFeedback();
HAL_Delay(3000);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
DHT11Info dht11Info = {0};
my_printf("MAIN\r\n");
DHT11_ReadInfo(&dht11Info);
my_printf("humi_H:%d\r\n",dht11Info.humi_H);
my_printf("humi_L:%d\r\n",dht11Info.humi_L);
my_printf("temputer_H:%d\r\n",dht11Info.temputer_H);
my_printf("temputer_L:%d\r\n",dht11Info.temputer_L);
my_printf("check:%d\r\n",dht11Info.check);
HAL_Delay(2000);
}
/* USER CODE END 3 */
}
说明下为啥在唤醒测试结束后延时3S呢,是为了在开始主逻辑前读取数据前读取数据时序不受影响,保证当前唤醒时序结束,这里可以改成其他值(完成一个读取时序周期即可)。
测试结果:
完整代码:
DHT11.c
/*
模块名称:温湿度传感器
芯片:DHT11
*/
#include "DHT11.h"
/*LED显示模块*/
#include "test.h"
DHT11Info g_dht11_info = {0}; //温湿度信息
//Time3us延时
void DHT11_DelayUs(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);
}
//设置IO口为输入
void DHT11_SetGpioInput()
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
//设置引脚9
GPIO_InitStruct.Pin = GPIO_PIN_9;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
}
//设置IO口为输出
void DHT11_SetGpioOutput()
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
//设置引脚9
GPIO_InitStruct.Pin = GPIO_PIN_9;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
}
//主机初始信号设置
void MasterResponseTest()
{
//设置当前IO口为输出
DHT11_SetGpioOutput();
//拉低当前信号
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_9, GPIO_PIN_RESET);
//拉低30us
HAL_Delay(30);
//再次拉高当前信号
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_9, GPIO_PIN_SET);
DHT11_DelayUs(40);
}
//dht11响应测试
Bool DHT11_ResponseSignalFeedback()
{
int temp = 0;
Bool ret = cFalse;
//主机初始信号测试
MasterResponseTest();
//设置当前IO为输入信号
DHT11_SetGpioInput();
//读取到低信号并且未超过响应时间
while(GPIO_PIN_RESET != HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_9) && (temp < RES_TIME_MAX))
{
temp ++;
DHT11_DelayUs(1);
}
//未超时 响应信号结束
if(temp < RES_TIME_MAX)
{
temp = 0;
}
//当读取高信号
while(GPIO_PIN_SET != HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_9) && (temp < RES_TIME_MAX))
{
temp ++;
DHT11_DelayUs(1);
}
//未超时
if(temp < RES_TIME_MAX)
{
temp = 0;
ret = cTrue;
}
return ret;
}
//bit位数据读取
Uint8 DHT11_BitDataRead()
{
int temp = 0;
//读取到低电频信号 :数据起始信号
while(GPIO_PIN_RESET != HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_9))
{
DHT11_DelayUs(1);
}
//读取到高电频信号 :数据信号
while(GPIO_PIN_SET != HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_9))
{
DHT11_DelayUs(1);
}
//延时一个数据起始信号
DHT11_DelayUs(30);
//读取到下起始信号 判断读取时间
if(GPIO_PIN_SET == HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_9))
{
temp = 1;
}
else
{
temp = 0;
}
return temp;
}
//数据读取函数
Uint8 DHT11_DataRead()
{
Uint8 Index = 0;
Uint8 data = 0;
Uint8 buff[7] = {0};
for(;Index < 8;Index++)
{
buff[Index] = DHT11_BitDataRead();
data |= (buff[Index] << (7 - Index));
}
return data;
}
//数据读取函数
void DHT11_ReadInfo(DHT11Info *dht11Info)
{
//空指针检查
if(NULL != dht11Info)
{
//发送起始信号
Bool temp = DHT11_ResponseSignalFeedback();
//起始信号响应成功
if(cTrue == temp)
{
dht11Info->humi_H = DHT11_DataRead();
dht11Info->humi_L = DHT11_DataRead();
dht11Info->temputer_H = DHT11_DataRead();
dht11Info->temputer_L = DHT11_DataRead();
dht11Info->check = DHT11_DataRead();
}
else
{
my_printf("DHT11 Err!!\r\n");
}
}
}
DHT11.h
#ifndef DHT11_H_
#define DHT11_H_
#include "Comm.h"
#include "gpio.h"
#include "tim.h"
#define RES_TIME_MAX 100
#define DATA_TIME_MAX 132
#define DATA_TIME 118
//DHT11 信息结构体
typedef struct
{
Uint8 temputer_H; //温度高位
Uint8 temputer_L; //温度低位
Uint8 humi_H; //湿度高位
Uint8 humi_L; //湿度低位
Uint8 check; //检查数据位置
}DHT11Info;
//us延时
void DHT11_DelayUs(uint16_t delay);
//dht11响应
Bool DHT11_ResponseSignalFeedback();
//GPIO输入
void DHT11_SetGpioInput();
//GPIO输出
void DHT11_SetGpioOutput();
//主机响应信号测试
void MasterResponseTest();
//数据读取函数
void DHT11_ReadInfo(DHT11Info *dht11Info);
#endif