DHT11概述
特性
DHT11采用单总线式数字信号,一根数据线即可实时采集温湿度数据,模块配带专用的数字数据技术,具有极高的可靠性和稳定性。DHT11通过一根数据线即可实现与MCU间的通信,资源开销小,其时序通过数据总线延时调整。相关参数如下
参数 | /// |
---|---|
供电电压 | 3.3-5.5V DC |
输出 | 单总线数字信号 |
测量精度 | 0.1℃ |
时序
DHT11温湿度采集模块配置时序极其重要,时序图如图 所示。MCU将总线拉低大于18ms,作为保证DHT11能检测到的最小时间,紧接着拉高电平并延时20us~40us,等待DHT11低电平响应,若返80us的低电平即证明DHT11接收正常,紧接着DHT11翻转电平80us,进入准备状态发送数据。当最后一bit数据传送完毕后,DHT11拉低总线50us,随后总线由上拉电阻拉高进入空闲状态。
校验
DHT11数据包包含8位湿度整数+8位湿度小数+8为温度整数+8位温度小数+8位校验,且高位在前低位在后。当检验和等于温湿度各8位相加时,数据有效。
代码
#include "tim.h"
#include "dht11.h"
#define CPU_FREQUENCY_MHZ 8 //8M晶振,可修改,根据板子来
uint8_t Data[5];
void Delay_us(__IO uint32_t delay) //抵达延时
{
int last, curr, val;
int temp;
while (delay != 0)
{
temp = delay > 900 ? 900 : delay;
last = SysTick->VAL;
curr = last - CPU_FREQUENCY_MHZ * temp;
if (curr >= 0)
{
do
{
val = SysTick->VAL;
}
while ((val < last) && (val >= curr));
}
else
{
curr += CPU_FREQUENCY_MHZ * 1000;
do
{
val = SysTick->VAL;
}
while ((val <= last) || (val > curr));
}
delay -= temp;
}
}
void DHT_GPIO_SET_OUTPUT(void) //向DHT11发送激活信号
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.Pin=GPIO_PIN_0;
GPIO_InitStructure.Mode=GPIO_MODE_OUTPUT_PP;
GPIO_InitStructure.Speed=GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOB,&GPIO_InitStructure);
}
void DHT_GPIO_SET_INPUT(void) //向MCU发送数据
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.Pin=GPIO_PIN_0;
GPIO_InitStructure.Mode=GPIO_MODE_INPUT;
GPIO_InitStructure.Speed=GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOB,&GPIO_InitStructure);
}
uint8_t DHT_Read_Byte(void) //8位一组
{
uint8_t ReadData=0;
uint8_t temp;
uint8_t retry=0;
uint8_t i;
for(i=0; i<8; i++)
{
while(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0)==0 && retry<100)
//等待DHT应答
{
Delay_us(1);
retry++;
}
retry=0;
Delay_us(40);
if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0)==1) temp=1;
else temp=0;
while(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0)==1 && retry<100)
//等待一位采集完毕,即输出低电平
{
Delay_us(1);
retry++;
}
retry=0;
ReadData<<=1;
ReadData |= temp;
}
return ReadData;
}
uint8_t DHT_Read(void) //40位数据
{
uint8_t retry=0;
uint8_t i;
DHT_GPIO_SET_OUTPUT();
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_0,GPIO_PIN_RESET); //唤醒
HAL_Delay(18);
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_0,GPIO_PIN_SET); //拉高等待
Delay_us(20);
DHT_GPIO_SET_INPUT();
Delay_us(20);
if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0)==0) //低电平响应
{
while(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0)==0 && retry<100) 等待低电平响应结束
{
Delay_us(1);
retry++;
}
retry=0;
while(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0)==1 && retry<100) //高电平开始,唤醒响应结束,开始接收数据
{
Delay_us(1);
retry++;
}
retry=0;
for(i=0; i<5; i++)
{
Data[i] = DHT_Read_Byte();
// printf("aa");
// printf("\r\n");
}
Delay_us(50);
// printf("Tem:%2d.%d",Data[2],Data[3]);
// printf("\r\n");
// printf("Hum:%2d.%d",Data[0],Data[1]);
// printf("\r\n");
// printf("Hello World!\r\n");
}
uint32_t sum=Data[0]+Data[1]+Data[2]+Data[3]; //校正
if((sum)==Data[4]) return 1;
else return 0;
}
LCD1602 IIC总线
由于LCD1602过多的占用IO口资源,故采用IIC转8位并口的PCF8574转接板作为扩展板。PCF8574无法通过8位控制LCD,而是采用4线通信模式,将8位拆分为高4位和低4位,分两次发送指令数据。
代码
#include "lcd1602.h"
#include "main.h"
extern I2C_HandleTypeDef hi2c1;
#define SLAVE_ADDRESS_LCD 0x4E
int number_pow(char x,char y)
{
char i=0;
int result=1;
for(i=1;i<y;i++)
result*=x;
return result;
}
void lcd_send_cmd (char cmd){
char data_u, data_l;
uint8_t i2c_frame_data[4];
data_u = (cmd&0xf0);
data_l = ((cmd<<4)&0xf0);
i2c_frame_data[0] = data_u|0x0C; //en=1, rs=0
i2c_frame_data[1] = data_u|0x08; //en=0, rs=0
i2c_frame_data[2] = data_l|0x0C; //en=1, rs=0
i2c_frame_data[3] = data_l|0x08; //en=0, rs=0
HAL_I2C_Master_Transmit(&hi2c1, SLAVE_ADDRESS_LCD, (uint8_t *) i2c_frame_data, 4, 100);
HAL_Delay(1);
}
void lcd_send_data (char data){
char data_u, data_l;
uint8_t i2c_frame_data[4];
data_u = (data&0xf0);
data_l = ((data<<4)&0xf0);
i2c_frame_data[0] = data_u|0x0D; //en=1, rs=0
i2c_frame_data[1] = data_u|0x09; //en=0, rs=0
i2c_frame_data[2] = data_l|0x0D; //en=1, rs=0
i2c_frame_data[3] = data_l|0x09; //en=0, rs=0
HAL_I2C_Master_Transmit(&hi2c1, SLAVE_ADDRESS_LCD, (uint8_t *) i2c_frame_data, 4, 100);
HAL_Delay(1);
}
void lcd_clear (void){
lcd_send_cmd (0x01);
HAL_Delay(1);
}
void lcd_put_cur(int row, int col){
switch(row){
case 0:
col |= 0x80;
break;
case 1:
col |= 0xC0;
break;
}
lcd_send_cmd(col);
}
void lcd_init (void){
// HAL_Delay(50);
// lcd_send_cmd(0x30);
// HAL_Delay(5);
// lcd_send_cmd(0x30);
// HAL_Delay(1);
// lcd_send_cmd(0x30);
// HAL_Delay(10);
// lcd_send_cmd(0x20);
// HAL_Delay(10);
lcd_send_cmd(0x28);
HAL_Delay(1);
lcd_send_cmd(0x28);
HAL_Delay(1);
lcd_send_cmd(0x28);
HAL_Delay(1);
HAL_Delay(1);
lcd_send_cmd(0x01);
HAL_Delay(1);
lcd_send_cmd(0x06);
HAL_Delay(1);
lcd_send_cmd(0x0E);
HAL_Delay(1);
}
void lcd_send_string (char *str){
while(*str){lcd_send_data(*str++);}
HAL_Delay(1);
}
void lcd_show(char x,char y,int num,int len,int flag)
{
char i=0;
lcd_put_cur(x, y);
for(i=len;i>0;i--)
{
if(flag)
{
lcd_send_data('0'+num/number_pow(10,i-1)/10);
}else lcd_send_data('0'+num/number_pow(10,i-1)%10);
}
}