STM32F103RB扩展板(DHT11+LCD显示)
一、DHT11基础储备
DHT11 数字温湿度传感器是一款含有已校准数字信号输出的温湿度复合传感器,内部由一个 8 位单片机控制一个电阻式感湿元件和一个 NTC 测温元件。DHT11 虽然也是采用单总线协议,但是该协议与 DS18B20 的单总线协议稍微有些不同之处。
相比于 DS18B20 只能测量温度,DHT11 既能检测温度又能检测湿度,不过 DHT11 的精度和测量范围都要低于 DS18B20,其温度测量范围为 0~50℃,误差在±2℃;湿度的测量范围为 20%~90%RH(Relative Humidity 相对湿度—指空气中水汽压与饱和水汽压的百分比),误差在±5%RH。DHT11 电路很简单,只需要将 Dout 引脚连接单片机的一个 I/O 即可,不过该引脚需要上拉一个 5K 的电阻,DHT11 的供电电压为 3~5.5V。
二、协议及数据格式
DHT11 采用单总线协议与单片机通信,单片机发送一次复位信号后,DHT11 从低功耗模式转换到高速模式,等待主机复位结束后,DHT11 发送响应信号,并拉高总线准备传输数据。一次完整的数据为 40bit,按照高位在前,低位在后的顺序传输。
数据格式为:8bit 湿度整数数据+8bit 湿度小数数据+8bit 温度整数数据+8bit 温度小数数据+8bit 校验和,一共 5 字节(40bit)数据。由于 DHT11 分辨率只能精确到个位,所以小数部分是数据全为 0。校验和为前 4 个字节数据相加,校验的目的是为了保证数据传输的准确性。
DHT11 只有在接收到开始信号后才触发一次温湿度采集,如果没有接收到主机发送复位信号,DHT11 不主动进行温湿度采集。当数据采集完毕且无开始信号后,DHT11 自动切换到低速模式。
注意:由于 DHT11 时序要求非常严格,所以在操作时序的时候,为了防止中断干扰总线时序,先关闭总中断,操作完毕后再打开总中断。
三、操作时序
3.1、 主机发送复位信号
DHT11 的初始化过程同样分为复位信号和响应信号。
首先主机拉低总线至少 18ms,然后再拉高总线,延时 20~40us,取中间值 30us,此时复位信号发送完毕。
3.2、DHT11 发送响应信号
DHT11 检测到复位信号后,触发一次采样,并拉低总线 80us 表示响应信号,告诉主机数据已经准备好了;然后 DHT11 拉高总线 80us,之后开始传输数据。如果检测到响应信号为高电平,则 DHT11 初始化失败,请检查线路是否连接正常。
当复位信号发送完毕后,如果检测到总线被拉低,就每隔 1us 计数一次,直至总线拉高,计算低电平时间;当总线被拉高后重新计数检测 80us 的高电平。如果检测到响应信号之后的80us 高电平,就准备开始接收数据。实际上 DHT11 的响应时间并不是标准的 80us,往往存在误差,当响应时间处于 20~100us 之间时就可以认定响应成功。
3.3、数据传输
DHT11 在拉高总线 80us 后开始传输数据。每 1bit 数据都以 50us 低电平时隙开始,告诉主机开始传输一位数据了。DHT11 以高电平的长短定义数据位是 0 还是 1,当 50us 低电平时隙过后拉高总线,高电平持续 26~28us 表示数据“0”;持续 70us 表示数据“1”。
当 最后 1bit 数据传送完毕后,DHT11 拉低总线 50us,表示数据传输完毕,随后总线由上拉电阻拉高进入空闲状态。
3.4、区分数据0/1的巧法
还是像检测响应时间那样计算高电平持续时间那就太麻烦了!!!
数据“0”的高电平持续 26~28us,数据“1”的高电平持续70us,每一位数据前都有 50us 的起始时隙。如果我们取一个中间值 40us 来区分数据“0”和数据“1”的时隙。
当数据位之前的 50us 低电平时隙过后,总线肯定会拉高,此时延时 40us 后检测总线状态,如果为高,说明此时处于 70us 的时隙,则数据为“1”;如果为低,说明此时处于下一位数据 50us 的开始时隙,那么上一位数据肯定是“0”。
为什么延时 40us?
由于误差的原因,数据“0”时隙并不是准确 26~28us,可能比这短,也可能比这长。
当数据“0”时隙大于 26~28us 时,
如果延时太短,无法判断当前处于数据“0”的时隙还是数据“1”的时隙;
如果延时太长,则会错过下一位数据前的开始时隙,导致检测不到后面的数据
四,接收数据组成(偶校验)
五,奇偶校验(偶校验同理)
六,程序设计
unsigned int dht11_read(void)
{
int i;
long long val;
int timeout;
GPIO_ResetBits(GPIOA, GPIO_Pin_7); //0
delay_us(18000); //pulldown for 18ms
GPIO_SetBits(GPIOA, GPIO_Pin_7); //1
delay_us( 20 ); //pullup for 30us
mode_input();//输入状态
//等待DHT11拉高,80us
timeout = 5000;
while( (! GPIO_ReadInputDataBit (GPIOA, GPIO_Pin_7)) && (timeout > 0) ) timeout--; //wait HIGH
//等待DHT11拉低,80us
timeout = 5000;
while( GPIO_ReadInputDataBit (GPIOA, GPIO_Pin_7) && (timeout > 0) ) timeout-- ; //wait LOW
#define CHECK_TIME 28
for(i=0;i<40;i++) //40bit
{
timeout = 5000;
while( (! GPIO_ReadInputDataBit (GPIOA, GPIO_Pin_7)) && (timeout > 0) ) timeout--; //wait HIGH
delay_us(CHECK_TIME);
if ( GPIO_ReadInputDataBit (GPIOA, GPIO_Pin_7) ) //读取PA7的输入状态
{
val=(val<<1)+1; //左移一位+1
} else {
val<<=1;
}
timeout = 5000;
while( GPIO_ReadInputDataBit (GPIOA, GPIO_Pin_7) && (timeout > 0) ) timeout-- ; //wait LOW
}
mode_output();//推挽输出
GPIO_SetBits(GPIOA, GPIO_Pin_7);
//每个差值8bit 一个字节
if (((val>>32)+(val>>24)+(val>>16)+(val>>8) -val ) & 0xff ) return 0; //数据校验位比较,不成功返回0;
else return val>>8; //
//校验成功返回温湿度数据,不包括校验位。
}
void dht11_init (void )
{
GPIO_InitTypeDef GPIO_InitStructure;
/* Enable clock */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE);
/* Configure Ports */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_SetBits(GPIOA, GPIO_Pin_7);
}
void mode_input(void )
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
void mode_output(void )
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
七,main.c
#include "led.h"
#include "lcd.h"
#include "usart.h"
#include "dht11.h"
unsigned int dht11_Val;
unsigned char ucled, ucSec1;
unsigned char pucStr[21];
unsigned long ulTick_ms;
void DHT_Proc(void);
int main(void)
{
SysTick_Config(72000); // 定时1ms(HCLK = 72MHz)
LED_Init();
ucled=0;
STM3210B_LCD_Init();
LCD_Clear(Blue);
LCD_SetBackColor(Blue);
LCD_SetTextColor(White);
dht11_init();
ucled=0;//关闭所有LED
while(1)
{
LED_Disp(ucled);
DHT_Proc();
}
}
void DHT_Proc(void)
{
sprintf((char *)pucStr, " Humidity: %2d%%", dht11_Val>>24);
LCD_DisplayStringLine(Line3, pucStr);//数据左移24位得到湿度数据高八位(整数位)
sprintf((char *)pucStr, " Temperature: %2dC", (dht11_Val>>8)&0xff);
LCD_DisplayStringLine(Line5, pucStr);// &0xff的目的是为了只得到温度高八位数据(整数位)
}
// SysTick中断处理程序
void SysTick_Handler(void)
{
ulTick_ms++;
if(ulTick_ms%1500 == 0) //设置采样周期
{dht11_Val = dht11_read();}
}
注意:最小采样周期应该给足够时间
完整工程下载>>GitHub
选择第9个工程
作者:江多多(在校学生)
版权所有,欢迎保留原文链接进行转载:)
不忘初心,牢记使命,励志成为一名优秀的嵌入式工程师! (我的第九篇博客)