动态

详情 返回 返回

《ESP32-S3使用指南—IDF版 V1.6》第二十九章 DS18B20實驗 - 动态 详情

第二十九章 DS18B20實驗

1)實驗平台:正點原子DNESP32S3開發板

2)章節摘自【正點原子】ESP32-S3使用指南—IDF版 V1.6

3)購買鏈接:https://detail.tmall.com/item.htm?&id=768499342659

4)全套實驗源碼+手冊+視頻下載地址:http://www.openedv.com/docs/boards/esp32/ATK-DNESP32S3.html

5)正點原子官方B站:https://space.bilibili.com/394620890

6)正點原子DNESP32S3開發板技術交流羣:132780729

本章,我們將介紹ESP32-S3如何讀取外部温度傳感器的温度,來得到較為準確的環境温度。我們將學習單總線技術,通過它來實現ESP32-S3和外部温度傳感器DS18B20的通信,並把從温度傳感器得到的温度顯示在LCD上。
本章分為如下幾個小節:
29.1 DS18B20簡介
29.2 硬件設計
29.3 程序設計
29.4 下載驗證

29.1 DS18B20介紹

29.1.1 DS18B20簡介

DS18B20是由DALLAS半導體公司推出的一種“單總線”接口的温度傳感器,實物圖如下圖所示。

圖29.1.1.1 DS18B20實物圖
與傳統的熱敏電阻等測温元件相比,它是一種新型的體積小、適用電壓寬、與微處理器接口簡單的數字化温度傳感器。單總線結構具有簡潔且經濟的特點,可使用户輕鬆地組建傳感器網絡,從而為測量系統的構建引入全新的概念,測試温度範圍為-55~+125℃,精度為±0.5℃。現場温度直接以單總線的數字方式傳輸,大大提高了系統的抗干擾性。它能直接讀出被測温度,並且可根據實際要求通過簡單的編程實現9~12位的數字值讀數方式。它工作在3~5.5V的電壓範圍,採用多種封裝形式,從而使系統設置靈活、方便,設定分辨率以及用户設定的報警温度存儲在EEPROM中,掉電後依然保存。其內部結構如下圖所示。

圖29.1.1.1 DS18B20內部結構圖
ROM中的64位序列號是出廠前被標記好的,它可以看作使該DS18B20的地址序列碼,每個DS18B20的64位序列號均不相同。64位ROM的排列是:前8位是產品家族碼,接着48位是DS18B20的序列號,最後8位是前面56位的循環冗餘校驗碼(CRC=X8+X5+X4+1)。ROM作用是使每一個DS18B20都各不相同,這樣設計可以允許一根總線上掛載多個DS18B20模塊同時工作且不會引起衝突。

29.1.2 DS18B20時序介紹

所有單總線器件要求採用嚴格的信號時序,以保證數據的完整性。DS18B20共有6種信號類型:復位脈衝、應答脈衝、寫0、寫1、讀0和讀1。所有這些信號,除了應答脈衝以外,都是由主機發出同步信號。並且發送所有的命令和數據都是字節的低位在前。這裏我們簡單介紹這幾個信號的時序。
1,復位脈衝和應答脈衝

圖29.1.2.1 復位脈衝和應答脈衝時序圖
單總線上的所有通信都是以初始化序列開始。主機輸出低電平,保持低電平時間至少要在480us,以產生復位脈衝。接着主機釋放總線,4.7K的上拉電阻將單總線拉高,延時時間要在15~60us,並進入接收模式(Rx)。接着DS18B20拉低總線60~240us,以產生低電平應答脈衝。
2,寫時序

圖29.1.2.2 寫時序圖
寫時序包括寫0時序和寫1時序。所有寫時序至少需要60us,且在兩次獨立的寫時序之間至少需要1us的恢復時間,兩種寫時序均起始於主機拉低總線。寫1時序:主機輸出低電平,延時2us,然後釋放總線,延時60us。寫0時序:主機輸出低電平,延時60us,然後釋放總線延時2us。
3,讀時序

