DS18B20數字溫度計 (三) 1-WIRE匯流排 ROM搜索演算法和實際測試

来源:https://www.cnblogs.com/milton/archive/2022/06/12/16367287.html
-Advertisement-
Play Games

以下說明當匯流排上存在多個 DS18B20 晶元時, 識別各個 DS18B20 的編號併進行通信的演算法. 其實這是 1-Wire 匯流排的搜索演算法, 當 1-Wire 匯流排上掛接了多個設備時, 匯流排控制端需要通過 ROM Search 命令來判斷匯流排上存在的設備以及獲取他們的8位元組唯一ROM. 1-WI... ...


目錄

DS18B20 搜索演算法

以下說明當匯流排上存在多個 DS18B20 晶元時, 識別各個 DS18B20 的編號併進行通信的演算法.

其實這是 1-Wire 匯流排的搜索演算法, 當 1-Wire 匯流排上掛接了多個設備時, 匯流排控制端需要通過 ROM Search 命令來判斷匯流排上存在的設備以及獲取他們的8位元組唯一ROM.

1-WIRE SEARCH ALGORITHM 演算法規則和實現機制

ROM搜索演算法的核心規則, 是在搜索中重覆進行一個簡單的三步操作

步驟1: 讀一次: 得到一位的值

總控讀取1個bit. 這時每個設備都會將ROM當前這一位的bit值放到匯流排上, 如果這位是0, 就會對匯流排寫0(拉低匯流排), 如果這位是1, 則會對匯流排寫1, 允許匯流排保持高電平. 如果兩者都存在, 總控讀取的是0(低電平).

步驟2: 再讀一次: 得到這位的補碼

總控繼續讀一個bit, 這時候每個設備會將ROM當前這一位的bit的補碼放到匯流排上, 如果這位是0就會寫1, 如果這位是1則會寫0, 如果兩者都存在, 總控會讀到一個0, 這樣總控就會知道存在多個設備, 並且它們的ROM在這一位上的值不同.

步驟3: 寫一次: 指定這一位的目標值

總控寫入一個bit, 比如寫入0, 表示在後面的搜索中選擇這一位為0的設備, 屏蔽掉這一位為1的設備

迴圈

匯流排控制端在8位元組ROM的每一位上執行這個三步操作後, 就能知道一個 DS18B20 的 8位元組 ROM 值, 如果匯流排上有多個 DS18B20, 則需要重覆多次.

搜索示例

示例數據

下麵的例子假設匯流排上有4個設備, 對應的ROM值分別為

  • ROM1 00110101...
  • ROM2 10101010...
  • ROM3 11110101...
  • ROM4 00010001...

示例搜索過程

搜索步驟如下

  1. 單線匯流排控制端(以下簡稱總控)執行 RESET, 所有的 DS18B20設備(以下簡稱設備)響應這個RESET
  2. 總控執行 Search ROM 命令
  3. 總控讀取1個bit. 這時每個設備都會將自己的ROM的第一個bit放到匯流排上, ROM1 和 ROM4 會對匯流排寫0(拉低匯流排), 而 ROM2 和 ROM3 則會對匯流排寫1, 允許匯流排保持高電平. 這時候總控讀取的是0(低電平).
  4. 總控繼續讀下一個bit, 每個設備會將第一個bit的補碼放到匯流排上, 這時候 ROM1 和 ROM4 寫1, 而 ROM2 和 ROM3 寫0, 因此總控依然讀到一個0, 這時候總控會知道存在多個設備, 並且它們的ROM在這一位上的值不同.
  5. (說明)從每次的兩步讀取中觀察到的值分別有以下的含義
    • 00 有多個設備, 且在這一位上值不同
    • 01 所有設備的 ROM在這一位上的值是0
    • 10 所有設備的 ROM在這一位上的值是1
    • 11 匯流排上沒有設備
  6. 總控寫入一個bit, 比如寫入0, 表示在後面的搜索中屏蔽 ROM2 和 ROM3, 僅留下 ROM1 和 ROM4
  7. 總控再執行兩次讀操作, 讀到的值為0,1, 這表示匯流排上所有設備在這一位上的值都是0
  8. 總控寫入一個bit, 因為值是確定的, 這次寫入的是0
  9. 總控再執行兩次讀操作, 讀到的值為0,0, 這表示匯流排上還有多個設備, 在這一位上的值不同
  10. 總控寫入一個bit, 這次寫入0, 這將屏蔽 ROM1, 僅留下 ROM4
  11. 總控重覆進行三步操作, 讀出 ROM4 剩餘的位, 完成第一次搜索
  12. 總控再次重覆之前的搜索直到第7位
  13. 總控寫入一個bit, 這次寫入1, 將屏蔽 ROM4, 僅保留 ROM1
  14. 總控通過重覆三步操作, 讀出 ROM1 剩餘的位
  15. 總控再次重覆之前的搜索直到第3位
  16. 總控寫入一個bit, 這次寫入1, 將屏蔽 ROM1 和 ROM4 僅保留 ROM2 和 ROM3
  17. 重覆之前的邏輯, 當所有00讀數都被處理, 說明設備的ROM已經全部被讀取.

