顯示具有 SimpleDs18b20 標籤的文章。 顯示所有文章
顯示具有 SimpleDs18b20 標籤的文章。 顯示所有文章

2018年1月2日 星期二

DS18B20-讀取溫度值【整合篇】

萬事俱備,整合吧!

 在上一篇中了解了DS18B20的暫存器架構及溫度值的計算後,本篇就是完成讀取溫度值的最後一步:整合!
底下我們從接線方式、程式架構及程式碼等三個方面依序說明!

接線方式

 再次重申,此程式只適用於Arduino與DS18B20於一對一的連接狀況下喲!此外,請注意,於Arduino的接線pin腳是7不是2,若你的接腳有調整時,請調整程式內的設定!


程式架構

底下之讀取溫度架構圖是關鍵函式-getTemp()的架構解析,而數字所標明的區域為前面所寫的工具副程式:

1、重啟程序(綠色):Arduino將電位拉低並維持480μs,DS18B20應簽低電位值既代表重啟程序OK。

2、命令程序(藍色):命令拆解成為位元,再使用寫位元(Write bit)程序使用DS18B20

3、讀取程序(黃色):讀取DS18B20給予之位元值,並組合成一字元(Byte)

4、計算溫度值(紅色):將讀取程序的位元值(LSB及MSB)轉換為溫度值
 除了整合上述四個工具副程式外,其中,要注意的是等待轉換的時間設定,
不同的位元(9-12位元)精密度有不同的轉換時間如下表所示,在這裡直接設定750μs,可確保其轉換時間充足。


程式碼及輸出

 程式碼有點長,但其實就是初始設定、關鍵程式庫(getTemp()-程式行號為17至31行)及工具程式碼的結合!
 

程式碼

const uint8_t  g_dq_pin =7; //Arduino數位腳位7接到DS18B20
#define Skip_ROM    0xcc //用於1對1時,省略每次作ROM序號確認程序
#define Convert_T   0x44 //啟動溫度轉換
#define Read_Scratchpad  0xbe //讀取暫存器值,有9個字元

void setup() {
 Serial.begin(9600);
 Serial.println("<>");
}

void loop() {
 Serial.print("DS18b20's Temperature ->");
 Serial.println(getTempC());
 delay(1000);
}

float getTempC()
{
 while(!CommandReset());       //重啟成功,再進行動作
 SendCommand(Skip_ROM);  //主機下0xcc命令(1對1的省略ROM確認作業)
 SendCommand(Convert_T);  //0x44啟動溫度轉換
 delay(750);
 //再次重啟,因為要作通訊作業
 while(!CommandReset());          //重啟成功,再進行動作
 SendCommand(Skip_ROM);        //主機下0xcc命令(1對1的省略ROM確認作業)
 SendCommand(Read_Scratchpad);   //0xBE溫度暫存器中的訊息(共9個字元)
 //前兩個字元就是溫度的訊息
 uint8_t LSB = ReceiveData();   //第一個讀到的是低位
 uint8_t MSB = ReceiveData();      //第二個讀到的是高位
 return Caculate_Temperature(LSB,MSB);
}

float Caculate_Temperature(uint8_t temp_LSB , uint8_t temp_MSB)
{
 boolean  fp_minus = false;        //温度正負標誌:預設為false,因為通常為零度以上
 if(temp_MSB > 0x7f)  //當temp_MSB>7代表此溫度為負數值時,測到的數值需要先反相再加 1
 {
  fp_minus = true;     //溫度正負指標,負數時fg_Minus=0
  temp_MSB = ~temp_MSB;     //將temp_MSB中的每一位元反相(0、1互換)
  temp_LSB = ~temp_LSB + 1;
  //將temp_LSB中的每一位元反相(0、1互換),要記得加一,才能正確的反應其值
 }
 //以十六位元空箱來結合MSB及LSB
 uint16_t raw_temp =
     (((uint16_t)temp_MSB) << 8) | //將MSB先用空的十六位元左移8個位元,等於乘256
     (((uint16_t)temp_LSB));
 float fp_temp = raw_temp * 0.0625;
 //將十六位元的整數值再乘於0.0625的單位值,既得溫度值
 if(fp_minus) {fp_temp = -fp_temp;} //當fp_minus是1,代表是負數,將溫度加上負號
 return fp_temp;
}

