複数のレジスタを読む

IRA_REG_Mレジスタを読むことは、I2Cプロトコルの理解を試すには良いものでした。 しかし、レジスタの内容はおもしろみのない情報です。

今回は、実際のセンサの測定値がわかる磁力計のレジスタを読みます。 6つの連続したレジスタが関係しており、そのレジスタは0x03番地のOUT_X_H_Mから始まります。

前のプログラムを、これら6つのレジスタを読むように修正します。ほんの少しの修正だけで済みます。

磁力計に要求するアドレスを、IRA_REG_MからOUT_X_H_Mに変更します。


# #![allow(unused_variables)]
#fn main() {
    // 読みたい`OUT_X_H_M`レジスタのアドレスを送信します
    i2c1.txdr.write(|w| w.txdata().bits(OUT_X_H_M));
#}

1バイトだけではなく、6バイトを要求します。


# #![allow(unused_variables)]
#fn main() {
    // RESTARTをブロードキャストします
    // 磁力計のアドレスをR/WビットをReadに設定して、ブロードキャストします
    i2c1.cr2.modify(|_, w| {
        w.start().set_bit();
        w.nbytes().bits(6);
        w.rd_wrn().set_bit();
        w.autoend().set_bit()
    });
#}

1バイトだけ読むのではなく、バッファを埋めます。


# #![allow(unused_variables)]
#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: u8 = 0b001_1110;

// 磁力計レジスタのアドレス
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に設定して、ブロードキャストします
        i2c1.cr2.write(|w| {
            w.start().set_bit();
            w.sadd1().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に設定して、ブロードキャストします
        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);
    }
}

これを実行すると、毎秒新しい6バイトの配列がitmdumpのコンソールに表示されるはずです。 配列内の値は、ボードを動かすと変化するはずです。

$ # itmdump terminal
(..)
[0, 45, 255, 251, 0, 193]
[0, 44, 255, 249, 0, 193]
[0, 49, 255, 250, 0, 195]

しかし、これらのバイト列は、それほど意味がありません。実際の測定値に変換しましょう。


# #![allow(unused_variables)]
#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 terminal
(..)
(44, 196, -7)
(45, 195, -6)
(46, 196, -9)

これは、磁力計のXYZ軸で分解された地球磁場です。

次のセクションでは、これらの数字を理解する方法を学びます。