總控通過單線匯流排讀取所有設備, 每個設備需要的時間為960 µs + (8 + 3 x 64) 61 µs = 13.16 ms, 識別速度為每秒鐘75個設備.

代碼邏輯

使用代碼實現時, 整體的邏輯是按一個固定的方向(先0後1)深度優先遍歷一個二叉樹.

數據結構

  • 預設一個8位元組數組 Buff 用於記錄路徑(即ROM的讀數)
  • 預設一個8位元組數組 Stack, 用於記錄每一位的值是否確定, 如果確定就是1, 未確定就是0.
  • 預設一個整數變數 Split_Point 用於記錄每一輪搜索中得到的最深分叉點的位置, 下一次到這一位就用1進行分叉.

遍歷邏輯

在每一輪遍歷中

  1. 從低位開始, 每一位進行兩次讀, 得到這一位的值和補碼
  2. 對前面的結果進行判斷
    1. 如果為11, 說明沒有設備, 直接退出
    2. 如果為01, 說明這一位都是0, 寫入 Buff, 同時將 Stack 這一位設成 1, 表示這一位已確認
    3. 如果為10, 說明這一位都是1, 寫入 Buff, 同時將 Stack 這一位設成 1, 表示這一位已確認
    4. 如果為00, 說明這一位產生了分叉, 需要繼續判斷
  3. 對分叉的判斷, 與 Split_Point 記錄的值進行比較
    1. 如果當前位置比已知的分叉點更淺, 說明還沒到該分叉的位置, 繼續設置成 Buff 中上一次使用的值, Stack不變
    2. 如果當前位置等於分叉點, 說明已經到了上次定好的分叉位置, 上次已經用0分叉過了, 這次就用1進行分叉, 這一位就確認了, 將 Stack 這一位設成 1, 表示已確認
    3. 如果當前位置比已知的分叉點位置還要深, 說明發現了新的分叉點(例如用1分叉後, 進入了新的子樹, 發現下麵還有分叉), 更新 Split_Point 記錄分叉點位置, 將 Stack 這一位設成 0 (未確認), 用預設的0繼續往下走
  4. 在這輪遍歷結束後, Buff 就得到了一個新的地址
  5. 檢查 Split_Point 是否需要往上挪: 在 Stack 上找到 Split_Point 標識的位置, 如果值為1, 則將 Split_Point 設置到最淺的一個0的位置. (例如這次正好在分叉點使用1分叉, 當前點確認了, 而之後又全是確認的情況, 需要將分叉點往上移)
  6. 結束條件: 和深度遍歷一樣, 每一輪遍歷後分叉點可能會上下變化, 當分叉點的位置為0時, 說明遍歷結束

代碼實現

搜索邏輯的C語言代碼實現

/**
 * buff, stack 和 split_point 都是全局變數, 由外部傳入
 * 
 */
