単一のレジスタを読み取る
これまでの理論をすべて実践に移してみましょう!
まず最初に、チップ内の加速度計と磁力計の両方の対象アドレスを知る必要があります。 これらは LSM303AGR のデータシートの 39 ページに記載されており、次のとおりです:
- 加速度計は 0011001
- 磁力計は 0011110
注 これらはアドレスの先頭 7 ビットにすぎないことを忘れないでください。 8 ビット目は、読み取りを行うのか書き込みを行うのかを 決定するビットになります。
次に、読み取るレジスタが必要です。世の中にある多くの I2C チップには、
コントローラーが読み取るための何らかのデバイス識別レジスタが用意されています。
これは、世の中に何千(あるいは何百万)もの I2C チップが存在することを考えると、
いつか同じアドレスを持つ 2 つのチップが作られる可能性が非常に高いためです
(結局のところ、アドレス幅は「たった」7 ビットしかありません)。この
デバイス ID レジスタがあれば、ドライバーは実際に LSM303AGR と通信しているのであって、
たまたま同じアドレスを持つ別のチップではないことを確認できます。
LSM303AGR のデータシート(具体的には 46 ページと 61 ページ)を読むとわかるように、
このデバイスには 0x0f 番地の WHO_AM_I_A と 0x4f 番地の WHO_AM_I_M
という 2 つのレジスタがあり、そこにはデバイス固有のビットパターンが含まれています
(A は accelerometer、M は magnetometer を表します)。
ここで不足しているのはソフトウェア側、つまりこのために microbit / HAL
クレートのどの API を使うべきか、という点だけです。しかし、使用している nRF チップの
データシートを読むと、実際には I2C ペリフェラルを持っていないことがすぐにわかります。
とはいえ幸いなことに、UART と UARTE の場合と同じく、使用するチップに応じて
TWI (Two Wire Interface) と TWIM という I2C 互換のものが用意されています。
ここまでに集めたほかのすべての情報と microbit クレートの twi(m) module の
ドキュメントを組み合わせると、2 つのデバイス ID を読み出して表示するためのこの
コードになります:
#![deny(unsafe_code)]
#![no_main]
#![no_std]
use cortex_m_rt::entry;
use rtt_target::{rtt_init_print, rprintln};
use panic_rtt_target as _;
use microbit::hal::prelude::*;
#[cfg(feature = "v1")]
use microbit::{
hal::twi,
pac::twi0::frequency::FREQUENCY_A,
};
#[cfg(feature = "v2")]
use microbit::{
hal::twim,
pac::twim0::frequency::FREQUENCY_A,
};
const ACCELEROMETER_ADDR: u8 = 0b0011001;
const MAGNETOMETER_ADDR: u8 = 0b0011110;
const ACCELEROMETER_ID_REG: u8 = 0x0f;
const MAGNETOMETER_ID_REG: u8 = 0x4f;
#[entry]
fn main() -> ! {
rtt_init_print!();
let board = microbit::Board::take().unwrap();
#[cfg(feature = "v1")]
let mut i2c = { twi::Twi::new(board.TWI0, board.i2c.into(), FREQUENCY_A::K100) };
#[cfg(feature = "v2")]
let mut i2c = { twim::Twim::new(board.TWIM0, board.i2c_internal.into(), FREQUENCY_A::K100) };
let mut acc = [0];
let mut mag = [0];
// First write the address + register onto the bus, then read the chip's responses
i2c.write_read(ACCELEROMETER_ADDR, &[ACCELEROMETER_ID_REG], &mut acc).unwrap();
i2c.write_read(MAGNETOMETER_ADDR, &[MAGNETOMETER_ID_REG], &mut mag).unwrap();
rprintln!("The accelerometer chip's id is: {:#b}", acc[0]);
rprintln!("The magnetometer chip's id is: {:#b}", mag[0]);
loop {}
}
初期化を除けば、前述の I2C プロトコルを理解していれば、このコードはわかりやすいはずです。
ここでの初期化は UART の章のものと同様に機能します。
コンストラクタには、ペリフェラルに加えてチップとの通信に使用するピンを渡し、
その後でバスを動作させたい周波数を指定します。この場合は 100 kHz (K100) です。
テストする
いつものように、Embed.toml を自分の MCU に合うように変更し、その後で次を使用できます:
# micro:bit v2 の場合
$ cargo embed --features v2 --target thumbv7em-none-eabihf
# micro:bit v1 の場合
$ cargo embed --features v1 --target thumbv6m-none-eabi
これで、この小さなサンプルプログラムをテストできます。