2015年6月20日 星期六

[ MAX6675 + LCD @Arduino ] 整合型 LCD 顯示 MAX6675 K 型熱電偶溫度及開路偵測

網頁最後修改時間:2016/12/19 

MAX6675 是一個具有冷結點 ( Cold-Junction ) 補償,將 K 型熱電偶 ( Thermometer, 簡稱 TC ) 溫度轉換為數位資料的傳換晶片 ( 如上圖上最左邊已做成電路板模組的形式 ),有 12-bit 的解析度和 0.25 ℃ 的精度,還具有 K 型熱電偶開路偵測的功能;配合不同的溫度探頭形式,可用在狹小或是密閉空間中的溫度偵測,溫度可達 1023.75 ℃。本篇將以賣場這款 "MAX6675 - K 型熱電偶溫度轉換模組" 配合 Arduino 顯示溫度在 "{5V} 整合型 LCD" 上作範例來做說明,並且在熱電偶被拔除或是開路時,會顯示 "TC is open !" 以及每隔 5 秒在偵測一次 TC 是否已恢復正常 ? 再繼續溫度的顯示。
*********************************************************************************
MAX6675 - K 型熱電偶溫度轉換模組可至露天賣場訂購:
整合型 LCD可至露天賣場訂購:
*********************************************************************************

材料列表:


上面的材料中沒有列出佈線所使用的杜邦線和小麵包板,請自己根據手邊現有材料自己變通使用!

佈線電路圖:
K 型熱電偶溫度讀取,周邊接線圖
完成的實際接線可參考下面照片。
MAX6675 K 型熱電偶溫度讀取接線示意圖
MAX6675 通訊說明:

MAX6675 使用 SPI ( Serial Peripheral Interface, 串列周邊介面 ) 通訊的方式來與微處理器做溝通,但是實際上它只用唯讀模式,也就是微處理器只要負責讀取就好!

看下圖。當 /CS 由高準位變化為低準位,形成降緣觸發 ( Falling edge-trigger ) 後,就可以開始讀取 16 個 bit 的資料。

讀取時,要傳送 SCK ( 時脈 ) 的訊號。每一個時脈,會讓 MAX6675 知道,將數據依順序由高位元 ( D15 ) 至低位元 ( D0 ) 的方式放置在 SO 接腳,等待微處理器讀取,直到全部讀取完畢。最後再將 /CS 切回高準位,關閉晶片。

MAX6755 - Serial Interface Protocol, source: max6675 datasheet, page 6

使用者還要注意一點!若是使用的微處理器的速度很快 ( 單一指令處理的速度快於 100 ns ( 100 * 10-9 秒 ) ),就必須考慮到在程式碼中加入延遲時間,避免資料讀取錯誤。( 這部分請參考下方程式碼中;購買者可直接上雲端硬碟下載完整程式碼 )
MAX6675 - Serial Interface Timing, source: max6675 datassheet, page 3 and 6

讀取的數據共有 16 個 bit ( 2 bytes ),除了 bit 3 - 14 ( 溫度數據 ) 和 bit 2 ( TC 狀態 ) 需要考慮之外,其他的 bit 讀取之後就可以不去管它。

bit 2:此 bit 為 1,表示 TC 開路,需要檢查;為 0 表示正常。
bit 3 - 14:12-bit 的溫度數據,但因為包含小數點 ( 0.25 ℃ 溫度解析度 ),所以取得的數據必須再除以 4 才是真正的 K 型熱電偶所偵測到的溫度。溫度範圍在 0 - 1023.75 ℃。
BITDUMMY
SIGN BIT
12-BIT
溫度讀值
熱電偶
連接狀態
裝置
ID
狀態
Bit1514131211109876543210
0MSBLSB0三態

程式碼:

Arduino IDE 版本:使用 V1.5.8, V1.6.4 測試。
程式碼以去除掉一些註解,所以行號不一定會連續。

Arduino 的程式主要分成兩個部分:
  • 整合型 LCD @ I2C 模式下的控制程式
    因為不一定使用者使用的 LCD 與網頁中一樣,因此在程式開頭處可以取消這個裝置的操作,加入自己的 LCD 控制程式碼。
  • K 型熱電偶 MAX6675 模組讀取溫度的程式碼
    使用 SPI 通訊的方式來做讀取。
程式中的所有的輸出,都會由串列埠輸出以及顯示在整合型 LCD 上,可直接使用 Arduino IDE 的 Serial Monitor 觀看輸出情形。

* 變數設定
11 #include <stdio.h>
12 #include <Wire.h> // I2C library for I2C LCD
13 
14 #define USEI2CLCD // 不使用就在前面加 //
15 #ifdef USEI2CLCD
16  #define ADDRI2CLCD 0x3C
17 #endif
18 
19 #define _SCK 4
20 #define _CS  2
21 #define _SO  6
22 static volatile uint8_t is_tc_open;