uint8_t DS18B20_Search(uint8_t *buff, uint8_t *stack, uint8_t split_point)
{
    uint8_t len = 64, pos = 0;
    /* 分叉點的初始值應該用0xFF, 如果輸入參數為0, 將其設為0xFF */
    split_point = (split_point == 0x00)? 0xFF : split_point;
    /* Reset line */
    DS18B20_Reset();
    /* Start searching */
    DS18B20_WriteByte(ONEWIRE_CMD_SEARCHROM);

    // len 初始值為64, 對 8 位元組 ROM 做一個遍歷
    while (len--)
    {
        // 兩次讀, 讀取這一位bit值和補碼
        __BIT pb = DS18B20_ReadBit();
        __BIT cb = DS18B20_ReadBit();
        if (pb && cb) // 都是1, 表示沒有設備
        {
            return 0;
        }
        else if (pb) // pb=1, cb=0, 說明這一位為1
        {
            // 在buff上記錄這一位
            *(buff + pos / 8) |= 0x01 << (pos % 8);
            DS18B20_WriteBit(SET);
            // 在stack上將這一位記錄為1, 表示已確認
            *(stack + pos / 8) |= 0x01 << (pos % 8);
        }
        else if (cb) // pb=0, cb=1, 說明這一位為0
        {
            // 在buff上記錄這一位
            *(buff + pos / 8) &= ~(0x01 << (pos % 8));
            DS18B20_WriteBit(RESET);
            // 在stack上將這一位記錄為1, 表示已確認
            *(stack + pos / 8) |= 0x01 << (pos % 8);
        }
        else // 出現分叉點
        {
            if (split_point == 0xFF || pos > split_point)
            {
                // 比上次記錄的點更深, 出現了新的分叉點
                *(buff + pos / 8) &= ~(0x01 << (pos % 8));
                DS18B20_WriteBit(RESET);
                // 在stack上將這一位記錄為0, 表示未確認
                *(stack + pos / 8) &= ~(0x01 << (pos % 8));
                // 記錄新的分叉點位置
                split_point = pos;
            }
            else if (pos == split_point)
            {
                // 到達了上次記錄的分叉點位置, 這次使用1繼續往下走
                *(buff + pos / 8) |= 0x01 << (pos % 8);
                DS18B20_WriteBit(SET);
                // 在stack上將這一位記錄為1, 表示已確認
                *(stack + pos / 8) |= 0x01 << (pos % 8);
            }
            else
            {
                // 這個分叉點處於中間位置, 還沒到處理時間, 繼續使用上次記錄的值
                DS18B20_WriteBit(*(buff + pos / 8) >> (pos % 8) & 0x01);
            }
        }
        pos++;
    }
    // 重新定位分叉點, 將其指向到stack上最後一個未確認的位置
    while (split_point > 0 && *(stack + split_point / 8) >> (split_point % 8) & 0x01 == 0x01) split_point--;
    return split_point;
}

調用方法

sp = 0;
do
{
    // ROM search and store ROM bytes to addr
    sp = DS18B20_Detect(addr, Search_Stack, sp);
    // Print the new split point and address
    UART1_TxHex(sp);
    UART1_TxChar(' ');
    PrintArray(addr, 0, 8);
    UART1_TxString("\r\n");
} while (sp);

運行實測

對一個掛載了19個 DS18B20 的 1-Wire 匯流排進行實際測試, 用1uF電容和1N4148模擬寄生供電電路, 與上位機只連了兩根線.

DS18B20搜索測試

實際的測試輸出如下, 第一列輸出的是Split_Point的值, 表示當前的分叉深度, 後半部分是這個DS18B20採樣的溫度值和CRC

