一、DS18B20
1. DS18B20簡介
DS18B20是一種常見的數字溫度感測器,其控制命令和資料都是以數字訊號的方式輸入輸出,相比較於模擬溫度感測器,具有功能強大、硬體簡單、易擴充套件、抗干擾性強等特點
- 測溫範圍:-55°C 到 +125°C
- 通訊介面:1-Wire(單匯流排)
- 其它特徵:可形成匯流排結構、內建溫度報警功能、可寄生供電
2. 電路原理圖
其中1和3號引腳分別連線GND
和VCC
,而二號引腳則用於使用1-Wire(單匯流排)介面進行通訊。
即:
3. 內部結構
內部完整結構框圖
- 64-BIT ROM:作為器件地址,用於匯流排通訊的定址
- SCRATCHPAD(暫存器):用於匯流排的資料互動
- EEPROM:用於儲存溫度觸發閾值和配置引數
其中配置暫存器可以配置溫度變化的精度值。
儲存器結構
當我們希望修改EEPROM中儲存的內容時,我們需要先將資料寫入到暫存器中,然後再傳送一條指令使從機將暫存器中的資料寫入到EEPROM中。
二、單匯流排(1-Wire BUS)
由於DS18B20使用的通訊介面是1-Wire,因此我們需要學習1-Wire相關的通訊協議,這樣才能使微控制器和它進行通訊。
1. 單匯流排簡介
單匯流排(1-Wire BUS)是由Dallas
公司開發的一種通用資料匯流排
- 一根通訊線:DQ
- 非同步、半雙工
- 單匯流排只需要一根通訊線即可實現資料的雙向傳輸,當採用寄生供電時,還可以省去裝置的VDD線路,此時,供電加通訊只需要DQ和GND兩根線
2. 電路規範
- 裝置的DQ均要配置成開漏輸出模式
- DQ新增一個上拉電阻,阻值一般為4.7KΩ左右
- 若此匯流排的從機採取寄生供電,則主機還應配一個強上拉輸出電路
3. 單匯流排的時序結構
① 初始化:
- 主機將匯流排拉低至少480us
- 然後釋放匯流排,等待15~60us
- 存在的從機拉低匯流排60~240us以響應主機
- 最後從機將釋放匯流排
對應的訊號時序圖:
② 傳送一位:
- 主機將匯流排拉低60~120us,然後釋放匯流排,表示傳送0;
- 主機將匯流排拉低1~15us,然後釋放匯流排,表示傳送1。
- 從機將在匯流排拉低30us後(典型值)讀取電平,整個時間片應大於60us
對應的訊號時序圖:
③ 接收一位:
- 主機將匯流排拉低1~15us,然後釋放匯流排
- 然後主機在拉低後15us內讀取匯流排電平(儘量貼近15us的末尾)
- 讀取為低電平則為接收0,讀取為高電平則為接收1 ,整個時間片應大於60us
對應的訊號時序圖:
④ 傳送一個byte和接收一個byte:
這個過程和使用I2C傳送和接收一個位元組的資料的過程類似,都是重複傳送一位或接受一位8次即可傳送或接受一個byte的資料了:
注意傳送和接收到的資料都是低位在前的。
4. DS18B20操作流程
- 初始化:從機復位,主機判斷從機是否響應
- ROM操作:ROM指令+本指令需要的讀寫操作
- 功能操作:功能指令+本指令需要的讀寫操作
對應的ROM操作和功能操作的指令如下:
ROM指令 | 功能指令 |
---|---|
SEARCH ROM [0xF0] | CONVERT T [0x44] |
READ ROM [0x33] | WRITE SCRATCHPAD [0x4E] |
MATCH ROM [0x55] | READ SCRATCHPAD [0xBE] |
SKIP ROM [0xCC] | COPY SCRATCHPAD [0x48] |
ALARM SEARCH [0xEC] | RECALL E2 [0xB8] |
READ POWER SUPPLY [0xB4] |
各個功能指令的作用:
- CONVERT T:使用溫度感測器更新溫度值
- WRITE SCRATCHPAD:將各個EEPROM中的值寫入到暫存器中
- READ SCRATCHPAD:讀取暫存器中的值
- COPY SCRATCHPAD:將暫存器中的內容複製到EEPROM中
- RECALL E2:將EEPROM中的內容複製到暫存器中
- READ POWER SUPPLY:讀取裝置的供電模式【獨立供電|寄生供電】
5. DS18B20資料幀
① 溫度變換:初始化→跳過ROM →開始溫度變換
② 溫度讀取:初始化→跳過ROM →讀暫存器→連續的讀操作
6. 溫度的儲存格式
裡面的溫度是使用補碼形式儲存的,具體的例子如下:
溫度表示的範圍是-55到+125度
三、編碼實現
1. 單匯流排部分
我們將這部分編寫到OneWire
模組中
首先是DQ引腳,根據電路原理圖我們可以知道他對應的是晶片的P37
引腳,因此先把它定義出來:
sbit OneWire_DQ = P3 ^ 7;
① 初始化:
unsigned char OneWire_Init() {
unsigned char i;
unsigned char Ack;
EA=0;
{
OneWire_DQ = 1;
OneWire_DQ = 0; // 拉低
i = 227; while (--i); // delay 500μs
OneWire_DQ = 1; // 釋放
i = 29; while (--i); // delay 70μs,等待從機將電平拉低
// 獲取從機應答
Ack = OneWire_DQ;
i = 227; while (--i); // delay 500μs 等待時序走完
}
EA=1;
// 將從機應答返回,0表示有應答,1表示無應答
return Ack;
}
可以發現,我們的程式碼中出現了EA=0;
和EA=1;
,這兩句程式碼分別是操作終端系統的總開關進行關閉和開啟中斷系統的,因為在單匯流排傳送訊號的過程中,如果突然來了中斷,CPU轉而執行中斷程式,那麼延時就會受到巨大的影響:
例如當前執行到delay 70μs的位置,此時來了一箇中斷訊號,當執行完中斷程式回來的時候,可能就已經延時了10ms了,這對單匯流排通訊的影響是致命的,故我們在使用單匯流排傳送或接受訊號時都需要先關閉中斷。
tips:其中的延時程式碼都是使用stc-isp軟體進行生成的,在生成程式碼時需要注意晶振和8051指令集的選擇:
② 傳送一位:
void OneWire_SendBit(unsigned char Bit) {
unsigned char i;
EA=0;
{
OneWire_DQ = 0; // 拉低
i = 3; while (--i); // delay 10μs
// 若為1則釋放匯流排,代表傳送1;若為0一直為低電平,代表傳送0
OneWire_DQ = Bit;
i = 22; while (--i); // delay 50μs
OneWire_DQ = 1; // 最後釋放匯流排
}
EA=1;
}
③ 接收一位:
unsigned char OneWire_ReceiveBit() {
unsigned char i;
unsigned char Bit;
EA=0;
{
OneWire_DQ = 0; // 拉低
i = 1; while (--i); // delay 5μs
OneWire_DQ = 1; // 釋放
i = 1; while (--i); // delay 5μs
Bit = OneWire_DQ; // 取樣
i = 22; while (--i); // delay 50μs
}
EA=1;
return Bit;
}
④ 傳送一個byte和接收一個byte:
void OneWire_SendByte(unsigned char byte) {
unsigned char i;
for(i = 0; i < 8; i++) {
OneWire_SendBit(byte & (0x01 << i));
}
}
unsigned char OneWire_ReceiveByte() {
unsigned char i;
unsigned char byte = 0x00;
for(i = 0; i < 8; i++) {
if(OneWire_ReceiveBit()) {
byte |= (0x01 << i);
}
}
return byte;
}
2. DS18B20模組
首先我們把需要的命令和依賴的模組(OneWire模組)新增進來:
#include "OneWire.h"
#define DS18B20_SKIP_ROM 0xCC
#define DS18B20_CONVERT_T 0x44
#define DS18B20_READ_SCRATCHPAD 0xBE
① 溫度轉換:
void DS18B20_ConvertT() {
OneWire_Init();
OneWire_SendByte(DS18B20_SKIP_ROM);
OneWire_SendByte(DS18B20_CONVERT_T);
}
② 溫度讀取:
float DS18B20_ReadT() {
unsigned char TLSB, THSB;
int Temp;
float T;
OneWire_Init();
OneWire_SendByte(DS18B20_SKIP_ROM);
OneWire_SendByte(DS18B20_READ_SCRATCHPAD);
TLSB = OneWire_ReceiveByte(); // 接收低八位
THSB = OneWire_ReceiveByte(); // 接收高八位
Temp = (THSB << 8) | TLSB;
T = Temp / 16.0; // 轉換成float型別
return T;
}
3. main.c部分
void main() {
float T, TShow;
LCD_Init();
DS18B20_ConvertT();
Delay(1000);
while (1) {
DS18B20_ConvertT(); // 溫度轉換
T = DS18B20_ReadT(); // 讀取溫度
// 顯示符號位
if (T < 0) {
LCD_ShowString(2, 0, "-");
TShow = -T;
} else {
LCD_ShowString(2, 0, "+");
TShow = T;
}
LCD_ShowNum(2, 2, TShow, 3); // 整數部分
LCD_ShowString(2, 5, "."); // 小數點
LCD_ShowNum(2, 6, (unsigned long) (TShow * 10) % 10, 1); // 小數部分,一位小數
}
}
這樣我們就可以實時地展示溫度了。
tips:在開頭進行一次溫度轉換並
Delay(1000);
的原因是:溫度轉換是需要一定的時間的,如果我們不進行延時而直接取出溫度值就會得到初始值25.0。