ESP_IDF写DHT11驱动协议源代码(不用库)

 一、接线布局以及开发环境

        

硬件:ESP32-S3,DHT11模块(三线和四线一样,这个模块自带上拉电阻10kΩ)

接线:DHT11模块正负极连电源正负极,最好是3.3v,因为模块自带了上拉电阻。ESP32上随便找个引脚(我用引脚15)连DHT11的S脚。

开发环境:ESP-IDF,VSCODE。

二、DHT11模块原理

        单总线通讯原理,非常好理解。主从双方都是既输出又输入,不过启动信号是主机发起。识别0、1靠高电平时间长短。一次固定传输40bits,总用时大概4.5ms。

        模块难点是时间单位用us,微秒,并且时间容错较低,2us左右出入都有可能通信失败。

        模块能检测湿度和温度信号,使用3.3v或5v电源都行。

        ESP32引脚开启上拉电阻和OD(开漏)输出。

三、上传源代码

#include <string.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include "driver/gpio.h" 
#include <esp_log.h>
#include "esp_timer.h"
#include "esp_rom_sys.h"

#define DHT11_GPIO	15		// DHT11引脚定义
const static char *TAG = "DHT11";

// 温度 湿度buffer
uint8_t buffer[5];
int64_t phase_duration[3]={0};
int64_t bit_duration_low[40]={0};
int64_t bit_duration_high[40]={0};

// DHT11 初始化引脚,等待1s上电时间
void DHT11_Init()
{
    
	gpio_config_t cnf={
    .mode = GPIO_MODE_OUTPUT_OD,
    .pin_bit_mask =  1ULL<<DHT11_GPIO,
    .pull_up_en=1,
    };
    gpio_config(&cnf);
    vTaskDelay(1200/portTICK_PERIOD_MS);
}

/*
timeout单位是us
*/
esp_err_t wait_pin_state(uint32_t timeout, int expected_pin_state)
{
    /*用这段固定时间的代码也可以*/
    // esp_rom_delay_us(timeout);
    // if(gpio_get_level(DHT11_GPIO)==expected_pin_state)
    //     return ESP_OK;
    // else
    //     return ESP_FAIL;

    /*建议用这段代码*/
    int64_t start_time;
    start_time=esp_timer_get_time();
    while(esp_timer_get_time()-start_time<=timeout)
    {
        if(gpio_get_level(DHT11_GPIO)==expected_pin_state)
            return ESP_OK;
        esp_rom_delay_us(1);
    }
    return ESP_FAIL;

}

esp_err_t DataRead()
{
    int64_t time_since_waiting_start;
    esp_err_t result=ESP_FAIL;
    memset(buffer,0,sizeof(buffer));
    
    gpio_set_direction(DHT11_GPIO,GPIO_MODE_OUTPUT_OD);
    gpio_set_level(DHT11_GPIO,1);
    vTaskDelay(2000/portTICK_PERIOD_MS);
    gpio_set_level(DHT11_GPIO,0);
    vTaskDelay(25/portTICK_PERIOD_MS);
    gpio_set_level(DHT11_GPIO,1);
    time_since_waiting_start=esp_timer_get_time();
    gpio_set_direction(DHT11_GPIO,GPIO_MODE_INPUT);
    
    result=wait_pin_state(19,0);  //
    if(result == ESP_FAIL)
    {
        ESP_LOGE(TAG, "Phase A Fail, slave not set LOW.");
        return ESP_FAIL;
    }
    phase_duration[0]=esp_timer_get_time()-time_since_waiting_start;
    time_since_waiting_start=esp_timer_get_time();
    /*等从机拉低总线83us,再拉高87us*/
    result=wait_pin_state(80,1);
        if (result == ESP_FAIL)
        {
            ESP_LOGE(TAG, "Phase B Fail, slave not set HIGH.");
            return ESP_FAIL;
        }
    phase_duration[1]=esp_timer_get_time()-time_since_waiting_start;
    time_since_waiting_start=esp_timer_get_time();
    result = wait_pin_state(80, 0);
        if (result == ESP_FAIL)
        {
            ESP_LOGE(TAG, "Phase C Fail, slave not set LOW to start sending.");
            return ESP_FAIL;
        }
    phase_duration[2]=esp_timer_get_time()-time_since_waiting_start;
    time_since_waiting_start=esp_timer_get_time();

    for(int j=0;j<5;j++)
    {
        for(int i =0;i<8;i++)
        {
            /*数位低电平时间*/
            while(gpio_get_level(DHT11_GPIO)==0)
            {
                esp_rom_delay_us(1);
            }
            bit_duration_low[j*8+i]=esp_timer_get_time()-time_since_waiting_start;

            /*检测数字高位的时间长度来判断是1或0*/
            time_since_waiting_start=esp_timer_get_time();
            do
            {
                if (gpio_get_level(DHT11_GPIO) == 0)
                {
                    if (esp_timer_get_time() - time_since_waiting_start > 40)
                    {
                        /*数字1*/
                        buffer[j] = buffer[j] | (1U << (7 - i));
                        /*数字0不用处理*/
                    }
                    
                    bit_duration_high[j*8+i]=esp_timer_get_time()-                    
                    time_since_waiting_start;
                    time_since_waiting_start=esp_timer_get_time();
                    break;
                }
            } while (esp_timer_get_time() - time_since_waiting_start < 74);
           
        }
    }

    result=wait_pin_state(56,1);
    if (result == ESP_FAIL)
    {
        ESP_LOGE(TAG, "Data is all read.But CAN not set high.");
        return ESP_FAIL;
    }
    return ESP_OK;
}

