前回の続き
こんにちは!くろべこです。
今回は実際にArduinoに書き込んでいきたいと思います。と言っても接続は2線のみでスケッチもOneWireライブラリのサンプルを参考にしていきます。
■前編

うわー手抜き、、、と思うかもしれませんが、スケッチの解説はできるだけ丁寧に行っていきますね。
配線
今回はデモなのでArduino_unoを使っていきます。
また、冒頭でも述べましたが今回は通信線での電源供給で接続するために2線のみを使用してVCC線はGNDに落とします。

スケッチ
スケッチ全体
| 
					 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105  | 
						#include <OneWire.h> OneWire  ds(10);  // on pin 10 (a 4.7K resistor is necessary) void setup(void) {   Serial.begin(9600); } void loop(void) {   byte i;   byte present = 0;   byte type_s;   byte data[12];   byte addr[8];   float celsius, fahrenheit;   if ( !ds.search(addr)) {     Serial.println("No more addresses.");     Serial.println();     ds.reset_search();     delay(250);     return;   }   Serial.print("ROM =");   for( i = 0; i < 8; i++) {     Serial.write(' ');     Serial.print(addr[i], HEX);   }   if (OneWire::crc8(addr, 7) != addr[7]) {       Serial.println("CRC is not valid!");       return;   }   Serial.println();   // the first ROM byte indicates which chip   switch (addr[0]) {     case 0x10:       Serial.println("  Chip = DS18S20");  // or old DS1820       type_s = 1;       break;     case 0x28:       Serial.println("  Chip = DS18B20");       type_s = 0;       break;     case 0x22:       Serial.println("  Chip = DS1822");       type_s = 0;       break;     default:       Serial.println("Device is not a DS18x20 family device.");       return;   }    ds.reset();   ds.select(addr);   ds.write(0x44, 1);        // start conversion, with parasite power on at the end   delay(1000);     // maybe 750ms is enough, maybe not   // we might do a ds.depower() here, but the reset will take care of it.   present = ds.reset();   ds.select(addr);       ds.write(0xBE);         // Read Scratchpad   Serial.print("  Data = ");   Serial.print(present, HEX);   Serial.print(" ");   for ( i = 0; i < 9; i++) {           // we need 9 bytes     data[i] = ds.read();     Serial.print(data[i], HEX);     Serial.print(" ");   }   Serial.print(" CRC=");   Serial.print(OneWire::crc8(data, 8), HEX);   Serial.println();   // Convert the data to actual temperature   // because the result is a 16 bit signed integer, it should   // be stored to an "int16_t" type, which is always 16 bits   // even when compiled on a 32 bit processor.   int16_t raw = (data[1] << 8) | data[0];   if (type_s) {     raw = raw << 3; // 9 bit resolution default     if (data[7] == 0x10) {       // "count remain" gives full 12 bit resolution       raw = (raw & 0xFFF0) + 12 - data[6];     }   } else {     byte cfg = (data[4] & 0x60);     // at lower res, the low bits are undefined, so let's zero them     if (cfg == 0x00) raw = raw & ~7;  // 9 bit resolution, 93.75 ms     else if (cfg == 0x20) raw = raw & ~3; // 10 bit res, 187.5 ms     else if (cfg == 0x40) raw = raw & ~1; // 11 bit res, 375 ms     //// default is 12 bit resolution, 750 ms conversion time   }   celsius = (float)raw / 16.0;   fahrenheit = celsius * 1.8 + 32.0;   Serial.print("  Temperature = ");   Serial.print(celsius);   Serial.print(" Celsius, ");   Serial.print(fahrenheit);   Serial.println(" Fahrenheit"); }  | 
					
OneWireのサンプルコードそのままですね。単純に温度だけ計りたいよって方はこのこのコードを使用すればいいと思います。バスラインに複数並列に接続すれば自動的にシリアルNoを検索してくれて値を表示してくれます。
ちょっと解説
これ以降はスケッチの解説になります。
①通信線の定義
| 
					 1  | 
						OneWire ds(10); // on pin 10 (a 4.7K resistor is necessary)  | 
					
カッコ内にArduinoのIOピンNoを入れるだけです。それ以上でもそれ以下でもありません。
②バスラインに接続されているDS18B20のサーチ
| 
					 1 2 3 4 5 6 7  | 
						 if ( !ds.search(addr)) {     Serial.println("No more addresses.");     Serial.println();     ds.reset_search();     delay(250);     return;   }  | 
					
ds.search(addr)はバスラインに接続されているスレーブをサーチする。デバイスが存在する場合は1を返す。ここのスケッチではデバイスが存在する場合はaddrにROMデータを書き込む。0(デバイスが存在しない)の場合は「デバイスがないよ」と教えてくれて以下の処理を実行しないで戻す動作をします。
③ROMデータを16進数でシリアル表示する
| 
					 1 2 3 4 5  | 
						Serial.print("ROM =");   for( i = 0; i < 8; i++) {     Serial.write(' ');     Serial.print(addr[i], HEX);   }  | 
					
