障害物を回避するための距離センサーの値を直接Aruduinoに取り込まず、I2C通信でAruduinoとセンサーを繋ぎ、センサーの値を読取ってモニタに表示します。
今回は、光学距離センサー(GP2Y0E03)を電源電圧5Vで繋いだ場合です。さらにセンサーのアプリケーションノートの見所、I2C通信の通信手順をまとめみましたので、I2C通信を理解する取っ掛かりになれば嬉しいです。是非、ご覧ください。
では、早速いってみましょう。
- I2C通信できる、光学距離センサー(GP2Y0E03)を接続するとこんな感じです。
- 光学距離センサー(GP2Y0E03)とは :仕様
- 接続図
- アプリケーションノートの見所
- Aruduino の スケッチ
- 注意するポイント
- まとめ
- 最後に
1.I2C通信できる、光学距離センサー(GP2Y0E03)を接続するとこんな感じです。
モニタでは、センサーから直接読取った値と計算された値が表示され、それぞれが、変化しているのが分かります。
以前紹介しました、赤外線距離センサモジュール(GP2Y0A21YK0)はI2C通信できないアナログタイプのセンサーでしたが、今回はI2C通信で接続できるセンサーとAruduinoとセンサーのやり取りをまとめています。
また、I2C通信については、こちらでも紹介しましたが、もう少し詳しく紹介します。
★やりたいこと :測距センサーの測定値をシリアルモニターで表示させるというものです。
2.光学距離センサー(GP2Y0E03)とは :どんな仕様でしょうか?
このGP2Y0E03は光学式測距センサーで三角測量の原理により、測定対象物で反射した光が受光素子上に作る光スポットの位置情報を検出し、測定対象物までの距離を測定します。受光素子に CMOS イメージセンサを採用することにより、小型でありながら高精度の距離測定をすることができます。(SHARP、GP2Y0Eシリーズのアプリケーションノートより抜粋)
●主な仕様
・出力:I2Cおよびアナログ出力
・電源電圧(VDD):2.7~5.5V :電圧5Vまで使えます。
・測距範囲:4~50cm
L=50cmにおいて
VOUT(A)1=0.3~0.8V
D1=45~50cm
L=4cmにおいて
VOUT(A)3=2.1~2.3V
D3=3~5cm
この測距センサユニットは、アナログ出力もI2C出力もできるタイプです。またこのセンサーは他のブログを見ても、実際に自分で使ってみても、30cmくらいまでの測定値はほぼ直線的な値が出ています。(今回のロボットでは、距離というより、障害物のあるなしで使っていますが、もう少し精度の高い機器にも使用できそうです)
測距センサユニット(2) CMOSタイプ|製品ラインアップ|電子デバイス/ディスプレイ:シャープ (jp.sharp)
3.接続図 :センサーとAruduinoの接続はどうすればいいか?
接続図です。
センサモジュール(GP2Y0E03)は、信号のレベル変換が必要ない、5Vで使用しました。その代わり、VIN(IO)には、3.3Vの入力が必要になります。(後述のデータシート参照)
ある意味、当たり前と思いますが、VIN(IO)の3.3VはAruduino Nanoの3.3Vで入力しました。実はセンサーの電圧を3.3Vとし、レベル変換基板を使ってやってみましたが、その変換基板の端子配列を見誤っており、苦戦してしまいました。そこで、ふと5Vを基準電圧にすることもできるとアプリケーションノートに書いてあったことを見つけ、やってみると、かなりシンプルな構成にすることができました。さらに、I2C通信についてたくさん調べられたので、すんなり行くより経験値が増えてよかったかも知れません。
4.アプリケーションノートの見所 :どこを見ればいいか?何が書いてあるのか?
下記のアプリケーションノートから抜粋。日本語の記載なので、英語のデータシートなどの仕様書類より読みやすいです。
*-* I2Cバスタイミング (jp.sharp)
アプリケーションノートやデータシートを見るとき、どこを見るか、そのデバイスを使うと決まっているとすると、という条件で、ちょっと強引に見える化?してみました。
項目 | 見た方がいい度合 | 何に必要か / どんなとき見ているか | |
1 | 端子の種類・配列 | ◎ | 接続や配置を考えるのに必要。 |
2 | タイミングチャート | ○ | 何かを参考にして作ったり、特殊な使い方をしなければ、参考で確認するくらい。 |
3 | インターフェース | ◎ | 通信時の仕様として、フォーマット、タイミング、レジスタなど確認が必要。 |
4 | センサの原理 | ○ | ほんとは、まず最初に見て理解したいですが、概要をさっと。(必要時、がっつり・・という感じです) |
5 | センサの特性・設定 | ◎/○ | 電圧やセンサ特有の性能(それが特性?)などの使用に必要な特性は、まず確認します。それ以外はうまく行かないとき確認します。 |
6 | その他の注意点 | ○/△ | 通常、そこまで見てないです(汗)。なにかトラブって関係しそうな時は、血眼でみます。 |
私の場合の見た方がいい度合いですが、◎ :必見・理解/把握したい 、○ :概要は理解する 、△ :必要に応じて読む という感じでしょうか。
正直なところ、アプリケーションノートと言われても、どこを見たらいいのか、また読んでも何のことが書いてあるか分かりませんでした。いろいろ調べているうちに、少しづつ、書いている内容が見えてきましたので、今回、作るにあたって使用したポイントを赤い四角で囲んでいます。
各社、書いている内容は同じだと思いますが、I2C通信などの技術的な知識がないと、注意点や自分が知りたいことが、分かるまで埋もれて見えないことが多いです。何が知りたいかはっきりしていたり、こういう情報がこの情報の次くらいに載っているはずだという当たりが付けられると、分かりやすいように思います。
5.Aruduino の スケッチ
#include <Wire.h>
// デバイスアドレス(スレーブ)
#define E03_DEVICE_ADDRESS 0x40
#define DISTANCE_ADDRESS 0x5E
void setup()
{
Serial.begin(9600);
Wire.begin();
delay(500);
}
void loop() {
unsigned int dis[2];
unsigned int i, distance;
Wire.beginTransmission(E03_DEVICE_ADDRESS);
Wire.write(DISTANCE_ADDRESS);
distance = Wire.endTransmission();
if (distance == 0) {
distance = Wire.requestFrom(E03_DEVICE_ADDRESS, 2);
dis[0] = Wire.read();
Serial.print("dis[0]= ");
Serial.println(dis[0]);
dis[1] = Wire.read();
Serial.print("dis[1]= ");
Serial.println(dis[1]);
distance = dis[0] * 16 + dis[1];
Serial.print("計算前distance= ");
Serial.println(distance);
distance = (distance / 16) / 4;
Serial.print("距離 = ");
Serial.print(distance);
Serial.println("cm") ;
Serial.println(" ") ;
} else {
Serial.print("ERROR = ");
Serial.println(distance);
}
delay(1500);
}
スケッチの概要は、測距センサーの測定値をシリアルモニターで表示させるというものです。
<通信のストーリー>
(通信の例え、です)まず通信(I2C通信)なので、話す、と聞くが存在します。また誰と誰が話しているかというと、お父さん(先生)と子供(生徒)をイメージしてください。そして、お父さんをマスター、子供をスレーブとします。
個々のスレーブがアドレスを持っていて、データの中にアドレスが含まれていることと、1バイト転送毎に受信側からACK信号の返送をして、互いに確認を取りながらデータ転送を行っています。
【 通信の手順 】
①Wire.beginTransmission( I2Cアドレス ) :どのモジュール(スレーブ)と通信するか連絡。
Wire.beginTransmission(E03_DEVICE_ADDRESS);
//マスターが宣言したアドレス、E03_DEVICE_ADDRESS :0x40 をスレーブへ送ります。
②指定されたスレーブが返事します。 :ACK信号を返信。 (Acknowledg) :認める。
ここから、受け取ったスレーブ(アドレスが一致したスレーブ)だけが通信を行います。
③Wire.write( レジスタ のアドレス ) :どこに書き込んだらいいか連絡。
Wire.write(DISTANCE_ADDRESS);
//マスターが書き込むレジスタを送信します。(どこに書き込んだらいいか)
④先ほどと同様に、受け取ったスレーブは返事します。 :ACK信号。
⑤Wire.write( 送信する値 ) :書き込み
//マスターはデータを送り、スレーブはデータを受け取って、レジスタにデータを書き込む(今回のスケッチでは省略、終了と合わせてひとつの命令に)
⑥Wire.endTransmission() :マスターは通信終了を知らせる。
distance = Wire.endTransmission();
//この行でデータ送信・書き込み・終了を実施。
⑦Wire.requestFrom( I2Cアドレス, バイト数 )
:指定したスレーブのアドレスから指定サイズ分のデータ取得を要求
distance = Wire.requestFrom(E03_DEVICE_ADDRESS, 2);
//E03_DEVICE_ADDRESSに入っているデータを2byte分、要求。ライブラリ内部にデータを読みだし。distanceに入力。
⑧Wire.read() :レジスタからのデータを取得する。指定したサイズ分、繰り返しで取得可能。
dis[0] = Wire.read(); dis[1] = Wire.read();
//unsigned int dis[2];で宣言したdisに順に入力します。これで、読み出せました。
●スレーブアドレス
アプリケーションノートに、「GP2Y0E シリーズは I2C バスの規格に準じた7ビットのスレーブアドレスを備えており、I2C バスから測距値の読み出しや、レジスタ値を変更することにより本センサの各種設定を行うことができます。」という記載があります。
その7ビットのスレーブアドレスは"1000000"(0x40)になります。
さらに、アプリケーションノートに、書き込み/読込み要求の1ビットを加えた合計8ビットでwhite/Readフォーマットが記載されています。
R/W=0 : "1000000"+"0" になりますから、0x80
R/W=1 : "1000000"+"1" になりますから、0x81
GP2Y0E03アドレスは内部の不揮発性メモリに書込まれています。(メモリの書き換えは1回だけ可能)
●距離への計算の流れ
GP2Y0E03の距離用データは、0x5E,0x5Fに格納されています。
データは12ビットです、0x5Eに上位の11-4ビット、0x5Fに下位の3-0ビットが格納されています。
そのデータを使って計算するために、スケッチでは、下記のような計算をしています。
distance = dis[0] * 16 + dis[1];
//dis[0]の値を、16倍して4ビット(4桁)左へずらし、残りの4ビット(dis[1])を足します。
まず、それぞれに格納されている値を使って、12ビットに戻しています。
dis[0] = 0x5E (11-4 Bit)
dis[1] = 0x5F ( 3-0 Bit)
さらに、10-6のレジスタマップにあるように変換します。
distance = (distance / 16) / 4;
これで、測定値になりました。単位はcmで確認することができます。
6.注意するポイント
①デバイスと通信する時に、デバイスのアドレスが分からない場合の調べ方。
I2Cデバイスをデータシートなどで調べればいいのですが、分からないときなど、アドレスを調べる方法があります。
下記のリンク先にある、i2cscannerというツールのソースコードを使えば、I2Cデバイスから取得したアドレスが表示されます。(右上にある「Code」からZIPをダウンロードして解凍)
GitHub - asukiaaa/I2CScanner
②デバイスのアドレスに重複が無いこと
同じセンサーを複数個使う場合など、アドレスが重複する場合がありますが、重複しないように設定する必要があります。
GP2Y0E03のアドレスの変更は、不揮発性メモリに新しいスレーブ アドレスを書き込む必要がありますしデバイスによって変わると思いますので、今回は省略します。
下のリンク先に、詳しく載っているのに参考にしてみて下さい。
③Aruduinoに3.3Vのデバイスを接続する場合は、信号のレベル変換が必要。
Aruduino Nanoは5V電源で動作しているので、I2C通信信号も5Vです。しかしながら、3.3Vで動作するデバイスは電圧が足りなくて信号が正しく認識できないなど動作に影響を及ぼすことがあります。
その場合、「レベルコンバーター」が必要になります。
<参考>
・Arduinoで簡単に距離センサーGP2Y0E03からI2Cで値を取得する
7.まとめ
①光学距離センサー(GP2Y0E03)を使って、I2C通信でAruduinoと通信を行う。
(データの入出力でセンサの測定値をモニタする)
②センサは7ビット+1ビットのスレーブアドレスを持っている。
③通信の方法は、アドレス言って、返事、レジスタ言って、返事、データ書き込み。
④書き込んだデータを指定のアドレスから必要サイズ要求して取り出し、格納されているビット数に応じて計算。
⑤さらに、人間が分かるような数値へ変換する(モニタ出来るようにする)
8.最後に
モータをI2C通信で動かす時は、ライブラリを使うことで出来たので、分かりませんでしたが、通信でのやりとりの大変さが今回は非常によくわかりました。
I2C通信を分かりやすく書いたものがないとどこかのブログに書いていましたが、確かにひとつの情報だけではわからなかったです。参考にさせて頂いたサイトの方々、ありがとうございました。次にI2C通信をやってみたい方の参考になれば嬉しいです。つづく。
<参考にしたリンク先>
・測距モジュール(GP2Y0E03)で物体の距離を測ってみます(2/2)
・Arduinoでi2c通信でセンサから値をとってみる
・第5回 Arduino入門 I2C通信編
・I2C通信の使い方
・I2Cバス仕様およびユーザーマニュアル