第二十六章18B20數字溫度傳感器實驗
本章,我們将介紹通過STM32MP157讀取外部溫度傳感器的溫度,來得到較為準确的環境溫度。本章節我們先了解單總線技術,再了解溫度傳感器DS18B20,然後實作STM32MP157和DS18B20進行通信,把擷取到的溫度通過序列槽列印出來。
本章分為如下幾個小節:
26.1、單總線和DS18B20簡介;
26.2、硬體設計;
26.3、軟體設計;
26.4、編譯和測試;
26.1 單總線和DS18B20簡介
26.1.1 單總線特點
單總線(1-Wire Bus),即隻有一根線,是由美國DALLAS半導體公司推出的擴充總線技術,和SPI、I2C不同的是,單總線隻有一根信号線(地線除外),整個通信過程的資料交換和控制都由這根線完成,可以說單總線既作為時鐘線,又作為資料線,資料在單總線上傳輸是雙向的。采用單總線的好處是:節省IO資源、硬體開銷小、資源結構簡單、易于控制、易于擴充以及後期維護、成本低等,單總線結構具有簡潔且經濟的特點,可使使用者輕松地組建傳感器網絡,進而為測量系統的建構引入全新的概念。
挂在單總線上的器件我們稱為單總線器件,目前,常見的單總線器件主要有:數字溫度傳感器(如DS18B20和DS1821)、門禁、身份識别器(如DS1990A)、A/D轉換器(如DS2450)等。單總線的主要特點如下:
①、隻有一根線;
②、傳輸速度一般是16.3Kbit/s,最大可達142Kbit/s;
③、隻有一個單主機(可以是微控制器),可以是一個或多個從機,所挂的從機數量幾乎不受限制;
④、總線空閑時為高電平,資料傳輸是雙向的;
⑤、總線是分時工作的,同一個時刻隻能和一個從機進行通信;
⑥、連接配接到單總線的每個器件必須具有漏極開路或三态輸出;
⑦、總線上的信号,除了應答脈沖外,其它都是由主機發出同步信号,并且發出的所有指令和資料位元組的低位在前。
圖26.1.1.1 單總線挂載多個裝置
26.1.2 單總線硬體接口結構
如下是單總線的硬體接口示意圖,裝置(主機或者從機)通過漏極開路或者三态端口接到單總線上,當裝置不發送資料時釋放總線,以便總線給其它裝置使用。由于是漏極開路,需要在電路中加一個上拉電阻,圖中,單總線外接一個4.7KΩ的上拉電阻,是以,當單總線為空閑狀态時,總線狀态為高電平。不管什麼原因,如果傳輸過程需要暫時挂起,且要求傳輸過程還能夠繼續的話,則總線必須處于空閑狀态。位傳輸之間的恢複時間沒有限制,隻要總線在恢複期間處于空閑狀态(高電平)。如果總線保持低電平超過480us,總線上的所有器件将複位。
我們來看看主機和從機發送資料的過程:
①、當主機通過Tx端發送邏輯1時,經過反相器處理,總線呈現邏輯0,邏輯0經過1-Wire端口的反相器傳輸給從機的Rx端後為1,則從機接收到邏輯1;
②、當主機通過Tx端發送邏輯0時,經過反相器處理,總線呈現邏輯1,邏輯1經過1-Wire端口的反相器傳輸給從機的Rx端後為0,則從機接收到邏輯0;
③、當從機通過Tx發送邏輯1時,Tx處的NMOS管導通,總線呈邏輯0,到了主機的Rx反相器後,主機得到邏輯1;
④、當從機通過Tx發送邏輯0時,Tx處的NMOS管截止,總線呈現邏輯1,到了主機的Rx反相器後,主機收到邏輯0。
圖26.1.2.1 單總線的硬體接口示意圖
26.1.3 DS18B20簡介
1. DS18B20簡介
在工農業以及日常生活中經常需要測試溫度,傳統的方式是使用熱電偶或熱電阻,測試的值需要經過A/D轉換,将模拟量轉換為數字量得到,采用這種方式進行設計,硬體結構較複雜,制作成本較高。DS18B20是由美國DALLAS半導體公司推出的一種“單總線”接口的溫度傳感器,因其突出的優點而廣泛用于農業生産、工業制造、氣象觀測、倉庫管理、科學研究、彈藥庫測溫等衆多領域,與傳統的熱敏電阻等測溫元件相比,其主要特點有:
①、單總線接口方式,隻需要一根線即可與微控制通信(地線除外),在使用中不需要任何外圍元件,使用簡單,節約硬體成本,且以單總線的數字方式傳輸,大大提高了抗幹擾性;
②、最高12位分辨率,測試溫度範圍為-55~+125℃,精度為±0.5℃,測溫範圍廣;
③、12位分辨率時,在750毫秒(最大)内可将12位溫度轉換為數字,轉換速度快;
④、工作在3~5.5V的電壓範圍,适用電壓寬;
⑤、全數字溫度轉換和輸出,能直接讀出被測溫度,可實作9~12位的數字值讀數方式(可程式設計裝置溫度讀數);
⑥、體積小、多種封裝形式,适應狹小空間測溫,使系統設定靈活、友善;
⑦、設定分辨率以及使用者設定的報警溫度存儲在EEPROM中,掉電後依然儲存;
⑧、每個DS18B20都包含唯一的晶片序列号,是以同一條1-Wire總線上可以挂接多個DS18B20。
如下圖是不同封裝的DS18B20,主要引腳有3個:
引腳 | 符号 | 值 | 描述 |
1 | GND | 0V | 地線 |
2 | DQ | -0.3V~5.5V | 資料輸入/輸出線 |
3 | VDD | 3.0V~5.5V | 電源電壓 |
其它 | NC | 空貼 |
表26.1.3.1 DS18B20引腳
圖26.1.3.1 DS18B20的封裝和引腳排列
2. DS18B20内部結構
如下是DS18B20的内部結構圖:
圖26.1.3.2 DS18B20内部結構圖
上圖中顯示了DS18B20的主要元件,其溫度檢測和數字輸出全部集中在一個晶片中,主要部分有:
(1)64位的ROM
ROM中的64位序列号是出廠前被标記好的,它可以看作是該DS18B20的位址序列碼,每個DS18B20的64位序列号均不相同。64位ROM的排列是:低8位是産品家族碼,接着48位是DS18B20的序列号,最高8位是前面56位的循環備援校驗碼(CRC=X8+X5+X4+1)。總線主機可以從64位ROM的前56位計算出CRC值,并将其與DS18B20的8位CRC的值進行比較,以确定總線主機是否已正确接收ROM資料。
ROM作用是使每一個DS18B20都各不相同,這樣就實作了一根總線上挂載多個DS18B20。
圖26.1.3.3 64位序列号
(2)溫度傳感器
DS18B20的溫度傳感器部分用于完成溫度測試,測試得到的值傳輸到高速緩存存儲器中以供主機擷取。DS18B20的分辨率可配置(9、10、11或12位),這相當于0.5℃、0.25℃、0.125℃或0.0625℃的溫度分辨率,而12位讀數為出廠預設狀态。我們以12位為例進行介紹:資料以16位符号擴充二進制的補碼形式存儲在高速緩存存儲器中,以0.0625℃/LSB的形式進行表達,MSB包含符号位“S”,該位用于表示溫度是整數還是負數:
圖26.1.3.4 DS18B20溫度格式(12位)
轉化後得到的12位資料存儲在兩個位元組的RAM中,以二進制表示,最高5位是符号位,表示負數還是正數:如果測試的溫度大于0,這5位為0,這16位測試得到的值乘以0.0625就是溫度值;如果溫度小于0,這5位為1,這16位測試得到的值先取反再加1,最後再乘以0.0625就得到溫度值。
例如,+125℃的數值為0X07D0H,計算方法:
0X07D0Hà二進制00000 0111 1101 0000à十進制2000à溫度=0.0625*2000=125℃。
例如,-55℃的數值為FC90H,計算方法是:
FC90Hà二進制1111 1100 1001 0000à取反後0000 0011 0110 1111à加1後為 0000 0011 0111 0000à十進制880à溫度=0.0625*880=-55℃。
溫度 | 數字輸出(二進制) | 數字輸出(16進制) |
+125°C | 0000 0111 1101 0000 | 07D0h |
+85°C | 0000 0101 0101 0000 | 0550h(1) |
+25.0625°C | 0000 0001 1001 0001 | 0191h |
+10.125°C | 0000 0000 1010 0010 | 0191h |
+0.5°C | 0000 0000 0000 1000 | 0008h |
0°C | 0000 0000 0000 0000 | 0000h |
-0.5°C | 1111 1111 1111 1000 | FFF8h |
-10.125°C | 1111 1111 0101 1110 | FF5Eh |
-25.0625°C | 1111 1110 0110 1111 | FF6Fh |
-55°C | 1111 1100 1001 0000 | FC90h |
表26.1.3.2 DS18B20溫度資料表
(1)上電複位寄存器值為+85℃
DS18B20的測量結果将存儲在高速緩存存儲器中,可通過主機發出存儲功能指令讀取高速緩存存儲器的内容,進而獲得數值以便進行計算出溫度值,關于存儲功能指令我們後面會介紹到。
(3)高溫和低溫觸發器(TH和TL)
溫度警報觸發器TH和TL分别由1位元組EEPROM組成,如果未将警報搜尋指令應用于DS18B20,則這些寄存器可用作通用使用者存儲器。可使用存儲器功能指令完成TH,TL和配置位元組的寫入,通過高速緩存存儲器對這些寄存器進行讀寫時,首先讀取和寫入的是所有資料的最低有效位。
(4)配置寄存器
配置寄存器在高速緩存存儲器中占用一個位元組,配置寄存器包含裝置将溫度到數字轉換的分辨率資訊,其中,讀取低5位預設得到1,R0和R1位是分辨率設定位,出廠預設值為R0=1和R1=1(12位分辨率),最高位是測試模式位,用于設定DS18B20處于工作模式(0),還是測試模式(1),預設出廠的時候該位為0,表示工作模式,其它位無法更改,能改動的隻有R0和R1位。
圖26.1.3.5 配置寄存器
R0和R1位的配置如下:
R1 | R0 | 分辨率 | 溫度最大轉換時間 |
0 | 0 | 9位 | 93.75 ms |
0 | 1 | 10位 | 187.5 ms |
1 | 0 | 11位 | 375 ms |
1 | 1 | 12位 | 750 ms |
表26.1.3.3 配置寄存器的R1和R0位
(5)DS18B20的記憶體映射
DS18B20的記憶體映射如下,由高速緩存存儲器和非易失性電可擦除EEPROM組成,後者存儲高低溫觸發器TH和TL以及配置寄存器。高速緩存存儲器為8個位元組的記憶體(0~7),前2個位元組分别包含所測溫度資訊的LSB和MSB,第三、第四和第五個位元組分别是TH、TL和配置寄存器的易失性副本,每次上電複位時都會重新整理,新的數值從EEPROM得到。第六、第七和第八位元組用于内部計算或保留的位元組。還有一個位元組我們可以稱為第9位元組,它包含一個循環備援校驗(CRC)位元組,它存儲的是前八個位元組的CRC值。
圖26.1.3.6 DS18B20的記憶體映射
當溫度轉換指令發出後,轉換得到的溫度值以二位元組補碼的形式存儲在高速緩存存儲器的第0和第1個位元組中,主機可通過單總線接口讀取這兩個位元組的數值,且讀取的時候,低位在前,高位在後。讀取得到的值用于計算溫度值的方法在前面第(2)小節已經講解。
下面,我們結合DS18B20來了解單總線時序,通過時序我們可以知道怎麼去操作DS18B20。
26.1.4 DS18B20時序
根據DS18B20的通信協定,主機通過1-Wire端口與DS18B20通信,主機控制DS18B20完成溫度轉換必須經過以下步驟:初始化à發送一條ROM指令à發送RAM指令。我們逐個分析這些過程:
1. 初始化
單線總線上的所有任務都以初始化序列開始,初始化序列由總線主裝置發送的複位脈沖和從裝置DS18B20發送的存在脈沖組成,存在脈沖讓總線上的主機知道DS18B20在總線上并且已經準備就緒。主機通過将總線拉低發出複位信号,要求拉低至少480µs,這樣確定總線上的從機都複位,然後主機釋放總線,因總線上拉4.7KΩ的電阻,是以總線變成高電平,DS18B20在檢測到DQ引腳上的上升沿後,DS18B20等待15-60µs,然後DS18B20發送存在脈沖(60-240µs的低信号)來産生應答,主機接收過程至少480us,主機收到此脈沖後,表示複位成功,初始化過程完成。
圖26.1.4.1初始化過程=複位脈沖+從機應答脈沖
2. ROM指令
複位成功後,一旦總線主機檢測到從機存在,它可以發出5個ROM功能指令之一:1)讀取ROM;2)比對 ROM;3)搜尋 ROM;4)跳過ROM或5)警告搜尋。所有ROM功能指令都有8位長,這些指令的清單如下:
指令 | 指令代碼 | 說明 |
讀ROM | 33h | 讀DS18B20中的64位ROM。僅當總線上有一個DS18B20時,才能使用此指令,如果總線上存在多個從機,則當所有從機同時嘗試傳輸時,将發生資料沖突。 |
比對ROM | 55h | 比對ROM後面緊跟64位ROM序列,主機在總線上尋址特定的DS18B20,隻有與64位ROM序列完全比對的DS18B20才會響應,所有與64位ROM序列不比對的從機将等待複位脈沖。此指令可用于總線上的單個或多個裝置。 |
跳過ROM | CCh | 忽略64位ROM位址,直接向DS1820發送讀取溫度指令,這樣可以節省時間。該方式适用于單個從機,當有多個從機時,總線上将發生資料沖突。 |
搜尋ROM | F0h | 當系統最初啟動時,總線主機可能不知道單線總線上的裝置數量或它們的64位ROM代碼,搜尋ROM用于确定挂接在同一總線上DS18B20的個數和識别64位ROM位址,為操作各器件作好淮備。 |
告警搜尋 | ECh | 執行後隻有溫度超過設定上限或下限值的從機才做出響應,DS18B20僅在上次溫度測量時遇到報警情況時才會響應此指令(報警條件定義為溫度高于TH或低于TL)。 |
表26.1.4.1 操作ROM指令
3. RAM指令
成功執行ROM功能序列後,主機可以通路存儲和控制功能,然後主機可以提供6個存儲和控制功能指令中的任何一個:1)寫緩存器;2)讀緩存器;3)溫度轉換;4)複制緩存器;5)重調緩存器;6)讀供電。首先讀取和寫入所有資料的最低有效位。
指令 | 指令代碼 | 說明 |
寫緩存器 | 4Eh | 發出向内部RAM的第2、3和4位元組寫上、下限溫度資料指令,緊跟該指令之後,是傳送三位元組的資料。 |
讀緩存器 | BEh | 連續讀内部RAM的9位元組的内容。 |
溫度轉換 | 44h | 啟動DS18B20進行溫度轉換,會将EEPROM的溫度值和配置寄存器的值傳輸給RAM(此操作也會在DS18B20上電後發生,即重新整理操作,RAM才能獲得有效資料)。12位轉換時間最長為750ms,9位為93.75ms(詳見表32.1.3.2) |
複制緩存器 | 48h | 将RAM的第2、3和4位元組位元組的内容複制到EEPROM中 |
重調緩存器 | B8h | 将存儲在EEPROM中的值複制到RAM中的第2、3和4位元組,此操作在DS18B20上電時也會發生。 |
讀供電 | B4h | 讀DS1820的供電模式:寄生供電時DS1820發送“0”,外接電源供電時DS1820發送“1” |
表26.1.4.2 操作RAM指令
主機和從機之間的資料交換和控制全部在單總線上完成,要想保證資料的完整性,單總線器件必須按照嚴格的協定來和主機進行通信,要求:除應答信号外,其它信号都由主機發出,并且發出的資料或者指令位元組的低位在前,高位在後;主機和從機的資料傳輸通過時隙(其實也就是時序)來完成,完成1bit傳輸的時間叫做一個時隙,一個位元組8bi就需要8個時隙,這裡分為寫時隙和讀時隙,寫時隙是主機把資料傳送給從機,讀時隙是從機把資料傳給主機;寫時隙和讀時隙有有:寫0時隙、寫1時隙、讀0時隙和讀1時隙。
(1)寫時隙
當主機将資料線從高電平拉到低電平時,寫時隙被啟動,所有寫時序至少需要60us,兩次獨立的寫時隙之間至少需要1us的恢複時間(圖中的tREC)。從圖中可以看出:主機要産生一個寫0時間隙,就必須把資料線拉低并保持60 us;主機要産生一個寫1時間隙,就必須把資料線拉低,在寫時間隙開始後的15 us内允許資料線拉高。當資料線拉低後,在15 ~ 60 us的時間視窗内對資料線進行采樣:如果資料線為低電平,就是寫0,如果資料線為高電平,就是寫1。
圖26.1.4.2 寫時隙
(2)讀時隙
當主機将資料線從邏輯高電平拉到邏輯低電平時,啟動讀取時隙。當主機把總線拉低時,資料線必須保持在低邏輯電平至少1µ後釋放,必須在15 us内讀取資料。所有讀取時隙的持續時間必須至少為60µs。兩次獨立的讀時隙之間至少需要1us的恢複時間(圖中的tREC):
圖26.1.4.3 讀時隙
26.2 硬體設計
1. 例程功能
把DS18B20傳感器插在預留的接口上,采用DS18B20預設的12位分辨率測試目前的環境溫度,并将溫度通過序列槽列印出來。實驗中通過觀察LED0閃爍提示程式在運作。預留的接口在開發闆底闆的位置如下:
圖26.2.1 開發闆硬體示意圖
将DS18B20插到開發闆的預留接口的時候,位置一定要插對,不能将DS18B20的GND插在VCC上,否則會燒壞DS18B20甚至開發闆,使用過程中不要用手觸摸DS18B20露出來的金屬部分。如下圖,正面對着DS18B20平面的那一側,管腳向下,從左到右依次為GND、DQ、VDD,上圖中的開發闆預留接口的4個引腳從左到右依次為VCC、DQ、GND、GND,是以DS18B20這樣插入開發闆:
圖26.2.2硬體連接配接示意圖
2. 硬體資源
1)LED燈:LED0
2)UART4
3)DHT11(接在PF2上)
LED0 | DS18B20 | UART4_TX | UART4_RX |
PI0 | 接在PF2上 | PG11 | PB2 |
表26.2.1硬體資源
3. 原理圖
如下圖,DS18B20接在PF2上,我們通過該IO口模拟單總線的時序來控制DS18B20:
圖26.2.3原理圖部分
26.3 軟體設計
本實驗配置好的實驗工程已經放到了開發闆CD光牒中,路徑為:開發闆CD光牒A-基礎資料\1、程式源碼\11、M4 CubeIDE裸機驅動例程\CubeIDE_project\ 19 DS18B20。
26.3.1 建立和配置工程
建立工程DS18B20,然後配置LED0和UART4,再配置PF2為開漏輸出、上拉、高速模式:
圖26.3.1.1 配置GPIO
本節實驗會用到微秒延時函數,需要将第二十三章實驗的delay.h和delay.c檔案。将上一章節實驗的BSP檔案夾拷貝到M4工程的Core/Src下,然後在BSP檔案夾下建立ds18b20.c檔案,在BSP/Include下建立ds18b20.h檔案,最後的部分工程如下:
圖26.3.2 工程部分目錄
26.3.2 添加使用者代碼
關于LED0和UART4相關的代碼請參考前面實驗章節部分,下面我們直接添加DS18B20相關的代碼。DS18B20驅動代碼在ds18b20.c和ds18b20.h檔案中,下面我們開始添加使用者代碼:
1. ds18b20.h檔案代碼
ds18b20.h的檔案代碼如下,主要是引腳的定義、IO操作函數定義和函數的聲明:
#ifndef __DS18B20_H
#define __DS18B20_H
#include"gpio.h"
/* DS18B20引腳 定義 */
#define DS18B20_DQ_GPIO_PORT GPIOF
#define DS18B20_DQ_GPIO_PIN GPIO_PIN_2
/* PF口時鐘使能 */
#define DS18B20_DQ_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOF_CLK_ENABLE(); }while(0)
/* IO操作函數 */
#define DS18B20_DQ_OUT(x) do{ x ? \
HAL_GPIO_WritePin(DS18B20_DQ_GPIO_PORT, DS18B20_DQ_GPIO_PIN, GPIO_PIN_SET):\
HAL_GPIO_WritePin(DS18B20_DQ_GPIO_PORT, DS18B20_DQ_GPIO_PIN, GPIO_PIN_RESET);\
}while(0) /* 資料端口輸出 */
/* 資料端口輸入 */
#define DS18B20_DQ_IN HAL_GPIO_ReadPin(DS18B20_DQ_GPIO_PORT, DS18B20_DQ_GPIO_PIN)
uint8_t ds18b20_init(void); /* 初始化DS18B20 */
uint8_t ds18b20_check(void); /* 檢測是否存在DS18B20 */
short ds18b20_get_temperature(void); /* 擷取溫度 */
#endif
DS18B20_DQ_IN用于讀取PF2引腳(總線)的電平,DS18B20_DQ_OUT通過參數X設定引腳PF2的電平,當X為0時,設定PF2輸出0,當X為1時,設定PF2輸出1。
2. ds18b20.c檔案代碼
ds18b20.c檔案代碼中有幾個函數,分别是:初始化DS18B20的IO口、複位DS18B20、等待DS18B20的回應、從DS18B20讀取一個位、從DS18B20讀取一個位元組、寫一個位元組到DS18B20、開始溫度轉換以及從DS18B20得到溫度值。我們分别進行講解:
(1)複位DS18B20
/**
* @brief複位DS18B20
* @param要寫入的資料
* @retval無
*/
static void ds18b20_reset(void)
{
DS18B20_DQ_OUT(0); /* 主機拉低DQ,複位 */
delay_us(750); /* 主機拉低750us */
DS18B20_DQ_OUT(1); /* DQ=1, 主機釋放複位 */
delay_us(15); /* 延遲15US */
}
經過前面時序的分析,複位DS18B20函數很好了解,先将DQ拉低750us(在480us~960us時間内),然後再拉高DQ,即釋放DQ,然後再延時15us等待DS18B20應答。
(2)等待DS18B20的回應
/**
* @brief等待DS18B20的回應
* @param無
* @retval正常
異常/不存在
*/
uint8_t ds18b20_check(void)
{
uint8_t retry = 0;
uint8_t rval = 0;
/* 讀取DQ值,等待DQ變低, 等待200us */
while (DS18B20_DQ_IN && retry < 200)
{
retry++;
delay_us(1);
}
/* 當等待時間大于200us時,DS18B20異常 */
if (retry >= 200)
{
rval = 1;
}
/* 當等待時間小于200us時,DS18B20正常 */
else
{
retry = 0;
/* 等待DQ變高, 等待240us */
while (!DS18B20_DQ_IN && retry < 240)
{
retry++;
delay_us(1);
}
/* 超過240us,則認為DS18B20異常 */
if (retry >= 240) rval = 1;
}
return rval;
}
該函數的實作也是依據時序圖進行邏輯判斷,例如當主機發送了複位信号之後,按照時序圖,DS18B20應拉低資料線60~240us,整個過程中主機接收最小時間為480us,是以我們就依據這兩個硬性條件進行判斷:首先需要設定一個時限等待DS18B20響應,時間為200us,DS18B20通過将總線拉低來應答;然後,将總線拉高,模拟DS18B20釋放總線,并且延時240us,因為主機的整個接收過程至少為480us(200+240=480),當滿足這兩個條件後即判斷DS18B20成功響應。
(3)從DS18B20讀取一個位
/**
* @brief從DS18B20讀取一個位
* @param無
* @retval讀取到的位值: 0 / 1
*/
static uint8_t ds18b20_read_bit(void)
{
uint8_t data = 0; /* 讀到的值預設為0 */
DS18B20_DQ_OUT(0); /* 主機将總線DQ拉低 */
delay_us(2); /* 延時2us */
DS18B20_DQ_OUT(1); /* 主機将總線DQ拉高,釋放總線 */
delay_us(12); /* 延時12us,等主機讀取 */
if (DS18B20_DQ_IN) /* 讀到的值為1 */
{
data = 1;
}
delay_us(50); /* 延時50us,因為整個讀的時間至少為60us */
return data;
}
從DS18B20讀一個位元組比較簡單,按照時序圖進行,主機将總線拉低2us後再釋放總線,然後延時12us,因為主機要在15us内讀取,這裡就設定為12us,然後在總線拉高的狀态再延時50us,因為主機讀取的過程至少要60us(2+12+50>60),是以這裡設定50us。
(4)從DS18B20讀取一個位元組
從DS18B20讀取一個位元組比較簡單,每次讀一位,分别讀取8次即可:
/**
* @brief從DS18B20讀取一個位元組
* @param無
* @retval讀到的資料
*/
static uint8_t ds18b20_read_byte(void)
{
uint8_t i, b, data = 0;
for (i = 0; i < 8; i++) /* 一個位元組8位,分8次 */
{
b = ds18b20_read_bit(); /* DS18B20先輸出低位資料 ,高位資料後輸出 */
data |= b << i; /* 填充data的每一位 */
}
return data;
}
(5)寫一個位元組到DS18B20
根據讀寫時序,編寫如下的程式:
/**
* @brief寫一個位元組到DS18B20
* @param要寫入的位元組
* @retval無
*/
static void ds18b20_write_byte(uint8_t data)
{
uint8_t j;
for (j = 1; j <= 8; j++)
{
/* 寫 1 操作*/
if (data & 0x01)
{
DS18B20_DQ_OUT(0); /* 主機拉低DQ */
delay_us(2); /* 拉低2 us的時間 */
DS18B20_DQ_OUT(1); /* DQ=1, 主機釋放DQ */
delay_us(60); /* 延時60us */
}
/* 寫 0 操作*/
else
{
DS18B20_DQ_OUT(0); /* 主機拉低DQ */
delay_us(60); /* 拉低60 us的時間 */
DS18B20_DQ_OUT(1); /* DQ=1, 主機釋放DQ */
delay_us(2); /* 延時2 us */
}
data >>= 1; /* 右移,擷取高一位資料 */
}
}
(6)開始溫度轉換
溫度轉換的過程是先複位DS18B20,等待DS18B20應答,主機收到應答後,在隻有一個從機時,可跳過ROM直接執行RAM指令進行溫度轉換,代碼如下:
/**
* @brief開始溫度轉換
* @param無
* @retval無
*/
static void ds18b20_start(void)
{
ds18b20_reset(); /* 複位DS18B20 */
ds18b20_check(); /* 等待DS18B20的回應 */
ds18b20_write_byte(0xcc); /* 跳過ROM */
ds18b20_write_byte(0x44); /* 開始溫度轉換 */
}
(7)從DS18B20擷取溫度值
1 /**
2 * @brief從DS18B20得到溫度值(精度:0.1C)
3 * @param無
4 * @retval溫度值(-550~1250)
5 * @note傳回的溫度值放大了10倍.
6 實際使用的時候,要除以10才是實際溫度.
7 */
8 short ds18b20_get_temperature(void)
9 {
10 uint8_t flag = 1; /* 預設溫度為正數 */
11 uint8_t TL, TH;
12 short temp;
13
14 ds18b20_start(); /* 開始溫度轉換 */
15 ds18b20_reset(); /* 複位DS18B20 */
16 ds18b20_check(); /* 等待DS18B20應答 */
17 ds18b20_write_byte(0xcc); /* 跳過ROM */
18 ds18b20_write_byte(0xbe); /* 讀緩存器 */
19 TL = ds18b20_read_byte(); /* 擷取溫度低位值 LSB */
20 TH = ds18b20_read_byte(); /* 擷取溫度高位值 MSB */
21
22 if (TH > 7) /* 判斷溫度正負 */
23 {
24 TH = ~TH;
25 TL = ~TL;
26 TL+=1;
27 flag = 0; /* 溫度為負 */
28 }
29
30 temp = TH; /* 獲得高八位 */
31 temp <<= 8;
32 temp += TL; /* 獲得底八位 */
33 temp =(double)temp * 0.625; /* 轉換 */
34
35 if (flag == 0) /* 如果溫度為負數 */
36 {
37 temp = -temp; /* 将溫度轉換成負溫度 */
38 }
39
40 return temp;
41 }
第10行,設定标志位flag,如果flag為1,表示溫度為整數,如果flag為0,表示溫度為負數(即零下溫度);
第14~18行,溫度轉換的步驟是:先開始溫度轉換,此時EEROM裡得到最新的溫度值,且值從EEROM傳輸給RAM,然後複位DS18B20并等待應答,因為隻有一個DS18B20,是以可以直接跳過ROM,直接向DS1820發送讀取溫度指令去讀取溫度值,這些值是從RAM中得到的。
第19~20行,讀取到的溫度值,高8位儲存在MSB中,低8位儲存在LSB中。
第22~28行,判斷溫度值是正數還是負數,第32.1.3小節有介紹過,如果高5位是0,溫度為正值;如果高5位為1,溫度為負值;是以當為負數時,高5位的值至少大于7(二進制是0000 011)。
第30~33行,分别擷取高8位和低8位的值,兩個值組合得到用于計算溫度的值。在前面第32.1.3小節我們有講解溫度的計算方法:預設采用12位分辨率時,如果測試的溫度大于0,這5位為0,這16位測試得到的值乘以0.0625就是溫度值;如果溫度小于0,這5位為1,這16位測試得到的值先取反再加1,最後再乘以0.0625就得到溫度值。
第35~38行,如果溫度值是負數,将溫度轉換成負值。
(8)初始化DS18B20的IO口
最後不要忘了要初始化總線,即DS18B20的IO口(PF2),初始化過程:開啟PF2時鐘、配置IO口為開漏輸出、上拉、高速模式:
/**
* @brief初始化DS18B20的IO口 DQ 同時檢測DS18B20的存在
* @param無
* @retval正常
不存在/不正常
*/
uint8_t ds18b20_init(void)
{
GPIO_InitTypeDef gpio_init_struct;
DS18B20_DQ_GPIO_CLK_ENABLE(); /* 開啟DQ引腳時鐘 */
gpio_init_struct.Pin = DS18B20_DQ_GPIO_PIN;
gpio_init_struct.Mode = GPIO_MODE_OUTPUT_OD; /* 開漏輸出 */
gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */
gpio_init_struct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; /* 高速 */
/* 初始化DS18B20_DQ引腳 */
HAL_GPIO_Init(DS18B20_DQ_GPIO_PORT, &gpio_init_struct);
/* DS18B20_DQ引腳模式設定,開漏輸出,上拉, 這樣就不用再設定IO方向了, 開漏輸出的時候(=1), 也可以讀取外部信号的高低電平
ds18b20_reset(); /* 複位DS18B20 */
return ds18b20_check(); /* 傳回DS18B20應答結果,這裡可以判斷DS18B20是否存在 */
}
最後兩行代碼,先複位DS18B20後再等待其應答,主要是判斷DS18B20是否存在,存在的話,我們才可以進行後續的讀寫操作。
3. main.c檔案代碼
main.c檔案部分代碼如下,我們在标紅的字型之間手動添加代碼:
1 #include "main.h"
2 #include "usart.h"
3 #include "gpio.h"
4 /* USER CODE BEGIN Includes */
5 #include "./BSP/Include/led.h"
6 #include "./BSP/Include/delay.h"
7 #include "./BSP/Include/ds18b20.h"
8 /* USER CODE END Includes */
9
10 void SystemClock_Config(void);
11
12 int main(void)
13 {
14 HAL_Init(); /* 初始化HAL庫 */
15 if(IS_ENGINEERING_BOOT_MODE())
16 {
17 SystemClock_Config(); /* 系統時鐘初始化 */
18 }
19 MX_GPIO_Init(); /* GPIO初始化 */
20 MX_UART4_Init(); /* UART4初始化 */
21 /* USER CODE BEGIN 2 */
22 HAL_UART_Receive_IT(&huart4,&RxBuffer,1); /* 以中斷方式接收函數 */
23 led_init(); /* 初始化LED */
24 delay_init(209); /* 延時初始化延時函數 */
25 uint8_t t = 0;
26 short temperature;
27 /* USER CODE END 2 */
28 while (1)
29 {
30 /* USER CODE BEGIN 3 */
31 while (ds18b20_init()) /* DS18B20初始化 */
32 {
33 printf("DS18B20 Error!\r\n");
34 delay_ms(1000);
35 }
36 while (1)
37 {
38 t++;
39 if (t == 20)
40 {
41 t = 0;
42 temperature = ds18b20_get_temperature(); /* 擷取溫度值*/
43 /* 計算溫度值,注意最後要除以10(因前面程式乘以0.625)*/
44 printf("DS18B20 Temp=%.1f\r\n", temperature / 10.0f);
45 LED0_TOGGLE(); /* LED0閃爍 */
46 }
47 delay_ms(10); /* 延時10ms */
48 }
49 }
50 /* USER CODE END 3 */
51 }
第31~35行,初始化DS18B20;
第42行,擷取溫度值;
第44行,列印溫度值,注意要再除以10才得到真正的溫度值。
26.4 編譯和測試
開發闆上電,進入仿真運作後結果如下,可以看到一開始列印85.0,這是因為上電後,複位寄存器值預設為85℃。然後,序列槽列印此時測試的環境溫度,且LED0在閃爍。