DTH11是一个使用单总线驱动的温湿度传感器,此demo用到的是PC13引脚连接到传感器的OUT引脚进行获取温湿度数据,由于PC13默认作为RTC的入侵检测功能引脚,在作为普通IO口使用时需要额外的配置才能正常使用。
还有一点需要注意的是,STM32F4的库函数中寄存器BSRRL、BSRRH用于控制IO的高低电平时,是反过来的,这一点导致了我在刚开始的时候一直无法正常获取数据,以为是延时不够精确的问题,这一点希望大家注意!!!
#define dht11_high {dht11_GPIO_PORT->BSRRL=dht11_PIN;}
#define dht11_low {dht11_GPIO_PORT->BSRRH=dht11_PIN;}
#include "dth11.h"
#include "delay.h"
//数据
unsigned int rec_data[4];
void DH11_GPIO_Init_IN()
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd (dht11_GPIO_CLK, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
PWR_BackupAccessCmd( ENABLE );/* 允许修改RTC和后备寄存器*/
RTC_TamperCmd(RTC_Tamper_1, DISABLE);
// 配置 GPIO 引脚模式为输入
GPIO_InitStructure.GPIO_Pin = dht11_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(dht11_GPIO_PORT, &GPIO_InitStructure);
PWR_BackupAccessCmd(DISABLE);/* 禁止修改RTC和后备寄存器*/
}
void DH11_GPIO_Init_OUT()
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd (dht11_GPIO_CLK, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
PWR_BackupAccessCmd( ENABLE );/* 允许修改RTC和后备寄存器*/
RTC_TamperCmd(RTC_Tamper_1, DISABLE);
GPIO_InitStructure.GPIO_Pin = dht11_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(dht11_GPIO_PORT, &GPIO_InitStructure);
PWR_BackupAccessCmd(DISABLE);/* 禁止修改RTC和后备寄存器*/
}
//主机发送开始信号
void DHT11_Start(void)
{
DH11_GPIO_Init_OUT(); //输出模式
dht11_high; //先拉高
delay_us(30);
dht11_low; //拉低电平至少18us
delay_ms(20);
dht11_high; //拉高电平20~40us
delay_us(30);
DH11_GPIO_Init_IN(); //输入模式
}
//获取一个字节
char DHT11_Rec_Byte(void)
{
unsigned char i = 0;
unsigned char data;
for(i=0;i<8;i++) //1个数据就是1个字节byte,1个字节byte有8位bit
{
while( Read_Data == 0); //从1bit开始,低电平变高电平,等待低电平结束
delay_us(30); //延迟30us是为了区别数据0和数据1,0只有26~28us
data <<= 1; //左移
if( Read_Data == 1 ) //如果过了30us还是高电平的话就是数据1
{
data |= 1; //数据+1
}
while( Read_Data == 1 ); //高电平变低电平,等待高电平结束
}
return data;
}
//获取数据
void DHT11_REC_Data(void)
{
unsigned int R_H,R_L,T_H,T_L;
unsigned char RH,RL,TH,TL,CHECK;
char buf[20];
DHT11_Start(); //主机发送信号
dht11_high; //拉高电平
if( Read_Data == 0 ) //判断DHT11是否响应
{
while( Read_Data == 0); //低电平变高电平,等待低电平结束
while( Read_Data == 1); //高电平变低电平,等待高电平结束
R_H = DHT11_Rec_Byte();
R_L = DHT11_Rec_Byte();
T_H = DHT11_Rec_Byte();
T_L = DHT11_Rec_Byte();
CHECK = DHT11_Rec_Byte(); //接收5个数据
dht11_low; //当最后一bit数据传送完毕后,DHT11拉低总线 50us
delay_us(55); //这里延时55us
dht11_high; //随后总线由上拉电阻拉高进入空闲状态。
if(R_H + R_L + T_H + T_L == CHECK) //和检验位对比,判断校验接收到的数据是否正确
{
RH = R_H;
RL = R_L;
TH = T_H;
TL = T_L;
}
}
rec_data[0] = RH;
rec_data[1] = RL;
rec_data[2] = TH;
rec_data[3] = TL;
sprintf(buf, "Temp = %d", rec_data[2]);
//sprintf(buf, "Temp = %d",0);
Gui_DrawFont_GBK16(20,30,BLUE,GRAY0, buf);
memset(buf, 0, sizeof(buf));
sprintf(buf, "Humi = %d", rec_data[0]);
Gui_DrawFont_GBK16(20,80,BLUE,GRAY0, buf);
memset(buf, 0, sizeof(buf));
}
#ifndef _DTH11_H_
#define _DTH11_H_
#include "main.h"
#define dht11_PIN GPIO_Pin_13
#define dht11_GPIO_PORT GPIOC
#define dht11_GPIO_CLK RCC_AHB1Periph_GPIOC
#define dht11_high {dht11_GPIO_PORT->BSRRL=dht11_PIN;}
#define dht11_low {dht11_GPIO_PORT->BSRRH=dht11_PIN;}
#define Read_Data GPIO_ReadInputDataBit(dht11_GPIO_PORT, dht11_PIN)
void DHT11_GPIO_Init_OUT(void);
void DHT11_GPIO_Init_IN(void);
void DHT11_Start(void);
unsigned char DHT11_REC_Byte(void);
void DHT11_REC_Data(void);
#endif
delay.c
#include "delay.h"
static u8 fac_us=0;//us延时倍乘数
static u16 fac_ms=0;//ms延时倍乘数
//初始化延迟函数
//SYSTICK的时钟固定为HCLK时钟的1/8
//SYSCLK:系统时钟
void delay_init(u8 SYSCLK)
{
SysTick->CTRL&=0xfffffffb;//bit2清空,选择外部时钟 HCLK/8
fac_us=SYSCLK/8;
fac_ms=(u16)fac_us*1000;
}
//延时nms
//注意nms的范围
//SysTick->LOAD为24位寄存器,所以,最大延时为:
//nms<=0xffffff*8*1000/SYSCLK
//SYSCLK单位为Hz,nms单位为ms
//对72M条件下,nms<=1864
void delay_ms(u16 nms)
{
u32 temp;
SysTick->LOAD=(u32)nms*fac_ms;//时间加载(SysTick->LOAD为24bit)
SysTick->VAL =0x00; //清空计数器
SysTick->CTRL=0x01 ; //开始倒数
do
{
temp=SysTick->CTRL;
}
while(temp&0x01&&!(temp&(1<<16)));//等待时间到达
SysTick->CTRL=0x00; //关闭计数器
SysTick->VAL =0X00; //清空计数器
}
//延时nus
//nus为要延时的us数.
void delay_us(u32 nus)
{
u32 temp;
SysTick->LOAD=nus*fac_us; //时间加载
SysTick->VAL=0x00; //清空计数器
SysTick->CTRL=0x01 ; //开始倒数
do
{
temp=SysTick->CTRL;
}
while(temp&0x01&&!(temp&(1<<16)));//等待时间到达
SysTick->CTRL=0x00; //关闭计数器
SysTick->VAL =0X00; //清空计数器
}
#ifndef __DELAY_H
#define __DELAY_H
#include "main.h"
void delay_init(u8 SYSCLK);
void delay_ms(u16 nms);
void delay_us(u32 nus);
#endif