uint8_t ReceiveData()
{
 uint8_t byte_in=0;
 for(uint8_t i = 0; i < 8; i++)
 {
  //此時所測到的電位,就是此位元的資料
  if(ReadSlot()) {
   //看看此時主機線的電位狀況若為高位,就是1
   bitSet(byte_in, i);     //將byte_in第i個位元值,設置為1
  }
  else {
   bitClear(byte_in, i);   //將byte_in第i個位元值,設置為0
  }
 }
 return (byte_in);
}

uint8_t ReadSlot() {
 delayMicroseconds(1);
 pinMode(g_dq_pin, OUTPUT);       //轉為輸出,可達到高電位
 digitalWrite(g_dq_pin, LOW);   //將電位拉低告訴DS18B20,主機已準備好了
 delayMicroseconds(2);
 pinMode(g_dq_pin, INPUT);    //轉為輸入狀態,同時釋放線路
 delayMicroseconds(10);     //加前面的延時,約於12~13us時取樣
 uint8_t fp=digitalRead(g_dq_pin);
 delayMicroseconds(55);    //加上延時過渡此段作業時間60us
 return fp;
}

void SendCommand(uint8_t instruction)
{
 for(uint8_t i = 0; i < 8; i++) {
  WriteSolt(bitRead(instruction, i));
 }
}

void WriteSolt(uint8_t order_bit)
{
 if(order_bit) {        //當值為1時的處理,
  pinMode(g_dq_pin, OUTPUT);      //先將pin腳改為輸出狀態
  digitalWrite(g_dq_pin, LOW);    //將電位拉低,等於通知DS18B20要do something
  delayMicroseconds(10);       //至少要等待1us,但於15us前轉為高電位
  pinMode(g_dq_pin, INPUT);     //將接收轉成INPUT狀態,轉為高電位
  delayMicroseconds(60);      //加前段的延時至少等待60us過此周期
 }
 else {           //當寫入值為'0'時,Tx拉低電位時段60~120us
  pinMode(g_dq_pin, OUTPUT);    //先轉為輸出狀態
  digitalWrite(g_dq_pin, LOW);   //將電位輸出低電位
  delayMicroseconds(65);         //靜靜的等待DS18B20來讀取資料
  pinMode(g_dq_pin, INPUT);      //釋放電位控制轉回輸入狀態
  delayMicroseconds(5);     //等待上拉電阻將電位復位為HIGH
 }
}

uint8_t CommandReset()
{
 pinMode(g_dq_pin, OUTPUT);
 digitalWrite(g_dq_pin, LOW);
 delayMicroseconds(720);
 pinMode(g_dq_pin, INPUT);
 delayMicroseconds(70);
 uint8_t is_exist = !digitalRead(g_dq_pin);
 delayMicroseconds(410);
 return is_exist;
}

程式輸出 



結語

 歷經了前面淺水區的練功期,再加上此階段的暫存器、溫度值轉換,如今,終於看到溫度值了!不過,高興一下就好!

這樣就結束了嗎?當然沒那麼簡單,讀到溫度值只是此階段的甜頭,但不是終點,因為,溫度值雖然出來了,但我們要保證讀取到DS18B20的溫度值是正確無誤,要達到這個功能就必須掌握DS18B20的檢核功能-CRC碼檢驗,只要通過了檢驗,就代表此數值無誤,我們可以安心使用了!

那麼,就讓我們邁入檢核程式階段吧!

2017年12月26日 星期二

DS18B20-Read-讀個位元

跨入淺水區的第三步


二話不說,在上一步,我們知道如何寫入一個位元值到DS18B20,此篇是告訴你如何讀取DS18B20的位元值,重點仍是時間,特別是位元值"1"的取樣時間,底下將作細部說明!

從DS18B20讀取位元值

 要謮取DS18B20的位元值,最主要的關鍵是對於位元值"1"的取樣時間點的掌握,底下會先說明官方資料的內容,再作區段說明及程式碼步驟分析。

官方資料

讀取位元值的文字說明於DS18B20的Datasheet的第16頁下方(Read Time Slots),有興趣的可參考一下!

