stm32f103c8t6+dht11测量温湿度(单总线协议)

核心问题:如何根据电路和时序来写程序
接口协议:单总线(意味着一根数据线实现接收和发送,所以相应的GPIO口要设置为2种模式(输入/输出))
电路:如果没有在电路中作5k上拉,可以把GPIO模式设置成GPIO_Mode_IPU实现软件上拉的效果在这里插入图片描述时序:在这里插入图片描述时序解读:初始化(MCU复位+DHT11响应)、数据传输、结束
(1)初始化
在这里插入图片描述初始化可分为2部分,即主机复位+从机响应
主机复位的时序图为红框所示:主机至少拉低18us,然后主机拉高20~40us
(注意;此时的GPIO口为输出模式,并且为了模拟上拉效果,使用GPIO_Mode_IPU)

void DHT11_User_Config(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
}

/*GPIO初始化*/
void DHT_Mode(uint16_t mode)//mode==1时,配置为输出模式; mode==0,配置为输入模式
{
	GPIO_InitTypeDef GPIO_InitStructure;	
	if(mode == 1)
	{
		GPIO_InitStructure.GPIO_Pin = DHT;
		GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
		GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	}	
	else
	{
		GPIO_InitStructure.GPIO_Pin = DHT;
		GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//软件实现5K上拉电阻
	}	
	GPIO_Init(GPIOB,&GPIO_InitStructure);	//调用库函数初始化GPIOB	
}

/*向dht11发送启动信号,这也叫复位信号*/
void Send_Start(void)
{
	DHT_Mode(OUT);//OUT被宏定义为1,即DHT_Mode(1):输出模式
	
	DHT_Low;//#define DHT_Low    GPIO_ResetBits(DHT_PROT,DHT)
	delay_ms(18);//拉低总线,保持18ms
	
	DHT_High;//释放总线,#define DHT_High   GPIO_SetBits(DHT_PROT,DHT)
	delay_us(40);//释放总线后要等待20~40us
}

DHT11响应的时序是绿色框所示
在这里插入图片描述先拉低80us,再拉高80us
这里为了省事(不严谨),不管dht11有没有应答,都用程序来模拟“先拉低80us,再拉高80us”的信号,让单片机认为dht11应答成功
(注意:此时DHT11发送应答信号,是从机向主机发送信号,所以GPIO口配置为输入模式)

/*传感器发送应答信号*/
//当器件检测到MCU复位信号后,拉低总线80us表示应答。再拉高总线80us开始传输数据
//这里强制认为dht11应答成功
void DHT_Send_Response(void)
{
	DHT_Mode(INT);//单片机的引脚改为接收信号的状态
	
	//检测从机低电平的时间长度,如果100us内从机不应答就退出while,继续往下执行
	while((GPIO_ReadInputDataBit(DHT_PROT,DHT) == 0) && (Time < 100))//每1us,Time自增1次   判断100us内,总线信号有没有一直保持0
	{
	Time++;
	delay_us(1);
	}
	Time = 0;//上述while循环的判断结束后,Time清零
	
	//检测从机高电平的时间长度
	while((GPIO_ReadInputDataBit(DHT_PROT,DHT) == 1) && (Time < 100))//每1us,Time自增1次   判断100us内,总线信号有没有一直保持1
	{
	Time++;
	delay_us(1);
	}
	Time = 0;//上述while循环的判断结束后,Time清零
	
}

(2)数据传输
数据无非是0/1
在这里插入图片描述dht11向主机发送高低电平
50us的低电平+26~28us高电平,表示数据0

在这里插入图片描述dht11向主机发送高低电平
50us的低电平+70us高电平,表示数据1
(注意:把数据中该写1的部分写1之后,其余部分自动写0,所以程序中只要写1的部分。为什么?我也不知道,B站的一个视频是这么说的)

