基于STM32單片機對DS18B20溫度傳感器的驅動設計方案


原標題:基于STM32單片機對DS18B20溫度傳感器的驅動設計方案
基于STM32單片機對DS18B20溫度傳感器的驅動設計方案
在當今物聯網與智能控制系統飛速發展的時代,溫度數據采集作為環境感知的重要組成部分,其精度與穩定性直接影響到整個系統的性能。DS18B20作為一款經典的數字溫度傳感器,以其獨特的單總線(One-Wire)通信方式、寬廣的測量范圍、高精度以及優秀的環境適應性,在工業控制、消費電子、醫療設備、智能家居等諸多領域得到了廣泛應用。本設計方案將深入探討如何基于功能強大的STM32系列微控制器,實現對DS18B20溫度傳感器的穩定、高效驅動,涵蓋硬件選型、軟件設計、通信協議解析、數據處理以及常見問題解決方案等多個方面。
1. 系統概述與設計目標
本系統旨在構建一個基于STM32微控制器驅動DS18B20溫度傳感器的高精度溫度采集模塊。其核心目標包括:
高精度溫度采集: 利用DS18B20傳感器本身的高精度(可配置9-12位分辨率)確保測量結果的準確性。
穩定可靠的通信: 實現STM32與DS18B20之間基于單總線協議的穩定數據傳輸,抵抗干擾。
靈活的硬件接口: 設計合理的硬件連接方案,便于集成與擴展。
高效的軟件驅動: 編寫模塊化、可移植的軟件代碼,實現DS18B20的初始化、溫度讀取、配置等功能。
友好的數據輸出: 將采集到的溫度數據通過串口或其他接口輸出,便于上位機顯示或進一步處理。
低功耗設計(可選): 考慮在特定應用場景下,如何通過軟件和硬件協同實現低功耗運行。
2. 核心元器件選型與功能分析
成功的嵌入式系統設計離不開對核心元器件的精確選擇與深刻理解。本章節將詳細闡述STM32微控制器與DS18B20溫度傳感器的選型依據、功能特性以及其在整個系統中的關鍵作用,并介紹其他必要的輔助元器件。
2.1 微控制器單元(MCU):STM32系列
2.1.1 選型推薦
對于DS18B20的驅動,由于其單總線協議對時序的嚴格要求,以及未來可能的功能擴展(如LCD顯示、網絡通信等),推薦選用STM32F103C8T6或STM32F401RCT6。
STM32F103C8T6: 這是一款基于ARM Cortex-M3內核的微控制器,屬于STM32F1系列的主流產品。
CPU內核(Cortex-M3): 提供強大的處理能力,可以高效執行DS18B20的單總線時序操作和數據解析算法。
GPIO(通用輸入輸出): 用于與DS18B20的DQ線直接連接,并通過軟件控制其輸出高低電平,或配置為輸入模式讀取數據。F103C8T6的GPIO可以配置為推挽輸出、開漏輸出、浮空輸入、上拉/下拉輸入等多種模式,這對于單總線協議中DQ線的控制至關重要。
定時器(Timers): DS18B20的單總線協議對時序有嚴格要求,例如復位脈沖的持續時間、數據位的采樣點等。STM32的定時器可以提供精確的微秒級延時,確保通信的準確性。
NVIC(嵌套向量中斷控制器): 可以管理外部中斷,雖然DS18B20通常不需要中斷來驅動,但在多任務系統中,或需要對通信錯誤進行實時響應時,中斷功能會非常有用。
Flash存儲器和SRAM: 用于存儲程序代碼和運行時數據,包括DS18B20的驅動代碼、溫度數據、以及其他應用邏輯。
高性價比: F103系列是STM32家族中性價比較高的入門級芯片,非常適合學習和中小型項目開發。它的成本效益使其成為許多原型設計和量產應用的首選。
豐富的資源: 擁有72MHz的主頻,64KB的Flash存儲器和20KB的SRAM,以及多個通用GPIO、定時器、USART、SPI、I2C等外設。這些資源足以滿足DS18B20的時序要求,并為未來擴展其他傳感器、通信模塊(如藍牙、Wi-Fi)或顯示設備預留了足夠的硬件基礎。
成熟的生態系統: ST公司為STM32F1系列提供了非常完善的開發工具鏈(如Keil MDK, STM32CubeIDE)和豐富的例程、庫函數(如HAL庫、LL庫),社區支持廣泛,方便開發者快速上手和調試。
管腳兼容性: TSSOP48封裝,引腳數量適中,便于PCB設計和手工焊接。
選擇理由:
功能:
STM32F401RCT6: 這是一款基于ARM Cortex-M4內核的微控制器,屬于STM32F4系列的入門級產品。
FPU(浮點單元): 大幅提升浮點運算速度,對于需要精確溫度計算和顯示的應用非常有利。
更高效的DMA(直接存儲器訪問): 可以實現數據在內存和外設之間的高速傳輸,減輕CPU負擔,提高系統整體效率。雖然DS18B20的通信量不大,但DMA在多任務系統中依然能發揮作用。
更寬的工作溫度范圍和更高可靠性: 部分F4系列芯片設計用于更嚴苛的工業環境,這可能是一個額外優勢。
更高性能: F401系列擁有84MHz的主頻,配備浮點單元(FPU),使其在進行復雜數學運算(如溫度數據的浮點轉換、濾波算法等)時效率更高。如果未來的應用需要更復雜的信號處理或圖形顯示,F401會提供更好的性能儲備。
更大的存儲空間: 256KB的Flash和64KB的SRAM,為更大型的應用程序和數據存儲提供了充足的空間。
更多高級外設: 比如更多的定時器、DMA控制器、以及更快的ADC等。雖然DS18B20本身不直接使用ADC,但如果系統中還有其他模擬傳感器,這些高級外設會很有用。
適用于更復雜的系統: 如果考慮到未來項目可能擴展到更復雜的系統,例如需要運行RTOS(實時操作系統)、進行大量數據處理或與更高速外設通信,F401提供了更強的處理能力和更豐富的外設接口。
選擇理由:
功能: 除了F103的所有功能外,F401還具備:總結: 對于初學者和大多數基本溫度采集應用,STM32F103C8T6是更具性價比和易用性的選擇。如果項目對性能有更高要求,或者有未來擴展至更復雜系統的考慮,STM32F401RCT6將是更好的選擇。
2.2 溫度傳感器:DS18B20
2.2.1 選型推薦
DS18B20是Maxim Integrated公司(現已被Analog Devices收購)生產的一款單總線數字溫度傳感器。
選擇理由:
單總線接口: 這是DS18B20最顯著的特點,只需一根數據線(DQ)即可與微控制器進行通信,大大簡化了硬件連接。DQ線同時用于供電(寄生電源模式)和數據傳輸,減少了布線復雜性。
寬測量范圍: -55°C 至 +125°C,覆蓋了大多數日常及工業應用場景。
高精度: 在-10°C至+85°C范圍內,精度可達±0.5°C。用戶可配置9位、10位、11位或12位的測量分辨率,對應的轉換時間分別為93.75ms、187.5ms、375ms、750ms,精度越高,轉換時間越長。
數字輸出: 直接輸出數字溫度值,避免了傳統模擬溫度傳感器(如熱敏電阻、PT100)需要進行AD轉換的環節,簡化了硬件設計,提高了抗干擾能力,且無需校準。
獨特ID: 每個DS18B20都有一個獨一無二的64位ROM序列碼,允許多個DS18B20并聯在同一根單總線上,并通過ROM匹配指令進行尋址,實現多點溫度測量。
寄生電源模式: 在某些應用中,可以通過數據線(DQ)直接供電,無需額外電源線,進一步簡化了布線。但通常建議使用外部電源供電,以保證通信的穩定性和轉換的準確性,尤其是在長距離傳輸或多點測量時。
封裝多樣性: 提供TO-92、SOP8、防水探頭(如不銹鋼封裝)等多種封裝形式,滿足不同應用環境的需求。TO-92封裝適合板載或短距離測溫,防水探頭則適合液體或潮濕環境。
功能:
溫度測量: 將測得的溫度值轉換為數字信號,存儲在其內部的2字節溫度寄存器中。
用戶可編程分辨率: 用戶可以通過寫入配置寄存器來設置溫度轉換的分辨率。
報警功能: 具有可編程的高低溫度報警觸發點(TH和TL寄存器),當測得的溫度超出設定范圍時,DS18B20會設置一個報警標志,并通過報警搜索命令被識別出來。
暫存器(Scratchpad): 包含溫度寄存器、高/低報警觸發寄存器(TH/TL)、配置寄存器等。
EEPROM: 用于存儲配置數據(TH/TL和分辨率設置),即使斷電也能保存。
64位ROM序列碼: 唯一的硬件地址,用于區分總線上的多個DS18B20。2.3 輔助元器件
2.3.1 上拉電阻
選型推薦: 4.7kΩ ~ 10kΩ 精密電阻。 通常選擇4.7kΩ。
作用: DS18B20的單總線協議是一個開漏(Open-Drain)總線結構。這意味著DS18B20或微控制器只能將DQ線拉低(輸出邏輯0),而不能主動拉高(輸出邏輯1)。當DQ線空閑時,或者需要傳輸邏輯1時,必須依靠外部的上拉電阻將總線拉高到VCC電平。
選擇理由:
協議要求: DS18B20數據手冊明確指出需要一個上拉電阻。
保證邏輯高電平: 沒有上拉電阻,DQ線將無法達到VCC電平,導致通信錯誤。
平衡上升/下降時間: 電阻值過大會導致上升時間過長,影響通信速度和可靠性;電阻值過小會增加功耗,并可能對DS18B20的開漏輸出電流能力造成過大負擔。4.7kΩ是經過驗證的典型值,能在確保可靠通信的同時保持較低功耗。對于長距離傳輸或多點連接,可能需要適當減小電阻值或增加有源上拉電路。
功能:
當總線空閑時,通過電阻將DQ線拉高至邏輯高電平。
當DS18B20或STM32釋放總線(停止拉低)時,迅速將DQ線拉高。
為DS18B20提供寄生電源模式下的充電電流。2.3.2 濾波電容
選型推薦: 100nF(0.1μF)陶瓷電容,并聯在DS18B20的VCC和GND之間。對于更長的引線或噪聲較大的環境,可以在電源入口增加一個10μF或47μF的電解電容。
作用: 濾波電容用于濾除電源線上的高頻噪聲,提供穩定的電源,以及在DS18B20進行內部操作(如溫度轉換)時提供瞬時電流。
選擇理由:
去耦: 微控制器和傳感器在工作時會產生瞬態電流需求,導致電源線上的電壓波動。100nF陶瓷電容具有低ESR(等效串聯電阻)和良好的高頻特性,能有效地進行高頻去耦,保證VCC的穩定性。
抗干擾: 減少外部電磁干擾(EMI)對DS18B20電源的沖擊,提高測量的準確性和通信的穩定性。
寄生電源模式下的儲能: 在寄生電源模式下,DS18B20在溫度轉換期間會消耗較大的電流。這個電容可以作為能量儲存器,在DQ線被拉低時,為DS18B20提供所需的瞬時能量。
功能:
旁路高頻噪聲。
提供穩定的電源,確保DS18B20內部電路的正常工作。
在電流高峰時為DS18B20提供瞬時能量,防止電源跌落。2.3.3 排針/排母
選型推薦: 2.54mm間距的直插或彎角排針/排母。
作用: 提供便捷的硬件連接接口,便于DS18B20模塊與STM32開發板之間的插拔連接,也方便進行調試和測試。
選擇理由:
標準化: 2.54mm(0.1英寸)是電子元器件的常用間距,兼容性好。
易于連接: 方便使用杜邦線進行連接,或者直接插在面包板、洞洞板上進行原型開發。
靈活性: 方便更換DS18B20傳感器或將模塊集成到更大的系統中。
功能:
提供機械連接和電氣連接。
方便調試和維護。
3. 硬件連接方案
DS18B20與STM32的硬件連接相對簡單,主要涉及供電、地線和數據線(DQ)。
3.1 基本連接
VCC (電源): 連接到STM32開發板的3.3V或5V電源(DS18B20支持3.0V至5.5V供電)。為確保穩定,通常建議連接到5V,因為DS18B20手冊規定在溫度轉換時需要較大的瞬間電流,5V電源可以提供更穩定的供電能力。
GND (地線): 連接到STM32開發板的GND。
DQ (數據線): 連接到STM32的任意一個通用GPIO口。例如,可以連接到PA1。
上拉電阻: 一個4.7kΩ的上拉電阻(Rpu)必須連接在DQ線和VCC之間。這是單總線協議的強制要求。
濾波電容: 一個100nF(0.1μF)的陶瓷電容并聯在DS18B20的VCC和GND引腳之間,盡可能靠近DS18B20,用于電源去耦。3.2 連接示意圖
+-----------------+ | STM32 | | | DS18B20 | | (TO-92/SOP8) | | ----------- | | | | | | | VCC----|------VCC (3.3V/5V) | | | | | GND----|------GND | | | | | DQ-----|------PA1 (GPIO) | | | | ----------- | | | | +-----------------+ | | Rpu (4.7kΩ) | --- VCC --- | --- 100nF --- 電容 | GND
說明:
圖中DS18B20的VCC引腳接STM32的電源,GND接STM32的地。
DS18B20的DQ引腳通過一個4.7kΩ上拉電阻接到VCC,然后直接連接到STM32的某個GPIO引腳(例如PA1)。
100nF的濾波電容并聯在DS18B20的VCC和GND引腳之間。3.3 多點連接
DS18B20的單總線特性允許在同一根DQ線上連接多個傳感器。此時,每個DS18B20仍然需要自己的上拉電阻和濾波電容(雖然通常情況下,一個總線上的多個DS18B20可以共用一個主上拉電阻,但為每個傳感器提供局部濾波電容仍然是好的實踐)。STM32通過發送ROM指令(如跳過ROM、匹配ROM、搜索ROM)來與特定的DS18B20通信。
4. 單總線(One-Wire)通信協議解析
DS18B20的驅動核心在于理解并精確實現其單總線通信協議。該協議定義了一系列嚴格的時序要求,包括初始化、ROM指令和功能指令。所有通信都以最低有效位(LSB)優先傳輸。
4.1 單總線時序基礎
單總線協議是主從協議,STM32作為主設備,DS18B20作為從設備。所有通信都由主設備發起。
4.1.1 協議信號概述
復位(Reset)和存在(Presence)脈沖: 這是每次通信開始的第一個時序。
主設備將DQ線拉低至少480微秒(us),然后釋放(上拉電阻將其拉高)。
DS18B20檢測到總線被拉低后,會等待15-60us,然后將DQ線拉低60-240us作為存在脈沖,表示其已準備好通信。
主設備在DS18B20釋放總線后(即DQ線再次被上拉電阻拉高)15-60us內讀取DQ線的狀態。如果檢測到低電平,則表示存在DS18B20。
寫入時隙(Write Time Slot): 主設備向從設備發送數據。每個寫入時隙至少需要60us,并在120us內完成。
寫入邏輯1: 主設備將DQ線拉低1-15us,然后釋放總線(拉高)。
寫入邏輯0: 主設備將DQ線拉低60-120us,然后釋放總線(拉高)。
在每次寫入時隙結束后,總線必須保持空閑至少1us。
讀取時隙(Read Time Slot): 主設備從從設備讀取數據。每個讀取時隙至少需要60us,并在120us內完成。
主設備將DQ線拉低1-15us,然后立即釋放總線(拉高)。
主設備在拉低總線后15us內采樣DQ線的狀態。如果從設備要發送0,則DQ線將保持低電平;如果發送1,則DQ線將保持高電平。
在每次讀取時隙結束后,總線必須保持空閑至少1us。4.2 ROM指令
ROM指令用于選擇或識別總線上的DS18B20設備。
0x33 - READ ROM: (讀取ROM碼) 主機發出此命令后,DS18B20將自己的64位ROM碼傳輸給主機。此命令只適用于總線上只有一個DS18B20的情況。
0xCC - SKIP ROM: (跳過ROM碼) 主機發出此命令后,DS18B20直接執行后續的功能命令,不再等待ROM碼匹配。適用于總線上只有一個DS18B20,或者你確定要與所有DS18B20通信。
0x55 - MATCH ROM: (匹配ROM碼) 主機發出此命令后,需要接著發送一個64位的ROM碼。總線上只有與該ROM碼匹配的DS18B20才會響應后續的功能命令。用于多點測溫時精確選擇目標DS18B20。
0xF0 - SEARCH ROM: (搜索ROM碼) 用于在多點測溫系統中查找所有DS18B20的64位ROM碼。這是一個復雜的過程,通常通過專門的算法實現。4.3 功能指令
功能指令用于控制DS18B20進行溫度轉換、讀寫暫存器等操作。在發送功能指令前,必須先發送ROM指令(通常是SKIP ROM或MATCH ROM)。
0x44 - CONVERT T: (啟動溫度轉換) DS18B20開始進行溫度轉換。轉換過程需要一定時間(取決于分辨率設置,最長750ms)。在轉換期間,DQ線會保持低電平,轉換完成后會拉高。
0xBE - READ SCRATCHPAD: (讀取暫存器) 主機發出此命令后,DS18B20將其內部9字節的暫存器內容傳輸給主機。暫存器內容包括:
字節0和字節1:溫度數據(LSB和MSB)。
字節2和字節3:TH(高溫報警閾值)和TL(低溫報警閾值)。
字節4:配置寄存器(分辨率設置)。
字節5、字節6、字節7:保留。
字節8:CRC校驗碼(對前8個字節的CRC校驗)。
0x4E - WRITE SCRATCHPAD: (寫入暫存器) 主機發出此命令后,需要接著發送TH、TL和配置寄存器三個字節的數據。
0x48 - COPY SCRATCHPAD: (復制暫存器) 將暫存器中的TH、TL和配置寄存器數據復制到EEPROM中,斷電后仍然保存。
0xB8 - RECALL E2: (召回EEPROM數據) 將EEPROM中存儲的TH、TL和配置寄存器數據召回到暫存器中。通常在DS18B20上電后自動執行,但也可以手動觸發。
0xEC - READ POWER SUPPLY: (讀取電源供電模式) DS18B20會返回其當前的供電模式(外部供電或寄生供電)。4.4 溫度數據解析
DS18B20輸出的溫度數據是2字節(16位)補碼形式。其中,低字節包含溫度的低8位,高字節包含溫度的高8位。
數據格式:
S Sign (符號位,1為負,0為正)
MSB (最高有效位)
LSB (最低有效位)例如,當分辨率為12位時,數據格式如下:
Bit 15Bit 14Bit 13Bit 12Bit 11Bit 10Bit 9Bit 8Bit 7Bit 6Bit 5Bit 4Bit 3Bit 2Bit 1Bit 0
SSSSSSSST7T6T5T4T3T2T1T0
導出到 Google 表格
其中,Bit 0 到 Bit 7 是溫度的低8位,Bit 8 到 Bit 10 是溫度的高3位(12位分辨率),Bit 11 到 Bit 15 是符號位。
轉換為攝氏度:讀取到16位數據后,需要進行符號擴展和除以對應的權重。
對于正溫度:直接將16位數據乘以對應分辨率的權重。
12位分辨率:1/16=0.0625 (circtextC/count)
11位分辨率:1/8=0.125 (circtextC/count)
10位分辨率:1/4=0.25 (circtextC/count)
9位分辨率:1/2=0.5 (circtextC/count)
對于負溫度:先取反加1(補碼轉換),然后乘以權重,結果為負值。示例(12位分辨率):假設讀取到數據為0x0191 (二進制 0000 0001 1001 0001) 這是正數,直接換算:0x0191=401 (十進制) 溫度 = 401times0.0625=25.0625 (circtextC)
假設讀取到數據為0xFE6F (二進制 1111 1110 0110 1111) 這是一個負數(最高位為1)。 取反:0000 0001 1001 0000加1:0000 0001 1001 0001 (十進制 401) 溫度 = ?401times0.0625=?25.0625 (circtextC)
5. 軟件驅動設計與實現
軟件驅動是實現DS18B20功能的關鍵。本節將詳細介紹基于STM32HAL庫的軟件驅動設計,包括GPIO配置、延時函數、單總線基本操作(復位、讀寫位/字節)以及DS18B20高級功能實現。
5.1 開發環境與庫選擇
開發環境: Keil MDK-ARM或STM32CubeIDE。推薦使用STM32CubeIDE,它集成了STM32CubeMX,方便進行圖形化配置。
庫選擇: STM32HAL庫。 HAL庫是ST官方推薦的高級抽象層庫,提供了簡單易用的API,可以快速開發。雖然LL庫(Low-Layer)能提供更精細的控制和更高的效率,但對于DS18B20這種對時序精度要求高,但數據量不大的應用,HAL庫的性能也足夠。5.2 GPIO配置
DS18B20的DQ線是雙向的,既可以作為輸出(主設備拉低DQ線),也可以作為輸入(主設備讀取DQ線)。因此,STM32的GPIO需要頻繁地在輸出和輸入模式之間切換。
C
// 定義DS18B20連接的GPIO端口和引腳#define DS18B20_PORT GPIOA#define DS18B20_PIN GPIO_PIN_1// 設置DQ線為輸出模式 (推挽輸出)void DS18B20_SetPinOutput(void){ GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitStruct.Pin = DS18B20_PIN; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; // 推挽輸出 GPIO_InitStruct.Pull = GPIO_NOPULL; // 無上拉/下拉,依靠外部4.7kΩ上拉電阻 GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; // 高速模式 HAL_GPIO_Init(DS18B20_PORT, &GPIO_InitStruct);}// 設置DQ線為輸入模式 (浮空輸入)void DS18B20_SetPinInput(void){ GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitStruct.Pin = DS18B20_PIN; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; // 浮空輸入 GPIO_InitStruct.Pull = GPIO_NOPULL; // 無上拉/下拉,外部已有上拉 GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; // 高速模式 HAL_GPIO_Init(DS18B20_PORT, &GPIO_InitStruct);}// 拉低DQ線#define DS18B20_DQ_LOW() HAL_GPIO_WritePin(DS18B20_PORT, DS18B20_PIN, GPIO_PIN_RESET)// 拉高DQ線 (實際上是釋放,依靠上拉電阻拉高)#define DS18B20_DQ_HIGH() HAL_GPIO_WritePin(DS18B20_PORT, DS18B20_PIN, GPIO_PIN_SET)// 讀取DQ線狀態#define DS18B20_DQ_READ() HAL_GPIO_ReadPin(DS18B20_PORT, DS18B20_PIN)
GPIO模式選擇的說明:
輸出模式: 必須配置為推挽輸出(GPIO_MODE_OUTPUT_PP)。雖然DS18B20是開漏設備,但STM32的GPIO在推挽模式下既可以輸出高電平也可以輸出低電平。當需要拉低總線時,輸出低電平;當需要釋放總線時,輸出高電平,但實際上DQ線是通過外部上拉電阻拉高的,所以即使STM32的輸出高電平能力與DS18B20的開漏特性不完全匹配,只要我們配合外部上拉電阻,并確保在需要DS18B20響應時STM32釋放DQ線(即配置為輸入或輸出高電平),就能正常工作。
輸入模式: 必須配置為浮空輸入(GPIO_MODE_INPUT)。這是因為DQ線需要由DS18B20或外部上拉電阻來控制高低電平,STM32只需被動讀取其狀態。如果配置為上拉/下拉輸入,可能會干擾總線上的電平。
速度: 配置為高速(GPIO_SPEED_FREQ_HIGH),以確保GPIO電平切換足夠快,滿足DS18B20的嚴格時序要求。5.3 精確延時函數
DS18B20通信對延時精度要求很高,HAL庫的HAL_Delay()函數基于系統時鐘節拍(毫秒級),不適合微秒級延時。需要編寫一個基于CPU空轉或DWT(Data Watchpoint and Trace Unit)的微秒級延時函數。
方法一:基于DWT_CYCCNT寄存器的微秒延時(推薦,精度高)
DWT_CYCCNT是ARM Cortex-M內核自帶的一個32位周期計數器,每過一個CPU時鐘周期就會加1,因此可以實現非常精確的微秒甚至納秒級延時。
C
// 在core_cmInstr.h中定義了DWT相關寄存器// 需要在主函數或初始化函數中啟用DWT// DWT_Delay_Init() 函數void DWT_Delay_Init(void){ CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; // 使能DWT DWT->LAR = 0xC5ACCE55; // 解鎖DWT DWT->CYCCNT = 0; // 清零計數器 DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk; // 使能計數器}// 微秒延時函數void DWT_Delay_us(uint32_t us){ uint32_t start_tick = DWT->CYCCNT; uint32_t delay_ticks = us * (SystemCoreClock / 1000000); // 1us對應的時鐘周期數 while ((DWT->CYCCNT - start_tick) < delay_ticks);}
注意: SystemCoreClock 變量通常由STM32CubeMX生成,表示CPU主頻。使用DWT延時需要在CubeMX中配置調試接口為Trace模式。
方法二:基于__nop()的軟件延時(精度較差,不推薦用于精確時序)
這種方法通過執行空指令來消耗CPU周期,但精度受編譯器優化、CPU緩存、中斷等因素影響。
C
// 偽代碼,實際延時需要根據CPU主頻和指令周期數進行調整// 不推薦用于DS18B20這類需要精確時序的場景void delay_us(uint32_t us){ for (volatile uint32_t i = 0; i < us * (SystemCoreClock / 1000000 / NOP_PER_US); i++) { __nop(); // 執行空操作 }}
5.4 單總線基本操作實現
基于前面定義的GPIO操作和延時函數,可以實現單總線協議中的復位、寫位和讀位。
5.4.1 單總線初始化(復位與存在脈沖)
C
// DS18B20復位函數,返回1表示復位成功,0表示DS18B20不存在uint8_t DS18B20_Reset(void){ uint8_t presence = 0; DS18B20_SetPinOutput(); // 設置為輸出模式 DS18B20_DQ_LOW(); // 拉低DQ線 DWT_Delay_us(480); // 保持低電平至少480us (復位脈沖) DS18B20_DQ_HIGH(); // 釋放DQ線 (上拉電阻拉高) DWT_Delay_us(70); // 等待DS18B20響應 (15-60us等待,這里取70us確保DS18B20開始響應) DS18B20_SetPinInput(); // 設置為輸入模式,讀取存在脈沖 if (DS18B20_DQ_READ() == GPIO_PIN_RESET) // 檢測到低電平,表示DS18B20存在 { presence = 1; } DWT_Delay_us(410); // 等待存在脈沖結束 (DS18B20將DQ拉低60-240us,然后釋放,這里等待410us確保其完全釋放) return presence;}
5.4.2 寫入一個位(Write Bit)
C
// DS18B20寫入一個位void DS18B20_WriteBit(uint8_t bit){ DS18B20_SetPinOutput(); // 設置為輸出模式 if (bit == 1) // 寫入邏輯1 { DS18B20_DQ_LOW(); // 拉低DQ線 DWT_Delay_us(5); // 保持低電平1-15us (這里取5us) DS18B20_DQ_HIGH(); // 釋放DQ線 DWT_Delay_us(65); // 等待時隙結束 (至少60us,這里取65us) } else // 寫入邏輯0 { DS18B20_DQ_LOW(); // 拉低DQ線 DWT_Delay_us(65); // 保持低電平60-120us (這里取65us) DS18B20_DQ_HIGH(); // 釋放DQ線 DWT_Delay_us(5); // 等待時隙結束 (確保總線空閑) }}
5.4.3 讀取一個位(Read Bit)
C
// DS18B20讀取一個位,返回0或1uint8_t DS18B20_ReadBit(void){ uint8_t bit = 0; DS18B20_SetPinOutput(); // 設置為輸出模式 DS18B20_DQ_LOW(); // 拉低DQ線 DWT_Delay_us(5); // 保持低電平1-15us (這里取5us) DS18B20_DQ_HIGH(); // 釋放DQ線 (準備采樣) DS18B20_SetPinInput(); // 設置為輸入模式,讀取DQ線 DWT_Delay_us(10); // 等待15us采樣窗口的中間點 (5us拉低 + 10us等待 = 15us) if (DS18B20_DQ_READ() == GPIO_PIN_SET) // 采樣DQ線 { bit = 1; } DWT_Delay_us(50); // 等待讀取時隙結束 (總共60us,已經用了15us,再等50us) return bit;}
5.4.4 寫入一個字節(Write Byte)
C
// DS18B20寫入一個字節void DS18B20_WriteByte(uint8_t data){ for (int i = 0; i < 8; i++) { DS18B20_WriteBit(data & 0x01); // 從最低位開始寫入 data >>= 1; }}
5.4.5 讀取一個字節(Read Byte)
C
// DS18B20讀取一個字節uint8_t DS18B20_ReadByte(void){ uint8_t data = 0; for (int i = 0; i < 8; i++) { data >>= 1; // 每次讀取一位,數據左移,低位進來 if (DS18B20_ReadBit()) { data |= 0x80; // 將讀取到的位放到最高位(因為每次右移,最高位會被擠掉,所以需要反向操作) } } return data;}
修正: DS18B20_ReadByte 的邏輯,由于DS18B20通信是LSB優先,讀取時應該從最低位開始組裝字節。
C
// DS18B20讀取一個字節 (LSB優先)uint8_t DS18B20_ReadByte(void){ uint8_t data = 0; for (int i = 0; i < 8; i++) { // 每次讀取一位,然后將該位放到字節的正確位置 if (DS18B20_ReadBit()) { data |= (0x01 << i); // 將讀取到的位放到對應位置 } } return data;}
5.5 DS18B20高級功能實現
結合上述基本操作,可以實現DS18B20的溫度讀取等功能。
5.5.1 獲取溫度函數
C
// 讀取DS18B20溫度函數float DS18B20_GetTemp(void){ uint8_t temp_H, temp_L; int16_t raw_temp; float temp_C; if (DS18B20_Reset() == 0) // 復位失敗,DS18B20可能不存在或通信異常 { return -200.0; // 返回一個特殊值表示錯誤 } DS18B20_WriteByte(0xCC); // 跳過ROM命令 (適用于單DS18B20) DS18B20_WriteByte(0x44); // 啟動溫度轉換 // 等待溫度轉換完成 // 可以通過讀取DQ線,等待其變為高電平(非寄生供電模式) // 或者直接延時750ms(12位分辨率最長轉換時間) // 推薦使用DWT_Delay_us(750000); 等待 // 更穩妥的方式是輪詢DQ線,直到它變為高電平 while(DS18B20_ReadBit() == 0) // 等待DQ線變為高電平 (轉換完成) { // 可以加入超時機制防止死循環 // HAL_Delay(1); // 每次等待1ms // count++; // if(count > 1000) break; // 超時 } // 或者直接使用固定延時(簡單但可能不準確) DWT_Delay_us(750000); // 12位分辨率最長轉換時間 if (DS18B20_Reset() == 0) // 再次復位,準備讀取數據 { return -200.0; } DS18B20_WriteByte(0xCC); // 跳過ROM命令 DS18B20_WriteByte(0xBE); // 讀取暫存器命令 temp_L = DS18B20_ReadByte(); // 讀取溫度低字節 temp_H = DS18B20_ReadByte(); // 讀取溫度高字節 // 組合成16位原始溫度值 raw_temp = (temp_H << 8) | temp_L; // 轉換為攝氏度(假設12位分辨率) temp_C = (float)raw_temp * 0.0625f; // 12位分辨率,每位0.0625℃ // 這里可以進一步讀取CRC校驗字節,進行數據校驗,提高可靠性 // uint8_t crc = DS18B20_ReadByte(); return temp_C;}
5.5.2 設置DS18B20分辨率(可選)
DS18B20允許設置9-12位的分辨率。
C
// 設置DS18B20分辨率函數// resolution: 9, 10, 11, 12uint8_t DS18B20_SetResolution(uint8_t resolution){ uint8_t config_reg = 0; // 配置寄存器值 if (DS18B20_Reset() == 0) { return 0; // 失敗 } // 讀取當前暫存器內容,保留TH和TL,只修改配置寄存器 DS18B20_WriteByte(0xCC); // SKIP ROM DS18B20_WriteByte(0xBE); // READ SCRATCHPAD // 丟棄前兩個字節的溫度數據 (這里我們不讀取,因為我們只關心配置寄存器) DS18B20_ReadByte(); // temp_L DS18B20_ReadByte(); // temp_H uint8_t TH = DS18B20_ReadByte(); // TH uint8_t TL = DS18B20_ReadByte(); // TL config_reg = DS18B20_ReadByte(); // 配置寄存器 // 根據分辨率設置配置寄存器 switch (resolution) { case 9: config_reg &= 0x7F; // R1=0, R0=0 -> 9位 break; case 10: config_reg &= 0x9F; // R1=0, R0=1 -> 10位 config_reg |= 0x20; break; case 11: config_reg &= 0xBF; // R1=1, R0=0 -> 11位 config_reg |= 0x40; break; case 12: config_reg |= 0x60; // R1=1, R0=1 -> 12位 break; default: return 0; // 無效分辨率 } if (DS18B20_Reset() == 0) { return 0; // 失敗 } DS18B20_WriteByte(0xCC); // SKIP ROM DS18B20_WriteByte(0x4E); // WRITE SCRATCHPAD DS18B20_WriteByte(TH); // 寫入TH DS18B20_WriteByte(TL); // 寫入TL DS18B20_WriteByte(config_reg); // 寫入配置寄存器 if (DS18B20_Reset() == 0) // 復位,準備復制到EEPROM { return 0; // 失敗 } DS18B20_WriteByte(0xCC); // SKIP ROM DS18B20_WriteByte(0x48); // COPY SCRATCHPAD (保存到EEPROM) HAL_Delay(10); // 等待復制完成,大約10ms return 1; // 成功}
5.6 CRC校驗(提高數據可靠性)
DS18B20的暫存器讀取包含一個8位的CRC校驗碼。在讀取數據后計算CRC并與讀取到的CRC校驗碼進行比對,可以有效檢測通信過程中是否發生錯誤,提高數據可靠性。
實現CRC校驗需要一個CRC8算法。DS18B20使用的是Dallas Semiconductor的CRC8算法。
5.7 多點DS18B20驅動(ROM搜索)
如果需要連接多個DS18B20,則需要實現ROM搜索算法。ROM搜索是一個迭代過程,通過發送0xF0 SEARCH ROM指令,DS18B20會返回其ROM碼中的沖突位,主機根據沖突位來遍歷所有可能的ROM碼,最終找到總線上所有DS18B20的唯一ID。這個算法相對復雜,需要維護一個搜索狀態變量和ROM地址數組。
大致步驟:
發送復位和存在脈沖。
發送SEARCH ROM (0xF0) 命令。
循環8次(每個字節)或64次(每個位): a. 讀取兩位(DS18B20返回的“0”和“1”的沖突信息)。 b. 根據沖突信息和上一次搜索路徑,決定發送“0”或“1”以解決沖突。 c. 記錄當前路徑的ROM碼。
重復直到所有ROM碼被發現。由于篇幅限制,這里不提供完整的ROM搜索算法代碼,但這是實現多點DS18B20測量的基礎。
6. 軟件流程圖與主程序設計
一個典型的DS18B20溫度采集主程序流程如下:
6.1 主程序流程圖
代碼段
graph TD A[系統初始化:時鐘、GPIO、DWT等] --> B{DS18B20復位成功?} B -- 否 --> C[報錯:傳感器不存在或通信異常] B -- 是 --> D[發送SKIP ROM命令 (0xCC)] D --> E[發送CONVERT T命令 (0x44)] E --> F[等待溫度轉換完成 (約750ms或輪詢DQ線)] F --> G{DS18B20再次復位成功?} G -- 否 --> C G -- 是 --> H[發送SKIP ROM命令 (0xCC)] H --> I[發送READ SCRATCHPAD命令 (0xBE)] I --> J[讀取9字節暫存器數據 (包括溫度高低字節和CRC)] J --> K[進行CRC校驗] K -- 校驗失敗 --> C K -- 校驗成功 --> L[解析溫度數據] L --> M[將溫度數據通過串口/LCD等方式顯示/發送] M --> N[延時一段時間 (例如1秒) ] N --> D
6.2 主程序代碼骨架
#include "main.h"
#include "stm32f1xx_hal.h" // 根據實際MCU系列選擇
#include "ds18b20.h" // 包含DS18B20驅動頭文件
#include <stdio.h> // 用于printf輸出
// 定義串口句柄,用于調試輸出
extern UART_HandleTypeDef huart1; // 假設使用UART1
void SystemClock_Config(void); // 系統時鐘配置函數
static void MX_GPIO_Init(void); // GPIO初始化函數
static void MX_USART1_UART_Init(void); // 串口初始化函數
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART1_UART_Init();
DWT_Delay_Init(); // 初始化DWT用于微秒延時
float temperature = 0.0f;
char temp_str[30];
// 可選:設置DS18B20分辨率為12位
// DS18B20_SetResolution(12); // 如果需要設置,請在循環外執行一次
while (1)
{
temperature = DS18B20_GetTemp_WithCRC(); // 獲取溫度 (帶CRC校驗)
if (temperature > -100.0f) // 假設-200.0或-201.0是錯誤碼
{
sprintf(temp_str, "Temperature: %.2f C ", temperature);
}
else if (temperature == -200.0f)
{
sprintf(temp_str, "DS18B20 Not Found or Reset Failed! ");
}
else if (temperature == -201.0f)
{
sprintf(temp_str, "DS18B20 CRC Error! Data Corrupted! ");
}
HAL_UART_Transmit(&huart1, (uint8_t*)temp_str, strlen(temp_str), 100);
HAL_Delay(1000); // 1秒更新一次
}
}
// 示例:SystemClock_Config, MX_GPIO_Init, MX_USART1_UART_Init 由STM32CubeMX生成
//
7. 調試與注意事項
在DS18B20驅動設計與實現過程中,可能會遇到一些問題。理解并解決這些問題是確保系統穩定運行的關鍵。
7.1 常見問題與解決方案
DS18B20復位失敗(無存在脈沖):
檢查硬件連接: 確保VCC、GND、DQ線連接正確,特別是4.7kΩ上拉電阻是否正確連接且阻值合適。
檢查供電: 確保DS18B20供電電壓在3.0V-5.5V之間,且供電穩定。嘗試使用獨立供電而不是寄生電源模式。
檢查GPIO配置: 確保STM32的GPIO設置為正確的輸出/輸入模式,并且在釋放總線時能正確變為輸入或高電平輸出。
檢查延時函數: DS18B20時序對延時精度要求很高。確保微秒延時函數(DWT_Delay_us)工作正常且準確。示波器是調試時序的利器。
DS18B20損壞: 少數情況下傳感器本身可能損壞。嘗試更換一個DS18B20。讀取溫度數據異常(始終為85℃或0℃):
檢查READ SCRATCHPAD (0xBE) 命令是否正確發送。
檢查DS18B20_ReadByte()函數,確保數據位能正確地被STM32讀取。使用示波器觀察DQ線的數據波形。
檢查溫度數據解析算法,特別是負數的補碼轉換和浮點數轉換。檢查CONVERT T (0x44) 命令是否正確發送。
確保在發送完CONVERT T命令后有足夠的等待時間(12位分辨率需要750ms)讓DS18B20完成轉換。85℃: DS18B20在上電或復位后的默認溫度值是85℃。如果一直讀到這個值,說明溫度轉換沒有成功啟動或者數據讀取有問題。
0℃: 可能是在讀取數據時,接收到的都是0,或者數據解析錯誤。多點測溫時無法識別所有傳感器:
ROM搜索算法問題: ROM搜索算法比較復雜,任何一個細節錯誤都可能導致部分或全部傳感器無法被發現。仔細對照DS18B20數據手冊中的SEARCH ROM流程。
總線負載過大: 當連接的DS18B20數量過多或DQ線過長時,總線的容性負載會增加,導致信號上升沿變慢。此時可能需要減小上拉電阻的阻值(例如2.2kΩ),或者增加有源上拉電路(如使用一個MOSFET)。CRC校驗失敗:
通信不穩定: 檢查電源穩定性,確保沒有大的噪聲干擾。
時序問題: 再次檢查所有時序延時是否嚴格符合DS18B20數據手冊要求。任何微小的時序偏差都可能導致數據傳輸錯誤。
CRC算法實現錯誤: 仔細核對CRC8算法是否嚴格按照Dallas Semiconductor的規范實現。7.2 調試工具
示波器: 這是調試單總線通信必不可少的工具。通過示波器可以直觀地觀察DQ線的波形,判斷復位脈沖、存在脈沖、寫入時隙和讀取時隙的時序是否符合要求,以及數據位的電平是否正確。特別注意信號的上升沿和下降沿。
邏輯分析儀: 如果沒有示波器,或者需要同時觀察多路信號,邏輯分析儀也是一個很好的選擇。它可以捕捉數字信號,并解析出協議內容,方便分析通信數據。
串口調試助手: 用于接收STM32發送的溫度數據和調試信息,驗證程序邏輯是否正確。
STM32在線調試器(ST-Link/J-Link): 用于程序的燒錄、單步調試、變量觀察,是定位軟件邏輯錯誤的必備工具。7.3 功耗優化(可選)
在某些對功耗有嚴格要求的應用中,可以考慮以下優化:
降低DS18B20的采樣頻率: DS18B20在進行溫度轉換時功耗最高。減少溫度讀取的頻率可以顯著降低系統平均功耗。
STM32進入低功耗模式: 在等待DS18B20轉換完成的長時間延時期間,可以將STM32進入停止模式(Stop Mode)或待機模式(Standby Mode),然后通過定時器或外部中斷喚醒。
控制DS18B20的供電: 在極端低功耗場景下,可以通過一個GPIO控制MOSFET來切換DS18B20的VCC供電,只在需要測量時上電。
8. 總結與展望
本設計方案詳細闡述了基于STM32微控制器驅動DS18B20溫度傳感器的全過程,從核心元器件選型、硬件連接、單總線通信協議解析到軟件驅動的詳細實現,并提供了關鍵代碼片段。通過精確的GPIO控制、嚴格的時序延時以及完善的CRC校驗,可以構建一個穩定、高精度的溫度采集系統。
DS18B20因其獨特的單總線特性和優秀的性能,在眾多應用中仍占有一席之地。未來,在此基礎上可以進一步擴展,例如:
多點溫度網絡: 實現復雜的ROM搜索算法,構建多達上百個DS18B20的溫度傳感器網絡。
數據存儲與顯示: 將溫度數據存儲到EEPROM或SD卡中,并通過LCD、OLED顯示屏進行實時顯示。
無線傳輸: 集成Wi-Fi(ESP8266/ESP32)、藍牙或LoRa模塊,將溫度數據上傳至云平臺或遠程監控。
溫度控制: 根據采集到的溫度數據,通過PID算法或其他控制策略,控制風扇、加熱器等執行機構,實現智能溫度控制。
更高級的軟件架構: 引入實時操作系統(RTOS),將DS18B20驅動封裝為獨立的任務,提高系統的并發性和可維護性。掌握DS18B20的驅動不僅是溫度采集的基礎,更是深入理解單總線通信協議和嵌入式系統時序控制的絕佳實踐。希望本設計方案能為您的開發工作提供全面而有價值的參考。
責任編輯:David
【免責聲明】
1、本文內容、數據、圖表等來源于網絡引用或其他公開資料,版權歸屬原作者、原發表出處。若版權所有方對本文的引用持有異議,請聯系拍明芯城(marketing@iczoom.com),本方將及時處理。
2、本文的引用僅供讀者交流學習使用,不涉及商業目的。
3、本文內容僅代表作者觀點,拍明芯城不對內容的準確性、可靠性或完整性提供明示或暗示的保證。讀者閱讀本文后做出的決定或行為,是基于自主意愿和獨立判斷做出的,請讀者明確相關結果。
4、如需轉載本方擁有版權的文章,請聯系拍明芯城(marketing@iczoom.com)注明“轉載原因”。未經允許私自轉載拍明芯城將保留追究其法律責任的權利。
拍明芯城擁有對此聲明的最終解釋權。