STM32F103C8T6读取DHT11温湿度数据显示到OLED上,数据校验和出错(已解决)

后续:问题就出在DHT11.c的DHT11_ReadByte函数里少了一句timeout = 0,导致下一个timeout计数很快溢出,从而导致读取dht11高低电平时机过早,后面所有数据的读取全部出错。

排查过DHT11硬件模块没有问题,用别的代码就能正常读出温湿度数据,用我自己的代码就是数据校验和出错的状态,五个字节都是不正常的数据。

DHT11.c代码如下:

#include "stm32f10x.h"                  // Device header
#include "Delay.h"

#define DHT11_RCC RCC_APB2Periph_GPIOA
#define DHT11_GPIO GPIOA
#define DHT11_Pin GPIO_Pin_0

void DHT11_Init_Out(void)
{
//	RCC_APB2PeriphClockCmd(DHT11_RCC, ENABLE);

	GPIO_InitTypeDef GPIO_InitStruct;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStruct.GPIO_Pin = DHT11_Pin;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(DHT11_GPIO, &GPIO_InitStruct);
	
	GPIO_SetBits(DHT11_GPIO, DHT11_Pin);
}

void DHT11_Init_In(void)
{
//	RCC_APB2PeriphClockCmd(DHT11_RCC, ENABLE);

	GPIO_InitTypeDef GPIO_InitStruct;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_InitStruct.GPIO_Pin = DHT11_Pin;
//	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(DHT11_GPIO, &GPIO_InitStruct);
}

void DHT11_BitValue(uint8_t BitValue)
{
	GPIO_WriteBit(DHT11_GPIO, DHT11_Pin, (BitAction)BitValue);
}

void DHT11_Start(void)
{
	DHT11_Init_Out();
	DHT11_BitValue(0);
	Delay_ms(20);
	DHT11_BitValue(1);
	Delay_us(30);
}

//返回1:DHT11响应失败
//返回0:响应成功,准备传输40位温湿度数据
uint8_t DHT11_Response(void)
{
	uint8_t timeout = 0;
	DHT11_Init_In();
	while((GPIO_ReadInputDataBit(DHT11_GPIO, DHT11_Pin) == 1) && timeout < 100)//等待dht11响应拉低电平
	{
		timeout++;
		Delay_us(1);
	}
	if(timeout >= 100)
		return 1;
	else
		timeout = 0;
	while((GPIO_ReadInputDataBit(DHT11_GPIO, DHT11_Pin) == 0) && timeout < 100)//dht11已拉低电平,会拉低80us,等待dht11拉高电平
	{
		timeout++;
		Delay_us(1);
	}
	if(timeout >= 100)
		return 1;
	return 0;
}

uint8_t DHT11_Init(void)
{
	RCC_APB2PeriphClockCmd(DHT11_RCC, ENABLE);

	GPIO_InitTypeDef GPIO_InitStruct;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStruct.GPIO_Pin = DHT11_Pin;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(DHT11_GPIO, &GPIO_InitStruct);
	
	GPIO_SetBits(DHT11_GPIO, DHT11_Pin);//默认高电平
	
	DHT11_Start();
	return DHT11_Response();
}

uint8_t DHT11_ReadByte(void)
{	
	uint8_t i, timeout = 0, DHT11_Byte = 0x00;
	for(i = 0; i < 8; i++)
	{
		while((GPIO_ReadInputDataBit(DHT11_GPIO, DHT11_Pin) == 1) && timeout < 100)//dht11已拉高电平,等待dht11拉低电平,读第一位数据前会拉高80us,后面的数据只有读取到1之后才需要等一会低电平
		{
			timeout++;
			Delay_us(1);
		}
        timeout = 0;//加上这句就没问题了,之前没有把timeout清零导致下面的timeout溢出,最终导致读取数据的时机错乱
		while((GPIO_ReadInputDataBit(DHT11_GPIO, DHT11_Pin) == 0) && timeout < 100)//dht11拉低50us,等待dht11发送0/1数据
		{
			timeout++;
			Delay_us(1);
		}
		Delay_us(40);//dht11超过40us仍处于高电平则读取该位为1,否则读取该位为0
		if(GPIO_ReadInputDataBit(DHT11_GPIO, DHT11_Pin) == 1)
		{
			DHT11_Byte |= (0x80 >> i);
		}
	}
	return DHT11_Byte;
}

uint8_t DHT11_ReadData(uint8_t *a, uint8_t *b, uint8_t *c, uint8_t *d, uint8_t *e)
{
	uint8_t i, DHT11_Data[5];
	
	DHT11_Start();
	if(!DHT11_Response())
	{
		for(i = 0; i < 5; i++)
		{
			DHT11_Data[i] = DHT11_ReadByte();
		}
//		if(DHT11_Data[0]+DHT11_Data[1]+DHT11_Data[2]+DHT11_Data[3] == DHT11_Data[4])
//		{
			*a = DHT11_Data[0];
			*b = DHT11_Data[1];
			*c = DHT11_Data[2];
			*d = DHT11_Data[3];
			*e = DHT11_Data[4];
//		}
//		else return 1;
	}
	else return 1;//读取失败
	return 0;//读取成功
}

main.c代码如下:

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "LED.h"
#include "Key.h"
#include "DHT11.h"

uint8_t a, b, c, d, e;