line 14:不使用整合型 LCD,則直接在此行前面加上 // 雙斜線取消掉。
line 19 - 21:定義 MAX6675 SPI 通訊使用的接腳。

* setup()
150 void setup() {
151   Serial.begin(9600); // debug output
152   Wire.begin();
153   max6675_init();
154   Serial.println( "MAX6675 Ready !" );
155 
156   #ifdef USEI2CLCD
157    initLCD();
158    delay(100);
159    clearLCD();
160    delay(100);
161    displayCharOnLCD( 1, 1, "MAX6675 Ready ! ", 16 );
162   #endif
163 }

line 151:Arduino 串列埠初始化;此埠當作除錯用。
line 152:I2C 函式庫初始化;沒使用整合型 LCD 可以將這行前面加 // 雙斜線取消掉。
line 153:MAX6675 接腳初始化。
83 void max6675_init()
84 {
85  pinMode( _SCK, OUTPUT );
86  pinMode( _SO, INPUT );
87  pinMode( _CS, OUTPUT );
88 
89  digitalWrite( _CS, HIGH );
90 }

line 154:串列埠輸出"準備好"的提示文字
line 156 - 162:如果有使用整合型 LCD,初始化 LCD、清除螢幕文字、最後顯示"準備好"的提示文字在螢幕上。

* loop()
165 void loop() {
166  // 確認 TC 是否開路或是故障
167  if( is_tc_open )
168  {
169   static uint8_t tc_check;
170   Serial.println( "TC is Open !");
171   #ifdef USEI2CLCD
172    displayCharOnLCD( 1, 1, "  TC is Open !  ", 16 );
173    displayCharOnLCD( 2, 1, "                ", 16 );
174    char a = 0x35 - (char)tc_check;
175    displayCharOnLCD( 2, 8, &a , 1 );
176   #endif
177 
178   // 每 5 秒鐘重新確認 TC 是否已重新連接上
179   if( tc_check ++ > 3 )
180   {
181    max6675_getCelsius();
182    tc_check = 0;
183   }
184  }
185  else
186  {
187   float C = max6675_getCelsius();
188   float F = max6675_getFahrenheit();
189   Serial.print( C );
190   Serial.println(" C");
191   Serial.print( F );
192   Serial.println(" F");
193   #ifdef USEI2CLCD
194    char buf[17];
195    uint16_t number;
196    uint8_t fractional;
197    number = (int)C;
198    fractional = ( C - (float)number ) * 100;
199    sprintf(buf, "%4d.%2d C       ", number, fractional );
200    displayCharOnLCD( 1, 1, buf, 16);
201    number = (int)F;
202    fractional = ( F - (float)number ) * 100;
203    sprintf(buf, "%4d.%2d F       ", number, fractional );
204    displayCharOnLCD( 2, 1, buf, 16);
205   #endif
206  }
207  delay(1000);
208 }

loop() 一開始就會先檢查 TC 是否開路,但因為 is_tc_open 設定為全局靜態變數,所以條件不會成真,開電後第一次會直接從 line 185 開始執行。

line 207:延遲 1 秒。每隔 1 秒鐘讀取一次 MAX6675 資料。
line 167 - 184:TC 開路偵測以及處理。只要取回來的數據中 bit 2 1,就會將 "TC is open !" 的文字訊息送到串列埠以及 LCD 螢幕上;使用者可以有 5 秒鐘的時間處理 TC 開路的問題,時間會以倒數的方式顯示在 LCD 螢幕的第 2 行,然後再讀取一次 MAX6675 的數據,判斷是否已恢復正常。
line 185 - 206:讀取 MAX6675 的攝氏 ( ℃ ) 和華氏 ( °F )的溫度,並將數值由串列埠輸出和顯示在 LCD 螢幕上。如果不使用整合型 LCD,則 line 193 - 205 之間的程式碼不會執行。

* max6675_getFahrenheit()
139float max6675_getFahrenheit()
140 {
141  float f = max6675_getCelsius();
142  if( f != NAN )
143  {
144   return ( f * 9.0/5.0 + 32.0 );
145  }
146  else
147   return NAN;
148 }

讀取 MAX6675 的計算之後是攝氏 ºC,要使用華氏 ºF 必須先讀取華氏溫度後再經公式做轉換。如果取得的溫度是有效的 ( line 142 ) 就直接回傳計算之後華氏的溫度值;不然就傳回 NaN ( line 147 )。

