网站建设公司的公司哪家好/百度推广一个月费用
DHT11单总线通讯
DHT11为单总线双向通信,一次通讯时间4ms左右
引脚定义:
1脚:连接3-5V;2脚:数据线;3脚悬空;4脚接地
STM32cubemx 中只需要开启IIC 参数默认即可
在keil中加入相关头文件 和 c源文件(文件名打错了 应该是dht11 不过不影响)
要根据通讯过程写驱动代码
首先主机要给数据引脚输出一个至少18ms的低电平 然后拉高等待20-40微妙
等待DHT11回应 这时GPIO就要改变为输入状态,DHT11响应拉低80微妙 再拉高80微妙
这个过程结束后就开始发送数据。
数据为二进制0 1 区别方法为高电平的时间。如果高电平为26-28微妙时 为数据0,高电平时间为70微妙时 为数据1 基本上完成了一次数据采集
然后就是相关文件的代码
.H文件中
#ifndef __HT11_H_
#define __HT11_H_
#include "main.h"extern TIM_HandleTypeDef htim2; //引入变量//函数原型
void Delay_us(uint8_t); //微妙延时函数,启用了一个定时器。因为DHT11通讯过程涉及微妙延时
void GPIO_Input(void); //GPIO 状态转变的函数 CUBEMX默认的GPIO初始化我只开启了相关总线的使能
//把GPIO状态(输入 输出)封装成了两个函数
void GPIO_Output(void);
void DHT11_Rst(void); //主机开始采集的信号
uint8_t DHT11_Check(void); //检查DHT是否回应
uint8_t DHT11_Init(void); //初始化函数
uint8_t DHT11_ReadBit(void); //读取一位
uint8_t DHT11_ReadByte(void); //读取一个字节
uint8_t DHT11_ReadData(uint8_t *); //读取数据(40个位) #endif
C源文件
#include "ht11.h"void Delay_us(uint8_t us)
{HAL_TIM_Base_Start(&htim2);__HAL_TIM_SetCounter(&htim2,us);while(__HAL_TIM_GetCounter(&htim2) > 0);HAL_TIM_Base_Stop(&htim2);
}void GPIO_Input(void)
{GPIO_InitTypeDef GPIO_InitStruct = {0};/*Configure GPIO pin : PB1 */GPIO_InitStruct.Pin = GPIO_PIN_1;GPIO_InitStruct.Mode = GPIO_MODE_INPUT;GPIO_InitStruct.Pull = GPIO_PULLUP;HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}void GPIO_Output(void)
{GPIO_InitTypeDef GPIO_InitStruct = {0};/*Configure GPIO pin : PB1 */GPIO_InitStruct.Pin = GPIO_PIN_1;GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);}void DHT11_Rst(void) //主机开始信号
{GPIO_Output();HAL_GPIO_WritePin(GPIOB,GPIO_PIN_1,GPIO_PIN_RESET);HAL_Delay(20);HAL_GPIO_WritePin(GPIOB,GPIO_PIN_1,GPIO_PIN_SET);Delay_us(30);
}uint8_t DHT11_Check(void)
{uint8_t retry = 0;GPIO_Input();while(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1) && retry <100) //等待回应拉位低电平{retry++;Delay_us(1);}if(retry >= 100)return 1;else retry = 0; //当变量值大于100 返回1 说明无响应 返回 0 则为正确响应while(!HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1) && retry <100) //等待变为高电平{retry++;Delay_us(1);}if(retry >= 100)return 1;return 0;
}uint8_t DHT11_Init(void)
{DHT11_Rst();return DHT11_Check();
}uint8_t DHT11_ReadBit(void) //读取一个位
{uint8_t retry = 0;while(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1) && retry <100) //等待变为低电平{retry++;Delay_us(1);}retry = 0;while(!HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1) && retry <100) //等待变为高电平{retry++;Delay_us(1);}Delay_us(40); //40us 后如果为低电平 数据为0 高电平数据为1if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1))return 1;else return 0;
}uint8_t DHT11_ReadByte(void) //读取一个字节 返回值位采集值
{uint8_t i,dat;dat = 0;for(i = 0;i < 8;i++){dat <<= 1; //数据左移一位dat |= DHT11_ReadBit(); //每读取到一个位 放到dat的最后一位}return dat;
}uint8_t DHT11_ReadData(uint8_t *h)
{uint8_t buf[5];uint8_t i;DHT11_Rst();if(DHT11_Check() == 0){for(i = 0;i < 5;i++){buf[i] = DHT11_ReadByte();}if(buf[0] + buf[1] + buf[2] + buf[3] == buf[4]){*h = buf[0];h++;*h = buf[2];}}else return 1;return 0;
}
在主函数中调用相关功能
#include "ht11.h" //引入头文件
#include "oled.h" //我这里用到了OLED显示
//OLED的相关调用就不展示了
uint8_t data[2]; /定义一个数据数组
//初始化的返回值为0则是正常回应 可以采集数据if(DHT11_Init() == 0){//这里显示了“温度”“湿度等字符”OLED_ShowCHinese(0,2,5);OLED_ShowCHinese(18,2,6);oled_display_char(54,2,'%',16);OLED_ShowCHinese(64,2,3);OLED_ShowCHinese(82,2,4);}else{OLED_ShowStr(0,0,"DHT11 error!",1);}HAL_Delay(1000); //采集数据之前先延迟1ms 等待相关DHT11进入稳定状态while (1){/* USER CODE END WHILE */if(DHT11_ReadData(data) == 0){ //显示相关数据oled_display_char(36,2,data[0] /10 + 0x30,16);oled_display_char(46,2,data[0] %10 + 0x30,16);oled_display_char(100,2,data[1] /10 + 0x30,16);oled_display_char(110,2,data[1] %10 + 0x30,16);}HAL_Delay(1000); //DHT11每次采集一定要间隔1s}
以上就是DHT11的基本使用方法,代码是根据**杜洋老师《stm32入门一百步》**中的逻辑移植的。