Read的時序圖


 上圖為官方文件中的第16頁中,有關讀位元的時序圖,可分為三部份,第三部份是線條示意,就不必特別介紹。

第一部分是DS18B20的時序輸出(左為位元"0"值輸出時序,右為位元"1"值的輸出時序)

第二部份中的長條狀Master(此為Arduino)讀取點。

關鍵是DS18B20給予的位元值的時間點,是在電位拉低後,1~15μs的時間點中,如果位元值為"0"時,問題不大,主要麻煩的是當位元值為"1"時,如何準確的掌握讀取的時間點,才是重點。

官方建議的讀取位元圖

  可能是讀取位元值"1"的時序很重要,當然也可能是上圖畫的不清楚,所以又給了一張[建議主機讀取時序圖],簡單的說啟始時間(Tint)及電位回復高電位(Trc)越短越好,而主機取樣點靠近信號尾端為佳。


讀取位元時序整合及關鍵

 底下將上述兩圖整合,再拆解成七個步驟來解析其讀取位元的順序及重點說明!

時序對照圖

 重點仍是放在當DS18B20給予的位元值為"1"時,如何正確的讀取其值。上圖為整體圖,而下圖為讀取位元值"1"的步驟及時序圖,其中紫色長條為Arduino的取樣時點(意謂著,當Arduino於此時點取樣時,1就是1,0就是0,不會搞錯)

區段概念分析

 在步驟分析之前,先看一下訊息區段,其實可分為四段

1-啟始訊號段:電位由高轉為低電位,等於通知DS18B20準備將位元值作輸出動作)

2-DS18B20接手段:主機放手:主機端放開電位控制,由DS18B20接手控制

3-訊息混亂段:
   位元值為"0"時,DS18B20會讓電位維持在低電位
   位元值為"1"時,DS18B20會放手讓上拉電阻將電位拉至高電位,這段時間(Trc)為混亂段,
   如果Arduino於此段(灰色段)讀取資料會出現問題

4-正確訊息段:上圖綠色的部份就是最好的取樣時段,在此區段取樣可獲得正確的位元值

關鍵步驟說明

程式碼中有附上詳細的說明,在此只提重點

1-確保讀取時序間,要有至少1μs的間隔

2-整個的讀取時序,最好是60μs

3- 步驟3/5/7的時間,可自行調整,如one wire中的設定是3/10/53,而官方建議是4/8/55,都可行,不妨都試試。

程式碼

ReadSlot()副程式中,分為7個步驟,其中fp為取樣值的變數名稱,讀取位元值並傳回。
uint8_t ReadSlot() {
 //步驟01:確保與上一個讀時序有1us的間隔
 delayMicroseconds(1);

 //步驟02:啟始信號--拉低電位
 pinMode(g_dq_pin, OUTPUT);       //轉為輸出,可達到高電位
 digitalWrite(g_dq_pin, LOW);   //將電位拉低告訴DS18B20,主機已準備好了

 //步驟03:保持低電位最少1us
 delayMicroseconds(3);

 //步驟04:釋放線路電位
 pinMode(g_dq_pin, INPUT);    //轉為輸入狀態,同時釋放線路

 //步驟05:等待時間,
 delayMicroseconds(9);     //加前面的延時,於2+9<=11us時取樣為保險值

 //步驟06:讀取slot的電位值
 uint8_t fp=digitalRead(g_dq_pin);

 //Step07:延時動作達到讀時序時段全長為60us
 delayMicroseconds(48);    //1+2+9+48=60us
 return fp;
}

基本功過關了

恭禧!到此,你已掌握了Arduino控制DS18B20的三個最基本工具:重啟、寫入一個位元值、讀取一個位元值。

下一步,我們就可以讀取溫度值了!


2017年12月20日 星期三

DS18B20-Reset-3-連線檢測

 特務接頭前的觀察要點

一個特務,一定了解執行任何一次的接頭任務前,都要先觀察據點是否正常。意味著,它可能被"破了",若有出現"異常"的訊息,則立馬回報組織。

Arduino與DS18B20的連接檢測

