diff --git a/mdbook/src/09-led-compass/README.md b/mdbook/src/09-led-compass/README.md index a5b2b7d..83f920b 100644 --- a/mdbook/src/09-led-compass/README.md +++ b/mdbook/src/09-led-compass/README.md @@ -8,17 +8,21 @@ Magnetic fields have both a magnitude, measured in Gauss or Teslas, and a *direc magnetometer on the micro:bit measures both the magnitude and the direction of an external magnetic field, but it reports back the *decomposition* of said field along *its axes*. -The magnetometer has three axes associated to it. When the board is held flat with the LEDs facing -uupward and the logo facing forward, the X and Y axes span the plane that is the floor. The X axis -points to the top (logo) edge of the board. The Y axis points to the left edge. The Z axis points -"into the floor", so downwards: "upside down" since the chip is mounted on the back. This is a -"right-handed" coordinate system. It's all a bit confusing, since the reported field strengths are -differences from the magnetic field vector. - -You should already be able to write a program that continuously prints the magnetometer -data on the RTT console from the [I2C chapter](../08-i2c/index.md). After you wrote that -program, locate where north is at your current location. Then line up your micro:bit with -that direction and observe how the sensor's measurements look. +The magnetometer has three axes associated with it. When the board is held flat with the LEDs facing +uupward and the logo facing forward, the X and Y axes span the plane that is the floor. The X axis +points to the left edge of the board. The Y axis points to the bottom (card connector) edge of the +board. The Z axis points "into the floor", so downwards: "upside down" since the chip is mounted on +the back. This is a "right-handed" coordinate system. It's all a bit confusing, since the reported +field strengths are components of the magnetic field vector. + +

+ +

+ +You should already be able to write a program that continuously prints the magnetometer data on the +RTT console from the [I2C chapter](../08-i2c/index.md). After you write that program, locate where +north is at your current location. Then line up your micro:bit with that direction and observe how +the sensor's X and Y measurements look. Now rotate the board 90 degrees while keeping it parallel to the ground. What X, Y and Z values do you see this time? Then rotate it 90 degrees again. What values do you see? diff --git a/mdbook/src/10-punch-o-meter/.cargo/config.toml b/mdbook/src/10-punch-o-meter/.cargo/config.toml index 05b87e8..556174e 100644 --- a/mdbook/src/10-punch-o-meter/.cargo/config.toml +++ b/mdbook/src/10-punch-o-meter/.cargo/config.toml @@ -1,8 +1,8 @@ [build] target = "thumbv7em-none-eabihf" -[target.'cfg(all(target_arch = "arm", target_os = "none"))'] +[target.thumbv7em-none-eabihf] +runner = "probe-rs run --chip nRF52833_xxAA" rustflags = [ - "-C", "linker=rust-lld", "-C", "link-arg=-Tlink.x", ] diff --git a/mdbook/src/10-punch-o-meter/README.md b/mdbook/src/10-punch-o-meter/README.md index d5edee3..8bc6866 100644 --- a/mdbook/src/10-punch-o-meter/README.md +++ b/mdbook/src/10-punch-o-meter/README.md @@ -7,6 +7,11 @@ actually the maximum acceleration that you can reach because acceleration is wha measure. Strength and acceleration are proportional though so it's a good approximation. As we already know from previous chapters the accelerometer is built inside the LSM303AGR package. -And just like the magnetometer, it is accessible using the I2C bus. It also has the same coordinate -system as the magnetometer. +And just like the magnetometer, it is accessible using the +I2C bus. +The accelerometer also has the same coordinate system as the magnetometer. Here's a reminder: + +

+ +

diff --git a/mdbook/src/10-punch-o-meter/examples/show-accel.rs b/mdbook/src/10-punch-o-meter/examples/show-accel.rs new file mode 100644 index 0000000..a6eef9d --- /dev/null +++ b/mdbook/src/10-punch-o-meter/examples/show-accel.rs @@ -0,0 +1,37 @@ +#![deny(unsafe_code)] +#![no_main] +#![no_std] + +use cortex_m_rt::entry; +use rtt_target::{rtt_init_print, rprintln}; +use panic_rtt_target as _; + +use microbit::{ + hal::{Timer, twim}, + 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); + + let mut sensor = Lsm303agr::new_with_i2c(i2c); + sensor.init().unwrap(); + sensor.set_accel_mode_and_odr( + &mut timer0, + AccelMode::HighResolution, + AccelOutputDataRate::Hz10, + ).unwrap(); + loop { + if sensor.accel_status().unwrap().xyz_new_data() { + let (x, y, z) = sensor.acceleration().unwrap().xyz_mg(); + rprintln!("Acceleration: x {} y {} z {}", x, y, z); + } + } +} diff --git a/mdbook/src/10-punch-o-meter/gravity-is-up.md b/mdbook/src/10-punch-o-meter/gravity-is-up.md index 05c9ac8..e630679 100644 --- a/mdbook/src/10-punch-o-meter/gravity-is-up.md +++ b/mdbook/src/10-punch-o-meter/gravity-is-up.md @@ -4,25 +4,28 @@ What's the first thing we'll do? Perform a sanity check! -You should already be able to write a program that continuously prints the accelerometer -data on the RTT console from the [I2C chapter](../08-i2c/index.md). Do you observe something -interesting even when holding the board parallel to the floor with the LED side facing down? +You should already be able to write a program that continuously prints the accelerometer data on the +RTT console from the [I2C chapter](../08-i2c/index.md). Mine is in `examples/show-accel.rs`. Do you +observe something interesting even when holding the board parallel to the floor with the back side +facing up? (Remember that the accelerometer is mounted on the back of the board, so holding it +upside-down like this makes the Z axis point up.) -What you should see like this is that both the X and Y values are rather close to 0, while the -Z value is at around 1000. Which is weird because the board is not moving yet its acceleration is -non-zero. What's going on? This must be related to the gravity, right? Because the acceleration of -gravity is `1 g` (aha, `1 g` = 1000 from the accelerometer). But the gravity pulls objects downwards -so the acceleration along the Z axis should be negative not positive +What you should see when holding the board like this is that both the X and Y values are rather +close to 0, while the Z value is at around 1000. Which is weird: the board is not moving, yet its +acceleration is non-zero. What's going on? This must be related to the gravity, right? Because the +acceleration of gravity is `1 g` (aha, `1 g` = -1000 from the accelerometer). But the gravity pulls +objects downwards so the acceleration along the Z axis should be positive, not negative. Did the program get the Z axis backwards? Nope, you can test rotating the board to align the gravity to the X or Y axis but the acceleration measured by the accelerometer is always pointing up. -What happens here is that the accelerometer is measuring the *proper acceleration* of the board not +What happens here is that the accelerometer is measuring the *proper acceleration* of the board, not the acceleration *you* are observing. This proper acceleration is the acceleration of the board as seen from an observer that's in free fall. An observer that's in free fall is moving toward the center of the Earth with an acceleration of `1g`; from its point of view the board is actually moving upwards (away from the center of the Earth) with an acceleration of `1g`. And that's why the proper acceleration is pointing up. This also means that if the board was in free fall, the -accelerometer would report a proper acceleration of zero. Please, don't try that at home. +accelerometer would report a proper acceleration of zero. Please, don't try that at home. Or do, if +you're willing to risk your MB2 by dropping it. Yes, physics is hard. Let's move on. diff --git a/mdbook/src/10-punch-o-meter/my-solution.md b/mdbook/src/10-punch-o-meter/my-solution.md index 26a0604..75a0cfa 100644 --- a/mdbook/src/10-punch-o-meter/my-solution.md +++ b/mdbook/src/10-punch-o-meter/my-solution.md @@ -1,91 +1,8 @@ # My solution -``` rust -#![deny(unsafe_code)] -#![no_main] -#![no_std] - -use cortex_m_rt::entry; -use rtt_target::{rtt_init_print, rprintln}; -use panic_rtt_target as _; - -use microbit::{ - hal::twim, - pac::twim0::frequency::FREQUENCY_A, -}; - -use lsm303agr::{ - AccelScale, AccelOutputDataRate, Lsm303agr, -}; - -use microbit::hal::timer::Timer; -use microbit::hal::prelude::*; -use nb::Error; - -#[entry] -fn main() -> ! { - const THRESHOLD: f32 = 0.5; - - rtt_init_print!(); - let board = microbit::Board::take().unwrap(); +Here's my solution (`src/main.rs`). Note that you can get quite high G values by rapping the edge of +your MB2 on a table. Note also that this can break the accelerometer, so probably don't? - let i2c = { twim::Twim::new(board.TWIM0, board.i2c_internal.into(), FREQUENCY_A::K100) }; - - let mut countdown = Timer::new(board.TIMER0); - let mut delay = Timer::new(board.TIMER1); - let mut sensor = Lsm303agr::new_with_i2c(i2c); - sensor.init().unwrap(); - sensor.set_accel_odr(AccelOutputDataRate::Hz50).unwrap(); - // Allow the sensor to measure up to 16 G since human punches - // can actually be quite fast - sensor.set_accel_scale(AccelScale::G16).unwrap(); - - let mut max_g = 0.; - let mut measuring = false; - - loop { - while !sensor.accel_status().unwrap().xyz_new_data {} - // x acceleration in g - let g_x = sensor.accel_data().unwrap().x as f32 / 1000.0; - - if measuring { - // Check the status of our contdown - match countdown.wait() { - // countdown isn't done yet - Err(Error::WouldBlock) => { - if g_x > max_g { - max_g = g_x; - } - }, - // Countdown is done - Ok(_) => { - // Report max value - rprintln!("Max acceleration: {}g", max_g); - - // Reset - max_g = 0.; - measuring = false; - }, - // Since the nrf52 and nrf51 HAL have Void as an error type - // this path cannot occur, as Void is an empty type - Err(Error::Other(_)) => { - unreachable!() - } - } - } else { - // If acceleration goes above a threshold, we start measuring - if g_x > THRESHOLD { - rprintln!("START!"); - - measuring = true; - max_g = g_x; - // The documentation notes that the timer works at a frequency - // of 1 Mhz, so in order to wait for 1 second we have to - // set it to 1_000_000 ticks. - countdown.start(1_000_000_u32); - } - } - delay.delay_ms(20_u8); - } -} +``` rust +{{#include src/main.rs}} ``` diff --git a/mdbook/src/10-punch-o-meter/src/main.rs b/mdbook/src/10-punch-o-meter/src/main.rs index 9a38836..8e2979b 100644 --- a/mdbook/src/10-punch-o-meter/src/main.rs +++ b/mdbook/src/10-punch-o-meter/src/main.rs @@ -2,17 +2,74 @@ #![no_main] #![no_std] -use cortex_m::asm::wfi; +const TICKS_PER_SEC: u32 = 400; +const THRESHOLD: f32 = 1.5; + +use cortex_m::asm::nop; use cortex_m_rt::entry; +use rtt_target::{rtt_init_print, rprintln}; use panic_rtt_target as _; -use rtt_target::rtt_init_print; + +use microbit::{ + hal::{Timer, twim}, + pac::twim0::frequency::FREQUENCY_A, +}; + +use lsm303agr::{AccelMode, AccelOutputDataRate, AccelScale, Lsm303agr}; #[entry] fn main() -> ! { rtt_init_print!(); - let _board = microbit::Board::take().unwrap(); + let board = microbit::Board::take().unwrap(); + + let i2c = { twim::Twim::new(board.TWIM0, board.i2c_internal.into(), FREQUENCY_A::K100) }; + + let mut delay = Timer::new(board.TIMER0); + let mut sensor = Lsm303agr::new_with_i2c(i2c); + sensor.init().unwrap(); + sensor.set_accel_mode_and_odr( + &mut delay, + AccelMode::Normal, + AccelOutputDataRate::Hz400, + ).unwrap(); + // Allow the sensor to measure up to 16 G since human punches + // can actually be quite fast + sensor.set_accel_scale(AccelScale::G16).unwrap(); + + let mut max_g = 0.; + let mut countdown_ticks = None; loop { - wfi(); + while !sensor.accel_status().unwrap().xyz_new_data() { + nop(); + } + // x acceleration in g + let (x, _, _) = sensor.acceleration().unwrap().xyz_mg(); + let g_x = x as f32 / 1000.0; + + if let Some(ticks) = countdown_ticks { + if ticks > 0 { + // countdown isn't done yet + if g_x > max_g { + max_g = g_x; + } + countdown_ticks = Some(ticks - 1); + } else { + // Countdown is done: report max value + rprintln!("Max acceleration: {}g", max_g); + + // Reset + max_g = 0.; + countdown_ticks = None; + } + } else { + // If acceleration goes above a threshold, we start measuring + if g_x > THRESHOLD { + rprintln!("START!"); + + max_g = g_x; + countdown_ticks = Some(TICKS_PER_SEC); + } + } } } diff --git a/mdbook/src/10-punch-o-meter/the-challenge.md b/mdbook/src/10-punch-o-meter/the-challenge.md index 10b1c10..3f7e895 100644 --- a/mdbook/src/10-punch-o-meter/the-challenge.md +++ b/mdbook/src/10-punch-o-meter/the-challenge.md @@ -16,13 +16,7 @@ Here's what the punch-o-meter must do: Give it a try and let me know how hard you can punch `;-)`. -> **NOTE** There are two additional APIs that should be useful for this task we haven't discussed yet. -> First the [`set_accel_scale`] one which you need to measure high g values. -> Secondly the [`Countdown`] trait from `embedded_hal`. If you decide to use this to keep your measurement -> intervals you will have to pattern match on the [`nb::Result`] type instead of using the `block!` macro - we have seen in previous chapters. - - -[`set_accel_scale`]: https://docs.rs/lsm303agr/0.2.2/lsm303agr/struct.Lsm303agr.html#method.set_accel_scale -[`Countdown`]: https://docs.rs/embedded-hal/0.2.6/embedded_hal/timer/trait.CountDown.html -[`nb::Result`]: https://docs.rs/nb/1.0.0/nb/type.Result.html +> **NOTE** There is an additional API that should be useful for this task that we haven't +> discussed yet: the [`set_accel_scale`] one which you need to measure high g values. +> +> [`set_accel_scale`]: https://docs.rs/lsm303agr/1.1.0/lsm303agr/struct.Lsm303agr.html#method.set_accel_scale diff --git a/mdbook/src/assets/mb2-axes.jpg b/mdbook/src/assets/mb2-axes.jpg new file mode 100644 index 0000000..a9a875c Binary files /dev/null and b/mdbook/src/assets/mb2-axes.jpg differ