Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

複数のレジスタを読み取る

IRA_REG_M レジスタを読むことは I2C プロトコルの理解を確認するには良いテストでしたが、そのレジスタには面白みのない情報しか入っていません。

今回は、実際にセンサーの測定値を提供する磁力計のレジスタを読み取ります。 対象となる連続したレジスタは 6 つあり、先頭はアドレス 0x03OUT_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 軸に沿って分解したものです。

次のセクションでは、これらの数値をどう解釈するかを学びます。