複数のレジスタを読み取る
IRA_REG_M レジスタを読むことは I2C プロトコルの理解を確認するには良いテストでしたが、そのレジスタには面白みのない情報しか入っていません。
今回は、実際にセンサーの測定値を提供する磁力計のレジスタを読み取ります。
対象となる連続したレジスタは 6 つあり、先頭はアドレス 0x03 の OUT_X_H_M です。
前回のプログラムを修正して、これら 6 つのレジスタを読み取るようにします。必要な変更はほんのわずかです。
磁力計に要求するアドレスを IRA_REG_M から OUT_X_H_M に変更する必要があります。
#![allow(unused)]
fn main() {
// 読み取りたいレジスタのアドレスを送信する: OUT_X_H_M
i2c1.txdr.write(|w| w.txdata().bits(OUT_X_H_M));
}
スレーブには、1 バイトではなく 6 バイトを要求する必要があります。
#![allow(unused)]
fn main() {
// RESTART を送出する
// R/W ビットを Read に設定した MAGNETOMETER アドレスを送出する
i2c1.cr2.modify(|_, w| {
w.start().set_bit();
w.nbytes().bits(6);
w.rd_wrn().set_bit();
w.autoend().set_bit()
});
}
そして、1 バイトだけを読むのではなくバッファを埋めます。
#![allow(unused)]
fn main() {
let mut buffer = [0u8; 6];
for byte in &mut buffer {
// レジスタの内容を受信するまで待つ
while i2c1.isr.read().rxne().bit_is_clear() {}
*byte = i2c1.rxdr.read().rxdata().bits();
}
// STOP を送出する(`AUTOEND = 1` のため自動)
}
データスループットを下げるためのディレイと一緒に、これらをすべてループ内にまとめると次のようになります。
#![deny(unsafe_code)]
#![no_main]
#![no_std]
#[allow(unused_imports)]
use aux14::{entry, iprint, iprintln, prelude::*};
// スレーブアドレス
const MAGNETOMETER: u16 = 0b0011_1100;
// 磁力計のレジスタのアドレス
const OUT_X_H_M: u8 = 0x03;
const IRA_REG_M: u8 = 0x0A;
#[entry]
fn main() -> ! {
let (i2c1, mut delay, mut itm) = aux14::init();
loop {
// START を送出する
// R/W ビットを Write に設定した MAGNETOMETER アドレスを送出する
i2c1.cr2.write(|w| {
w.start().set_bit();
w.sadd().bits(MAGNETOMETER);
w.rd_wrn().clear_bit();
w.nbytes().bits(1);
w.autoend().clear_bit()
});
// さらにデータを送信できるようになるまで待つ
while i2c1.isr.read().txis().bit_is_clear() {}
// 読み取りたいレジスタのアドレスを送信する: OUT_X_H_M
i2c1.txdr.write(|w| w.txdata().bits(OUT_X_H_M));
// 前のバイトが送信されるまで待つ
while i2c1.isr.read().tc().bit_is_clear() {}
// RESTART を送出する
// R/W ビットを Read に設定した MAGNETOMETER アドレスを送出する
i2c1.cr2.modify(|_, w| {
w.start().set_bit();
w.nbytes().bits(6);
w.rd_wrn().set_bit();
w.autoend().set_bit()
});
let mut buffer = [0u8; 6];
for byte in &mut buffer {
// 何かを受信するまで待つ
while i2c1.isr.read().rxne().bit_is_clear() {}
*byte = i2c1.rxdr.read().rxdata().bits();
}
// STOP を送出する(`AUTOEND = 1` のため自動)
iprintln!(&mut itm.stim[0], "{:?}", buffer);
delay.delay_ms(1_000_u16);
}
}
これを実行すると、itmdump のコンソールには毎秒 1 回、新しい 6 バイトの配列が表示されるはずです。ボードを動かすと、配列内の値が変化するはずです。
$ # itmdump ターミナル
(..)
[0, 45, 255, 251, 0, 193]
[0, 44, 255, 249, 0, 193]
[0, 49, 255, 250, 0, 195]
しかし、このままではこれらのバイト列はあまり意味をなしません。実際の測定値に変換してみましょう。
#![allow(unused)]
fn main() {
let x_h = u16::from(buffer[0]);
let x_l = u16::from(buffer[1]);
let z_h = u16::from(buffer[2]);
let z_l = u16::from(buffer[3]);
let y_h = u16::from(buffer[4]);
let y_l = u16::from(buffer[5]);
let x = ((x_h << 8) + x_l) as i16;
let y = ((y_h << 8) + y_l) as i16;
let z = ((z_h << 8) + z_l) as i16;
iprintln!(&mut itm.stim[0], "{:?}", (x, y, z));
}
これで見やすくなるはずです。
$ # `itmdump ターミナル
(..)
(44, 196, -7)
(45, 195, -6)
(46, 196, -9)
これは、地球の磁場を磁力計の XYZ 軸に沿って分解したものです。
次のセクションでは、これらの数値をどう解釈するかを学びます。