上一篇[DS18B20-Reset-02-啟動程序]中,執行時可以重啟DS18B20,且當收到DS18B20的應答訊息後,會顯示"DS18B20 Exist"的回饋訊息,而當無法重啟DS18B20時,它會顯示"No DS18B20 right here"。
當一切連線正常時,它會如實的執行,不過此程式只能偵測DS18B20的正常反應,卻無法處理另一狀態,就是Arduino與DS18B20斷線時的錯誤回饋,而此篇的程式就是補強此一短處。

而程式設計的關鍵也不難,如Datasheet中第10頁[Hardware Configuration]中所言,就是當Arduino與DS18B20正常連接並外接一上拉電阻時,它會處於高電位的狀態。反之,當未執行任何執行命令時,連線若出現低電位既是異常。

程式設計說明

簡單而言,程式設計的理念就是在執行啟動程序前,先觀察連線的電位狀態一段時間,期間若有任何的低電位出現,就是連線異常

※ 硬體連線資料請參考[DS18B20-Reset-02-啟動程序]

STEP-1-程式架構

 此程式使用了四個副程式,其架構如下圖:
1.TestConnect():測試連線副程式,為此篇的重點,主要是偵測輸入pin腳,當出現低電位時, 就顯示錯誤訊息"DQ_pin no connect",並傳"0"值給ResetResult()副程式,若通過觀察期, 它仍處於高電位,就傳"1"值給ResetResult()副程式。

2.ResetResult():啟動結果副程式,統合連線測試及啟動測試兩功能,但先測連線,再測啟動, 兩者皆OK才傳"1"值給ShowOkInfo(),否則傳"0"值給ShowOkInfo()。

3.isReset():啟動程序副程式,主要功能為傳回重啟狀態值

4.ShowOkInfo():顯示程程式,當一切正常為"Reset OK",有任何異常顯示"Reset Fail"



STEP-2-關鍵程式碼說明 

此次程式的關鍵是測試連線副程式TestConnect(),觀察期間為240μs,每隔4μs觀察一次電位值,觀察60次,中間若有任何一次出現低電位,則傳回"0"值並結束程式,若整個觀察期間皆為高電位,則傳回"1"值。
uint8_t TestConnect()
{
 uint8_t retries = 60;
 //先拉高電位(轉為讀取狀態)
 pinMode(g_dq_pin, INPUT);
 //若是正常就會處於高電位(DS18B20的一般狀態)
 while(!digitalRead(g_dq_pin))
 {
  retries--;
  if(retries == 0) {
   Serial.println("DQ_pin no connect!");
   return 0;
  }
  delayMicroseconds(4);
 }
 return 1;
}

STEP-3-程式碼

#define g_dq_pin 7
void setup(){
	Serial.begin(9600);
	Serial.println("<>");
}

void loop(){
	ShowOkInfo(ResetResult()) ;
	delay(2000);
}

uint8_t ResetResult()
{
	//傳回檢測Arduino與DS18B20連接狀態
	if(!TestConnect()) {return 0;}
	//直接傳回Reset結果,1既存在,0為非
	return isReset();
}

void ShowOkInfo(uint8_t isOk)
{
	if(isOk) {
		Serial.println("Reset OK");
	} else {
		Serial.println("Reset Fail");
	}
}
//測試Arduino與DS18B20的連接狀態並傳回測試值
uint8_t TestConnect()
{
	uint8_t retries = 60;
	//先拉高電位(轉為讀取狀態)
	pinMode(g_dq_pin, INPUT);
	//若是正常就會處於高電位(DS18B20的一般狀態)
	while(!digitalRead(g_dq_pin))
	{
		retries--;
		if(retries == 0) {
			Serial.println("DQ_pin no connect!");
			return 0;
		}
		delayMicroseconds(4);
	}
	return 1;
}
//啟動程序並傳回狀態值
uint8_t isReset() {
	pinMode(g_dq_pin, OUTPUT);
	digitalWrite(g_dq_pin, LOW);
	delayMicroseconds(720);
	pinMode(g_dq_pin, INPUT);
	delayMicroseconds(70);
	uint8_t is_exist = !digitalRead(g_dq_pin);
	delayMicroseconds(410);
	return is_exist;
}