圖29.1.2.3 讀時序圖
單總線器件僅在主機發出讀時序時,才向主機傳輸數據,所以,在主機發出讀數據命令後,必須馬上產生讀時序,以便從機能夠傳輸數據。所有讀時序至少需要60us,且在2次獨立的讀時序之間至少需要1us的恢復時間。每個讀時序都由主機發起,至少拉低總線1us。主機在讀時序期間必須釋放總線,並且在時序起始後的15us之內採樣總線狀態。典型的讀時序過程為:主機輸出低電平延時2us,然後主機轉入輸入模式延時12us,然後讀取單總線當前的電平,然後延時50us。
在瞭解單總線時序之後,我們來看一下DS18B20的典型温度讀取過程,DS18B20的典型温度讀取過程為:復位→發SKIP ROM(0xCC)→發開始轉換命令(0x44)→延時→復位→發送SKIP ROM命令(0xCC)→發送存儲器命令(0xBE)→連續讀取兩個字節數據(即温度)→結束。
DS18B20的簡介,我們就介紹到這裏,關於該傳感器的詳細説明,請大家參考其數據手冊。

29.2 硬件設計

29.2.1 例程功能

DS18B20每隔100ms左右讀取一次數據,並把温度顯示在LCD上。LED閃爍用於提示程序正在運行。

29.2.2 硬件資源

1.LED燈
LED-IO1
2.USART0
U0TXD-IO43
U0RXD-IO44
3.XL9555
IIC_SDA-IO41
IIC_SCL-IO42
4.SPILCD
CS-IO21
SCK-IO12
SDA-IO11
DC-IO40(在P5端口,使用跳線帽將IO_SET和LCD_DC相連)
PWR- IO1_3(XL9555)
RST- IO1_2(XL9555)
5.DS18B20
1WIRE_DQ-IO0(在P5端口,使用跳線帽將IWIRE_DQ和IO0相連)

29.2.3 原理圖

DS18B20原理圖,如下圖所示。

圖29.2.3.1 DS18B20原理圖
從上圖可以看出,1WIRE_DQ並沒有直接跟ESP32-S3的IO0相連,而是需要通過跳線帽進行連接。1WIRE_DQ和IO0連接圖如下圖所示。

圖29.2.3.2 WIRE_DQ和IO0連接圖

圖29.2.3.3 DS18B20與開發板連接的位置

29.3 程序設計

29.3.1 程序流程圖

程序流程圖能幫助我們更好的理解一個工程的功能和實現的過程,對學習和設計工程有很好的主導作用。下面看看本實驗的程序流程圖:

圖29.3.1.1 DS18B20實驗程序流程圖

29.3.2 DS18B20函數解析

這一章節除了涉及到GPIO的API函數,便沒有再涉及到其他API函數。因此,有關GPIO的API函數介紹,請讀者回顧此前的第十章的內容。接下來,筆者將直接介紹DS18B20的驅動代碼。

29.3.3 DS18B20驅動解析

在IDF版19_ds18b20例程中,作者在19_ds18b20\components\BSP路徑下新增了一個DS18B20文件夾,分別用於存放ds18b20.c、ds18b20.h這兩個文件。其中,ds18b20.h文件負責聲明DS18B20相關的函數和變量,而ds18b20.c文件則實現了DS18B20的驅動代碼。下面,我們將詳細解析這兩個文件的實現內容。
1,ds18b20.h文件
由於數據線會存在輸入輸出模式的切換以及高低電平的設置,所以為DS18B20_DQ_PIN做了相關宏函數供單總線時序函數調用。

#define DS18B20_DQ_GPIO_PIN       GPIO_NUM_0
/* DS18B20引腳高低電平枚舉 */
typedef enum
{
   DS18B20_PIN_RESET = 0u,
    DS18B20_PIN_SET
}DS18B20_GPIO_PinState;
/* IO操作 */
#define DS18B20_DQ_IN    gpio_get_level(DS18B20_DQ_GPIO_PIN)  /* 數據端口輸入 */
/* DS18B20端口定義 */
#define DS18B20_DQ_OUT(x) do{ x ?                                              \
                      gpio_set_level(DS18B20_DQ_GPIO_PIN,DS18B20_PIN_SET) :   \
                      gpio_set_level(DS18B20_DQ_GPIO_PIN, DS18B20_PIN_RESET); \
                      }while(0)

