Using a driver

As we already discussed in chapter 5 embedded-hal provides abstractions which can be used to write platform independent code that can interact with hardware. In fact all the methods we have used to interact with hardware in chapter 7 and up until now in chapter 8 were from traits, defined by embedded-hal. Now we'll make actual use of the traits embedded-hal provides for the first time.

It would be pointless to implement a driver for our LSM303AGR for every platform embedded Rust supports (and new ones that might eventually pop up). To avoid this a driver can be written that consumes generic types that implement embedded-hal traits in order to provide a platform agnostic version of a driver. Luckily for us this has already been done in the lsm303agr crate. Hence reading the actual accelerometer and magnetometer values will now be basically a plug and play experience (plus reading a bit of documentation). In fact the crates.io page already provides us with everything we need to know in order to read accelerometer data but using a Raspberry Pi. We'll just have to adapt it to our chip:

Take a look at the linked page for the Raspberry Pi Linux sample code.

Because we already know how to create an instance of an object that implements the embedded_hal::blocking::i2c traits from the previous page, adapting the sample code is straightforward (examples/show-accel.rs):

#![deny(unsafe_code)]
#![no_main]
#![no_std]

use cortex_m_rt::entry;
use panic_rtt_target as _;
use rtt_target::{rprintln, rtt_init_print};

use microbit::{
    hal::{twim, Timer},
    pac::twim0::frequency::FREQUENCY_A,
};

use lsm303agr::{AccelMode, AccelOutputDataRate, Lsm303agr};

#[entry]
fn main() -> ! {
    rtt_init_print!();
    let board = microbit::Board::take().unwrap();

    let i2c = { twim::Twim::new(board.TWIM0, board.i2c_internal.into(), FREQUENCY_A::K100) };
    let mut timer0 = Timer::new(board.TIMER0);

    // Code from documentation
    let mut sensor = Lsm303agr::new_with_i2c(i2c);
    sensor.init().unwrap();
    sensor
        .set_accel_mode_and_odr(
            &mut timer0,
            AccelMode::HighResolution,
            AccelOutputDataRate::Hz50,
        )
        .unwrap();
    loop {
        if sensor.accel_status().unwrap().xyz_new_data() {
            let (x, y, z) = sensor.acceleration().unwrap().xyz_mg();
            // RTT instead of normal print
            rprintln!("Acceleration: x {} y {} z {}", x, y, z);
        }
    }
}

Just like the last snippet you should just be able to try this out like this:

$ cargo embed --example show-accel

Furthermore if you (physically) move around your micro:bit a little you should see the acceleration numbers that are being printed change.