基于stm32温湿度监测系统设计(零基础学习STM32智能家居系统之温湿度检测子系统的实现)
基于stm32温湿度监测系统设计(零基础学习STM32智能家居系统之温湿度检测子系统的实现)STM32F103C8T6引脚表1 引脚连线图从图1中可知,项目利用DHT11温湿度传感器来采集环境中的温度和湿度信息,当温湿度的值超过设定值时,继电器工作,带动风扇工作;LED灯闪烁,作为系统运行指示。5.2 项目连线概况表1为项目的连线概况,请大家将元件按表中的信息连接至STM32系统板上。
今天,我们要进入到智能家居系统的制作了。首先我们制作的是温湿度子系统。
5.1 温湿度子系统项目概况
图1是温湿度子系统的概况图。
图1 温湿度子系统
从图1中可知,项目利用DHT11温湿度传感器来采集环境中的温度和湿度信息,当温湿度的值超过设定值时,继电器工作,带动风扇工作;LED灯闪烁,作为系统运行指示。
5.2 项目连线概况
表1为项目的连线概况,请大家将元件按表中的信息连接至STM32系统板上。
表1 引脚连线图
STM32F103C8T6引脚 |
元件 |
功能描述 |
PB15 |
DHT11_DATA |
继电器连线 |
GND |
DC- | |
PB12 |
IN或INx | |
PC13 |
LED1 |
LED连线 |
VCC3.3 |
阳极 | |
GND |
阴极(接电阻) |
5.3 DHT11的使用
1. DTH11基本情况
DHT11是一款湿温度一体化的数字传感器。该传感器包括一个电阻式测湿元件和一个NTC测温元件,并与一个高性能8位单片机相连接。
DHT11与STM32F103C8T6之间采用简单的单总线进行通信,仅仅需要一个I/O口与MCU相连就能够实时的采集本地湿度和温度。
DHT11 的技术参数如下:
工作电压范围:3.3V-5.5V
工作电流 :平均 0.5mA
输出:单总线数字信号
测量范围:湿度 20~90%RH,温度 0~50℃
精度 :湿度±5%,温度±2℃
分辨率 :湿度 1%,温度 1℃
图2和图3为DTH11实物和引脚图。
图2 DTH11实物图
图3 引脚图
图4为DTH11传感器模块图。
图4 DTH11传感器模块
2. DTH11单总线数据格式
DHT11数字湿温度传感器采用单总线数据格式。即单个数据引脚端口完成输入输出双向传输。其数据包由5Byte(40Bit)组成,具体格式如图5所示。在图5中byte4的8位为湿度的整数部分,byte3的8位为湿度的小数部分;byte2的8位为温度的整数部分,byte1的8位为温度的小数部分;最后byte0的8位为校验和。传感器数据输出的是未编码的二进制数据。一次完整的数据传输为40bit,高位先出。
图5 DTH11单总线数据格式
图5中所表示的温湿度数值分别为湿度45.0(%RH),温度28.0(℃),校验和为73。
具体计算方法,以湿度为例,byte3为小数,全为0,所以湿度的小数部分为0。湿度的整数部分为00101101,计算过程就是:
图6 湿度的整数部分计算过程
所以湿度就是45.0(%RH)。
温度计算方法一样,小数部分为0,整数部分byte2为00011100,所以温度的计算过程为:
图7 温度的整数部分计算过程
所以湿度就是28.0(℃)。
校验和的计算方法:
图8 校验和计算方法
而温度和湿度相加为:45 28=73,所以校验正确。
3. DTH11数据传输时序分析和代码实现
图9和图10为DTH11数据传输时序。
图9 DHT11数据传输时序
图10 DHT11数据传输时序
在图10中,我们可以看出DHT11的传输可以分为两大部分,第一部分为DHT11初始化阶段;第二部分是DHT11初始化完成后的数据传送阶段。
在DHT11初始化阶段,又分为两个部分,第一部分是黑线部分,是主机信号部分;第二部分是棕线部分,是DHT11的应答部分。整个过程是,先由STM32主机发出信号,然后再由DHT产生应答,这样初始化就完成了。
在主机信号部分,由t1和t2两部分组成,过程是主机先拉低数据线,保持t1(至少 18ms)时间,然后拉高数据线t2(保持20~40us)时间,然后延时等待,如果读取到DHT11拉低数据线,说明DHT11运行正常,如果等不到DHT11拉低数据线,则说明DHT11运行不正常,初始化失败。
如果DHT11运行正常的话,DHT11会拉低数据线,保持t3(40~50us)时间,作为响应信号,然后DHT11拉高数据线,保持t4(40~50us)时间后,开始输出数据。
以上两步就完成了DHT11的初始化。
代码分析:
首先主程序执行DHT11_Init(),转入u8 DHT11_Init(void)函数,开始DHT11的初始化。
具体函数如下:
//初始化DHT11的IO口DQ,同时检测DHT11的存在,IO口选取PB15
//返回1:不存在
//返回0:存在
u8 DHT11_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB ENABLE); //使能PB端口时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15; //PB15端口配置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz
GPIO_Init(GPIOB &GPIO_InitStructure); //初始化IO口
GPIO_SetBits(GPIOB GPIO_Pin_15); //PB15 输出高
DHT11_Rst(); //复位DHT11
return DHT11_Check();//等待DHT11的回应
}
在DHT11_Init()函数中,完成了几个任务:①初始化PB15 IO口,作为DHT11的输入输出口;②进行DHT11复位,执行DHT11_Rst()函数;③最后整个函数返回DHT11_Check()的值。
下面我们先来看DHT11_Rst()函数,这个函数完成的是DHT11复位功能
//复位DHT11
void DHT11_Rst(void)
{
DHT11_IO_OUT(); //设置DHT11_IO为输出
DHT11_DQ_OUT=0; //拉低DQ_OUT
delay_ms(20); //拉低至少18ms
DHT11_DQ_OUT=1; //拉高DQ_OUT
delay_us(30); //主机拉高20~40us
}
第一句:DHT11_IO_OUT(),设定IO口为输出,以供主机发出信号,具体代码为:
void DHT11_IO_OUT(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB ENABLE); //使能PB端口时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15; //PB15端口配置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz
GPIO_Init(GPIOB &GPIO_InitStructure); //初始化IO口
}
执行完第一句话DHT11_IO_OUT()之后,执行第二句和第三句:
DHT11_DQ_OUT=0; //拉低DQ_OUT
delay_ms(20); //拉低至少18ms
上面两句的作用是拉低IO口的电平,并且延时20ms,这样就满足了DHT11初始化阶段中t1时间段的要求。
接下面执行第四句和第五句:
DQ_OUT=1; //拉高DQ_OUT
delay_us(30); //主机拉高20~40us
上面两句的作用是拉高IO口的电平,并且延时20~40us,完成t2时间段主机的等待应答任务。
至此,主机部分完成了,下面开始DHT11的响应部分。
DHT11的响应部分由DHT11_Check()函数来完成,具体代码如下:
u8 DHT11_Check(void)
{
u8 retry=0;
DHT11_IO_IN();//SET INPUT
while (DHT11_DQ_IN&&retry<100)//DHT11会拉低40~50us
{
retry ;
delay_us(1);
};
if(retry>=100)return 1;
else retry=0;
while (!DHT11_DQ_IN&&retry<100)//DHT11拉低后会再次拉高40~50us
{
retry ;
delay_us(1);
};
if(retry>=100)return 1;
return 0;
}
图11 DHT11初始化响应过程
从图11及代码可以看出,首先完成的是DHT11的IO口的初始化,初始化为输入(DHT11_IO_IN(););然后执行循环:
while (DHT11_DQ_IN&&retry<100)//DHT11会拉低40~50us
{
retry ;
delay_us(1);
};
在该循环中,当DHT11_DQ_IN接收到的是高电平,而且retry<100时,retry一直累加。DHT11_DQ_IN接收到的是高电平,说明DHT11并没有输出低电平,也就是说图11当中的t3并没有实现,主机没有接收到DHT11的初始化信息。直到等到retry达到100时,退出循环。
退出循环时,执行以下两句:
if(retry>=100)return 1;
else retry=0;
以上两句,如果retry>=100,输出1,说明初始化不成功,否则,说明在等待时间范围,DHT11接低了信号线,主机接收到信号。
接下来执行的又是循环:
while (!DHT11_DQ_IN&&retry<100)//DHT11拉低后会再次拉高40~50us
{
retry ;
delay_us(1);
};
这回完成的是t4阶段,循环条件中如果DHT11_DQ_IN输出低电平((!DHT11_DQ_IN)或者retry<100,执行循环,说明t4没有拉高电平,直到拉高电平或retry>=100时,退出循环。
if(retry>=100)return 1;
return 0;
接下来又是输出,如果retry>=100,说明在规定的时间内DHT11始终没有输出高电平,初始化不成功。输出1,否则输出0。
综上所述,在DHT11初始化的两个阶段t3和t4中,只要有一个阶段没有实现,输出就为1,否则输出为0。当输出为0时,说明初始化成功。
第一部分完成了DHT11初始化,接下来完成数据的传输。
数据的传输分为三步:第一步,逐位读入DHT11数据;第二步:将每8位读入的数据形成数据字节;第三步,读取数据并进行数据校验。
第一步:逐位读入DHT11数据;
图12显示是DHT11中“0”的信号,从图中可知,0信号包括两个阶段,第一阶段为12—14us的低电平,第二个阶段为26—28us的高电平。当第二个阶段为26—28us的高电平时,输出为“0”。
图12 DHT11中“0”的时序
图13 DHT11中“1”的时序
图13显示是DHT11中“1”的信号,从图中可知,“1”信号也包括两个阶段,第一阶段为12—14us的低电平,第二个阶段为116—118us的高电平。当第二个阶段为116—118us的高电平时,输出为“1”。
代码为:
//从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输出信号第一阶段低电平的检测,第二段循环完成的是DHT11输出信号第二阶段高电平的检测。
最后的三句表示,在第二阶段高电平信号期间,如果40us时,DHT11_DQ_IN输出还是高电平,那么就输出1,如果输出是低电平,那么输出0。
第二步:将每8位读入的数据形成数据字节;
//从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;
}
在以上代码中,首先定义了两个U8的变量,i用来控制循环,而dat用来存储读入的字节,下面我们来看dat的变化过程。
首先dat=0,说明dat=00000000,进入循环后,当i=0时,dat<<=1,这时dat左移1位后再赋值给dat,结果为dat=00000000;
紧接着执行dat|= DHT11_Read_Bit(),以前面实例中湿度的整数部分byte4的值00101101为例,由于DHT11是高位先进,所以i=0时,DHT11_Read_Bit()取00101101的最高位,为0,执行dat|= DHT11_Read_Bit(),得出结果是dat=11111110。
接着执行i=1 DHT11_Read_Bit()取的是00101101的次高位,还是0,执行dat|= DHT11_Read_Bit()后,dat=11111100。
再接着执行i=2 DHT11_Read_Bit()取的是00101101的第三高位,为1,执行dat|= DHT11_Read_Bit()后,dat=11111001。
再接着执行i=3 DHT11_Read_Bit()取的是00101101的第四高位,为0,执行dat|= DHT11_Read_Bit()后,dat=11110010。
以此类推,可以得出dat的最终结果是00101101。
第三步,读取数据并进行数据校验。
//从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;
}
程序中首先定义了一个字符数组buf[5],buf[5]数组中包括5个8位的字符组,分别是buf[0]、buf[1]、buf[2]、buf[3]和buf[4]。
当DHT11初始化成功以后,系统依次读入5个字节的40位,buf[0]存放湿度整数、buf[1]存放湿度小数、buf[2]存放温度整数、buf[3]存放温度小数, buf[4]存放校验和的值。
然后执行:
if((buf[0] buf[1] buf[2] buf[3])==buf[4])
{
*humi=buf[0];
*temp=buf[2];
}
如果校验和没问题,那么将湿度整数的数值存入变量humi,将温度整数的数值存入变量temp。
4. 主程序的调用
#include "dht11.h"
DHT11_Init(); //温湿度传感器初始化
DHT11_Read_Data(&temp_value &humi_value); //读取温湿度值
以上是STM32 智能家居中温湿度子系统中DHT11传感器实现数据采集程序的解读,下一期将完成继电器带动风扇部分的设计。欢迎共同讨论,纠错。期待关注、点赞、转发。粉丝朋友可直接私信索要相关资料(项目源码)。