動1-Wire總線與DS18B20傳感器實戰(zhàn)指南)
簡介本資源是一套面向嵌入式初學者與8051單片機開發(fā)者的1-Wire總線通信實戰(zhàn)代碼包聚焦于單總線協(xié)議在溫度傳感等低功耗場景中的底層實現(xiàn)。資源以C51語言為核心完整呈現(xiàn)主控端如8051對DS18B20等典型1-Wire器件的初始化、ROM搜索、命令發(fā)送、時序控制及CRC校驗等關(guān)鍵環(huán)節(jié)適用于Keil uVision開發(fā)環(huán)境下的硬件調(diào)試與協(xié)議理解。壓縮包共17個文件含1個核心C源文件ledtest.c、1個匯編實現(xiàn)模塊wireasm.asm、1個1-Wire專用頭文件ledtest.h、1個可燒錄hex文件及多個編譯中間產(chǎn)物obj/lst/m51和工程配置備份Uv2.Bak、Opt.Bak整體僅49KB輕量易導入。已有317人學習下載配套www.pudn.com.txt提供來源說明目錄結(jié)構(gòu)體現(xiàn)典型Keil工程組織邏輯便于讀者逐層剖析時序細節(jié)、比對C與ASM混合編程策略并快速復現(xiàn)單總線多節(jié)點通信功能。1. 項目概述從“one.zip”到C51的1-Wire總線實戰(zhàn)最近在整理老項目的資料庫翻出了一個名為“one.zip”的壓縮包。這個文件名簡單到讓人會心一笑里面裝的正是我多年前用C51單片機折騰1-Wire總線時攢下的一堆代碼、原理圖和調(diào)試筆記。1-Wire總線這個由達拉斯半導體現(xiàn)屬美信發(fā)明的單線通信協(xié)議以其極簡的硬件連接一根數(shù)據(jù)線加地線和獨特的寄生供電能力在溫濕度傳感、電子標簽、設(shè)備認證等場景中經(jīng)久不衰。盡管如今ARM Cortex-M內(nèi)核大行其道但在一些對成本極度敏感、功能專一的小型設(shè)備上經(jīng)典的8051內(nèi)核單片機我們常說的C51依然有其用武之地。這個“one.zip”項目就是如何在資源有限的C51平臺上穩(wěn)定、可靠地實現(xiàn)1-Wire總線通信的完整實踐記錄。它不僅僅是一份代碼更是一套包含了時序精準控制、抗干擾設(shè)計、以及針對DS18B20這類經(jīng)典器件驅(qū)動的心法。如果你正在或即將在51系列單片機上對接1-Wire器件那么這份從老硬盤里挖出來的經(jīng)驗或許能幫你避開我當年踩過的那些坑。2. 1-Wire總線協(xié)議核心精要與C51適配挑戰(zhàn)1-Wire協(xié)議的精髓在于它僅用單根數(shù)據(jù)線就實現(xiàn)了雙向數(shù)據(jù)通信和對從設(shè)備的供電。這根線上掛載的所有設(shè)備都通過一個唯一的64位ROM ID進行尋址支持多設(shè)備掛載。通信完全由主機Master即我們的C51單片機發(fā)起和控制通過嚴格的時間槽來區(qū)分數(shù)位“0”、“1”以及復位、存在脈沖等命令。2.1 協(xié)議基礎(chǔ)時序就是一切1-Wire通信的基石是精確的微秒級時序。所有操作都始于一個由主機發(fā)出的復位脈沖至少480μs的低電平隨后主機釋放總線并切換到接收模式。在線路上的1-Wire從設(shè)備會在等待15-60μs后回應(yīng)一個存在脈沖60-240μs的低電平。主機檢測到這個脈沖便確認總線上有設(shè)備就緒。隨后的數(shù)據(jù)讀寫操作均以時間槽為單位。每個時間槽通常持續(xù)60-120μs用于傳輸1比特數(shù)據(jù)寫“1”時間槽主機拉低總線1-15μs然后釋放剩余時間保持高電平。寫“0”時間槽主機拉低總線至少60μs最多120μs然后釋放。讀時間槽主機拉低總線1-15μs后釋放然后在短暫的采樣窗口通常在起始下降沿后15μs內(nèi)讀取總線電平。高電平為“1”低電平為“0”。注意上述時間參數(shù)是DS18B20等經(jīng)典器件的典型值不同廠商、型號的1-Wire器件可能存在細微差異務(wù)必以具體器件的數(shù)據(jù)手冊為準。2.2 C51平臺的特殊挑戰(zhàn)與應(yīng)對思路在STM32等現(xiàn)代MCU上我們可以輕松使用硬件定時器產(chǎn)生精準延時甚至利用外部中斷或IO口翻轉(zhuǎn)功能來捕獲信號。但在傳統(tǒng)的C51如AT89C51、STC89C52上我們面臨幾個現(xiàn)實挑戰(zhàn)主頻較低且可變常見C51工作在11.0592MHz或12MHz每個機器周期1μs或約1μs。延時需要靠軟件循環(huán)實現(xiàn)精度受編譯器優(yōu)化和中斷影響。無高精度硬件定時器雖然有兩個16位定時器但用于μs級延時開銷過大且可能影響其他功能。中斷響應(yīng)延遲如果系統(tǒng)中開啟了中斷在讀寫1-Wire的關(guān)鍵時序窗口內(nèi)若發(fā)生中斷可能導致時序超差通信失敗。因此在C51上實現(xiàn)1-Wire驅(qū)動的核心思路是編寫高度優(yōu)化的、可重入的微秒級延時函數(shù)并在執(zhí)行關(guān)鍵1-Wire時序操作時短暫關(guān)閉全局中斷EA0。同時IO口應(yīng)配置為準雙向模式這是51單片機IO的默認模式具有弱上拉能力與1-Wire總線要求相符。3. C51驅(qū)動1-Wire總線的核心代碼實現(xiàn)與解析下面我將結(jié)合“one.zip”中的核心代碼模塊拆解如何在C51上構(gòu)建一個穩(wěn)健的1-Wire驅(qū)動層。我們以驅(qū)動最常見的DS18B20數(shù)字溫度傳感器為例。3.1 硬件連接與IO口定義假設(shè)使用P3.7口作為1-Wire總線。// 1-Wire 總線引腳定義 sbit DQ P3^7; // 數(shù)據(jù)線硬件上DQ引腳需要接一個4.7kΩ的上拉電阻至VCC這是1-Wire總線標準所要求的用于確??偩€在空閑時處于高電平狀態(tài)并為寄生供電的從設(shè)備提供能量。3.2 微秒級延時函數(shù)的實現(xiàn)這是整個驅(qū)動的精度基礎(chǔ)。由于C51指令周期固定我們可以用_nop_()空操作1個機器周期和循環(huán)來構(gòu)建延時。/** * brief 微秒級延時 (適用于12MHz晶振1機器周期1us) * param t: 需要延時的微秒數(shù)對于12MHzt即us數(shù)。 * 注意此函數(shù)本身有調(diào)用開銷需校準。 */ void Delay_us(unsigned char t) { while (t--) { _nop_(); _nop_(); _nop_(); _nop_(); // 根據(jù)實際測試調(diào)整_nop_數(shù)量以校準 } }實操心得這個延時函數(shù)非常不精確因為它受編譯器優(yōu)化和循環(huán)開銷影響。更可靠的方法是使用定時器。但對于簡單應(yīng)用可以通過示波器或邏輯分析儀調(diào)整循環(huán)體內(nèi)的_nop_()數(shù)量進行粗略校準。在“one.zip”的后期版本中我改用定時器0工作在模式28位自動重裝來產(chǎn)生一個基準的10μs或50μs中斷在此基礎(chǔ)上構(gòu)建更精確的延時。這是從“能用”到“穩(wěn)定”的關(guān)鍵一步。3.3 1-Wire復位與存在檢測這是每次通信會話的開始。/** * brief 1-Wire 復位脈沖并檢測存在脈沖 * retval 1: 有設(shè)備響應(yīng)0: 無設(shè)備響應(yīng)或總線錯誤 */ bit OW_Reset(void) { bit presence_pulse; EA 0; // 關(guān)閉全局中斷防止時序被干擾 DQ 0; // 主機拉低總線產(chǎn)生復位脈沖 Delay_us(480); // 保持低電平至少480us DQ 1; // 釋放總線主機切換為接收 Delay_us(70); // 等待15-60us后采樣這里取70us保證安全 presence_pulse DQ; // 采樣總線電平低電平表示有存在脈沖 Delay_us(410); // 等待存在脈沖周期結(jié)束 (最少480us - 70us) EA 1; // 重新開啟中斷 return !presence_pulse; // 如果采樣到低電平(presence_pulse0)則返回1有設(shè)備 }關(guān)鍵點解析EA0/EA1包裹關(guān)鍵時序操作這是C51上提高1-Wire通信可靠性的黃金法則。否則一個意外的定時器或串口中斷就可能導致拉低或采樣時間偏差幾十微秒導致失敗。延時參數(shù)480、70、410這些數(shù)值是基于12MHz晶振和粗略軟件延時校準后的結(jié)果。你需要根據(jù)自己系統(tǒng)的實際主頻和延時函數(shù)精度進行調(diào)整。返回值邏輯存在脈沖期間從機會拉低總線所以主機采樣到低電平DQ0表示有設(shè)備函數(shù)返回1。3.4 單比特讀寫操作所有字節(jié)的讀寫都建立在單比特操作上。/** * brief 向1-Wire總線寫入1比特數(shù)據(jù) * param bit_value: 要寫入的比特值0或1 */ void OW_WriteBit(bit bit_value) { EA 0; DQ 0; // 主機拉低總線啟動時間槽 _nop_(); _nop_(); // 極短延時約2us (12MHz) DQ bit_value; // 如果是寫1則釋放總線寫0則保持低電平 Delay_us(60); // 保持低電平時間對于寫0是60us寫1則很快釋放 DQ 1; // 釋放總線結(jié)束時間槽 // 兩個時間槽之間需要至少1us的恢復時間此處用短延時 _nop_(); _nop_(); EA 1; } /** * brief 從1-Wire總線讀取1比特數(shù)據(jù) * retval 讀取到的比特值0或1 */ bit OW_ReadBit(void) { bit read_value; EA 0; DQ 0; // 主機拉低總線啟動讀時間槽 _nop_(); _nop_(); // 極短延時約2us DQ 1; // 主機釋放總線準備采樣 _nop_(); _nop_(); _nop_(); _nop_(); // 等待約4us建立采樣窗口 read_value DQ; // 在下降沿后約15us內(nèi)采樣 Delay_us(55); // 等待讀時間槽剩余時間結(jié)束 EA 1; return read_value; }為什么讀和寫函數(shù)看起來這么像因為1-Wire的讀操作也是由主機發(fā)起的。主機先拉低總線1-15μs發(fā)出“讀命令”然后釋放并采樣。從設(shè)備會在主機拉低總線后決定是否將總線拉低來回應(yīng)“0”。所以O(shè)W_ReadBit函數(shù)開頭拉低又釋放的動作本質(zhì)上是在發(fā)起一個“讀時間槽”的請求。3.5 字節(jié)讀寫函數(shù)構(gòu)建有了單比特讀寫字節(jié)操作就是循環(huán)。/** * brief 向1-Wire總線寫入1字節(jié)數(shù)據(jù) * param byte_value: 要寫入的字節(jié) */ void OW_WriteByte(unsigned char byte_value) { unsigned char i; for (i 0; i 8; i) { OW_WriteBit(byte_value 0x01); // 先寫最低位 (LSB first) byte_value 1; } } /** * brief 從1-Wire總線讀取1字節(jié)數(shù)據(jù) * retval 讀取到的字節(jié) */ unsigned char OW_ReadByte(void) { unsigned char i, byte_value 0; for (i 0; i 8; i) { byte_value 1; // 先右移 if (OW_ReadBit()) { byte_value | 0x80; // 如果讀到1則設(shè)置最高位 } } return byte_value; }注意事項1-Wire協(xié)議規(guī)定先傳輸字節(jié)的最低位LSB First。OW_WriteByte中byte_value 0x01和OW_ReadByte中先右移再置位最高位的寫法正是遵循了這一規(guī)則。這是很多初學者容易出錯的地方。4. 驅(qū)動DS18B20溫度傳感器的完整應(yīng)用流程有了底層讀寫函數(shù)我們就可以按照DS18B20的命令序列來操作了。一次完整的溫度轉(zhuǎn)換和讀取流程如下4.1 初始化與ROM命令復位與存在檢測調(diào)用OW_Reset()確認傳感器在線。發(fā)送ROM命令如果總線上只有一個DS18B20可以使用跳過ROM命令0xCC直接對總線上所有設(shè)備進行操作。這是單設(shè)備場景下最常用的命令簡化了流程。OW_WriteByte(0xCC); // 跳過ROM命令4.2 發(fā)送功能命令與讀取數(shù)據(jù)發(fā)送功能命令緊接著發(fā)送啟動溫度轉(zhuǎn)換命令0x44。OW_WriteByte(0x44); // 啟動溫度轉(zhuǎn)換此時DS18B20開始進行溫度轉(zhuǎn)換。對于12位分辨率默認轉(zhuǎn)換時間最長為750ms。在此期間主機可以釋放總線去做其他事情或者通過OW_ReadBit()讀取DQ線如果為低則表示轉(zhuǎn)換未完成為高則表示完成也可用延時等待。再次初始化轉(zhuǎn)換完成后需要再次發(fā)起復位和存在檢測序列。發(fā)送讀取命令再次發(fā)送跳過ROM命令0xCC然后發(fā)送讀暫存器命令0xBE。OW_Reset(); OW_WriteByte(0xCC); // 跳過ROM OW_WriteByte(0xBE); // 讀暫存器命令讀取溫度數(shù)據(jù)連續(xù)讀取9個字節(jié)DS18B20暫存器共9字節(jié)。溫度值存儲在前兩個字節(jié)LSB和MSB。unsigned char temp_data[9]; for(i 0; i 9; i) { temp_data[i] OW_ReadByte(); }數(shù)據(jù)解析將兩個字節(jié)組合成一個16位有符號整數(shù)。DS18B20的溫度數(shù)據(jù)以1/16°C為LSB。int raw_temp (temp_data[1] 8) | temp_data[0]; // 合成16位數(shù)據(jù) float temperature; if (raw_temp 0x8000) { // 判斷是否為負數(shù) raw_temp ~raw_temp 1; // 取補碼 temperature -((float)raw_temp * 0.0625); } else { temperature (float)raw_temp * 0.0625; // 0.0625 1/16 }temp_data[1]是高字節(jié)temp_data[0]是低字節(jié)。0.0625是分辨率。4.3 寄生供電模式下的強上拉操作如果DS18B20采用寄生供電即VDD引腳接地完全依靠數(shù)據(jù)線在空閑時通過上拉電阻供電在執(zhí)行溫度轉(zhuǎn)換(0x44)或拷貝暫存器到EEPROM(0x48)等耗電較大的操作時必須在命令發(fā)出后的10μs內(nèi)將總線通過一個MOSFET強拉到VCC提供更大電流并持續(xù)到操作完成。這在C51上通常通過一個額外的IO口控制MOSFET實現(xiàn)。sbit POWER_CTRL P1^0; // 控制強上拉MOSFET的引腳 // 發(fā)送啟動轉(zhuǎn)換命令后 OW_WriteByte(0x44); POWER_CTRL 1; // 開啟強上拉 Delay_ms(750); // 等待轉(zhuǎn)換完成時間根據(jù)分辨率而定 POWER_CTRL 0; // 關(guān)閉強上拉重要提示如果使用外部電源為DS18B20供電VDD接3.3V或5V則不需要強上拉操作通信可靠性會更高。這是提升系統(tǒng)穩(wěn)定性的一個關(guān)鍵設(shè)計選擇。5. 多設(shè)備掛載與ROM ID搜索算法當一根1-Wire總線上掛有多個設(shè)備如多個DS18B20時就需要通過唯一的64位ROM ID來區(qū)分它們。這涉及到更復雜的搜索ROM算法命令0xF0。5.1 搜索算法原理簡述搜索算法是一個基于“沖突”的遞歸過程。主機通過“讀”操作讓所有設(shè)備在同一時間槽內(nèi)回復自己ROM ID的某一位。如果所有設(shè)備在該位都回復相同的值0或1主機就得到該位的確定值。如果有的設(shè)備回復0有的回復1就發(fā)生了“沖突”。主機此時必須做出“選擇”先向0方向搜索還是向1方向搜索。通過記錄這些選擇點主機可以遍歷總線上所有設(shè)備的ROM ID。5.2 C51上的簡化實現(xiàn)考量完整的搜索算法實現(xiàn)起來代碼量較大且對時序和狀態(tài)記錄要求高。在資源緊張的C51上有更實用的替代方案單設(shè)備跳過ROM如果應(yīng)用確定只有單一設(shè)備直接使用0xCC跳過ROM命令這是最簡單可靠的。已知ROM ID直接尋址如果設(shè)備ROM ID已知可通過編程器或一次性搜索后存入EEPROM則可以使用匹配ROM命令0x55后跟64位ROM ID直接與特定設(shè)備對話。unsigned char rom_code[8] {0x28, 0xFF, 0x...}; // 已知的ROM ID OW_Reset(); OW_WriteByte(0x55); // 匹配ROM命令 for(i0; i8; i) { OW_WriteByte(rom_code[i]); } // 接下來發(fā)送功能命令...僅在上電時搜索一次在系統(tǒng)初始化時執(zhí)行一次完整的搜索算法將找到的所有ROM ID存入數(shù)組。之后的正常操作中使用“匹配ROM”命令進行尋址。這樣可以避免在每次測溫時都進行耗時的搜索。對于大多數(shù)C51應(yīng)用方案2或3更為可行。完整的搜索算法代碼較長在“one.zip”中有一個實現(xiàn)版本其核心是維護一個“上次分歧位”的變量并遞歸地嘗試兩條路徑。由于篇幅所限這里不展開全部代碼但其關(guān)鍵在于正確處理讀回數(shù)據(jù)中的沖突位并管理好搜索路徑的回溯。6. 調(diào)試技巧、常見問題與穩(wěn)定性優(yōu)化在實際焊接電路和編寫代碼時問題總會不期而至。以下是我從“one.zip”項目的調(diào)試筆記中總結(jié)出的精華。6.1 硬件問題排查清單現(xiàn)象可能原因排查方法始終無存在脈沖1. 接線錯誤DQ、GND2. 上拉電阻未接或損壞3. 傳感器損壞4. 電源問題寄生供電時電流不足1. 用萬用表檢查通斷。2. 確認4.7kΩ上拉電阻正確連接至VCC。3. 更換傳感器測試。4. 嘗試改為外部供電VDD接3.3V/5V。偶爾通信失敗數(shù)據(jù)亂碼1. 時序不精確2. 總線受到干擾長線、靠近電機等3. 中斷干擾關(guān)鍵時序4. 寄生供電模式下未加強上拉1. 用邏輯分析儀抓取波形對比DS18B20時序圖。2. 縮短總線長度使用雙絞線遠離干擾源。3. 確認在OW_ReadBit、OW_WriteBit等函數(shù)中關(guān)閉了中斷EA0。4. 在轉(zhuǎn)換命令后增加強上拉電路并控制。讀回的溫度值固定為85°C或0°C85°C是上電默認值0°C可能是讀取錯誤。通常是因為復位/讀寫時序不符合要求導致DS18B20沒有正確執(zhí)行命令。重點檢查OW_Reset和OW_WriteBit/OW_ReadBit中的延時參數(shù)。使用邏輯分析儀是最高效的手段。6.2 軟件層面的穩(wěn)定性優(yōu)化增加重試機制任何一次OW_Reset()或讀寫操作失敗都應(yīng)加入重試邏輯。例如連續(xù)進行3次復位檢測只有兩次成功才認為初始化成功。bit OW_Reset_With_Retry(unsigned char retries) { while(retries--) { if(OW_Reset()) { return 1; // 成功 } Delay_ms(2); // 失敗后稍作延遲再試 } return 0; // 全部重試失敗 }CRC校驗DS18B20暫存器的第9字節(jié)是前面8字節(jié)的CRC校驗碼。在讀取溫度數(shù)據(jù)后應(yīng)計算前8字節(jié)的CRC并與讀回的第9字節(jié)對比。如果不匹配則丟棄本次數(shù)據(jù)并重試。這能有效發(fā)現(xiàn)因干擾導致的傳輸錯誤?!皁ne.zip”里包含了一個針對DS18B20的CRC8查表法計算函數(shù)。精準延時校準放棄不可靠的純軟件循環(huán)延時。使用定時器中斷來產(chǎn)生一個基準時間單元如10μs。所有1-Wire延時都基于這個基準單元進行計數(shù)精度和可移植性大幅提升。總線恢復在連續(xù)多次通信失敗后可以嘗試讓主機連續(xù)輸出多個480μs以上的低電平脈沖類似長復位這有助于將可能“卡住”的從設(shè)備狀態(tài)機拉回初始狀態(tài)。6.3 邏輯分析儀你的最佳搭檔沒有邏輯分析儀調(diào)試1-Wire就像蒙著眼睛走路。一個廉價的USB邏輯分析儀如Saleae Logic 8克隆版配合DSView等軟件可以直觀地看到復位脈沖、存在脈沖、每一個讀寫時間槽的電平變化。將抓取的波形與數(shù)據(jù)手冊中的時序圖嚴格對比是定位時序問題最快、最直接的方法。你可以清晰地看到你的“15μs低電平”在實際波形中是10μs還是20μs采樣點是否在有效窗口內(nèi)。7. 從C51到現(xiàn)代MCU的思考與項目擴展雖然本項目聚焦于C51但1-Wire協(xié)議的思想是通用的。在現(xiàn)代的STM32、GD32等ARM Cortex-M MCU上實現(xiàn)方式有了更多選擇GPIO模擬思路與C51完全相同但得益于更高的主頻和更精準的定時器如SysTick延時可以做得更精確。同時利用GPIO的“位帶”操作可以像操作C51的sbit一樣直接操作單個IO非常方便。外部中斷檢測可以利用MCU的外部中斷功能在主機拉低總線啟動讀時間槽后配置中斷在下降沿或上升沿觸發(fā)從而更精準地捕獲從設(shè)備的響應(yīng)減少軟件延時的依賴。定時器PWM與輸入捕獲更高級的用法是利用定時器的PWM模式產(chǎn)生精確的復位脈沖和寫時間槽利用輸入捕獲模式來測量存在脈沖的寬度和讀時間槽的響應(yīng)。這幾乎將CPU完全解放出來。“one.zip”項目雖然基于古老的C51平臺但它所涉及的精確時序控制、抗干擾設(shè)計、協(xié)議層狀態(tài)機實現(xiàn)、以及調(diào)試方法論是嵌入式開發(fā)中普適的技能。理解了這個在資源受限環(huán)境下的實現(xiàn)當你轉(zhuǎn)移到功能更強大的平臺時不僅能輕松移植更能理解那些高級外設(shè)如UART in smart-card mode, I2C等為何要如此設(shè)計。最后如果你打算深化這個項目可以考慮添加更多的1-Wire器件支持如DS2431 EEPROM、DS2413 IO擴展器甚至嘗試用C51做一個簡單的1-Wire網(wǎng)絡(luò)嗅探器那將會是對協(xié)議理解的又一次升華。本文還有配套的精品資源點擊獲取