/*dht11读取1字节数据*/
uint8_t DHT11_Read_Byte(void)
{
	uint8_t data = 0;
	uint8_t i;
	for(i=0;i<8;i++)//按位接受8位数据
	{
		DHT_Mode(INT);
		
		//来1个50us的延时,50us的低电平是数据传输的开始
		while((GPIO_ReadInputDataBit(DHT_PROT,DHT) == 0) && (Time < 50))//每1us,Time自增1次   判断50us内,总线信号有没有一直保持0
		{
			Time++;
			delay_us(1);
		}
		Time = 0;//上述while循环的判断结束后,Time清零
		
		
		data <<= 1;
		delay_us(40);//26~28us的高电平指数据0  70us的高电平指数据1   40是自己取的介于26~28与70之间的值
		//40us后依然为高电平就是数据1
		//这里只要写1就行,程序设置什么时候写1。剩下的自动填充为0
		
		if(GPIO_ReadInputDataBit(DHT_PROT,DHT) == 1)
		{
			data |=0x01;//数据从低位往高位移
			
			//等待高电平结束
			//30us+delay_us(40)==70us。70us的高电平即是数据1
			while((GPIO_ReadInputDataBit(DHT_PROT,DHT) == 1) && (Time < 30))//每1us,Time自增1次   判断100us内,总线信号有没有一直保持0
			{
				Time++;
				delay_us(1);
			}
			Time = 0;//上述while循环的判断结束后,Time清零
		}
	}
	return data;
}

(3)数据传输结束后,通过程序来解读温湿度并校验
一次完整的数据传输为40bit,高位先出。
数据格式:8bit湿度整数数据+8bit湿度小数数据+8bi温度整数数据+8bit温度小数数据+8bit校验和
数据传送正确时校验和数据等于“8bit湿度整数数据+8bit湿度

/*DATA[0]~DATA[4]:8位的湿度正数数据  8位的湿度小数数据  8位温度整数数据  8位温度小数数据 8位的校验和*/
//读取温湿度的值并校验
void DHT11_Read_Data(uint8_t *humi,uint8_t *temp)
{
	uint8_t i;
	uint8_t DATA[5] = {0,0,0,0,0};
	Send_Start();//初始化
	DHT_Send_Response();//dht11应答成功
	
	//dht11应答成功之后就开始传输数据了
	
	for( i=0;i<5;i++)//读取40位数据
	{
		DATA[i] = DHT11_Read_Byte();
	}
	
	delay_ms(1);
	
	if((DATA[0]+DATA[1]+DATA[2]+DATA[3] == DATA[4]))//如果校验没问题则输出测量结果
	{
		*humi = DATA[0];
		*temp = DATA[2];
	}
	
	else//否则数据清零 
	{
		for( i=0;i<5;i++)
		{
		DATA[i] = 0;
		}
	}
}

整体程序代码块
dht11.c

#include "dht11.h"
#include "delay.h"

uint16_t Time = 0;

/*开时钟*/
void DHT11_User_Config(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
}

/*GPIO初始化*/
void DHT_Mode(uint16_t mode)//mode==1时,配置为输出模式; mode==0,配置为输入模式
{
	GPIO_InitTypeDef GPIO_InitStructure;	
	if(mode == 1)
	{
		GPIO_InitStructure.GPIO_Pin = DHT;
		GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
		GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	}	
	else
	{
		GPIO_InitStructure.GPIO_Pin = DHT;
		GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//软件实现5K上拉电阻
	}	
	GPIO_Init(GPIOB,&GPIO_InitStructure);	//调用库函数初始化GPIOB	
}

/*向dht11发送启动信号,这也叫复位信号*/
void Send_Start(void)
{
	DHT_Mode(OUT);//OUT被宏定义为1,即DHT_Mode(1):输出模式
	
	DHT_Low;//#define DHT_Low    GPIO_ResetBits(DHT_PROT,DHT)
	delay_ms(18);//拉低总线,保持18ms
	
	DHT_High;//释放总线,#define DHT_High   GPIO_SetBits(DHT_PROT,DHT)
	delay_us(40);//释放总线后要等待20~40us
}

/*传感器发送应答信号*/
//当器件检测到MCU复位信号后,拉低总线80us表示应答。再拉高总线80us开始传输数据
//这里强制认为dht11应答成功
void DHT_Send_Response(void)
{
	DHT_Mode(INT);//单片机的引脚改为接收信号的状态
	
	//检测从机低电平的时间长度,如果100us内从机不应答就退出while,继续往下执行
	while((GPIO_ReadInputDataBit(DHT_PROT,DHT) == 0) && (Time < 100))//每1us,Time自增1次   判断100us内,总线信号有没有一直保持0
	{
	Time++;
	delay_us(1);
	}
	Time = 0;//上述while循环的判断结束后,Time清零
	
	//检测从机高电平的时间长度
	while((GPIO_ReadInputDataBit(DHT_PROT,DHT) == 1) && (Time < 100))//每1us,Time自增1次   判断100us内,总线信号有没有一直保持1
	{
	Time++;
	delay_us(1);
	}
	Time = 0;//上述while循环的判断结束后,Time清零
	
}


 /*dht11读取1字节数据*/
