ArduinoのWireライブラリに関するTips

 皆様、ご無沙汰しております。代表の國頭です。

 「デバッグ事例」のカテゴリでは、ほぼ6年ぶりの更新になりますがどうぞ宜しくお願い致します。

 さて、表題にありますように「ArduinoのWireライブラリ」について、ここ数年疑問だった事が解決したので、私の備忘録も兼ねて記事を書いてみたいと思います。

 Arduinoについてはご存じの方が多いと思うので、ここでは詳細な説明は省きますが、私はI2CやSPIなどで制御できるデバイスの評価用プラットフォームとして使っています。

 つい先日、温湿度センサーである、Sensirion社製「SHT31」を評価する機会がありましたので、その時に得られた知見をご報告したいと思います。

 評価用にSHT31が搭載されたモジュールを購入し、ブレッドボードにそのモジュールを取り付け、Arduinoと接続して、評価を始めました(写真1)。

SHT31を評価する写真1 SHT31を評価する。

 Arduinoには、I2Cを手軽に扱えるよう「Wireライブラリ」が標準で用意され、そのライブラリを使ってSHT31へコマンドを送り、そのコマンドによって指定されたデータが受信できているか確認してみました。

 先ずは、SHT31に対して測定コマンドを送ります。SHT31のデータシート(PDFファイルが開きます)を参照し、

  1. 単発測定モード
  2. 繰り返し精度レベル:高
  3. クロックストレッチ:無

としました。

 Arduinoのスケッチは以下。

//SHT31にI2C経由で、測定モード設定コマンドを送信する
 Wire.beginTransmission(SHT31_Address);
 //シングルショットモードで測定
 //コマンドのMSB側。0x24はクロック・ストレッチ無効とする
 Wire.write(0x24);
 //コマンドのLSB側。0x00は繰り返し精度「高」
 Wire.write(0x00);
 //コマンド送信終了
 Wire.endTransmission();

 このコマンドを送った時に、I2CバスのSCL信号とSDA信号がどの様になるか観測してみました。ハードウエアエンジニアの「さが」なのか、リアルな波形を観ないと落ち着かないのです(写真2参照)。

写真2 デジタルオシロスコープを使ってI2Cバスの波形を観測する

 測定には、キーサイトテクノロジーのDSOX2014Aを用い、このオシロスコープのCH1(黄色の波形)には「SCL」、CH2(緑の波形)には「SDA」が入力されています。

 DSOX2014Aには、各種シリアルインターフェースに対して、特定の条件に対して選択的にトリガをかけ、その波形をデコードする機能をオプションとして搭載することができます。この機能を使って、I2Cのスタート・コンディションでトリガをかけて波形を観測しその波形をデコードしています(図1参照)。

SHT31への測定コマンド波形

 スケッチで「Wire.beginTransmission(SHT31_Address)」に相当する部分が、「45Wa」です。アドレス45(ここではSHT31)のデバイスへのWrite(W)が行われ、Acknowledge(a)が帰ってきた事を示します。

 続いて、表示されている「24a」と「00a」が「Wire.write(0x24)」と「Wire.write(0x00)」に相当する部分で、スケッチで指定したコマンドがSHT31へと送られていることが分かります。

 さらに、00aに続いてI2CのSCL及びSDAが両方ともハイレベルとなっています。これは、スケッチの「Wire.endTransmission()」によって、I2Cバスがストップ・コンディションとなり、I2Cの通信を終了させていることが分かります。

 この波形によって、ArduinoのIDEで用意されているWireライブラリは、PHILIPS(現:NXPセミコンダクタ)が定めた規格(PDFファイルが開きます)に準拠していることが確認できました。

 続いて、データの受信です。

 Arduinoのスケッチは以下。

