1)實驗平臺:【正點原子】 NANO STM32F103 開發板
2)摘自《正點原子STM32 F1 開發指南(NANO 板-HAL 庫版)》關注官方微信號公眾號,獲取更多資料:正點原子
第二十六章 DS18B20 數字溫度傳感器實驗
STM32 中容量內部沒自帶溫度傳感器,所以,本章我們將向大家介紹如何通過 STM32 來
讀取外部數字溫度傳感器的溫度,來得到較為準確的環境溫度。在本章中,我們將學習使用單
總線技術,通過它來實現 STM32 和外部溫度傳感器(DS18B20)的通信,並把從溫度傳感器得
到的溫度顯示在數碼管上。本章分為如下幾個部分:
26.1 DS18B20 簡介
26.2 硬件設計
26.3 軟件設計
26.4 下載驗證
26.1 DS18B20 簡介
DS18B20 是由 DALLAS 半導體公司推出的一種的“一線總線”接口的溫度傳感器。與傳
統的熱敏電阻等測溫元件相比,它是一種新型的體積小、適用電壓寬、與微處理器接口簡單的
數字化溫度傳感器。一線總線結構具有簡潔且經濟的特點,可使用戶輕鬆地組建傳感器網絡,
從而為測量系統的構建引入全新概念,測量溫度範圍為-55~+125℃ ,精度為±0.5℃。現場溫
度直接以“一線總線”的數字方式傳輸,大大提高了系統的抗干擾性。它能直接讀出被測溫度,
並且可根據實際要求通過簡單的編程實現 9~l2 位的數字值讀數方式。它工作在 3—5.5 V 的電
壓範圍,採用多種封裝形式,從而使系統設計靈活、方便,設定分辨率及用戶設定的報警溫度
存儲在 EEPROM 中,掉電後依然保存。其內部結構如圖 26.1.1 所示:
ROM 中的 64 位序列號是出廠前被光記好的,它可以看作是該 DS18B20 的地址序列碼,每
DS18B20 的 64 位序列號均不相同。64 位 ROM 的排列是:前 8 位是產品家族碼,接著 48 位是
DS18B20 的序列號,最後 8 位是前面 56 位的循環冗餘校驗碼(CRC=X8+X5 +X4 +1)。ROM 作
用是使每一個 DS18B20 都各不相同,這樣就可實現一根總線上掛接多個。
所有的單總線器件要求採用嚴格的信號時序,以保證數據的完整性。DS18B20 共有 6 種信
號類型:復位脈衝、應答脈衝、寫 0、寫 1、讀 0 和讀 1。所有這些信號,除了應答脈衝以外,
都由主機發出同步信號。並且發送所有的命令和數據都是字節的低位在前。這裡我們簡單介紹
這幾個信號的時序:
1)復位脈衝和應答脈衝
單總線上的所有通信都是以初始化序列開始。主機輸出低電平,保持低電平時間至少 480
us,,以產生復位脈衝。接著主機釋放總線,4.7K 的上拉電阻將單總線拉高,延時 15~60 us,
並進入接收模式(Rx)。接著 DS18B20 拉低總線 60~240 us,以產生低電平應答脈衝,
若為低電平,再延時 480 us。
2)寫時序
寫時序包括寫 0 時序和寫 1 時序。所有寫時序至少需要 60us,且在 2 次獨立的寫時序之間
至少需要 1us 的恢復時間,兩種寫時序均起始於主機拉低總線。寫 1 時序:主機輸出低電平,
延時 2us,然後釋放總線,延時 60us。寫 0 時序:主機輸出低電平,延時 60us,然後釋放總線,
延時 2us。
3)讀時序
單總線器件僅在主機發出讀時序時,才向主機傳輸數據,所以,在主機發出讀數據命令後,
必須馬上產生讀時序,以便從機能夠傳輸數據。所有讀時序至少需要 60us,且在 2 次獨立的讀
時序之間至少需要 1us 的恢復時間。每個讀時序都由主機發起,至少拉低總線 1us。主機在讀
時序期間必須釋放總線,並且在時序起始後的 15us 之內採樣總線狀態。典型的讀時序過程為:
主機輸出低電平延時 2us,然後主機轉入輸入模式延時 12us,然後讀取單總線當前的電平,然
後延時 50us。
在瞭解了單總線時序之後,我們來看看 DS18B20 的典型溫度讀取過程,DS18B20 的典型
溫度讀取過程為:復位→發 SKIP ROM 命令(0XCC)→發開始轉換命令(0X44)→延時→復
位→發送 SKIP ROM 命令(0XCC)→發讀存儲器命令(0XBE)→連續讀出兩個字節數據(即
溫度)→結束。
DS18B20 的介紹就到這裡,更詳細的介紹,請大家參考 DS18B20 的技術手冊。
26.2 硬件設計
由於開發板上標準配置是沒有 DS18B20 這個傳感器的,只有接口,所以要做本章的實驗,
大家必須找一個 DS18B20 插在預留的 18B20 接口上。
本章實驗功能簡介:開機的時候先檢測是否有 DS18B20 存在,如果沒有,則提示錯誤。
只有在檢測到 DS18B20 之後才開始讀取溫度並顯示在 LCD 上,如果發現了 DS18B20,則程
序每隔 100ms 左右讀取一次數據,並把溫度顯示在 LCD 上。同樣我們也是用 DS0 來指示程序
正在運行。
所要用到的硬件資源如下:
1) 指示燈 DS0、DS3
2) 數碼管
3) DS18B20 溫度傳感器
前兩部分,在之前的實例已經介紹過了,而DS18B20溫度傳感器屬於外部器件(板上沒有
直接焊接),這裡也不介紹。本章,我們僅介紹DS18B20接口和STM32的連接電路,如圖26.2.1
所示:
從上圖可以看出,我們使用的是 STM32 的 PB9 來連接 U2 的 1WIRE_DQ 引腳,圖中 U2
為 DHT11(數字溫溼度傳感器)和 DS18B20 共用的一個接口,DHT11 我們將在下一章介紹。
DS18B20 只用到 U2 的 3 個引腳(U2 的 1、2 和 3 腳),將 DS18B20 傳感器插入到這個上
面就可以通過 STM32 來讀取 DS18B20 的溫度了。連接示意圖如圖 26.2.2 所示:
注意:DS18B20 的平面部分(有字的那面)應該朝內,而曲面部分朝外。然後插入如圖所
示的三個孔內。
26.3 軟件設計
打開我們的 DS18B20 數字溫度傳感器實驗工程可以看到我們添加了 ds18b20.c 文件以及其
頭文件 ds18b20.h 文件,所有 ds18b20 驅動代碼和相關定義都分佈在這兩個文件中。
打開 ds18b20.c,該文件代碼如下:
//復位 DS18B20
void DS18B20_Rst(void)
{
DS18B20_IO_OUT(); //設置為輸出
DS18B20_DQ_OUT=0; //拉低 DQ
delay_us(750); //拉低 750us
DS18B20_DQ_OUT=1; //DQ=1
delay_us(15); //15US
}
//等待 DS18B20 的回應
//返回 1:未檢測到 DS18B20 的存在
//返回 0:存在
u8 DS18B20_Check(void)
{
u8 retry=0;
DS18B20_IO_IN(); //設置為輸入
while (DS18B20_DQ_IN&&retry<200)
{
retry++;
delay_us(1);
};
if(retry>=200)return 1;
else retry=0;
while (!DS18B20_DQ_IN&&retry<240)
{
retry++;
delay_us(1);
};
if(retry>=240)return 1;
return 0;
}
//從 DS18B20 讀取一個位
//返回值:1/0
u8 DS18B20_Read_Bit(void)
{
u8 data;
DS18B20_IO_OUT(); //設置為輸出
DS18B20_DQ_OUT=0;
delay_us(2);
DS18B20_DQ_OUT=1;
DS18B20_IO_IN(); //設置為輸入
delay_us(12);
if(DS18B20_DQ_IN)data=1;
else data=0;
delay_us(50);
return data;
}
//從 DS18B20 讀取一個字節
//返回值:讀到的數據
u8 DS18B20_Read_Byte(void)
{
u8 i,j,dat;
dat=0;
for (i=1;i<=8;i++)
{
j=DS18B20_Read_Bit();
dat=(j<<7)|(dat>>1);
}
return dat;
}
//寫一個字節到 DS18B20
//dat:要寫入的字節
void DS18B20_Write_Byte(u8 dat)
{
u8 j;
u8 testb;
DS18B20_IO_OUT(); //設置為輸出
for (j=1;j<=8;j++)
{
testb=dat&0x01;
dat=dat>>1;
if(testb) // 寫 1
{
DS18B20_DQ_OUT=0;
delay_us(2);
DS18B20_DQ_OUT=1;
delay_us(60);
}
else //寫 0
{
DS18B20_DQ_OUT=0;
delay_us(60);
DS18B20_DQ_OUT=1;
delay_us(2);
}
}
}
//開始溫度轉換
void DS18B20_Start(void)
{
DS18B20_Rst();
DS18B20_Check();
DS18B20_Write_Byte(0xcc);// skip rom
DS18B20_Write_Byte(0x44);// convert
}
//初始化 DS18B20 的 IO 口 DQ 同時檢測 DS 的存在
//返回 1:不存在
//返回 0:存在
u8 DS18B20_Init(void)
{
GPIO_InitTypeDef GPIO_Initure;
__HAL_RCC_GPIOB_CLK_ENABLE(); //開啟 GPIB 時鐘
GPIO_Initure.Pin=GPIO_PIN_9; //PB9
GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP;//推輓輸出
GPIO_Initure.Pull=GPIO_PULLUP; //上拉
GPIO_Initure.Speed=GPIO_SPEED_HIGH; //高速
HAL_GPIO_Init(GPIOB,&GPIO_Initure); //初始化
DS18B20_Rst();
return DS18B20_Check();
}
//從 ds18b20 得到溫度值
//精度:0.1C
//返回值:溫度值 (-550~1250)
short DS18B20_Get_Temp(void)
{
u8 temp;
u8 TL,TH;
short tem;
DS18B20_Start (); //開始轉換
DS18B20_Rst();
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)
{
TH=~TH;
TL=~TL;
temp=0;//溫度為負
}else temp=1;//溫度為正
tem=TH; //獲得高八位
tem<<=8;
tem+=TL;//獲得底八位
tem=(double)tem*0.625;//轉換
if(temp)return tem; //返回溫度值
else return -tem;
}
該部分代碼就是根據我們前面介紹的單總線操作時序來讀取 DS18B20 的溫度值的,DS18B20
的溫度通過 DS18B20_Get_Temp 函數讀取,該函數的返回值為帶符號的短整形數據,返回值的
範圍為-550~1250,其實就是溫度值擴大了 10 倍。
然後我們打開 ds18b20.h,該文件下面主要是一些 IO 口位帶操作定義以及函數申明,沒有
什麼特別需要講解的地方。最後打開 main.c,該文件代碼如下:
// 共陰數字數組
// 0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F, .,全滅
u8 smg_num[]={0xfc,0x60,0xda,0xf2,0x66,0xb6,0xbe,0xe0,0xfe,
0xf6,0xee,0x3e,0x9c,0x7a,0x9e,0x8e,0x01,0x00};
short temperature;//溫度值
u8 smg_wei=4;//數碼管位選
u8 num=0;//數碼管數值
u16 led_t=0;//led 時間
u16 temp_t=0;//採樣時間
u8 flag=0;//溫度正負標誌位
int main(void)
{
HAL_Init(); //初始化 HAL 庫
Stm32_Clock_Init(RCC_PLL_MUL9); //設置時鐘,72M
delay_init(72); //初始化延時函數
uart_init(115200);
//串口初始化為 115200
LED_Init();
//初始化與 LED 連接的硬件接口
LED_SMG_Init(); //數碼管初始化
printf("NANO STM32\\r\\n");
printf("DS18B20 TEST\\r\\n");
while(DS18B20_Init()) //DS18B20 初始化
{
printf("DS18B20 Error\\r\\n");
delay_ms(200);
LED3=!LED3;//LED3 閃爍表示 DS18B20 初始化失敗
}
LED3=1;
printf("DS18B20 OK\\r\\n");
TIM3_Init(19,7199); //數碼管 2ms 定時顯示
while(1)
{
}
}
//回調函數,定時器中斷服務函數調用
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim==(&TIM3_Handler))
{
temp_t++;
if(temp_t==500)//DS18B20 1S 採樣
{
temp_t=0;
temperature=DS18B20_Get_Temp();//獲取溫度值
if(temperature<0)//若溫度為負數
{
temperature=-temperature;
flag=1;
}else flag=0;
}
switch(smg_wei)
{
case 4: if(flag)num = 0x02; //顯示"-"以表示負溫度
else num=0x00;
break;
case 5: num = smg_num[temperature/10/10];break; //溫度值
case 6: num = smg_num[temperature/10%10]|0x01; break;
case 7: num =smg_num[temperature%10];break;
}
LED_Write_Data(num,smg_wei);//寫數據到數碼管
LED_Refresh();//更新顯示
smg_wei++;
if(smg_wei==8) smg_wei=4;
led_t++;
if(led_t==250)//LED0 500ms 閃爍
{
led_t=0;
LED0=!LED0;
}
}
}
由於 DS18B20 的時序是使用軟件時序操作的,為了不打擾時序操作,我們將讀取溫度放到
在定時器中斷執行,以每隔 1S 時間採集溫度,數碼管同樣是以 2ms 時間動態掃描顯示採集的溫
度值,DS0 以每 500ms 閃爍一次,以表示程序正在運行。
至此,我們本章的軟件設計就結束了。
26.4 下載驗證
在代碼編譯成功之後,我們通過下載代碼到 ALIENTEK NANO STM32F1 開發板上,可以
看到數碼管顯示當前的溫度值(假定 DS18B20 已經接上去了),如圖 26.4.1 所示:
該程序還可以讀取並顯示負溫度值的,只是由於本人在廣州,是沒辦法看到了(除非放到
冰箱),具備條件的讀者可以測試一下。
閱讀更多 正點原子 的文章