0F 2854FD96F0013C1A........B20155057FA5A5669A CRC:9A ␍␊
0D 28D44496F0013C4C........BD0155057FA5A56660 CRC:60 ␍␊
0B 28744196F0013CC2........B50155057FA5A5664A CRC:4A ␍␊
09 280CCB96F0013C8D........B20155057FA5A5669A CRC:9A ␍␊
0B 28D2A396F0013C75........B50155057FA5A5664A CRC:4A ␍␊
0D 288AFB48F6973CFD.......BE0155057FA581665F CRC:5F ␍␊
0C 28AA8196F0013C37........B40155057FA5A56609 CRC:09 ␍␊
0A 283A9096F0013C37........B80155057FA5A56636 CRC:36 ␍␊
08 283E5996F0013C3A........B80155057FA5A56636 CRC:36 ␍␊
0B 2811E896F0013C2A........B70155057FA5816636 CRC:36 ␍␊
0C 28C90196F0013C66........B40155057FA5A56609 CRC:09 ␍␊
0D 28597196F0013CBA........B80155057FA5A56636 CRC:36 ␍␊
0A 28794648F65D3C26........B60155057FA5A5668F CRC:8F ␍␊
0B 2865BB96F0013CB5........BD0155057FA5A56660 CRC:60 ␍␊
0C 28ADCB96F0013CE6........BA0155057FA581664A CRC:4A ␍␊
09 281D1648F64B3CEA.......BD0155057FA5A56660 CRC:60 ␍␊
0B 2843E896F0013C6A........BB0155057FA5A566F3 CRC:F3 ␍␊
0A 289B0896F0013CD5........B70155057FA5816636 CRC:36 ␍␊
00 28EF5C96F0013C1B........BE0155057FA5A566A5 CRC:A5 ␍␊

參考


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 工作很多年後,才發現有很多工具類庫,可以大大簡化代碼量,提升開發效率,初級開發者卻不知道。而這些類庫早就成為了業界標準類庫,大公司的內部也都在使用,如果剛工作的時候就有人告訴我使用這些工具類庫,該多好! 一塊看一下有哪些工具類庫你也用過。 1. Java自帶工具方法 1.1 List集合拼接成以逗號 ...
  • 又到每天Python小技巧分享的時候了,今天給大家分享的是怎麼樣去爬取清純小姐姐照片(沒有人會拒絕美女吧,小聲說),這篇文章好像有點刺激,未成年的小伙伴就不要進來了。快來看看這些清純的小姐姐的容顏,話不多說,上教程。 先來看看效果圖 不好意思,圖片有點辣眼睛,被攔截了,還沒有還給我..... imp ...
  • 介紹了 wait notify notifyAll park unpark ReentrantLock等相關知識 ...
  • 第一種方案 使用itext填充靜態PDF模板 然後生成PDF新文件 1 使用Adobe Acrobat 編輯原PDF 添加文本域 itext依賴 <dependency> <groupId>com.itextpdf</groupId> <artifactId>itextpdf</artifactId ...
  • 一個工作了3年的粉絲私信我,在面試的時候遇到了這樣一個問題。 ”請說一下ReentrantLock的實現原理“,他當時根據自己的理解零零散散的說了一些。 但是似乎沒有說到關鍵點上,讓我出一期說一下回答思路。 好吧,關於這個問題,我們來看看普通人和高手的回答。 普通人: ReentrantLock的一 ...
  • 一、OpenFeign介紹 OpenFeign是⼀種聲明式,模版化的HTTP客戶端。使⽤OpenFeign進⾏遠程調⽤時,開發者完全感知不到這是在進⾏遠程調⽤,⽽是像在調⽤本地⽅法⼀樣。使⽤⽅式是註解+接⼝形式,把需要調⽤的遠程接⼝封裝到接⼝當中,映射地址為遠程接⼝的地址。在啟動SpringClou ...
  • 一、創建新的database clickhouse創建資料庫的語法幾乎和其他的關係型資料庫是一樣的,區別就是clickhouse存在集群cluster和庫引擎engine的概念,可以根據需要進行指定。如果沒有特殊需求,預設即可。 CREATE DATABASE [IF NOT EXISTS] db_ ...
  • 異常和正常代碼性能旗鼓相當,但是全局過濾器對性能影響比較大,大概降低了60%左右,全局過濾器走了管道,但是這跟微軟官方的性能優化又有衝突,想必微軟官方也是出於對全局過濾器異常處理的考慮吧。同時對於添加了業務的情況下,這個降低會被稀釋,沒去做壓測對比哈,正常用戶體量還不至於被這個給影響到穩定性。所以怎... ...