//SHTー31に対して、I2C経由で6バイト分のデータを読むようにリクエスト
Wire.requestFrom(SHT31_Address,6);
while (Wire.available()<6){
}
 //読み込み開始
 //温度データは16ビットで、8ビットずつ送られてくる。
 //最初、上位8ビットが出てくるので、
 //ビットシフト演算を使い左8ビットシフトし、変数に格納。
 //その変数に、下位8ビットを加算して、16ビットデータを作成。
 Ta_in=(Wire.read()<<8);
 Ta_in=Ta_in+(Wire.read());
 //このプログラムでは使わないが、温度データのCRCを変数に格納
 Ta_CRC=Wire.read();
 //温度データを浮動小数点変数へ格納
 Ta_data=Ta_in;
 //湿度データも16ビットで、8ビットずつ送られてくる。
 //最初、上位8ビットが出てくるので、
 //ビットシフト演算を使い左8ビットシフトし、変数に格納。
 //その変数に、下位8ビットを加算して、16ビットデータを作成。
 RH_in=(Wire.read()<<8);
 RH_in=RH_in+(Wire.read());
 //このプログラムでは使わないが、湿度データのCRCを変数に格納
 RH_CRC=Wire.read();
 // 湿度データを浮動小数点変数へ格納
 RH_data=RH_in;
 Wire.endTransmission();

 このスケッチで得られた波形を図2に示します。

図2 SHT31から送られてきたデータ波形

 「45Ra」は、「Wire.requestFrom(SHT31_Address,6)」によって、ホストからアドレス45を持つスレーブ・デバイスに対してデータをRead(R)する事を伝え、Acknowledge(a)が帰ってきたことを示しています。 その後6バイトのデータが連続してスレーブ・デバイスから出力されていることが分かります。最後の「85~a」でホストはデータの受信を終え、I2Cバスはストップ・コンディションとなり、SCL、SDA共にハイレベルになっています。

 この波形で気になるのは、最後に表示されている「45Wa」です。6バイト目のデータ「85~a」でI2Cバスはストップコンディションになり、バスとしての動作は終了しているはずなのですが、なぜかI2Cバスがスタート・コンディションになり、SHT31に対してコマンドを送ろうとしています。ただ、コマンドが何も指定されていない為か、すぐにストップコンディションになって、I2CバスのSCL及びSDAがハイレベルとなり、I2Cバスの通信が終了しています。

 スケッチをよくよく観てみると、湿度データのチェックサムを「RH_CRC=Wire.read();」で受け取ってから、Wireライブラリの「Wire.endTransmission();」を送っています。

 試しに、このコマンドをコメントアウトしてみました。

// 湿度データを浮動小数点変数へ格納
 RH_data=RH_in;
 //Wire.endTransmission();

この場合のI2Cバスの波形をデジタルオシロスコープで観測してみたところ、図3のようになりました。

図3 Wire.endTransmission();をコメントアウトした場合

 どうも、「Wire.endTransmission();」が悪さをしていたようで、

Wire.requestFrom(Slave_Address,Number_of_Byte);
 while (Wire.available()<Number_of_Byte){
 }

~~~~~~~~~~
Variable = Wire.read();

 と書けば、Arduinoが指定されたバイト数だけデータを読むと、I2Cバスを自動的にストップ・コンディションにするようです。

「Wire.read();」の直後に「Wire.endTransmission()」を挿入すると、「Wire.beginTransmission(Slave_Address)」と同じ波形が出てくるのは「謎」ですが、スレーブデバイスからデータを読み込むとき、「Wire.endTransmission()」は入れるべきでは無い、という結論に達しました。

 私が「Wire.endTransmission()」を入れた理由は、「Wire.endTransmission()」を送る事によって、「I2Cバスがストップ・コンディションになる」と、勝手に思い込んでいたからです。

 前々から、この謎の波形が気になっていたのですが、やっとスッキリしました。

 今後、I2Cを使ったデータ受信時には、「Wire.endTransmission()」は不要なのだ、と頭に叩き込んで置きたいと思います。

(補足)
なお、「Wire.endTransmission()」を入れても入れなくても、ソフトウエア的には全く問題無いようで、受信したデータをCOMに表示させても文字化け等のトラブルは起きません。図4-1、4-2を参照下さい。

図4-2 Wire.endTransmission()をコメントアウトした場合 (クリックで拡大)

図4-1 「Wire.endTransmission()」が入っていた場合 (クリックで拡大)

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください