STM32delay延时函数不准确,无法读取DHT11数据的问题
环境:KEIL5 、STM32F103C8T6 、 淘宝提供的例程
功能需求:stm32f103单片机读取dht11的温湿度数据
问题发生:在把商家提供的驱动包移植的我的工程中后,只有DHT11传感器无法正常工作,其他传感器能正常工作,初步分析是DHT11的驱动代码有问题。因为DHT11对微秒的延时要求比较高。
问题查找:
1、经过debug的调试,发现代码在if(DHT11_DATA_IN() == Bit_RESET)这里判断后,条件不成立,问题应该在DHT11_DATA_IN() 函数中。
DHT11_DATA_IN()函数的宏定义,函数功能为读取DHT11的响应信号,说明DHT11没有正常启动,所以没有返回信号。
2、当在程序中与波特率115200发送一个字节a,(大约0.0086ms),delay(50)毫秒进行延时时,发现两个延时的时间大于50毫秒(大约80ms)
3、推测:1、可能芯片的时钟有问题,可能性小 2、delay函数本来就不准确,博客上有相关的说法 3、我的程序中可能定义了某个中断,但我忘记了,所以出现延时。
4、找解决方案
在相关淘宝店上找了很多程序源码,但都是使用很多delay函数的,网上论坛上的代码也是,使用不能参考。
在一个博客上发现,可以通过while(…)函数去替代相关的delay()函数,那就可以不用考虑delay()函数是否准确的问题了。
5、替代函数分析
MCU发送开始信号和DHT11响应函数
发送起始信号后,通过while来替代两个应答的delay()函数
while(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0);//跳过DHT11响应时间
while(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 1);//跳过DHT11测量时间
相似的
到此,delay延时不准确问题基本解决,当数还是不准确的时候,可以通过调节上方的DelauUsaa(40);函数中的值,直到能测出数据。上方的函数是判断接收的数据是1还是0。
dht11.c的代码
#include "dht11.h"
//DHT11初始化函数
//DHT11 DATA---PB3
void Dht11_Init(void)
{GPIO_InitTypeDef GPIO_InitStruct;
//开B组时钟和AFIO时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB ,ENABLE);
//初始化IO口
//定义结构体变量
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP; //配置为开漏输出
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0; //选定管脚3
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; //输出速度为2MHZ
GPIO_Init( GPIOB, &GPIO_InitStruct);
GPIO_WriteBit(GPIOB, GPIO_Pin_0, Bit_SET);
DelayMs(1000); //延时1s越过不稳定状态
}
/*
* 函数名:DHT11_Mode_IPU
* 描述 :使DHT11-DATA引脚变为上拉输入模式
* 输入 :无
* 输出 :无
*/
static void DHT11_Mode_IPU(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/*选择要控制的DHT11_PORT引脚*/
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
/*设置引脚模式为浮空输入模式*/
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU ;
/*调用库函数,初始化DHT11_PORT*/
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
/*
* 函数名:DHT11_Mode_Out_PP
* 描述 :使DHT11-DATA引脚变为推挽输出模式
* 输入 :无
* 输出 :无
*/
static void DHT11_Mode_Out_PP(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/*选择要控制的DHT11_PORT引脚*/
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
/*设置引脚模式为通用推挽输出*/
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
/*设置引脚速率为50MHz */
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
/*调用库函数,初始化DHT11_PORT*/
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
//MCU发送开始信号函数
void Mcu_Send_Start_Sign(void)
{
DHT11_Mode_Out_PP();
GPIO_WriteBit(GPIOB, GPIO_Pin_0, Bit_RESET);
DelayMs(18); //拉低总线18ms以上
GPIO_WriteBit(GPIOB, GPIO_Pin_0, Bit_SET); //拉高总线等待DHT11响应
DHT11_Mode_IPU();
}
//等待DHT11响应函数
//返回值 1---响应失败 0-----响应成功
u8 Dht11_Ack(void)
{
u8 i = 0;
while(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 1)
{
i++;
DelayUs(1);
if(i>50)
{
return 1; //Dht11响应失败
}
}
//代码运行到这里代表DHT11响应成功
while(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0);//跳过DHT11响应时间
while(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 1);//跳过DHT11测量时间
return 0; //DHT11响应成功
}
//读DHT11测到的数据
u8 Read_Dht11_Data(void)
{
u8 i,data = 0;
for(i=0;i<8;i++)
{
while(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0);//跳过数据的1bit低电平开始位
DelayUs(40);
// data = data<<1;
if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 1) //数据1
{
while(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 1);//跳过数据'1'的剩余高电平时间
// data |= 1;
data|=(uint8_t)(0x01<<(7-i));
}
else{
data&=(uint8_t)~(0x01<<(7-i)); //把第7-i位置0,MSB先行
}
}
return data;
}
u8 Mcu_Control_Dht11(u8 *p)
{
u8 ret;
Mcu_Send_Start_Sign(); //发送开始信号
ret = Dht11_Ack();
if(ret)
{
return 1; //测量失败
}
p[0] = Read_Dht11_Data(); //湿度整数数据
p[1] = Read_Dht11_Data(); //湿度小数数据
p[2] = Read_Dht11_Data(); //温度整数数据
p[3] = Read_Dht11_Data(); //温度小数数据
p[4] = Read_Dht11_Data(); //8bit校验和
if(p[0]+p[1]+p[2]+p[3] != p[4])
{
return 1; //测量失败
}
return 0; //测量成功
}
dht11.h的代码
#ifndef _DHT11_H_ //防止头文件重定义
#define _DHT11_H_
#include "stm32f10x.h"
#include "delay.h"
static void DHT11_Mode_IPU(void);
static void DHT11_Mode_Out_PP(void);
void Dht11_Init(void);
u8 Mcu_Control_Dht11(u8 *p);
#endif