マスタリングWireライブラリ その1 スタートとストップ・コンディション
●通信に必要なものはデータとクロック
今現在、PCの周辺機器の多くはシリアル通信を利用します。対になる言葉がパラレル通信です。従来、信号が8本とか32本など、並行してデータを送っていましたが、PCの中で一番高速なメモリを除けばPCIeバスもシリアル通信です。シリアル通信ではデータを送る線は1本もしくは2本です。USBのように、2本の線を差動方式にすれば、高速でデータが送れます。
組み込み用コンピュータ・ボード上でも、シリアル通信が一般的です。パラレルのバスはCPUの外には出ていません。
シリアル通信では、データの始まりや終わり、有効状態などを知るためにクロックが必要です。データとクロックを分けて送る方式や混在して送る方式があります。
組み込み用コンピュータ・ボードでは、I2CとSPIが主に使われます。I2Cが100kHzと低速なデータ転送速度に比べて、SPIは10MHz以上の高速データ転送ができます。
●ArduinoではI2C通信は標準で使える
マイコンの多くはI2Cのインターフェースをハードウェアで用意しています。Arduino UNOは一組外部ピンに信号が出ています。ピンは次の二つです。
- SCL クロック
- SDA データ
どちらの信号も、何もないときはHIGHレベルです。Arduino UNOは電源電圧が5Vで動作するので、High=5Vです。データやクロックはLowになることで何らかの有効な信号になります。
通信は、マスタがスレーブに対して行います。マスタが複数(マルチマスタ)の場合も規定されています。もちろん、スレーブは複数存在することが前提です。それそれのデバイスはアドレスで判断します。したがって、I2Cバスには同じアドレスのデバイスは共存できません。アドレスには7ビットが使われますが、多くのデバイスでは、2~8通りのアドレスを変更できます。
7ビットなので、128個のデバイスをI2Cバスに接続できる仕様ではありますが、現実は、デバイスの端子のもつ容量などがたくさん並列につながるとマスタがバスを駆動できなくなるので、数十個が限界と思われます。
マイコンは8ビットが一つの単位です。7ビットがアドレスで、最後の1ビットがリード/ライト・ビットに使われます。マスタであるArduinoが、7ビットのスレーブ・アドレスと1ビットのライト・ビットをI2Cバスに送り出します。そうすると、スレーブ・デバイスはずっとアドレスを監視していて、自分宛てだ!とわかると、LowのACK(Acknowledge)1ビットをバスに出します。
マスタは、返事があったので次のコマンドを送ります。次のコマンドは、スレーブからデータを読む場合や、たとえばデバイスのコンフィギュレーション・レジスタに初期設定内容を変更する値を送ることを続けます。
●スタートとストップ
I2Cバスは、約10kΩの抵抗で電源にプルアップされています。対になる言葉はプルダウンです。端子と電源を抵抗でつなぐと電流が流れます。むだな電力になるのでやりたくないのですが、マイコンの多くの端子が入力と出力兼用になっていたりする回路構成なので、プルアップもしくはプルダウンしておかないと、ノイズなどの影響をうけます。
I2Cバスの論理的Highは3.5V以上で、Lowは1.5V以下と規定されています(電源が5Vのとき)。
I2C通信の始まりをスタート・コンディション、終わりをストップ・コンディションと呼びます。さきほど、最初に7ビットのアドレス+ライト・ビットで通信を始めるという説明をしました。これは論理的なI2C通信のやり取りの始まりです。
シリアル通信なので、8ビットを送るには、
これから通信を始めるよ
以上で1バイト送り終えました。このあと、ずーと複数バイトのやり取りがあってマスタがデータを読み取り終えたり送り終えたら、 通信を終了するよ |
という状態にします。始めるときがスタート・コンディション、終わるときがストップ・コンディションです。なぜコンディションというかは、実際の信号を見るとわかります。横軸は時間です。スタート・コンディションはSCLがHighのときにSDAをLowにする状態です。二つの信号の組み合わせで、通信の始まりと終わりを示しています。
スタートの次は、クロックSCLがHighの間、SDAがHighなら'1'、Lowなら'0'のデータと判断します。
マスタがデータを送り出すときは、マスタがSDA端子を出力モードに出力します。スレーブのデバイスがデータを送ってくるときは、マスタのSDA端子は入力状態です。つまり、SDA端子は入力と出力の両方を行います。クロックSCLはマスタが常に出力します。
実際に観測します。ツールはPicoScopeです。I2Cのシリアル・デコード機能を生かします。I2CデバイスはMCP9808という温度センサです。機能などは、ラズパイの記事を参照ください。
接続です。MCP9808ボード上にはプルアップ抵抗が取り付けられていますが、ラズパイの接続実験で取り去りました。ここでは、図にはありませんが、ブレッドボード上でプルアップ用6.8kΩの抵抗2本を取り付けました。
スケッチです。
#include <Wire.h>
unsigned char MCP9808_address = 0x18;
unsigned char Temp_register = 0x05;
void setup() {
Wire.begin();
Serial.begin(9600);
Wire.beginTransmission(MCP9808_address);
Wire.write(Temp_register);
Wire.endTransmission();
}
void loop() {
Serial.println("start ");
Wire.requestFrom(MCP9808_address, 2);
int temp = Wire.read();
temp = (temp & 0x1f ) << 8 ;
temp = temp + Wire.read();
Serial.println(temp*0.0625);
}
loop()の中では温度データの2バイトを読み出しています。最初はスレーブ・アドレスです。
先頭部分の波形です。データは正論理で読みます。クロックSCLがHighの中央付近のデータSDAの状態を読みます。手動でデコードすると0011000は0x18となり、デバイスのスレーブ・アドレス7ビットですね。画面ではPicoScopeのシリアル・デコード機能で0x18と表示されています。そのあとの緑色の部分はR/Wビットで、ReadなのでHighです。最後の黄色はスレーブ・デバイスが出したACK信号です。スレーブがアドレスを認識したという確認信号です。マスタはACKをもらったら、2バイトのデータを受け取るためにクロックを出します。
全体の3バイトの波形です。アドレスのあとのACKはスレーブ・デバイスが出します。データの1バイト目のあとのACKはマスタが受け取ったことを示すのでマスタが出しています。2バイト目のデータ後のNACKもマスタが出しています。オシロスコープではどちらが出しているACKかはわかりません。
最後の部分です。マスタは、2バイトを読み出すようにスケッチを書いてあるので、2バイトのあとでこれ以上受け取らないというNACK(Not Acknowledge)を出します。スレーブ・デバイスはそれ以上のデータを送るのをやめます。
MCP9808のデータシートのFIGURE 5-5と同じフォーマットになっていることが確認できます。