micro:bit v2 で LSM303AGR 加速度センサーと通信する方法
micro:bit v2 には、加速度センサー(および磁力計)用の LSM303AGR というチップが搭載されていることを学びました。では、micro:bit 上の nRF52 プロセッサは、実際にはどのようにしてこのチップと通信しているのでしょうか。
このセンサーと通信するために、I2C というプロトコルを使います。まだ I2C については扱っていないので、ここで簡単に見てみましょう。細かいところまでは立ち入りません。この演習で必要なことが理解できる程度にとどめます。
I2C(Inter-Integrated Circuit)シリアルバス
I2C(「アイ・スクエアド・シー」と発音され、I²C または IIC とも表記されます)は、マイクロコントローラがセンサー、ディスプレイ、メモリチップなどの外部デバイスとデータをやり取りできるようにする、シンプルな 2 線式通信プロトコルです。
I2C は、わずか 2 本の線を使ってデバイス同士がやり取りするチャットシステムのようなものだと考えてください。
2 本の線:
- SDA(Serial Data Line): 実際のデータが流れる線です。nRF52 プロセッサとセンサーの間でメッセージを送受信するために使われます。
- SCL(Serial Clock Line): 信号機のような役割をします。デバイスに、いつ自分の番が来たのかを知らせます。交差点で車が青信号を待つのと同じように、デバイスはクロック信号を待ってデータを送受信します。
最初に話し始めるのは誰か? コントローラとデバイスの役割を理解する
I2C 通信では、1 つのデバイスが通信の開始と制御を担当します。ほかのデバイスは、要求されたときに応答します。従来、これらの役割は master と slave と呼ばれていましたが、現在では別の用語も使われています。
-
Controller(以前の "master"): 通信を開始し、タイミングを制御するデバイスです。この場合、micro:bit の nRF52 チップがこれに当たります。
-
Peripheral または Device(以前の "slave"): コントローラからの指示を受け取り、応答するチップです。この場合、LSM303AGR センサーがこれに当たります。
コントローラを質問する人、デバイスをそれに答える人だと考えると分かりやすいでしょう。
I2C アドレス: コントローラが相手を識別する方法
I2C バス上の各デバイスには、それぞれ固有のアドレスがあります。通りに並ぶ家にそれぞれ番号があるのと同じです。これによって、コントローラはどのデバイスと通信しているのかを識別できます。
コントローラは、データを送る前にまずデバイスのアドレスを送信します。そのアドレスを持つデバイスだけが応答し、ほかのデバイスはすべて何も応答しません。
LSM303AGR のデータシートにあるとおり、加速度センサーのスレーブアドレスは 0011001b で、磁気センサーのスレーブアドレスは 0011110b です。
例:
- LSM303AGR の加速度センサーのアドレスは 0x19(2進数: 0011001)です。
- 磁力計の部分のアドレスは 0x1E(2進数: 0011110)です。
このように、両方とも同じチップの中にありますが、nRF52 は正しいアドレスを使うことで、それぞれと個別に通信できます。
micro:bit v2 上の I2C バス
micro:bit v2 では、I2C バスが内部用と外部用の 2 つに分かれています。内部 I2C は、nRF52833 プロセッサと、モーションセンサーやインターフェースチップなどのオンボード部品との通信に使われます。外部 I2C は、外部アクセサリを接続するためにエッジコネクタ(エッジコネクタとは何か気になる場合は、ハードウェア詳細セクション を参照してください)へ引き出されています。
- 内部 I2C では、nRF52833 の GPIO ピン P0.08 を SCL(
I2C_INT_SCL)に、P0.16 を SDA(I2C_INT_SDA)に使用します。 - 外部 I2C では、nRF52833 の GPIO ピン P0.26 を SCL(
I2C_EXT_SCL、エッジコネクタのピン P19)に、P1.00 を SDA(I2C_EXT_SDA、エッジコネクタのピン P20)に使用します。
加速度センサーは内部チップなので、内部 I2C バスを介して通信します。
ここでは、"microbit-bsp" クレートを使用します。このクレートでは、内部の SDA 線と SCL 線に次のようにアクセスできます。
#![allow(unused)] fn main() { let sda = board.i2c_int_sda; let scl = board.i2c_int_scl; }