gpio_get_level()與gpio_set_level()的介紹在GPIO章節已經介紹過了,在此不做贅述,請參照10.3.2小節的內容。
DS18B20_DQ_OUT(x)宏函數的作用是設置GPIO_NUM_0的電平狀態,x為0表示低電平,x為1表示高電平。
DS18B20_DQ_IN宏函數的作用是獲取GPIO_NUM_0的電平狀態,返回值為0表示低電平,返回值為1表示高電平。

2,ds18b20.c文件

/**
* @brief       初始化DS18B20
* @param       無
* @retval      0, 正常
*              1, 不存在/不正常
*/
uint8_t ds18b20_init(void)
{
    gpio_config_tgpio_init_struct;
   gpio_init_struct.intr_type = GPIO_INTR_DISABLE;         /* 失能引腳中斷 */
   gpio_init_struct.mode = GPIO_MODE_INPUT_OUTPUT_OD; /* 開漏模式的輸入和輸出 */
   gpio_init_struct.pull_up_en = GPIO_PULLUP_ENABLE;      /* 使能上拉 */
gpio_init_struct.pull_down_en = GPIO_PULLDOWN_DISABLE; /* 失能下拉 */
/* 設置的引腳的位掩碼 */
   gpio_init_struct.pin_bit_mask = 1ull << DS18B20_DQ_GPIO_PIN;
    gpio_config(&gpio_init_struct);                        /* 配置DS18B20引腳 */
    ds18b20_reset();
    return ds18b20_check();
}

在DS18B20初始化函數中,通過調用復位函數和自檢函數,可以知道DS18B20是否正常。
下面介紹一下在前面提及的幾個信號類型。

/**
* @brief   復位DS18B20
* @param   無
* @retval  無
*/
static void ds18b20_reset(void)
{
    DS18B20_DQ_OUT(0);           /* 拉低DQ,復位 */
   esp_rom_delay_us(750);      /* 拉低750us */
    DS18B20_DQ_OUT(1);           /* DQ=1, 釋放復位 */
    esp_rom_delay_us(15);        /* 延遲15US */
}
/**
* @brief   等待DS18B20的迴應
* @param   無
* @retval  0, DS18B20正常
*        1,DS18B20異常/不存在
*/
uint8_t ds18b20_check(void)
{
    uint8_t retry = 0;
    uint8_t rval = 0;
    while (DS18B20_DQ_IN && retry < 200)        /* 等待DQ變低, 等待200us */
    {
        retry++;
        esp_rom_delay_us(1);
    }
    if (retry >= 240)
    {
        rval = 1;
    }
    else
    {
        retry = 0;
        while (!DS18B20_DQ_IN && retry < 240)       /* 等待DQ變高, 等待240us */
        {
            retry++;
           esp_rom_delay_us(1);
        }
        if (retry >= 240)
        {
            rval = 1;
        }
    }
    return rval;
}

以上兩個函數分別代表着前面所説的復位脈衝與應答信號,大家可以對比前面的時序圖進行理解。由於復位脈衝比較簡單,所以這裏不做展開。現在看一下應答信號函數,函數主要是對於DS18B20傳感器的迴應信號進行檢測,對此判斷其是否存在。函數的實現也是依據時序圖進行邏輯判斷,例如當主機發送了復位信號之後,按照時序,DS18B20會拉低數據線60~240us,同時主機接收最小時間為480us,我們就依據這兩個硬性條件進行判斷,首先需要設置一個時限等待DS18B20響應,後面也設置一個時限等待DS18B20釋放數據線拉高,滿足這兩個條件即DS18B20成功響應。
下面介紹的是寫函數,其定義如下:

/**
* @brief   寫一個字節到DS18B20
* @param   data: 要寫入的字節
* @retval  無
*/
static void ds18b20_write_byte(uint8_t data)
{
    uint8_t j;
    for (j = 1; j <= 8; j++)
    {
        if (data & 0x01)
        {
           DS18B20_DQ_OUT(0);  
           esp_rom_delay_us(2);
           DS18B20_DQ_OUT(1);
           esp_rom_delay_us(60);
        }
        else
        {
           DS18B20_DQ_OUT(0);  
           esp_rom_delay_us(60);
           DS18B20_DQ_OUT(1);
           esp_rom_delay_us(2);
        }
        data >>= 1;            
    }
}

