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

Simple Rust Program to Read Values from the Accelerometer

Let's start by writing a simple Rust program that reads and prints values from the accelerometer. We will display the acceleration along the X, Y, and Z axes, expressed in milligravity (mg) units. We will be print the readings to the system console.

In later chapters, we will explore some fun exercises using these values. For now, let's get started!

Create Project from template

For this exercise, we will be using the microbit-bsp crate with Embassy support. Technically, we could work directly with the embassy-nrf crate, as it is sufficient for this simple example. However, in the next chapter, we will play a sound or display something on the LED matrix. For that, we will rely on the extra features provided by microbit-bsp. So we are layering things now to make those future tasks easier.

To generate a new project using the template, run the following command:

cargo generate --git https://github.com/lulf/embassy-template.git -r f3179dc

You will be prompted to enter a project name.

After that, you will be asked to select the target microcontroller (MCU). From the list, choose:

nrf52833

Update Cargo.toml

We will use the microbit-bsp crate. Open the Cargo.toml file and add the following lines:

# microbit-bsp = "0.3.0"
microbit-bsp = { git = "https://github.com/lulf/microbit-bsp", rev = "9c7d52e" }
lsm303agr = { version = "1.1.0", features = ["async"] }

The "lsm303agr" crate is a new dependency we are introducing. It is a platform-agnostic Rust driver for the LSM303AGR sensor, providing a convenient set of functions to read data from the device. We have enabled the "async" feature also for the driver.

Update the imports

Before we get started, let's update the imports needed for the I2C peripheral, timing, and the LSM303AGR sensor driver:

#![allow(unused)]
fn main() {
use defmt_rtt as _;
use embassy_executor::Spawner;
use embassy_time::{Delay, Duration, Timer};

// for I2C
use embassy_nrf::{self as hal, twim::Twim};
use hal::twim;

// LSM303AGR Driver
use lsm303agr::{AccelMode, AccelOutputDataRate, Lsm303agr};

// BSP Crate
use microbit_bsp::Microbit;
}

Initialize the Board

As usual, start by clearing out any existing lines inside the main function that come with the template. Then, add the following line to initialize the board:

#![allow(unused)]
fn main() {
let board = Microbit::default();
}

I2C Interrupt

We previously discussed interrupts in the Temperature Sensor chapter, so by now, you should have a basic understanding of how they work. For I2C communication, we need to enable the interrupt associated with the TWISPI0 peripheral.

TWISPI0 is a shared hardware peripheral on the nRF52833 that can function as either an I2C (TWI) or SPI interface, depending on how it's configured.

To do this, we use the bind_interrupts! macro from the embassy-nrf crate. This macro defines a unit struct named "Irqs" and connects the TWISPI0 interrupt to the appropriate handler. The macro also implements the required Binding trait for Irqs, which ensures a compile-time type check that the correct interrupt is configured. (If you are not sure what we are talking about here, refer to the Expanding bind_interrupts! Macro section.)

#![allow(unused)]
fn main() {
hal::bind_interrupts!(struct Irqs {
    TWISPI0 => twim::InterruptHandler<hal::peripherals::TWISPI0>;
});
}