MAKER UNO + で始めるSTEM (8) WireライブラリでBoschのセンサ②IDの読み出し

 I2Cの規格はNXP(旧フィリップス)から発行されていますが、わかりやすいものではありません。規格の内容は多岐にわたっていますが、多くは、7ビット・アドレスで、マスタは一つだけという接続で使われます。その限定された条件でも、センサからデータを読み出すには次のようなバリエーションがあります。

  • 初期設定は不要でレジスタを読み出すだけ
  • パワーオン・リセット時にはスリープ・モードなので、まず起こしてからレジスタを読みに行く
  • 複数のレジスタがあるので、レジスタを指定してからデータを読み出す
  • 複数のポインタがあるので、ポインタを指定してデータを読み出す
  • 上記の2例で、レジスタもしくはポインタを指定した後、リピーテッド・スタート・アドレスで続けてデータを読み出す
  • データを読み出す命令を送った後、データが用意できるまでSCLをLOW状態で引き延ばすクロック・ストレッチが必要なデバイス
  • データを読み出すタイミングを別途用意された端子がLOWになったら読み出すデバイス
  • 読み出すデータ通信経路でデータが正常に受け取れたかを検査するCRCが付加されているデバイス。読み出しのときは無視もできるが、レジスタを変更するときは、CRCを付加して送る必要がある。CRC-8には2種類ある

プルアップ抵抗

 I2Cの規格では、数kΩの抵抗で電源に対してプルアップ抵抗を接続します。ブレーク・アウト・ボード上に抵抗が用意されている場合は、その1枚だけ有効にします。プルアップ抵抗の位置は特に指定されていません。複数のスレーブ・デバイスのボード上にプルアップ抵抗がついていて、外せないとき、合成抵抗が1kΩ程度までなら、そのまま利用できます。

 10kが入っているボードと4.7kが入っているボードの抵抗を外さずにそのまま接続したときの計算方法です。

   1 / (1/10k + 1/4.7k) = 10k * 4.7k / (4.7k+10k) = 3.2kΩ

 データ転送速度のデフォルトは100kHzですが、最近のデバイスは最大400kHzが多いようです。1MHz以上に対応したデバイスをその速度で利用するときは、プルアップ抵抗の値を1kΩ以下にする必要があるようです。

タイミングに慣れる

 SCLはクロックで、マスタから出力されます。SDAはデータで、マスタがスレーブ・デバイスに何らかの設定情報を送るときと、スレーブ・デバイスがレジスタの内容を送るときは逆方向にデータは流れます。つまり、SDAは双方向のバスです。

 データの転送はマスタが始めます。

  • スタート・コンディション
  •  データ転送がこの間に行われる。データが連続するときはAckを送って繰り返す、なくなったらNoAck(Nak)を送る
  • ストップ・コンディション

 リピーテッド・スタート・コンディション;バスを開けることなくスタート・コンディションに移行する特別なタイミングを要求されることがあります。Wireライブラリでは、対応する記述ができます。バスは、空いていると別のマスタが利用します。といっても、マルチマスタの場合なので、Arduinoではそういう使い方はほとんどしないです。

タイミングを可視化

 ディジタル信号をオシロスコープで見ても、問題点をなかなか見つけられません。I2Cは最上位ビットMSBからデータが送られるので、画面に出たHIGH/LOWを目で追っていけば、暗算でも16進データに変換できます。プロトコル・アナライザでは、それぞれの信号を図化して表示するので、読み出しているデータがおかしいとか、必要なデータを読み出しているけど、みんな0xffばかりだということがすぐにわかり、効率の良いデバッグができます。
 専用の装置は高価です。I2Cは速度も遅く2本の信号線だけを観測すればよいので、低価格なツールで信号を観測できます。

 デバイスのデータシートには、I2Cのタイミングが書かれていますが、見ずらいです。スケッチAdafruit BME680 Library-bme680testの中で、下記の部分だけを有効にしてBME680の温度だけを読み出すタイミングを見ます。スケッチのうち温度以外の読み出しは、コメントアウトしました。

  Serial.print(bme.temperature);

 アナログ・デバイセズのADALM2000の表示です。

 DigilentのAnalog Discovery2の表示です。上はアナログの波形、下部はディジタルの表示です。同じ時間軸で表示されています。

 Pico TechnologyのPicoScope(5242B)の表示です。アナログ波形の画面にプロトコルの表示が追加できます。

 アナログ信号を見ると、Ackの電圧が中途半端だとか、トラブルの原因を探すのに役立ちます。

まずリセットをかけてIDを読み出す

 デバイス固有のchip_IDを読み出します。データシートによれば、0x61です。読み出せました。

 マスタがスレーブ・デバイスにコマンドや読み出すレジスタを指定する定番の書き方です。

Wire.beginTransmission(BME680_address);
    Wire.write(reset);
Wire.endTransmission();

 endTransmission()を送ると、ストップ・コンディションになります。リピーテッド・スタート・コンディションを要求されているときは、endTransmission(false)にします。

 2バイトのポインタを指定したいときは、8ビット単位なので、2回に分けます。

Wire.beginTransmission(BME680_address);
    Wire.write(ポインタの上位);
    Wire.write(ポインタの下位);
Wire.endTransmission();

 リセット・コマンドは送るだけですが、レジスタを読みたいときは、続けて、指定したレジスタもしくはポインタの内容を読み出します。

Wire.requestFrom(BME680_address, 1);

は、1バイト読み取ることを指示します。2バイトならば1は2になります。この後、指定したバイト数だけ読み取ります。今回のIDは1バイトなので、Wire.read()するだけです。2バイトならWire.read()を2回繰り返します。
 int ID = Wire.read(); とIDをintの型にしていますが、1バイトなので、byte ID = Wire.read();とすべきでした。型は、コンパイラがメモリを確保する大きさを指定します。

 Arduino UNOの、1バイトの型です。型の指定方法は複数あります。

◆0~255
byte
unsigned char
uint8_t

◆-128 ~ 127
char
int8_t

 Arduino UNOの、2バイトの型です。

◆0~65535
word
uint16_t
unsigned int

◆-32,768 ~ 32,767
int
int16_t

 スレーブ・アドレスは7ビットなので、上記のどの型を使っても、正しい結果が得られます。

 実行結果です。

前へ

MAKER UNO + で始めるSTEM (7) WireライブラリでBoschのセンサ①ライブラリを利用

次へ

MAKER UNO + で始めるSTEM (9) WireライブラリでBoschのセンサ③補償データ