今回はSPIデータの送受信の方法を説明したのち、Arduinoによる簡単な温度読みだプログラムを紹介します。
●SPI通信
SPIの特徴として送信と受信が同時に行われるということがあります。書き込みの場合はわかりやすいと思いますが、読出しはわかりにくいと思いますので簡単に説明します。
たとえば、温度などの2バイトのデータを読み出したい場合、最初にレジスタアドレスを書き込みます。そのあと2回、ダミーデータ(値はなんでもよい)を書き込むと、書き込みの度に温度データが1バイトずつ返ってきます。
具体的には、1回目にアドレスを書き込み(このとき返ってくる値は無効)、2回目のダミーデータの書き込みで返ってくる値が温度値の上位8ビット、3回目のダミーデータの書き込みで返ってくる値が下位8ビットとなりまます。
プログラム的には3回書き込み処理を実行して、2回目と3回目の戻り値を利用するということです。
なお、ADT7310のcontinuousモードでの温度読み出しは少し変則的ですので、プログラムの説明を参照してください。
●SPIのデータモード
SPIには、クロックの立ち上がり、または立下りでサンプリングするか、また、クロックが正論理か負論理かで4通りのモードがあります。このモードを駆動するデバイスに合わせないと適切なアクセスができません。
●SPIのCS(SS)
SPIでは、マスタ側がスレーブデバイスを選択するのに、各デバイスのCS(チップセレクト)/SS(スレーブセレクト)信号をアクティブにします。したがって、複数のSPIスレーブデバイスがある場合はその数分だけ、マスタ側にCS(SS)信号が必要です。
ArduinoのSPIライブラリ(SPIマスタ)では、CS信号を駆動する機能がないため、汎用の出力ポートを使って適当なタイミングで切り替える必要があります。また、デバイスごとにSPIデータモードが異なる場合は、その切り替えも必要です。具体的な方法はプログラムの説明のところで説明します。
SDカードはSPIを利用していますが、今回、ADT7310と並列接続する場合は、CS信号をSDカードと温度センサのそれぞれに用意して、プログラムで切り替える必要があります。
今回はディジタルポートD9を出力として、ADT7310のCS制御に使用しています。
次に接続図を示します。(9/30追加)
●SPI信号のプルアップ
SPI信号のSDI(MOSI)、SDO(MISO)はオープンドレインではないので、本来はプルアップは不要ですが、安定しないことがあったのでプルアップしています。10kΩ程度の抵抗器をSDI、SDO両端子と+5Vの間に入れておきます。なお、AVR内蔵のプルアップも利用可能です。
MISO/PB4 (Arduino D12)
MOSI/PB3 (Arduino D11)
内蔵プルアップを有効にするには、入力ポートに設定して”1″を書き込みます。
●単純読み出しのサンプルスケッチ
約500ms周期で温度を読み出して、シリアルで送信するプログラムを示します。分解能はデフォルトの13ビットです。continuousモードを使用しています。接続は次のようにしました。
/CS: D9 SDI: D11 SDO: D12 SCL: D13
連続温度測定のスケッチ
#include int pinCS = 9; // 温度センサCS D9 #define SPI_CS_ON digitalWrite(pinCS, LOW) // /cs = L #define SPI_CS_OFF digitalWrite(pinCS, HIGH) // /cs = H // 初期化 void setup(void) { pinMode(pinCS, OUTPUT); // spi cs SPI_CS_OFF; SPI.begin(); SPI.setBitOrder(MSBFIRST); SPI.setDataMode(SPI_MODE3); Serial.begin(19200); // continuous read mode SPI_CS_ON; SPI.transfer(0x54); // 測定開始 delay(240); // 240ms待つ } // メインループ void loop(void) { uint16_t val; float tmp; int ival; val = (uint16_t)SPI.transfer(0) << 8; // AD変換値 上位 val |= SPI.transfer(0); // AD変換値 下位 /* コマンドバイトの書き込みは最初の1回のみでよい */ val >>= 3; ival = (int)val; if(val & (0x8000 >> 3)) { // 符号判定 // 負数 ival = ival - 8192; } tmp = (float)ival / 16.0; Serial.println(tmp, 2); // xx.xx delay(500); }
AD変換値を読み出した後以降の摂氏温度計算はADT7410での処理と全く同じです。
SPI_CS_ON、SPI_CS_OFFはADT7310の/CSピン(D9)をLowまたはHighに設定するマクロです。SPIアクセス前にON、アクセス終了後のOFFにします。
SPI.setBitOrder()は転送にビット順序(MSBから入出力するか、LSBから入出力するか)、SPI.setDataMode()はSPIのデータモードを指定する関数です。
温度の読み出し手順は、最初に21行目でコマンドバイト(0×54)を一回だけ書き込み、メインループ中の32行目で温度値を2バイト読み出します。この2バイト読み出しを約500ms周期で繰り返します。
SPI.transfer()関数はSPIバスへ8ビット書き込み、同時に8ビット読み出す関数です。22行目では読み出した値が返ってきているのですが、不要ですので読み捨てています。また、33、34行目では0を書き込んでいますが、この値は意味がありません(何でもよい)。このときは返ってきた値だけ必要ですので、2バイト読み出して、16ビットに合成して摂氏温度の換算処理へ渡しています。
今回のプログラムでは、データシートに記載されているタイムチャートのように/CS信号をアクティブにしたまま温度値を繰り返し読み出していますが、これではSDカードなど他のデバイスを制御する場合に不都合です。
そこで試した結果、最初のコマンド(0×54)を書き込んだあとに一旦CS信号を非アクティブにし、2バイトの温度値読み出し毎にCS信号のアクティブ、非アクティブを繰り返しても正常に読み出せることを確認しています。SDカード併用時はそのようにする予定です。
23行目に240msのディレイが入っていますが、これが無いと、Arduinoだけリセットした時に温度が読み出せなくなることがあるため、試行錯誤の結果、入れてあります。200ms程度あれば安定しますが、ワンショットモードで必要とされる時間と同じにしてあります。
参考文献:1線 2線 3線インターフェース活用入門 CQ出版刊
Arduinoを使った記事はありませんが、I2C、SPI、OneWireの原理や使い方や、PIC、AVR、H8でのC言語、アセンブリ言語でのプログラムについて説明しています。
書籍案内→ http://www.wsnak.com/book/book4.html