④ROM読み取りのCRCチェック
| 
					 1 2 3 4  | 
						if (OneWire::crc8(addr, 7) != addr[7]) {       Serial.println("CRC is not valid!");       return;   }  | 
					
ROMメモリの上位8ビットに格納されているCRC(ROMコードの56bit分より計算される)とCRC生成式より算出した値を比較しROMの読み取りに誤りがないか確認を行う(ライブラリが行ってくれてます感謝!)
| 
					 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17  | 
						 switch (addr[0]) {     case 0x10:       Serial.println("  Chip = DS18S20");  // or old DS1820       type_s = 1;       break;     case 0x28:       Serial.println("  Chip = DS18B20");       type_s = 0;       break;     case 0x22:       Serial.println("  Chip = DS1822");       type_s = 0;       break;     default:       Serial.println("Device is not a DS18x20 family device.");       return;   }  | 
					
ROMメモリ0byte目に格納されているファミリーコードで使用チップの種類を判断する。
⑥温度変換処理
| 
					 1 2 3 4  | 
						ds.reset(); ds.select(addr); ds.write(0x44, 1);         delay(1000);       | 
					
ROMコマンドF0(サーチROM)を行ったので処理シーケンスは一旦リセットされるのでまず初期化を行い、その後ライブラリのコマンドds.select(addr)を行っている。これはROMコマンドMATCH ROM(55h)に該当し特定のスレーブとの通信を可能とする。そしてファンクションコマンド44hを書き込むことで温度変換を行っている。44hに続く1の意味はROM書き込み中の電源供給だと思う。通信線による電源供給をしている場合にROM書き込み時は通信線をプルアップし続けなくてはいけないが、外部電源を使用している場合は不要と思う。たぶん。末尾のディレイは変換時間を考慮したものだと思う。このスケッチはデフォルトの分解能12bitを使用しているので750ms最大変換時間がかかる。もし9bitの分解能であればここはdelay(100)でも多分OK。
⑦データメモリの読み込み
| 
					 1 2 3 4 5 6 7 8 9 10 11 12  | 
						present = ds.reset();   ds.select(addr);       ds.write(0xBE);            Serial.print("  Data = ");   Serial.print(present, HEX);   Serial.print(" ");   for ( i = 0; i < 9; i++) {                data[i] = ds.read();     Serial.print(data[i], HEX);     Serial.print(" ");   }  | 
					
まず処理シーケンス通りに初期化⇒ROMコマンド(55h)を行っている。その後、データメモリ読み取り(BEh)の書き込みを1byteずつ行って0byte~9byteのデータをdata配列に格納しているだけ。
⑧データ読み取りのCRCチェック
| 
					 1 2 3  | 
						Serial.print(" CRC=");   Serial.print(OneWire::crc8(data, 8), HEX);   Serial.println();  | 
					
ROM読み取りと同様にデータメモリ8byte目に格納されているCRCと算出CRCを比較して読み取りの誤りを確認している。
⑨摂氏・華氏変換
| 
					 1 2 3 4 5 6 7 8 9 10 11 12 13 14  | 
						int16_t raw = (data[1] << 8) | data[0];   if (type_s) {     raw = raw << 3; // 9 bit resolution default     if (data[7] == 0x10) {       raw = (raw & 0xFFF0) + 12 - data[6];     }   } else {     byte cfg = (data[4] & 0x60);     if (cfg == 0x00) raw = raw & ~7;  // 9 bit resolution, 93.75 ms     else if (cfg == 0x20) raw = raw & ~3; // 10 bit res, 187.5 ms     else if (cfg == 0x40) raw = raw & ~1; // 11 bit res, 375 ms   }   celsius = (float)raw / 16.0;   fahrenheit = celsius * 1.8 + 32.0;  | 
					
温度の上下byteを16bitのintに統合してチップごとの処理や分解能の処理を行って華氏・摂氏の変換処理を行っています。詳細の説明は以下のPDFにまとめました。
実装
と言っても上記のスケッチを確認してシリアルモニタで確認するのみです。

個人的にですがbit表示の方がわかりやすいのでデータメモリはbit表示にしています。
また、分解能は10bit(0.25℃)にしています。分解能の変更方法やアラーム温度の設定は後編で説明したいと思います。
おわりに
いかがだったでしょうか?今回はDS18B20を使用した水温検知を行いました。次の応用編ではデータメモリへの設定の書き込みや分解能毎の温度変換スピードの変更、バスラインに複数のDS18B20を接続したりしてみたいと思います。
ではでは~

  
  
  
  

コメント