Bare Metal Rust Morning Exercise

Compass

(back to exercise)

  1. #![no_main]
  2. #![no_std]
  3. extern crate panic_halt as _;
  4. use core::fmt::Write;
  5. use cortex_m_rt::entry;
  6. use core::cmp::{max, min};
  7. use embedded_hal::digital::InputPin;
  8. use lsm303agr::{
  9.     AccelMode, AccelOutputDataRate, Lsm303agr, MagMode, MagOutputDataRate,
  10. };
  11. use microbit::display::blocking::Display;
  12. use microbit::hal::twim::Twim;
  13. use microbit::hal::uarte::{Baudrate, Parity, Uarte};
  14. use microbit::hal::{Delay, Timer};
  15. use microbit::pac::twim0::frequency::FREQUENCY_A;
  16. use microbit::Board;
  17. const COMPASS_SCALE: i32 = 30000;
  18. const ACCELEROMETER_SCALE: i32 = 700;
  19. #[entry]
  20. fn main() -> ! {
  21.     let mut board = Board::take().unwrap();
  22.     // Configure serial port.
  23.     let mut serial = Uarte::new(
  24.         board.UARTE0,
  25.         board.uart.into(),
  26.         Parity::EXCLUDED,
  27.         Baudrate::BAUD115200,
  28.     );
  29.     // Use the system timer as a delay provider.
  30.     let mut delay = Delay::new(board.SYST);
  31.     // Set up the I2C controller and Inertial Measurement Unit.
  32.     writeln!(serial, "Setting up IMU...").unwrap();
  33.     let i2c = Twim::new(board.TWIM0, board.i2c_internal.into(), FREQUENCY_A::K100);
  34.     let mut imu = Lsm303agr::new_with_i2c(i2c);
  35.     imu.init().unwrap();
  36.     imu.set_mag_mode_and_odr(
  37.         &mut delay,
  38.         MagMode::HighResolution,
  39.         MagOutputDataRate::Hz50,
  40.     )
  41.     .unwrap();
  42.     imu.set_accel_mode_and_odr(
  43.         &mut delay,
  44.         AccelMode::Normal,
  45.         AccelOutputDataRate::Hz50,
  46.     )
  47.     .unwrap();
  48.     let mut imu = imu.into_mag_continuous().ok().unwrap();
  49.     // Set up display and timer.
  50.     let mut timer = Timer::new(board.TIMER0);
  51.     let mut display = Display::new(board.display_pins);
  52.     let mut mode = Mode::Compass;
  53.     let mut button_pressed = false;
  54.     writeln!(serial, "Ready.").unwrap();
  55.     loop {
  56.         // Read compass data and log it to the serial port.
  57.         while !(imu.mag_status().unwrap().xyz_new_data()
  58.             && imu.accel_status().unwrap().xyz_new_data())
  59.         {}
  60.         let compass_reading = imu.magnetic_field().unwrap();
  61.         let accelerometer_reading = imu.acceleration().unwrap();
  62.         writeln!(
  63.             serial,
  64.             "{},{},{}\t{},{},{}",
  65.             compass_reading.x_nt(),
  66.             compass_reading.y_nt(),
  67.             compass_reading.z_nt(),
  68.             accelerometer_reading.x_mg(),
  69.             accelerometer_reading.y_mg(),
  70.             accelerometer_reading.z_mg(),
  71.         )
  72.         .unwrap();
  73.         let mut image = [[0; 5]; 5];
  74.         let (x, y) = match mode {
  75.             Mode::Compass => (
  76.                 scale(-compass_reading.x_nt(), -COMPASS_SCALE, COMPASS_SCALE, 0, 4)
  77.                     as usize,
  78.                 scale(compass_reading.y_nt(), -COMPASS_SCALE, COMPASS_SCALE, 0, 4)
  79.                     as usize,
  80.             ),
  81.             Mode::Accelerometer => (
  82.                 scale(
  83.                     accelerometer_reading.x_mg(),
  84.                     -ACCELEROMETER_SCALE,
  85.                     ACCELEROMETER_SCALE,
  86.                     0,
  87.                     4,
  88.                 ) as usize,
  89.                 scale(
  90.                     -accelerometer_reading.y_mg(),
  91.                     -ACCELEROMETER_SCALE,
  92.                     ACCELEROMETER_SCALE,
  93.                     0,
  94.                     4,
  95.                 ) as usize,
  96.             ),
  97.         };
  98.         image[y][x] = 255;
  99.         display.show(&mut timer, image, 100);
  100.         // If button A is pressed, switch to the next mode and briefly blink all LEDs
  101.         // on.
  102.         if board.buttons.button_a.is_low().unwrap() {
  103.             if !button_pressed {
  104.                 mode = mode.next();
  105.                 display.show(&mut timer, [[255; 5]; 5], 200);
  106.             }
  107.             button_pressed = true;
  108.         } else {
  109.             button_pressed = false;
  110.         }
  111.     }
  112. }
  113. #[derive(Copy, Clone, Debug, Eq, PartialEq)]
  114. enum Mode {
  115.     Compass,
  116.     Accelerometer,
  117. }
  118. impl Mode {
  119.     fn next(self) -> Self {
  120.         match self {
  121.             Self::Compass => Self::Accelerometer,
  122.             Self::Accelerometer => Self::Compass,
  123.         }
  124.     }
  125. }
  126. fn scale(value: i32, min_in: i32, max_in: i32, min_out: i32, max_out: i32) -> i32 {
  127.     let range_in = max_in - min_in;
  128.     let range_out = max_out - min_out;
  129.     cap(min_out + range_out * (value - min_in) / range_in, min_out, max_out)
  130. }
  131. fn cap(value: i32, min_value: i32, max_value: i32) -> i32 {
  132.     max(min_value, min(value, max_value))
  133. }