通過形參決定是寫1還是寫0,按照前面對寫時序的分析,我們可以很清晰知道寫函數的邏輯處理。
有寫函數肯定就有讀函數,下面看一下讀函數:

/**
* @brief   從DS18B20讀取一個位
* @param   無
* @retval  讀取到的位值: 0 / 1
*/
static uint8_t ds18b20_read_bit(void)
{
    uint8_t data = 0;
    DS18B20_DQ_OUT(0);
   esp_rom_delay_us(2);
    DS18B20_DQ_OUT(1);
   esp_rom_delay_us(12);
    if (DS18B20_DQ_IN)
    {
        data = 1;
    }
   esp_rom_delay_us(50);
    return data;
}
/**
* @brief   從DS18B20讀取一個字節
* @param   無
* @retval  讀到的數據
*/
static uint8_t ds18b20_read_byte(void)
{
    uint8_t i, b, data = 0;
    for (i = 0; i < 8; i++)
    {
        b = ds18b20_read_bit();  /* DS18B20先輸出低位數據 ,高位數據後輸出 */
        
        data |= b << i;           /* 填充data的每一位 */
    }
    return data;
}

在這裏,ds18b20_read_bit函數從DS18B20處讀取1位數據,在前面已經對讀時序也進行了詳細的分析,所以這裏也不展開解釋了。
下面介紹讀取温度函數,其定義如下:

/**
* @brief   從ds18b20得到温度值(精度:0.1C)
* @param   無
* @retval  温度值 (-550~1250)
* @note    返回的温度值放大了10倍.
*          實際使用的時候,要除以10才是實際温度.
*/
shortds18b20_get_temperature(void)
{
    uint8_t flag = 1;             /* 默認温度為正數 */
    uint8_t TL, TH;
    short temp;
    ds18b20_start();              /*  ds1820 startconvert */
    ds18b20_reset();
    ds18b20_check();
   ds18b20_write_byte(0xcc);       /*  skip rom */
   ds18b20_write_byte(0xbe);    /*  convert */
    TL = ds18b20_read_byte();    /*  LSB */
    TH = ds18b20_read_byte();    /*  MSB */
    if (TH > 7)
    { /* 温度為負,查看DS18B20的温度表示法與計算機存儲正負數據的原理一致:
        正數補碼為寄存器存儲的數據自身,負數補碼為寄存器存儲值按位取反後+1
        所以我們直接取它實際的負數部分,但負數的補碼為取反後加一,但考慮到
        低位可能+1後有進位和代碼冗餘,我們這裏先暫時沒有作+1的處理,這裏需要留意  */
        TH = ~TH;
        TL = ~TL;
        flag = 0;        /* 温度為負 */
    }
    temp = TH;       /* 獲得高八位 */
    temp <<= 8;
    temp += TL;      /* 獲得底八位 */
    /* 轉換成實際温度 */
    if (flag == 0)
    {   /* 將温度轉換成負温度,這裏的+1參考前面的説明 */
        temp = (double)(temp + 1) * 0.625;
        temp = -temp;   
    }
    else
    {
        temp = (double)temp * 0.625;               
    }
    return temp;
}

在這裏簡單介紹一下上面用到的RAM指令:
跳過ROM(0xCC),該指令只適合總線只有一個節點,它通過允許總線上的主機不提供64位ROM序列號而直接訪問RAM,節省了操作時間。
温度轉換(0x44),啓動DS18B20進行温度轉換,結果存入內部RAM。
讀暫存器(0xBE),讀暫存器9個字節內容,該指令從RAM的第一個字節(字節0)開始讀取,直到九個字節(字節8,CRC值)被讀出為止。如果不需要讀出所有字節的內容,那麼主機可以在任何時候發出復位信號以中止讀操作。

29.3.4 CMakeLists.txt文件

打開本實驗BSP下的CMakeLists.txt文件,其內容如下所示:

set(src_dirs
           DS18B20
           IIC
           LCD
           LED
           SPI
           XL9555)
set(include_dirs
           DS18B20
           IIC
           LCD
           LED
           SPI
           XL9555)
set(requires
           driver)