int main(void)
{
	OLED_Init();
	LED_Init();
	Key_Init();
	
	OLED_Clear();
	
	while(DHT11_Init())
	{
		OLED_ShowString(1, 1, "DHT11_Init_ERROR!");
		Delay_s(1);
	}

	OLED_ShowString(1, 1, "DHT11_READY!");
	Delay_s(1);

	OLED_Clear();
	
	OLED_ShowString(1, 1, "a:    b:");
	OLED_ShowString(2, 1, "c:    d:");
	OLED_ShowString(4, 1, "e:");
	
	Delay_s(1);
	
	while(1)
	{

//		if(Key_GetState())
//		{
//			LED_Turn();
//		}
		
		if(DHT11_ReadData(&a, &b, &c, &d, &e))
		{
			OLED_ShowString(1, 3, "ERR");
			OLED_ShowString(2, 3, "ERR");
		}
		else
		{
			OLED_ShowNum(1, 3, a, 3);
			OLED_ShowNum(1, 9, b, 3);
			OLED_ShowNum(2, 3, c, 3);
			OLED_ShowNum(2, 9, d, 3);
			OLED_ShowNum(4, 3, e, 5);
		}
		Delay_s(1);
	}
}

### STM32F103C8T6 驱动 DHT11 温湿度传感器并通过 OLED 显示的代码示例 以下是完整的代码实现,涵盖了 STM32F103C8T6 的 GPIO 初始化、DHT11 数据读取以及 OLED 屏幕显示功能。 #### 主函数部分 ```c #include "stm32f10x.h" #include "dht11.h" #include "oled12864.h" int main(void) { uint8_t temp, humi; char str_temp[10], str_humi[10]; // 初始化模块 DHT11_Init(); OLED_Init(); // 清除屏幕并设置初始文字 OLED_Clear(); OLED_ShowString(0, 0, "DHT11 Test:"); OLED_ShowString(0, 2, "Temp:"); OLED_ShowString(0, 4, "Humi:"); while (1) { if (DHT11_Read_Data(&temp, &humi) == SUCCESS) { itoa(temp, str_temp, 10); itoa(humi, str_humi, 10); // 更新屏幕上的数值 OLED_SetCursor(48, 2); OLED_WriteString(str_temp); OLED_SetCursor(48, 4); OLED_WriteString(str_humi); delay_ms(1000); // 延时一秒再刷新数据 } } return 0; } ``` #### DHT11 头文件定义 头文件 `dht11.h` 定义了与 DHT11 相关的操作接口[^2]。 ```c #ifndef __DHT11_H_ #define __DHT11_H_ #include "stm32f10x.h" #include "delay.h" void DHT11_Init(void); uint8_t DHT11_Start(void); uint8_t DHT11_Read_Byte(void); Status DHT11_Read_Data(uint8_t *temperature, uint8_t *humidity); #endif /* __DHT11_H_ */ ``` #### DHT11 实现细节 下面是 DHT11 的具体实现逻辑,主要完成启动握手和数据接收操作。 ```c #include "dht11.h" // 初始化 DHT11 接口 void DHT11_Init() { GPIO_InitTypeDef GPIO_InitStruct; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); GPIO_InitStruct.GPIO_Pin = GPIO_Pin_12; // 使用 PB12 作为 DHT11 数据引脚 GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_OD; // 开漏输出模式 GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStruct); GPIO_SetBits(GPIOB, GPIO_Pin_12); // 设置为高电平 } // 启动手势检测 uint8_t DHT11_Start() { GPIO_ResetBits(GPIOB, GPIO_Pin_12); // 下拉至少 18ms Delay_Ms(20); GPIO_SetBits(GPIOB, GPIO_Pin_12); // 上拉等待响应 Delay_Useconds(30); GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU; // 输入上拉模式 GPIO_Init(GPIOB, &GPIO_InitStruct); // 等待低电平回应 uint32_t timeout = 0xFFFF; while (!(timeout--) && GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_12)); // 等待高电平确认 timeout = 0xFFFF; while (!(timeout--) && !GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_12)); return timeout ? SUCCESS : ERROR; } // 获取一字节数据 uint8_t DHT11_Read_Byte() { uint8_t i, ret = 0; for (i = 0; i < 8; ++i) { while (!GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_12)); // 等待上升沿 Delay_Useconds(30); // 判断时间窗口 if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_12)) // 如果仍为高,则记录为1 ret |= (1 << ((7 - i))); while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_12)); // 等待下降沿 } return ret; } // 读取温湿度数据 Status DHT11_Read_Data(uint8_t *temperature, uint8_t *humidity) { Status status = ERROR; if (DHT11_Start() == SUCCESS) { *humidity = DHT11_Read_Byte(); // 湿度整数部分 DHT11_Read_Byte(); // 湿度小数部分(忽略) *temperature = DHT11_Read_Byte(); // 温度整数部分 DHT11_Read_Byte(); // 温度小数部分(忽略) if (DHT11_Read_Byte() == (*humidity + *temperature)) { // 校验和验证 status = SUCCESS; } } return status; } ``` #### OLED 驱动代码片段 OLED 部分提供了初始化、清屏和字符串显示等功能。以下是一个简单的实现例子: ```c #include "oled12864.h" void OLED_Init() { // IIC 初始化代码省略... } void OLED_Clear() { // 清屏命令发送到 OLED ... } void OLED_ShowString(uint8_t x, uint8_t y, const char* str) { OLED_SetCursor(x, y); while (*str != '\0') { OLED_WriteChar(*str++); } } void OLED_SetCursor(uint8_t x, uint8_t y) { // 移动光标位置至指定坐标 ... } void OLED_WriteChar(char ch) { // 将字符写入 OLED 缓冲区 ... } ``` 以上代码实现了基于 STM32F103C8T6 控制器的 DHT11OLED 结合应用[^3]。 --- 相关问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值