Skip to content

Commit

Permalink
implement embedded-hal-async traits using maybe-async-cfg (#31)
Browse files Browse the repository at this point in the history
* implement embedded-hal-async traits using maybe-async-cfg

* Run rustfmt

* Have async variant of Magnetometer<_, MagContinuous>::magnetic_field return Poll::pending instead of nb::WouldBlock in case no data is ready

* Add example for microbit V2 on embassy

* Update to rust 1.75 for CI build, 1.79 for clippy

* Always enable async feature of embedded-hal-mock

* Clarify changelog entry and comment in linker script

Co-authored-by: Diego Barrios Romero <eldruin@gmail.com>

* Add reference to microbit-v2 example in README

* Add reference to microbit-v2 example in rustdoc

---------

Co-authored-by: Diego Barrios Romero <eldruin@gmail.com>
  • Loading branch information
hdoordt and eldruin committed Jun 24, 2024
1 parent 322cb03 commit 9a3666e
Show file tree
Hide file tree
Showing 14 changed files with 408 additions and 125 deletions.
8 changes: 8 additions & 0 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
## Settings for the micro:bit V2 example

[target.'cfg(all(target_arch = "arm", target_os = "none"))']
runner = "probe-rs run --chip nRF52833_xxAA --catch-reset --catch-hardfault --allow-erase-all"

rustflags = [
"-C", "link-arg=-Tlink.x",
]
4 changes: 2 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
rust: [stable, 1.60.0]
rust: [stable, 1.75.0]
TARGET:
- x86_64-unknown-linux-gnu
- x86_64-unknown-linux-musl
Expand Down Expand Up @@ -63,7 +63,7 @@ jobs:
- uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@master
with:
toolchain: 1.75.0
toolchain: 1.79.0
targets: x86_64-unknown-linux-gnu
components: clippy

Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
<!-- next-header -->
## [Unreleased] - ReleaseDate

### Added
- Add support for async behind the `async` feature flag

### Changed
- Update MSRV to 1.75.0

## [1.0.0] - 2024-01-22

### Changed
Expand Down
30 changes: 28 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,38 @@ edition = "2021"
embedded-hal = "1.0.0"
nb = "1.1"
bitflags = "2.3.3"
embedded-hal-async = { version = "1.0.0", optional = true }
maybe-async-cfg = "0.2.4"

[dev-dependencies]
embedded-hal-mock = { version = "0.10", default-features = false, features = ["eh1"] }
# Test dependencies
[target.'cfg(not(target_os = "none"))'.dev-dependencies]
embedded-hal-mock = { version = "0.11", default-features = false, features = ["eh1", "embedded-hal-async"] }

# Dependencies for the linux example
[target.'cfg(target_os = "linux")'.dev-dependencies]
linux-embedded-hal = "0.4.0"

# Dependencies for the microbit-v2 example
[target.'cfg(target_os = "none")'.dev-dependencies]
cortex-m = { version = "0.7.7", features = ["critical-section-single-core"] }
cortex-m-rt = "0.7.3"
embassy-nrf = { version = "0.1.0", features = ["nrf52833", "time-driver-rtc1", "time"] }
rtt-target = "0.5.0"
embedded-hal-async = "1.0.0"
embassy-executor = { version = "0.5.0", features = ["arch-cortex-m", "executor-thread", "integrated-timers"] }
embassy-time = "0.3.1"
panic-rtt-target = "0.1.3"

[features]
async = ["dep:embedded-hal-async"]
# Here just so `cargo test` doesn't invoke a build of the microbit-v2 example
microbit-example = []

[profile.release]
lto = true

[[example]]
name = "microbit-v2"
test = false
bench = false
required-features = ["async", "microbit-example"]
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
This is a platform agnostic Rust driver for the LSM303AGR ultra-compact
high-performance eCompass module: ultra-low-power 3D accelerometer and
3D magnetometer using the [`embedded-hal`] traits.
This driver also supports the [`embedded-hal-async`] traits if the `async` feature is enabled.

This driver allows you to:
- Connect through I2C or SPI. See: `new_with_i2c()`.
Expand Down Expand Up @@ -86,6 +87,8 @@ fn main() {
}
```

For an example of using the async support of this driver on a micro:bit V2, have a look at [microbit-v2 example](./examples/microbit-v2.rs).

## Support

For questions, issues, feature requests, and other changes, please file an
Expand Down
74 changes: 74 additions & 0 deletions examples/microbit-v2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#![no_main]
#![no_std]

//! Example of using the async feature of the LSM303AGR driver.
//! Uses [embassy](https://embassy.dev) for the HAL and the executor.
//!
//! Make sure [probe-rs](https://probe.rs) is installed.
//!
//! Run me using
//!
//! ```sh
//! cargo run --example microbit-v2 --target thumbv7em-none-eabihf --features async,microbit-example
//! ```

use embassy_nrf::{self as hal, twim::Twim};
use embassy_time::Delay;
use embedded_hal_async::delay::DelayNs;
use hal::twim;
use lsm303agr::Lsm303agr;
use rtt_target::{rprintln, rtt_init_print};

use panic_rtt_target as _; // Panic handler

hal::bind_interrupts!(struct Irqs {
SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0 => twim::InterruptHandler<hal::peripherals::TWISPI0>;
});

#[embassy_executor::main]
async fn main(_s: embassy_executor::Spawner) -> ! {
// Init RTT control block
rtt_init_print!();

let _cp = cortex_m::Peripherals::take().unwrap();
// Use ``dp` to get a handle to the peripherals
let dp = hal::init(Default::default());

rprintln!("Starting");

let config = twim::Config::default();
let twim0 = Twim::new(dp.TWISPI0, Irqs, dp.P0_16, dp.P0_08, config);

let mut sensor = Lsm303agr::new_with_i2c(twim0);
let id = sensor.magnetometer_id().await.unwrap();
rprintln!("{:#02x?}", id);

sensor.init().await.unwrap();
sensor
.set_mag_mode_and_odr(
&mut Delay,
lsm303agr::MagMode::HighResolution,
lsm303agr::MagOutputDataRate::Hz10,
)
.await
.unwrap();

let Ok(mut sensor) = sensor.into_mag_continuous().await else {
panic!("Error enabling continuous mode")
};
sensor.mag_enable_low_pass_filter().await.unwrap();
loop {
if sensor.mag_status().await.unwrap().xyz_new_data() {
let data = sensor.magnetic_field().await.unwrap();
rprintln!(
"Magnetic field: x {} y {} z {}",
data.x_nt(),
data.y_nt(),
data.z_nt()
);
} else {
rprintln!("No data")
}
Delay.delay_ms(200).await;
}
}
7 changes: 7 additions & 0 deletions memory.x
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Linker script for the nRF52833 MCU on the microbit:v2 necessary for the example. Please disregard it otherwise.
MEMORY
{
/* NOTE K = KiBi = 1024 bytes */
FLASH : ORIGIN = 0x00000000, LENGTH = 512K
RAM : ORIGIN = 0x20000000, LENGTH = 128K
}
31 changes: 21 additions & 10 deletions src/accel_mode_and_odr.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
use embedded_hal::delay::DelayNs;
use maybe_async_cfg::maybe;

use crate::{
interface::{ReadData, WriteData},
register_address::{CtrlReg1A, CtrlReg4A},
AccelMode, AccelOutputDataRate, AccelScale, Error, Lsm303agr,
};

#[maybe(
sync(cfg(not(feature = "async")), keep_self,),
async(cfg(feature = "async"), keep_self,)
)]
impl<DI, CommE, MODE> Lsm303agr<DI, MODE>
where
DI: ReadData<Error = Error<CommE>> + WriteData<Error = Error<CommE>>,
Expand All @@ -15,8 +20,14 @@ where
/// Returns `Error::InvalidInputData` if the mode is incompatible with
/// the given output data rate.
///
#[doc = include_str!("delay.md")]
pub fn set_accel_mode_and_odr<D: DelayNs>(
/// The given `delay` is used to wait for the sensor to turn on or change modes,
/// according to the times specified in Table 14 and Table 15 in the [datasheet].
/// You can opt out of this by using a no-op delay implementation, see
/// [`embedded_hal_mock::delay::MockNoop`] for an example of such an
/// implementation.
/// [datasheet]: https://www.st.com/resource/en/datasheet/lsm303agr.pdf
/// [`embedded_hal_mock::delay::MockNoop`]: https://docs.rs/embedded-hal-mock/latest/embedded_hal_mock/delay/struct.MockNoop.html
pub async fn set_accel_mode_and_odr<D: DelayNs>(
&mut self,
delay: &mut D,
mode: AccelMode,
Expand All @@ -26,7 +37,7 @@ where

check_accel_odr_is_compatible_with_mode(odr, mode)?;

let old_mode = self.get_accel_mode();
let old_mode = self.get_accel_mode().await;

let mut reg1 = self.ctrl_reg1_a.difference(CtrlReg1A::ODR);

Expand All @@ -43,17 +54,17 @@ where
let reg4 = self.ctrl_reg4_a.difference(CtrlReg4A::HR);

if mode != AccelMode::HighResolution {
self.iface.write_accel_register(reg4)?;
self.iface.write_accel_register(reg4).await?;
self.ctrl_reg4_a = reg4;
}

self.iface.write_accel_register(reg1)?;
self.iface.write_accel_register(reg1).await?;
self.ctrl_reg1_a = reg1;
self.accel_odr = odr;

if mode == AccelMode::HighResolution {
let reg4 = reg4.union(CtrlReg4A::HR);
self.iface.write_accel_register(reg4)?;
self.iface.write_accel_register(reg4).await?;
self.ctrl_reg4_a = reg4;
}

Expand All @@ -66,7 +77,7 @@ where
}

/// Get the accelerometer mode
pub fn get_accel_mode(&mut self) -> AccelMode {
pub async fn get_accel_mode(&mut self) -> AccelMode {
let power_down = self.ctrl_reg1_a.intersection(CtrlReg1A::ODR).is_empty();
let lp_enabled = self.ctrl_reg1_a.contains(CtrlReg1A::LPEN);
let hr_enabled = self.ctrl_reg4_a.contains(CtrlReg4A::HR);
Expand All @@ -87,15 +98,15 @@ where
/// This changes the scale at which the acceleration is read.
/// `AccelScale::G2` for example can return values between -2g and +2g
/// where g is the gravity of the earth (~9.82 m/s²).
pub fn set_accel_scale(&mut self, scale: AccelScale) -> Result<(), Error<CommE>> {
pub async fn set_accel_scale(&mut self, scale: AccelScale) -> Result<(), Error<CommE>> {
let reg4 = self.ctrl_reg4_a.with_scale(scale);
self.iface.write_accel_register(reg4)?;
self.iface.write_accel_register(reg4).await?;
self.ctrl_reg4_a = reg4;
Ok(())
}

/// Get accelerometer scaling factor
pub fn get_accel_scale(&self) -> AccelScale {
pub async fn get_accel_scale(&self) -> AccelScale {
self.ctrl_reg4_a.scale()
}
}
Expand Down
8 changes: 0 additions & 8 deletions src/delay.md

This file was deleted.

Loading

0 comments on commit 9a3666e

Please sign in to comment.