uint8_t DHT11_Read_Byte(void)
{
	uint8_t data = 0;
	uint8_t i;
	for(i=0;i<8;i++)//按位接受8位数据
	{
		DHT_Mode(INT);
		
		//来1个50us的延时,50us的低电平是数据传输的开始
		while((GPIO_ReadInputDataBit(DHT_PROT,DHT) == 0) && (Time < 50))//每1us,Time自增1次   判断50us内,总线信号有没有一直保持0
		{
			Time++;
			delay_us(1);
		}
		Time = 0;//上述while循环的判断结束后,Time清零
		
		
		data <<= 1;
		delay_us(40);//26~28us的高电平指数据0  70us的高电平指数据1   40是自己取的介于26~28与70之间的值
		//40us后依然为高电平就是数据1
		//这里只要写1就行,程序设置什么时候写1。剩下的自动填充为0
		
		if(GPIO_ReadInputDataBit(DHT_PROT,DHT) == 1)
		{
			data |=0x01;//数据从低位往高位移
			
			//等待高电平结束
			//30us+delay_us(40)==70us。70us的高电平即是数据1
			while((GPIO_ReadInputDataBit(DHT_PROT,DHT) == 1) && (Time < 30))//每1us,Time自增1次   判断100us内,总线信号有没有一直保持0
			{
				Time++;
				delay_us(1);
			}
			Time = 0;//上述while循环的判断结束后,Time清零
		}
	}
	return data;
}


/*DATA[0]~DATA[4]:8位的湿度正数数据  8位的湿度小数数据  8位温度整数数据  8位温度小数数据 8位的校验和*/
//读取温湿度的值并校验
void DHT11_Read_Data(uint8_t *humi,uint8_t *temp)
{
	uint8_t i;
	uint8_t DATA[5] = {0,0,0,0,0};
	Send_Start();//初始化
	DHT_Send_Response();//dht11应答成功
	
	//dht11应答成功之后就开始传输数据了
	
	for( i=0;i<5;i++)//读取40位数据
	{
		DATA[i] = DHT11_Read_Byte();
	}
	
	delay_ms(1);
	
	if((DATA[0]+DATA[1]+DATA[2]+DATA[3] == DATA[4]))//如果校验没问题则输出测量结果
	{
		*humi = DATA[0];
		*temp = DATA[2];
	}
	
	else//否则数据清零 
	{
		for( i=0;i<5;i++)
		{
		DATA[i] = 0;
		}
	}
}

dht11…h

#ifndef __DHT11_H
#define __DHT11_H	
#include "sys.h"

#define DHT        GPIO_Pin_5
#define DHT_PROT   GPIOB

#define OUT 1
#define INT 0

#define DHT_High   GPIO_SetBits(DHT_PROT,DHT)
#define DHT_Low    GPIO_ResetBits(DHT_PROT,DHT)


void DHT11_User_Config(void);
void DHT_Mode(uint16_t mode);
void Send_Start(void);
void DHT_Send_Response(void);
uint8_t DHT11_Read_Byte(void);
void DHT11_Read_Data(uint8_t *humi,uint8_t *temp);
#endif

main.c


#include "stm32f10x.h"
#include "stm32f10x_tim.h"
#include "GPIOLIKE51.h"//sys.h和GPIOLIKE51.h基本一致,delay.h中包含了sys.h所以不再使用GPIOLIKE51.h
#include "delay.h"

#include "oled.h"
#include "dht11.h"



//extern const unsigned char BMP1[];
//extern const unsigned char BMP2[];

uint8_t humi,temp;

int main(void)
{
	float length;
	delay_init();
	delay_ms(1500);
	
	OLED_Init();
	OLED_Clear();	

	DHT11_User_Config();

	while(1)
	{

		DHT11_Read_Data(&humi,&temp);
		OLED_ShowNum(80,3,humi,3,16);
		OLED_ShowNum(80,5,temp,3,16);
	}

}

oled的驱动文件就不贴了

  • 6
    点赞
  • 50
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值