STEP-4-程式輸出

輸出結果中,可看出有成功四次Reset,然後兩次的失敗,因為我將Arduino與DS18B20的連線拔除, 然後,再插回去就顯示重啟正常。


結語

 其實此一功能是整個讀取溫度程序都完成了之後,到[檢錯/中斷設置及類別化(Object)]時才加上去,當初認為是多此一舉,沒必要,但後來考量到整體的檢錯功能時,就知道不加不行,且要能防止系統出問題,需傳回一異常值(-127℃),才能保證系統就算有硬體連線/元件異常時,也不會出大錯。當然,先在此補充了此功能,也讓各位有一個觀念。

接下來,要用實際數值,來檢驗Datasheet的數字是"正確"的嗎?

2017年12月9日 星期六

DS18B20的過河導航行前說明!

 想在不用外部程式庫的狀態下,自己能讀取DS18B20嗎?Simple!就是底下這張圖,只要8個步驟,就可以掌握DS18B20喲!想知道嗎?來參加<<DS18B20的過河導航行前說明會吧>>

岸邊熱身(Setup):準備及前置知識

首先,我們會簡介1-Wire通訊的訊息,必須有的常識是DS18B20於"平常"狀態,處於高電位的實現:上拉電阻的使用及直觀替代概念(彈簧)

淺水區(STEP 1-3):工具製作及練習

 以特務接頭的例子,來說明DS18B20如何作啟始(Reset)、命令發佈(Write)及資料讀取(Read),並於階段過程中檢驗官方數據與DS18B20實際表現的差異。

第一島礁(STEP-4):簡易讀取溫度值


常前置基礎工具準備好後,既可以獲得DS18B20的溫度數據組(LSB/MSB),並於了解DS18B20的數據架構及原理後,既可透過轉換程序,獲得初步的溫度值


島礁探索(STEP 5-6):讀取暫存器及CRC檢驗 

 既然,我們可以讀取暫存區的溫度數據組(LSB/MSB),那就可以讀取整組數據,同樣的,也可讀取ROM資料(但不會深入,因為這不是此次的主題),然後,就是數據的檢驗(CRC)的實務操作(不說原理,只明操作)程序,來檢驗溫度數據的正確性。
有了CRC的檢驗,那麼,我們就可以透過測試結果,回頭檢測關鍵程序(Read),並透過合理調整參數值,來增其數據穩定性(小概念:好的產品不是檢查出來的,而是透過檢查改善製程來達到產品的良率提升)

深水區(STEP-7):檢錯/中斷設置及類別化(Object)

 我們強化的工具,並使用CRC檢核DS18B20溫度數據正確性,基本上已完成主要工作,而透過檢錯功能的加入及回饋訊息(-127℃),能讓其安全性提高。禁止/停止中斷的設置,可增加讀取DS18B20的穩定性。
當上述的程序皆已達成後,基本上程式目標已完成。接下來,是將程式碼類別轉換成程式庫的過程,簡單但同樣是不容易,過程中,會說明類別化其實就是從名店轉換成加盟總店的過程的例子讓你更了解。最後,我們會獲得SimpleDs18b20的類別程式碼(.h/.cpp)

彼岸(STEP-8) :程式庫及範例

一步一步的從SimpleDs18b20函式碼(.h/.cpp),先撰寫範例程式(example),確認可正確執行後,再進行程式庫相關文件的生成(keywords-程式關鍵字/README.MD/LICENSE)。
最後,就是上傳到GitHub的簡易程序,到此,搞定,收功,享用海鮮吧!

少說了一個,那個最前面的Ghost是什麼意思?


"Everything you want is on the other side of fear!"

看了行前說明,難嗎?有點,但就是煩了點,不過你會收獲什麼?
嗯!你會知道怎麼看Datasheet、會知道DS18B20的時序圖、內部資料結構、CRC檢核操作、程式類別化、程式庫的建立與上傳!
然後呢?當你完成了這些,我想你已進了Arduino的大門,透過格物的過程,你知了,你就可以善用DS18B20了,既然你會用DS18B20,那麼,其他的又有什麼問題呢?一法通萬法通!

想要嗎?那麼就前進吧!開始這偉大的航程!