単一のレジスタを読み取る
ここまでの理論を実践してみましょう!
まず最初に、チップ内の加速度計と磁力計の両方の対象アドレスを知る必要があります。これらは LSM303AGR のデータシートの 39 ページに記載されており、次のとおりです:
- 加速度計: 0011001
- 磁力計: 0011110
注記 これらはアドレスの先頭 7 ビットにすぎず、8 ビット目は 読み取りを行うのか書き込みを行うのかを決定するビットになることを忘れないでください。
次に必要になるのは、読み取り元のレジスタです。多くの I2C チップには、コントローラが読み取るための
何らかのデバイス識別レジスタが用意されています。世の中には何千、あるいは何百万もの I2C チップが
存在することを考えると、いつか同じアドレスを持つ 2 つのチップが作られてしまう可能性は非常に高いです
(アドレス幅は「たった」7 ビットしかないのですから)。このデバイス ID レジスタがあれば、ドライバは
実際に LSM303AGR と通信しているのであって、たまたま同じアドレスを持つ別のチップではないことを
確認できます。LSM303AGR のデータシート(具体的には 46 ページと 61 ページ)を読むとわかるとおり、
このデバイスには 2 つのレジスタ — アドレス 0x0f の WHO_AM_I_A と、アドレス 0x4f の
WHO_AM_I_M — があり、そこにはこのデバイス固有のビットパターンが入っています。
(“A” は “Accelerometer”、“M” は “Magnetometer” を表します。)
ここで欠けているのはソフトウェアの部分だけです。つまり、このために microbit または
HAL クレートのどの API を使うべきかを判断する必要があります。nRF52833 Product Specification を
読むと、これには実際には I2C 専用のペリフェラルがないことがすぐにわかります。代わりに、
TWI(“Two-Wire Interface”)、TWIM(“Two-Wire Interface Master”)、TWIS(“Two-Wire Interface Slave”)
と呼ばれる、より汎用的な I2C 互換ペリフェラルがあります。通常はコントローラモードで動作するため、
より新しい TWIM を使用します。これは “Easy DMA” をサポートしています — TWI は主に古いデバイスとの
後方互換性のために提供されています。
ここまでに集めた他の情報と microbit クレートの twi(m) module のドキュメントを
組み合わせると、2 つのデバイス ID を読み出して表示する次のコード
(examples/chip-id.rs)になります:
#![deny(unsafe_code)]
#![no_main]
#![no_std]
use cortex_m::asm::wfi;
use cortex_m_rt::entry;
use panic_rtt_target as _;
use rtt_target::{rprintln, rtt_init_print};
use embedded_hal::i2c::I2c;
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();
let mut i2c = { twim::Twim::new(board.TWIM0, board.i2c_internal.into(), FREQUENCY_A::K100) };
let mut acc = [0u8];
let mut mag = [0u8];
// 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 {
wfi();
}
}
初期化を除けば、このコードは、前述した I2C プロトコルを理解していれば素直に読めるはずです。
ここでの初期化は UART の章のものと同様に機能します。ペリフェラル本体と、チップとの通信に使用する
ピンをコンストラクタに渡し、その後でバスを動作させたい周波数を渡します。この場合は 100 kHz
(K100、識別子は数字で始められないため)です。
テストする
いつものように
$ cargo embed --example chip-id
を実行して、この小さなサンプルプログラムをテストします。