stm32写DHT11只能输出高电平的问题

问题描述:

最近在做某个小项目时,需要使用DHT11温湿度传感器。传感器使用单总线传输协议需要遵守一定的时序才能正常使用,但是最后却遇到了一个很奇怪的问题——无论怎么调试,结果都DHT11传输回来的信号都只有高电平,根本没有低电平。

问题原因:

在排除了硬件芯片烧毁、电路连接错误的问题之后,发现主要原因由两个:

一.时序错误

DHT11使用手册上写着“当总线空闲的时候,总线总是高电平。”以及“开启时序错误,传感器将不会响应”。既然传感器的总线无论怎样都只有高电平没有低电平,会不会是传感器的开启信号根本就是错的?

经过调试,答案的确是“是"。程序的时序有问题,问题的关键在这两个个delay_us()和delay_ms()上。

我原本使用的的两个delay函数是从当初初学时使用的正点原子的代码那边直接copy过来的。查看这两个函数可以发现,都是调用了系统的Ticky时钟来进行计时延迟的。那是使用的stm32芯片型号是STM32F103ZE,然而此刻我所使用的芯片型号是STM32F103C8,型号不同导致系统时钟不同,导致了同样的一个delay_ms(),延迟的时间不一样!!

使用kile5的debug查看延迟1s实际上使用了多少时间。可以看到实际上运行了7.2s!!由此可以得出结论——整个工程的时间延迟都是错的,直接导致启动信号不对,而启动信号的时序都已经错了,难怪之后传感器根本没有响应。

运行前系统时间0.0001866s

运行后系统时间7.2001912s,然而代码中写的应该延迟1s的。

解决办法:最简单的就是将所有延迟数据全部除以7.2,然而这只是治标不治本,对于其他同样需要使用系统时钟的外设(如定时器等),类似的错误将同样存在。所以,最根本的还是更改系统的时钟频率。

二.Debug的断点设置问题

这个实际上并不是错误,只是在调试过程走的一条弯路。

概括来说就是,不可在数据位高低电平传输过程中设置断点观察数据,而应该等待接收的一帧数据全部都接收完毕之后再检查数据。前者会导致接收到的数据异常,然而采用后者之后会发现根本没有错误。

推测原因还是DHT11的接收时序比较严格吧,设置断点对接收时序有很大影响。

总结一下,这个传感器并不难使用,但在使用过程中一定要注意时间的延迟问题。

最后附上亲测能用的程序。

DHT11.c:

#include "sys.h"
#include "delay.h"
#include "DHT11.h"
#include "led.h"


u8 temperoture = 0,humiture = 0;

	
//--------------------------------------------------------
//设置引脚输入
void setpin_input(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;      //浮空输入
	GPIO_Init(GPIOB, &GPIO_InitStructure); 
	
}


//--------------------------------------------------------
//设置引脚输出
void setpin_output(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;

	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; 
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
}

//--------------------------------------------------------
//开启应答信号
int reset_DHT11(void)
{
	u8 count = 0;
	
	setpin_output();   //Êä³öµÍµçƽ
	GPIO_ResetBits(GPIOB,GPIO_Pin_10);
	
	delay_ms(3);      //ÖÁÉÙ18msµÍµçƽ
	GPIO_SetBits(GPIOB,GPIO_Pin_10);

	delay_us(4);
	
	setpin_input();    //ÊäÈëģʽ,DHT11¿ªÊ¼Ó¦´ð
	
	while( GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_10) == 0 )     //80us低电平
	{	
	}
	
	while( GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_10) == 1 && count < 100)     //80us高电平
	{
		count++;
	}
	
	if(count == 100)           //超时,响应失败
	{
		return 0;
	}
	else
	{
		return 1;
	}
	
}

//--------------------------------------------------
//读取一位数据
uint8_t read_bit(void)
{
	uint8_t onebit = 0;
	
	while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_10) == 0);        //50us低电平
	
	delay_us(6); 
		 
	if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_10) == 1)           //50us后还是高电平的就是数据1
	{
		onebit = 1;
	}
	
	return onebit;
}


//--------------------------------------------------
//读取数据
void read_data(void)
{
	uint8_t data[5] = {0,0,0,0,0};
	u8 i,j,flag;
	
	do
	{
		flag = reset_DHT11();
	}
	while(flag != 1);             //直到响应成功
	

	for(i=0;i<5;i++)
	{
		for(j=0;j<8;j++)
		{
			data[i] <<= 1;           //左移一位
			data[i] |= read_bit();	 //按位或
		}		
	}	           
	
	while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_10) == 0);  //信号结束低电平

	setpin_output();             //设置输出模式,挂起信号线
			
	if(data[4] == data[0]+data[1]+data[2]+data[3])
	{
		humiture = data[0];
		temperoture = data[2];
	}

	delay_us(2);
}

DHT11.h:

#ifndef _DHT11_H_
#define _DHT11_H_

#include "stdio.h"	
#include "sys.h"

void setpin_input(void);
void setpin_output(void);
int reset_DHT11(void);
uint8_t read_bit(void);
void read_data(void);

#endif

猜你喜欢

转载自blog.csdn.net/xdearluo/article/details/83785874