idf_component_register(SRC_DIRS ${src_dirs}
INCLUDE_DIRS ${include_dirs} REQUIRES ${requires})
component_compile_options(-ffast-math -O3 -Wno-error=format=-Wno-format)

上述的紅色IIC、XLP555驅動需要由開發者自行添加,以確保IIC_EXIO驅動能夠順利集成到構建系統中。這一步驟是必不可少的,它確保了IIC_EXIO驅動的正確性和可用性,為後續的開發工作提供了堅實的基礎。

29.3.5 實驗應用代碼

打開main/main.c文件,該文件定義了工程入口函數,名為app_main。該函數代碼如下。

i2c_obj_t i2c0_master;
/**
* @brief       程序入口
* @param       無
* @retval      無
*/
void app_main(void)
{
    uint8_t err;
    uint8_t t = 0;
    short temperature;
    esp_err_t ret;
   
    /* 初始化NVS */
    ret = nvs_flash_init();
    if (ret ==ESP_ERR_NVS_NO_FREE_PAGES ||ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
    {
       ESP_ERROR_CHECK(nvs_flash_erase());
        ret = nvs_flash_init();
    }
    /* 初始化LED */
    led_init();
   
    /* 初始化IIC0 */
    i2c0_master = iic_init(I2C_NUM_0);
   
    /* 初始化SPI */
    spi2_init();
   
    /* 初始化XL9555 */
    xl9555_init(i2c0_master);
   
    /* 初始化LCD */
    lcd_init();
    lcd_show_string(30, 50, 200, 16, 16, "ESP32", RED);
    lcd_show_string(30, 70, 200, 16, 16, "DS18B20 TEST", RED);
    lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);
    /* 初始化DS18B20數字温度傳感器 */
    err = ds18b20_init();
    if (err != 0)
    {
        while (1)
        {
           lcd_show_string(30, 110, 200, 16, 16, "DS18B20Error", RED);
           vTaskDelay(200);
           lcd_fill(30, 110, 239, 130 + 16, WHITE);
           vTaskDelay(200);
        }
    }
    lcd_show_string(30, 110, 200, 16, 16, "DS18B20 OK", RED);
    lcd_show_string(30, 130, 200, 16, 16, "Temp:   .C", BLUE);
   
    while (1)
    {
        /* 每100ms讀取一次 */
        if (t % 10 == 0)
        {
           temperature = ds18b20_get_temperature();
            if (temperature < 0)
            {
                /* 顯示負號 */
               lcd_show_char(30 + 40, 130, '-', 16, 0, BLUE);
               
                /* 轉為正數 */
               temperature = -temperature;
            }
            else
            {
                /* 去掉負號 */
               lcd_show_char(30 + 40, 130, ' ', 16, 0, BLUE);
            }
            
            /* 顯示正數部分 */
           lcd_show_num(30 + 40 + 8, 130, temperature / 10, 2, 16, BLUE);
            
            /* 顯示小數部分 */
           lcd_show_num(30 + 40 + 32, 130, temperature % 10, 1, 16, BLUE);
        }
        vTaskDelay(10);
        t++;
        if (t == 20)
        {
            t = 0;
            
            /* LED閃爍 */
           LED_TOGGLE();
        }
}
}

在main函數中,完成一系列外設初始化之後,調用ds18b20_init函數完成DS18B20初始化,LCD顯示實驗信息。
在while循環中,間隔100毫秒調用ds18b20_get_temperature函數獲取DS18B20傳感器的温度數據,然後在LCD進行顯示。LED燈每隔200毫秒狀態翻轉,實現閃爍效果。

29.4 下載驗證

假定DS18B20傳感器已經接上去正確的位置,將程序下載到開發板後,可以看到LED0不停的閃爍,提示程序已經在運行了。LCD顯示當前的温度值的內容如下圖所示:

圖29.4.1 DS18B20實驗測試圖
該程序還可以讀取並顯示負温度值,具備零下温度條件可以測試一下。

user avatar sfbc 头像 wszgrcy 头像 wx709294 头像 stormjun94 头像 youyudeshuanggang 头像 anjingdexiaoyanyao_ciaxxr 头像 airy 头像 cppfudaodeagan 头像 ourbmc 头像
点赞 9 用户, 点赞了这篇动态!
点赞

Add a new 评论

Some HTML is okay.