一周排行
    -Advertisement-
    Play Games
  • GoF之工廠模式 @目錄GoF之工廠模式每博一文案1. 簡單說明“23種設計模式”1.2 介紹工廠模式的三種形態1.3 簡單工廠模式(靜態工廠模式)1.3.1 簡單工廠模式的優缺點:1.4 工廠方法模式1.4.1 工廠方法模式的優缺點:1.5 抽象工廠模式1.6 抽象工廠模式的優缺點:2. 總結:3 ...
  • 新改進提供的Taurus Rpc 功能,可以簡化微服務間的調用,同時可以不用再手動輸出模塊名稱,或調用路徑,包括負載均衡,這一切,由框架實現並提供了。新的Taurus Rpc 功能,將使得服務間的調用,更加輕鬆、簡約、高效。 ...
  • 本章將和大家分享ES的數據同步方案和ES集群相關知識。廢話不多說,下麵我們直接進入主題。 一、ES數據同步 1、數據同步問題 Elasticsearch中的酒店數據來自於mysql資料庫,因此mysql數據發生改變時,Elasticsearch也必須跟著改變,這個就是Elasticsearch與my ...
  • 引言 在我們之前的文章中介紹過使用Bogus生成模擬測試數據,今天來講解一下功能更加強大自動生成測試數據的工具的庫"AutoFixture"。 什麼是AutoFixture? AutoFixture 是一個針對 .NET 的開源庫,旨在最大程度地減少單元測試中的“安排(Arrange)”階段,以提高 ...
  • 經過前面幾個部分學習,相信學過的同學已經能夠掌握 .NET Emit 這種中間語言,並能使得它來編寫一些應用,以提高程式的性能。隨著 IL 指令篇的結束,本系列也已經接近尾聲,在這接近結束的最後,會提供幾個可供直接使用的示例,以供大伙分析或使用在項目中。 ...
  • 當從不同來源導入Excel數據時,可能存在重覆的記錄。為了確保數據的準確性,通常需要刪除這些重覆的行。手動查找並刪除可能會非常耗費時間,而通過編程腳本則可以實現在短時間內處理大量數據。本文將提供一個使用C# 快速查找並刪除Excel重覆項的免費解決方案。 以下是實現步驟: 1. 首先安裝免費.NET ...
  • C++ 異常處理 C++ 異常處理機制允許程式在運行時處理錯誤或意外情況。它提供了捕獲和處理錯誤的一種結構化方式,使程式更加健壯和可靠。 異常處理的基本概念: 異常: 程式在運行時發生的錯誤或意外情況。 拋出異常: 使用 throw 關鍵字將異常傳遞給調用堆棧。 捕獲異常: 使用 try-catch ...
  • 優秀且經驗豐富的Java開發人員的特征之一是對API的廣泛瞭解,包括JDK和第三方庫。 我花了很多時間來學習API,尤其是在閱讀了Effective Java 3rd Edition之後 ,Joshua Bloch建議在Java 3rd Edition中使用現有的API進行開發,而不是為常見的東西編 ...
  • 框架 · 使用laravel框架,原因:tp的框架路由和orm沒有laravel好用 · 使用強制路由,方便介面多時,分多版本,分文件夾等操作 介面 · 介面開發註意欄位類型,欄位是int,查詢成功失敗都要返回int(對接java等強類型語言方便) · 查詢介面用GET、其他用POST 代碼 · 所 ...
  • 正文 下午找企業的人去鎮上做貸後。 車上聽同事跟那個司機對罵,火星子都快出來了。司機跟那同事更熟一些,連我在內一共就三個人,同事那一手指桑罵槐給我都聽愣了。司機也是老社會人了,馬上聽出來了,為那個無辜的企業經辦人辯護,實際上是為自己辯護。 “這個事情你不能怪企業。”“但他們總不能讓銀行的人全權負責, ...