* max6675_getCelsius()
 92 float max6675_getCelsius()
 93 {
 94   uint16_t t_c = 0;
 95 
 96   //初始化溫度轉換
 97   digitalWrite( _CS, LOW );
 98     delay(2);
 99     digitalWrite( _CS, HIGH );
100     delay(220);
101 
102     // 開始溫度轉換
103     digitalWrite( _CS, LOW );
104 
105     // 15th-bit, dummy bit
106     digitalWrite( _SCK, HIGH );
107     delay(1);
108     digitalWrite( _SCK, LOW );
109 
110     // 14th - 4th bits, temperature valuw
111     for( int i = 11; i >= 0; i-- )
112     {
113      digitalWrite( _SCK, HIGH );
114      t_c += digitalRead( _SO ) << i;
115      digitalWrite( _SCK, LOW );
116     }
117 
118     // 3th bit, 此位元可以用來判斷 TC 是否損壞或是開路
119     // Bit D2 is normally low and goes high when the therometer input is open.
120     digitalWrite( _SCK , HIGH );
121     is_tc_open = digitalRead( _SO );
122     digitalWrite( _SCK, LOW );
123 
124     // 2nd - 1st bits,
125     // D1 is low to provide a device ID for the MAX6675 and bit D0 is three-state.
126     for( int i = 1; i >= 0; i-- )
127     {
128      digitalWrite( _SCK, HIGH );
129      delay(1);
130      digitalWrite( _SCK, LOW );
131     }
132 
133     // 關閉 MAX6675
134     digitalWrite( _CS, HIGH );
135 
136     return (float)(t_c * 0.25);
137 }

line 96 - 103:讓 /CS 的狀態維持在高準位,然後進入低準位形成降緣觸發。此時微處理器可以開始接收資料。
line 105 - 13116 個 bit 的接收。bit 15 沒有用處,直接以一個時脈訊號帶過 ( line 106 - 108 ),加個 1 ms 的延遲,避免時脈速度太快 ( 這在之前有解釋過為什麼要加延遲 )。
緊接著,接收 12 個 bit ( bit 14 - 3 ) 溫度資料,然後將數據暫時存放在 t_c 變數中。
再來接收 bit 2 單個 bit 的資料,將其存放在全局變數 is_tc_open 變數中。最後之接傳送兩個時脈訊號出去 ( line 126 - 131 ),結束整個數據讀取的流程,關閉 MAX6675 的通訊 ( line 134 )。
將溫度資料乘以 0.25 ( 除以 4 ) 後回傳,這就是實際的攝氏溫度讀值。

* 整合型 LCD 的功能函式

如果有使用整合型 LCD,下面就是所使用的函式宣告與實作程式碼。

27 #ifdef USEI2CLCD
28  void initLCD()
29  {
30   Wire.beginTransmission(ADDRI2CLCD);
31   Wire.write( 0x00 ); // N x commands
32   Wire.write( 0x38 ); // Function set
33   Wire.write( 0x0C ); // Display ON/OFF
34   Wire.write( 0x01 ); // Clear display
35   Wire.write( 0x06 ); // Entry mode set
36   Wire.endTransmission();
37  }
38 
39  void clearLCD()
40  {
41 
42   Wire.beginTransmission(ADDRI2CLCD);
43 
44   Wire.write( 0x80 ); // One command
45   Wire.write( 0x01 ); // Clear display 
46 
47   Wire.endTransmission();
48  }
49 
50  void displayCharOnLCD( int line, int column, const char *dp, unsigned char len )
51  {
52   unsigned char i;
53 
54   Wire.beginTransmission(ADDRI2CLCD);
55 
56   Wire.write( 0x80 );
57   Wire.write( 0x80 + ( line - 1 ) * 0x40 + ( column - 1 ) );
58   Wire.write( 0x40 );
59 
60   for( i = 0; i < len; i++)
61   {
62    Wire.write( *dp++ );
63   }
64 
65   Wire.endTransmission();
66  }
67 #endif

只要將上面的程式碼依行號整理一下,就是整個原始碼!使用者只要了解上面所說明的 MAX6675 資料讀取的方式,就可以充分了解程式碼寫作的重點。


程式執行結果:

為了讓溫度提高多一點!點了一根蠟燭並讓 TC 的探頭接近燭光 ( 不用放在上面燒 ),此時就會看到 LCD 螢幕的數值開始往上跑。使用者可以同時打開 Serial Monitor 看輸出的溫度值,與 LCD 螢幕上面所顯示都會是一樣的。
程式執行結果畫面
結論:

看過上面說明後,可以了解到使用 MAX6675 讀取 K 型熱電偶所偵測的溫度值是非常容易的!如果使用的環境不只是高溫而且還有酸鹼的環境,就可以更換其他有包覆鐵氟龍的 K 外套的 K 型熱電偶,不但防水、防油、防腐蝕、耐酸鹼,而且溫度可量測到 260 ℃。

測溫時,使用者應該要考慮的不只是可量測的溫度高低,還有注意現場環境的裝設難易度還有環境中是否具有酸鹼 ... 等,都是在選用溫度感測器需要考量的因素。

2 則留言:

  1. 電路佈線圖和程式碼的腳位不同喔
    圖上面MAX6675接arduion是2,4,6但程式碼是2,5,8喔!
    (因為我剛剛讀不到XDD

    回覆刪除
    回覆
    1. 哇! 終於有人發現了!
      大家應該都自己搞定,懶得跟我講了!
      錯誤已經更正,謝謝你的提醒 !

      刪除