// 主函数
void app_main(void)
{
	esp_err_t result;
    uint8_t i,j;
    DHT11_Init();
    while(1)
    {
        memset(phase_duration,0,sizeof(phase_duration));
        memset(bit_duration_low,0,sizeof(bit_duration_low));
        memset(bit_duration_high,0,sizeof(bit_duration_high));
        result = DataRead();
        if (result==ESP_OK)
        {
            ESP_LOGI(TAG,"Reading data succeed.");
            if(((buffer[0]+buffer[1]+buffer[2]+buffer[3])&0xFF) != buffer[4])
                ESP_LOGE(TAG, "But checksum error.");
            ESP_LOGI(TAG, "Temperature is:%d.%d, Humidity is:%d.%d", buffer[2], buffer[3], buffer[0],buffer[1]);
        }
        ESP_LOGI(TAG,"PhaseA duration is:%lld",phase_duration[0]);
        ESP_LOGI(TAG,"PhaseB duration is:%lld",phase_duration[1]);
        ESP_LOGI(TAG,"PhaseC duration is:%lld",phase_duration[2]);
        ESP_LOGI(TAG,"Bit duration is as follows:");
        for(j=0;j<5;j++)
        {
            for(i=0;i<8;i++)
            {
                printf("%lld,%lld-",bit_duration_low[j*8+i],bit_duration_high[j*8+i]);
            }
            printf("\n");
        }
    }


}

        每隔2秒左右输出结果如下:       

2024-04-26 16:37:44 I (926424) DHT11: Reading data succeed.
2024-04-26 16:37:44 I (926424) DHT11: Temperature is:26.1, Humidity is:71.2
2024-04-26 16:37:44 I (926424) DHT11: PhaseA duration is:19
2024-04-26 16:37:44 I (926424) DHT11: PhaseB duration is:79
2024-04-26 16:37:44 I (926434) DHT11: PhaseC duration is:79
2024-04-26 16:37:44 I (926434) DHT11: Bit duration is as follows:
2024-04-26 16:37:44 51,27-49,72-50,25-51,25-51,25-50,72-50,71-51,73-
2024-04-26 16:37:44 51,25-50,25-51,25-51,25-50,26-50,25-51,70-51,28-
2024-04-26 16:37:44 51,25-50,26-50,25-51,72-49,72-50,25-51,70-51,28-
2024-04-26 16:37:44 51,25-50,25-51,25-51,25-51,25-49,25-50,26-50,74-
2024-04-26 16:37:44 51,25-51,70-51,71-51,25-49,26-49,71-49,25-51,25-

        我将40位bit的时间高低都打印出来了,51,27表示低位持续51us,高位持续27us,这两段表示一个bit数字0。

四、聊一些体会

        这个DHT11难点是调用ESP32的微秒级计时器。在通讯过程千万不能用ESP_LOGI()这类打印函数,安插了这类打印函数在通信中途试图看时间点,试了很多次都通信失败,发现调用一次这个打印函数耗时2000多us.....

        vTaskDelay()这种是Freertos控制的定时器,依赖软系统,而Freertos的sys_tick默认是100ms,最低也只能设置到1ms,意思是Freertos对后台控制切换的精度最高是1毫秒一次,而我们要求精度去到微秒,因此不能用vtaskdelay(),要用硬件定时器的esp_timer_get_time()。

        试了很多次都是无法检测Phase A下降沿、读取40bit过程无法检测上拉电位等等,怀疑过是硬件线路搭建错误比如上拉电位达不到,也怀疑过是系统定时不够精确。经实验,系统硬件定时足够精确到1us。用示波器检查结果如下:

主机拉高保持18us,从机第一次拉低。
从机低电平保持80us,拉高。
从机高电平保持80us,拉低。
***下面开始发送数据bit***
从机低电平50us,拉高
从机高电平26us,拉低  信号0
从机低电平50us,拉高
从机高电平70us,拉低  信号1
从机低电平52us,拉高
从机高电平26us,拉低  信号0
从机低电平50us,拉高
从机高电平24us,拉低  信号0
从机低电平50us,拉高
从机高电平26us,拉低  信号0
***数据发送完毕***

从机低电平保持52us,拉高,完成一次传输

        原来这个模块的时间间隔与芯片手册描述的间隔有出入。

  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值