目录
3、DHT11检测起始信号(这里的DHT11_LOW,DHT11_HIGH 在下面的的第8点)
4、DHT11检测响应信号(这里的DHT11_IO_IN在下面的第8点)
8、IO口输出高、低电平以及读取IO口电平定义和需要添加的头文件
所用的工具:
1、芯片:STM32F103C8T6
2、软件:STM32CubeMX软件
3、IDE : MDK_Keil软件
知识概括:
DHT11基本知识和原理
DHT11通讯方式
STM32CubeTIM和USART的配置
DHT11数据采集实现
DHT11介绍:
DHT11性能说明:

接口说明
建议连接线短语20m时用5K的上拉电阻,大于20m时根据实际情况使用合适的上拉电阻
电源引脚
DHT11的供电电压为 3.3~5v。传感器上电后,要等待1s,以越过不稳定状态在此期间无需发送任何指令。电源引脚(VDD,GND)之间可增加一个100uf电容,用以去耦滤波。
串行接口



总线为低电平,说明DHT11发送响应信号,DHT11发送响应信号后,再把总线拉 高80us,准备发送数据,每一bit数据都以50us低电平时隙开始,高电平的长短定 了数据位是0还是1.格式见下面图示.如果读取响应信号为高电平,则DHT11没有 响应,请检查线路是否连接正常.当最后一bit数据传送完毕后,DHT11拉低总线 50us,随后总线由上拉电阻拉高进入空闲状态。
工程创建
1、设置RCC
2、时钟设置
3、项目文件设置
- 1 设置项目名称
- 2 设置存储路径
- 3 选择所用IDE
1、 复制所用文件的.C和.H
2、每个功能生成独立的.C和.H文件
4、设置IO口(DATA)
第二步的Moed设置GPIO_OUTPUT和GPIO_INPUT都可以,因为DHT11是单线双向的 ,所以在编写程序时我们需要改写的它IO模式。
5、USART配置
第二步 Mode设置为异步通信
程序编写:
首先来编写函数部分:
1、微秒(us)级延时函数:
void delay_us(uint32_t us)
{
uint32_t delay = (HAL_RCC_GetHCLKFreq() / 4000000 * us);
while (delay--)
{
;
}
}
该函数用了逻辑分析仪进行了比较,示图如下:
100us:
10us:
1us:
2、IO口配置
/**
* @brief DATA引脚(PA7)设置为输出模式
* @param 无
* @retval 无
*/
void DHT11_OUT(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
/**
* @brief DATA引脚(PA7)设置为输入模式
* @param 无
* @retval 无
*/
void DHT11_IN(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
3、DHT11检测起始信号(这里的DHT11_LOW,DHT11_HIGH 在下面的的第8点)
/**
* @brief DHT11检测起始信号
* @param 无
* @retval 无
*/
void DHT11_Strat(void)
{
DHT11_OUT(); //PA7设置为输出模式
DHT11_LOW; //主机拉低总线
HAL_Delay(20); //延迟必须大于18ms ;
DHT11_HIGH; //主机拉高总线等待DHT11响应
Delay_us(30);
}
4、DHT11检测响应信号(这里的DHT11_IO_IN在下面的第8点)
/**
* @brief DHT11发送响应信号
* @param 无
* @retval 返回值0/1 0:响应成功 1:响应失败
*/
uint8_t DHT11_Check(void)
{
uint8_t retry = 0 ;
DHT11_IN();
//采用while循环的方式检测响应信号
while(DHT11_IO_IN && retry <100) // DHT11会拉低 40us ~80us
{
retry++;
Delay_us(1);//1us
}
if(retry>=100) //判断当DHT11延迟超过80us时return 1 , 说明响应失败
{return 1;}
else retry = 0 ;
while(!DHT11_IO_IN && retry<100)// // DHT11拉低之后会拉高 40us ~80us
{
retry++;
Delay_us(1);//1us
}
if(retry>=100)
{return 1;}
return 0 ;
}
5、DHT11读取一bit数据
/**
* @brief DHT11读取一位数据
* @param 无
* @retval 返回值0/1 1:读取成功 0:读取失败
*/
uint8_t DHT11_Read_Bit(void)
{
uint8_t retry = 0 ;
while(DHT11_IO_IN && retry <100)//同上采用while循环的方式去采集数据
{
retry++;
Delay_us(1);
}
retry = 0 ;
while(!DHT11_IO_IN && retry<100)
{
retry++;
Delay_us(1);
}
Delay_us(40); //结束信号,延时40us
if(DHT11_IO_IN) return 1; //结束信号后,总线会被拉高 则返回1表示读取成功
else
return 0 ;
}
6、DHT11读取一个Byte数据
/**
* @brief DHT11读取一个字节数据
* @param 无
* @retval 返回值:dat 将采集到的一个字节的数据返回
*/
uint8_t DHT11_Read_Byte(void)
{
uint8_t i , dat ;
dat = 0 ;
for(i=0; i<8; i++)
{
dat <<= 1; //通过左移存储数据
dat |= DHT11_Read_Bit();
}
return dat ;
}
7、DHT11读取湿度和温度的数据
/**
* @brief DHT11读取数据
* @param temp:温度值 humi :湿度值
* @retval 返回值0/1 0:读取数据成功 1:读取数据失败
*/
uint8_t DHT11_Read_Data(uint8_t* temp , uint8_t* humi)
{
uint8_t buf[5]; //储存五位数据
uint8_t i;
DHT11_Strat(); //起始信号
if(DHT11_Check() == 0) //响应信号
{
for(i=0; i<5; i++)
{
buf[i] = DHT11_Read_Byte();
}
if(buf[0]+buf[1]+buf[2]+buf[3] == buf[4]) //校验数据
{
*humi = buf[0]; // 湿度
*temp = buf[2]; // 温度
}
}else return 1;
return 0 ;
}
以上就是需要用到的函数部分的编写,接下来是一些声明和定义:
8、IO口输出高、低电平以及读取IO口电平定义和需要添加的头文件
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define DHT11_HIGH HAL_GPIO_WritePin(GPIOA, DHT11_Pin, GPIO_PIN_SET) //输出高电平
#define DHT11_LOW HAL_GPIO_WritePin(GPIOA, DHT11_Pin, GPIO_PIN_RESET)//输出低电平
#define DHT11_IO_IN HAL_GPIO_ReadPin(GPIOA, DHT11_Pin)//读取IO口电平
/* USER CODE END PD */
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "stdio.h"
#include "string.h"
/* USER CODE END Includes */
9、温度、湿度和数据缓存数组的声明,函数的声明
/* USER CODE BEGIN 1 */
uint8_t temperature = 1; //温度
uint8_t humidity = 1; //湿度
uint8_t aTXbuf[32]; //串口发送缓存数组
DHT11_Strat(void);
/* USER CODE END 1 */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
void Delay_us(uint16_t delay); //us级延时函数
//DHT11_DATA ,IO口设置为输出模式(这里的IO口指的是STM32CubeMX中配置的IO口)
void DHT11_OUT(void);
void DHT11_IN(void); //DHT11_Data IO设置为输入模式
void DHT11_Strat(void); //主机发出起始信号
uint8_t DHT11_Check(void); //DHT11发送响应信号
uint8_t DHT11_Read_Bit(void); //读取DHT11一个BIT的数据
uint8_t DHT11_Read_Byte(void); //读取DHT11一个Byte的数据
uint8_t DHT11_Read_Data(uint8_t* temp , uint8_t* humi); //读取DHT11湿度和温度的数据
/* USER CODE BEGIN PFP */
10、主循环,(DHT11数据的读取和串口发送)
while (1)
{
//接收DHT11的温度和湿度的值
DHT11_Read_Data(&temperature , &humidity);
//将数据存放到aTXbuf这个数组当中去。 不了解"sprintf"用法的可以去查一下...
sprintf((char*)aTXbuf,"温度:%d℃,湿度: %d %%\r\n" ,temperature ,humidity);
//将数据通过串口发送到主机上的串口助手
HAL_UART_Transmit(&huart1, aTXbuf, strlen((const char*)aTXbuf), 200);
//延时500ms
HAL_Delay(500);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
到这里DHT11的程序就编写完了 ,下面就来看看效果吧
效果展示
1、将程序下载到开发板
2、串口助手查看数据
总结
通过本次DHT11温湿度传感器的学习,掌握了单线双向的数据通信方式,DHT11和主机之间数据通讯过程,而且在此过程中要注意延时函数的把控精度,在编写程序中DHT11数据读取方式,数据传输方式和数据存储方式,以及微秒级延迟函数的编写,本设计参考正点原子探索者开发板教程和STM32Cube高效开发指南(高级篇)。
附录
后面是整理的DHT11的.C和.H文件 ,下述文件的跟上面不同(请分开使用)微秒级延时无需用定时器操作这样只会占用外设资源。 这个延时没有问题,数据不出来的原因1、引脚的编号是否需改 2、DQ口是否给了上拉电阻,如果硬件上拉了,软件可以不上拉。(打印方式还是使用上面的串口打印的方式)
#include "dht11.h"
void delay_us(uint32_t us)
{
uint32_t delay = (HAL_RCC_GetHCLKFreq() / 4000000 * us);
while (delay--)
{
;
}
}
/**
* @brief DATA引脚(PA7)设置为输出模式
* @param 无
* @retval 无
*/
void DHT11_IO_OUT()
{
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(GPIOA, &GPIO_InitStruct);
}
/**
* @brief DATA引脚设置为输入模式
* @param 无
* @retval 无
*/
void DHT11_IO_IN()
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = DHT11_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
//复位DHT11
void DHT11_Rst(void)
{
DHT11_IO_OUT(); //SET OUTPUT
DHT11_LOW; //拉低DQ
HAL_Delay(20); //拉低至少18ms
DHT11_HIGH; //DQ=1
delay_us(30); //主机拉高20~40us
}
//等待DHT11的回应
//返回1:未检测到DHT11的存在
//返回0:存在
u8 DHT11_Check(void)
{
u8 retry=0;
DHT11_IO_IN();//SET INPUT
while (DHT11_DQ_IN&&retry<100)//DHT11会拉低40~80us
{
retry++;
delay_us(1);
};
if(retry>=100)return 1;
else retry=0;
while (!DHT11_DQ_IN&&retry<100)//DHT11拉低后会再次拉高40~80us
{
retry++;
delay_us(1);
};
if(retry>=100)return 1;
return 0;
}
//从DHT11读取一个位
//返回值:1/0
u8 DHT11_Read_Bit(void)
{
u8 retry=0;
while(DHT11_DQ_IN&&retry<100)//等待变为低电平
{
retry++;
delay_us(1);
}
retry=0;
while(!DHT11_DQ_IN&&retry<100)//等待变高电平
{
retry++;
delay_us(1);
}
delay_us(40);//等待40us
if(DHT11_DQ_IN)return 1;
else return 0;
}
//从DHT11读取一个字节
//返回值:读到的数据
u8 DHT11_Read_Byte(void)
{
u8 i,dat;
dat=0;
for (i=0;i<8;i++)
{
dat<<=1;
dat|=DHT11_Read_Bit();
}
return dat;
}
//从DHT11读取一次数据
//temp:温度值(范围:0~50°)
//humi:湿度值(范围:20%~90%)
//返回值:0,正常;1,读取失败
u8 DHT11_Read_Data(u8 *temp,u8 *humi)
{
u8 buf[5];
u8 i;
DHT11_Rst();
if(DHT11_Check()==0)
{
for(i=0;i<5;i++)//读取40位数据
{
buf[i]=DHT11_Read_Byte();
}
if((buf[0]+buf[1]+buf[2]+buf[3])==buf[4])
{
*humi=buf[0];
*temp=buf[2];
}
}else return 1;
return 0;
}
初始化DHT11的IO口 DQ 同时检测DHT11的存在
返回1:不存在
返回0:存在
//u8 DHT11_Init(void)
//{
// RCC->APB2ENR|=1<<8; //使能PORTG口时钟
// GPIOG->CRH&=0XFFFF0FFF;//PORTG.11 推挽输出
// GPIOG->CRH|=0X00003000;
// GPIOG->ODR|=1<<11; //输出1
// DHT11_Rst();
// return DHT11_Check();
//}
DHT11.H 程序
#ifndef __DHT11_H
#define __DHT11_H
#include "main.h"
//IO方向设置
#define DHT11_HIGH HAL_GPIO_WritePin(GPIOA, DHT11_Pin, GPIO_PIN_SET)
#define DHT11_LOW HAL_GPIO_WritePin(GPIOA, DHT11_Pin, GPIO_PIN_RESET)
#define DHT11_DQ_IN HAL_GPIO_ReadPin(GPIOA, DHT11_Pin)
#define u8 uint8_t
u8 DHT11_Init(void);//初始化DHT11
u8 DHT11_Read_Data(u8 *temp,u8 *humi);//读取温湿度
u8 DHT11_Read_Byte(void); //读出一个字节
u8 DHT11_Read_Bit(void); //读出一个位
u8 DHT11_Check(void); //检测是否存在DHT11
void DHT11_Rst(void); //复位DHT11
#endif