diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..e69de29 diff --git a/Cargo.toml b/Cargo.toml index a5b6385..946ad77 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,29 +8,30 @@ repository = "git@github.com:rland93/bmi088-rs.git" license = "MPL 2.0" description = "embedded-hal Rust driver for the Bosch Sensortec BMI088 inertial measurement unit (IMU)." readme = "README.md" -keywords = ["inertial", "accelerometer", "gyroscope", "imu", "embedded-hal-driver"] +keywords = [ + "inertial", + "accelerometer", + "gyroscope", + "imu", + "embedded-hal-driver", +] categories = ["embedded", "hardware-support", "no-std"] homepage = "https://github.com/rland93/bmi088-rs" -include = [ - "/**/*.rs", - "/Cargo.toml", - "/README.md", - "/CHANGELOG.md", - "/LICENSE", -] - +include = ["/**/*.rs", "/Cargo.toml", "/README.md", "/CHANGELOG.md", "/LICENSE"] [dependencies] embedded-hal = "1.0.0" +defmt = "0.3" # Necessary to load the example code. [dev-dependencies] -embedded-hal-bus = "0.1" +embedded-hal-bus = "0.2" defmt = "0.3" defmt-rtt = "0.4" -cortex-m = { version = "0.7", features = ["critical-section-single-core"]} +cortex-m = { version = "0.7", features = ["critical-section-single-core"] } cortex-m-rt = "0.7" -# For the i2c example, we need to use the stm32f411 feature. -# For the spi example, we need to use the stm32f401 feature. -stm32f4xx-hal = {version = "0.20", features = ["stm32f401"]} -panic-probe = {version = "0.3", features = ["print-defmt"]} \ No newline at end of file +stm32f4xx-hal = { version = "0.20", features = ["stm32f401", "rtic2"] } +panic-probe = { version = "0.3", features = ["print-defmt"] } +rtic = { version = "2.1.1", features = ["thumbv7-backend"] } +rtic-monotonics = { version = "2.0", features = ["cortex-m-systick"] } +rtic-monotonic = "1.0.0" diff --git a/README.md b/README.md index b58cf53..1fe9d07 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ Thank you to [@eldruin](https://github.com/eldruin/bmi160-rs). The design of thi - [x] Interrupt Configuration (3/4) - [x] Status Registers - [ ] Self Test - - [ ] Example Code + - [x] Example Code - Misc. - [ ] Publish on crates.io @@ -40,17 +40,36 @@ Thank you to [@eldruin](https://github.com/eldruin/bmi160-rs). The design of thi ## Example Code -There are two examples: +To load the example code, create your `.cargo` configuration: + +```toml +[target.thumbv7em-none-eabihf] +rustflags = [ + # --- KEEP existing `link-arg` flags --- + "-C", "link-arg=-Tlink.x", + "-C", "link-arg=--nmagic", + # --- ADD following new flag --- + "-C", "link-arg=-Tdefmt.x", +] + +[build] +target = "thumbv7em-none-eabihf" -- examples/stm32f411-i2c.rs -- examples/stm32f401-spi.rs +[env] +DEFMT_LOG = "trace" -Those are also the hardware test code. +[target.'cfg(all(target_arch = "arm", target_os = "none"))'] +runner = "probe-run --chip STM32F401RCTx" +``` -You can run them with any probe-rs compatible debug probe. +Replacing your board setup, of course. By default, the examples use SPI3: -The i2c example code can be built on an STM32F411RETx "Black Pill" board: https://stm32-base.org/boards/STM32F411CEU6-WeAct-Black-Pill-V2.0.html. Connect the sensor to i2c1. +- SCK: pc10 +- MISO: pc11 +- MOSI: pc12 +- Acc CS: pd2 +- Gyro CS: pb5 -I am not sure if there is a similarly cheap/easily accessible STM32F401 based dev board but you can modify the example to suit your needs. Be sure to modify memory.x and .cargo/config.toml. +However, you can easily replace the pin and peripheral setup with your own. -Or any other STM32 hardware can be used with minor modifications to the target, memory.x, and probe-rs settings. +You should be able to compile and run the code examples by doing `cargo run`. \ No newline at end of file diff --git a/examples/drdy.rs b/examples/drdy.rs new file mode 100644 index 0000000..5f53f29 --- /dev/null +++ b/examples/drdy.rs @@ -0,0 +1,202 @@ +#![no_main] +#![no_std] +#![feature(type_alias_impl_trait)] +/// In this example, we configure the BMI088 IMU to trigger an interrupt on the +/// DRDY pin when a new reading appears on the sensor. The interrupt is mapped +/// to the EXTI0 line on the STM32F401. The interrupt is configured to trigger +/// on a falling edge. +/// +use defmt_rtt as _; +use embedded_hal_bus as ebus; +use panic_probe as _; +use stm32f4xx_hal::{gpio, pac, prelude::*, spi}; + +type GyroDev = ebus::spi::AtomicDevice< + 'static, + spi::Spi, + gpio::Pin<'B', 5, gpio::Output>, + ebus::spi::NoDelay, +>; +type AccelDev = ebus::spi::AtomicDevice< + 'static, + spi::Spi, + gpio::Pin<'D', 2, gpio::Output>, + ebus::spi::NoDelay, +>; +type Spi3Bus = ebus::util::AtomicCell>; +static mut SPI3BUS: Option = None; + +use rtic_monotonics::systick_monotonic; +systick_monotonic!(Mono, 1000); + +use bmi088::interface; +use bmi088::Bmi088; +use rtic_monotonics::Monotonic; + +#[rtic::app(device = stm32f4xx_hal::pac, peripherals = true)] +mod app { + + use super::*; + + #[shared] + struct Shared { + dev_accel: Bmi088>, + dev_gyro: Bmi088>, + } + + #[local] + struct Local { + imu_drdy: gpio::Pin<'C', 0, gpio::Input>, + } + + #[init] + fn init(cx: init::Context) -> (Shared, Local) { + let mut dp = cx.device; + let rcc = dp.RCC.constrain(); + let hse = 12.MHz(); + let sysclk = 64.MHz(); + let clocks = rcc + .cfgr + .use_hse(hse) + .sysclk(sysclk) + .require_pll48clk() + .freeze(); + + Mono::start(cx.core.SYST, sysclk.to_Hz()); + + let mut syscfg = dp.SYSCFG.constrain(); + + let gpiob = dp.GPIOB.split(); + let gpioc = dp.GPIOC.split(); + let gpiod = dp.GPIOD.split(); + + defmt::info!("spi"); + let dev_spi: spi::Spi3 = spi::Spi3::new( + dp.SPI3, + ( + gpioc.pc10.into_alternate(), + gpioc.pc11.into_alternate(), + gpioc.pc12.into_alternate(), + ), + spi::Mode { + polarity: spi::Polarity::IdleLow, + phase: spi::Phase::CaptureOnFirstTransition, + }, + 8.MHz(), + &clocks, + ); + let mut acc_cs = gpiod.pd2.into_push_pull_output(); + let mut gyr_cs = gpiob.pb5.into_push_pull_output(); + acc_cs.set_high(); + gyr_cs.set_high(); + + let bus = unsafe { + SPI3BUS = Some(ebus::util::AtomicCell::new(dev_spi)); + SPI3BUS.as_ref().unwrap() + }; + + let mut dev_accel = + Bmi088::new_with_spi(ebus::spi::AtomicDevice::new_no_delay(&bus, acc_cs).unwrap()); + let mut dev_gyro = + Bmi088::new_with_spi(ebus::spi::AtomicDevice::new_no_delay(&bus, gyr_cs).unwrap()); + + defmt::info!( + "accel: {:x}, gyro: {:x}", + dev_accel.acc_chip_id_read().unwrap(), + dev_gyro.gyro_chip_id_read().unwrap() + ); + + // configure + let acc_config = bmi088::AccConfiguration::builder() + .acc_power_conf(bmi088::AccPowerConf::Active) + .acc_power_ctrl(bmi088::AccPowerCtrl::On) + .acc_bandwidth((bmi088::AccBandwidth::X4, bmi088::AccDataRate::Hz50)) + .acc_range(bmi088::AccRange::G3) + .acc_int2(bmi088::AccIntConfiguration { + int_pin: bmi088::IntPin::Output, + int_od: bmi088::PinBehavior::PushPull, + int_lvl: bmi088::PinActive::ActiveLow, + }) + .acc_int_map(bmi088::AccIntMap { + int1: bmi088::types::acc::IntMap::None, + int2: bmi088::types::acc::IntMap::Drdy, + }) + .build(); + + let gyro_config = bmi088::GyroConfiguration::builder() + .bandwidth(bmi088::types::gyro::GyroBandwidth::Hz64) + .power_conf(bmi088::types::gyro::GyroPowerConf::Normal) + .range(bmi088::types::gyro::GyroRange::Dps250) + .build(); + + dev_accel.configure_accelerometer(acc_config).unwrap(); + + // print configuration values + let mode = dev_accel.acc_conf_read().unwrap(); + defmt::debug!("acc mode CONF: {:?}", defmt::Debug2Format(&mode)); + let fifoconfig1 = dev_accel.acc_fifo_config1_read().unwrap(); + defmt::debug!("acc config1 CONF: {:?}", defmt::Debug2Format(&fifoconfig1)); + let downs = dev_accel.acc_fifo_downs_read().unwrap(); + defmt::debug!("acc downs CONF: {:?}", defmt::Debug2Format(&downs)); + let fifomode = dev_accel.acc_fifo_mode_read().unwrap(); + defmt::debug!("acc config0 CONF: {:?}", defmt::Debug2Format(&fifomode)); + let wtm = dev_accel.acc_fifo_wtm_read().unwrap(); + defmt::debug!("acc wtm CONF: {:?}", defmt::Debug2Format(&wtm)); + let int1 = dev_accel.acc_int1_io_ctrl_read().unwrap(); + defmt::debug!("acc int1 CONF: {:?}", defmt::Debug2Format(&int1)); + let int2 = dev_accel.acc_int2_io_ctrl_read().unwrap(); + defmt::debug!("acc int2 CONF: {:?}", defmt::Debug2Format(&int2)); + let int_map = dev_accel.acc_int1_int2_map_data_read().unwrap(); + defmt::debug!("acc int_map CONF: {:?}", defmt::Debug2Format(&int_map)); + let pwr_conf = dev_accel.acc_pwr_conf_read().unwrap(); + defmt::debug!("acc pwr_conf CONF: {:?}", defmt::Debug2Format(&pwr_conf)); + let pwr_ctrl = dev_accel.acc_pwr_ctrl_read().unwrap(); + defmt::debug!("acc pwr_ctrl CONF: {:?}", defmt::Debug2Format(&pwr_ctrl)); + let range = dev_accel.acc_range_read().unwrap(); + defmt::debug!("acc range CONF: {:?}", defmt::Debug2Format(&range)); + + dev_gyro.configure_gyro(gyro_config).unwrap(); + + let bandwith = dev_gyro.gyro_bandwidth_read().unwrap(); + defmt::debug!("gyro bandwith CONF: {:?}", defmt::Debug2Format(&bandwith)); + let power = dev_gyro.gyro_lpm_read().unwrap(); + defmt::debug!("gyro power CONF: {:?}", defmt::Debug2Format(&power)); + let range = dev_gyro.gyro_range_read().unwrap(); + defmt::debug!("gyro range CONF: {:?}", defmt::Debug2Format(&range)); + + // set up interrupt + let mut imu_drdy = gpioc.pc0.internal_pull_up(true); + imu_drdy.make_interrupt_source(&mut syscfg); + imu_drdy.trigger_on_edge(&mut dp.EXTI, gpio::Edge::Falling); + imu_drdy.enable_interrupt(&mut dp.EXTI); + + defmt::info!("setup done"); + + ( + Shared { + dev_accel, + dev_gyro, + }, + Local { imu_drdy }, + ) + } + + // Interrupt + #[task(binds = EXTI0, shared=[dev_accel, dev_gyro], local=[imu_drdy])] + fn imu_drdy(cx: imu_drdy::Context) { + cx.local.imu_drdy.clear_interrupt_pending_bit(); + sensor_process::spawn().ok(); + } + + //Task to read data from the IMU + #[task(shared=[dev_accel, dev_gyro])] + async fn sensor_process(mut cx: sensor_process::Context) { + let acc_data = cx.shared.dev_accel.lock(|a| a.acc_data().unwrap()); + let gyro_data = cx.shared.dev_gyro.lock(|g| g.gyro_rate_read().unwrap()); + defmt::info!( + "acc: {:?}, gyro: {:?}", + defmt::Debug2Format(&acc_data), + defmt::Debug2Format(&gyro_data) + ); + } +} diff --git a/examples/fifo_poll.rs b/examples/fifo_poll.rs new file mode 100644 index 0000000..404a049 --- /dev/null +++ b/examples/fifo_poll.rs @@ -0,0 +1,213 @@ +#![no_main] +#![no_std] +#![feature(type_alias_impl_trait)] +/// In this example, we configure the BMI088 IMU to store readings in the FIFO. +/// We poll the FIFO to read out new readings in bulk. +/// +use defmt_rtt as _; +use embedded_hal_bus as ebus; +use panic_probe as _; +use stm32f4xx_hal::{gpio, pac, prelude::*, spi}; + +type GyroDev = ebus::spi::AtomicDevice< + 'static, + spi::Spi, + gpio::Pin<'B', 5, gpio::Output>, + ebus::spi::NoDelay, +>; +type AccelDev = ebus::spi::AtomicDevice< + 'static, + spi::Spi, + gpio::Pin<'D', 2, gpio::Output>, + ebus::spi::NoDelay, +>; +type Spi3Bus = ebus::util::AtomicCell>; +static mut SPI3BUS: Option = None; + +use rtic_monotonics::{systick_monotonic, Monotonic}; +systick_monotonic!(Mono, 1000); + +use bmi088::Bmi088; + +#[rtic::app(device = stm32f4xx_hal::pac, peripherals = true, dispatchers = [EXTI1])] +mod app { + + use super::*; + + #[shared] + struct Shared { + dev_accel: Bmi088>, + dev_gyro: Bmi088>, + } + + #[local] + struct Local {} + + #[init] + fn init(cx: init::Context) -> (Shared, Local) { + let dp = cx.device; + let rcc = dp.RCC.constrain(); + let hse = 12.MHz(); + let sysclk = 64.MHz(); + let clocks = rcc + .cfgr + .use_hse(hse) + .sysclk(sysclk) + .require_pll48clk() + .freeze(); + + Mono::start(cx.core.SYST, sysclk.to_Hz()); + + let gpiob = dp.GPIOB.split(); + let gpioc = dp.GPIOC.split(); + let gpiod = dp.GPIOD.split(); + + defmt::debug!("spi setup"); + let dev_spi: spi::Spi3 = spi::Spi3::new( + dp.SPI3, + ( + gpioc.pc10.into_alternate(), + gpioc.pc11.into_alternate(), + gpioc.pc12.into_alternate(), + ), + spi::Mode { + polarity: spi::Polarity::IdleLow, + phase: spi::Phase::CaptureOnFirstTransition, + }, + 8.MHz(), + &clocks, + ); + let mut acc_cs = gpiod.pd2.into_push_pull_output(); + let mut gyr_cs = gpiob.pb5.into_push_pull_output(); + acc_cs.set_high(); + gyr_cs.set_high(); + + let bus = unsafe { + SPI3BUS = Some(ebus::util::AtomicCell::new(dev_spi)); + SPI3BUS.as_ref().unwrap() + }; + + let mut dev_accel = + Bmi088::new_with_spi(ebus::spi::AtomicDevice::new_no_delay(&bus, acc_cs).unwrap()); + let mut dev_gyro = + Bmi088::new_with_spi(ebus::spi::AtomicDevice::new_no_delay(&bus, gyr_cs).unwrap()); + + defmt::info!( + "accel: {:x}, gyro: {:x}", + dev_accel.acc_chip_id_read().unwrap(), + dev_gyro.gyro_chip_id_read().unwrap() + ); + + // configure + let config = bmi088::AccConfiguration::builder() + .acc_power_conf(bmi088::AccPowerConf::Active) + .acc_power_ctrl(bmi088::AccPowerCtrl::On) + .acc_bandwidth((bmi088::AccBandwidth::X4, bmi088::AccDataRate::Hz400)) + .acc_range(bmi088::AccRange::G3) + // stream mode - discards old data from the queue + .acc_fifo_mode(bmi088::AccFifoMode::Stream) + // enable fifo + .acc_fifo_conf1(bmi088::AccFifoConfig1 { + acc_en: true, + int1_input_en: false, + int2_input_en: false, + }) + // no downsampling + .acc_fifo_downs(0) + .build(); + + dev_accel.configure_accelerometer(config).unwrap(); + + let mode = dev_accel.acc_conf_read().unwrap(); + defmt::debug!("mode CONF: {:?}", defmt::Debug2Format(&mode)); + let fifoconfig1 = dev_accel.acc_fifo_config1_read().unwrap(); + defmt::debug!("config1 CONF: {:?}", defmt::Debug2Format(&fifoconfig1)); + let downs = dev_accel.acc_fifo_downs_read().unwrap(); + defmt::debug!("downs CONF: {:?}", defmt::Debug2Format(&downs)); + let fifomode = dev_accel.acc_fifo_mode_read().unwrap(); + defmt::debug!("config0 CONF: {:?}", defmt::Debug2Format(&fifomode)); + let wtm = dev_accel.acc_fifo_wtm_read().unwrap(); + defmt::debug!("wtm CONF: {:?}", defmt::Debug2Format(&wtm)); + let int1 = dev_accel.acc_int1_io_ctrl_read().unwrap(); + defmt::debug!("int1 CONF: {:?}", defmt::Debug2Format(&int1)); + let int2 = dev_accel.acc_int2_io_ctrl_read().unwrap(); + defmt::debug!("int2 CONF: {:?}", defmt::Debug2Format(&int2)); + let int_map = dev_accel.acc_int1_int2_map_data_read().unwrap(); + defmt::debug!("int_map CONF: {:?}", defmt::Debug2Format(&int_map)); + let pwr_conf = dev_accel.acc_pwr_conf_read().unwrap(); + defmt::debug!("pwr_conf CONF: {:?}", defmt::Debug2Format(&pwr_conf)); + let pwr_ctrl = dev_accel.acc_pwr_ctrl_read().unwrap(); + defmt::debug!("pwr_ctrl CONF: {:?}", defmt::Debug2Format(&pwr_ctrl)); + let range = dev_accel.acc_range_read().unwrap(); + defmt::debug!("range CONF: {:?}", defmt::Debug2Format(&range)); + + defmt::info!("setup done"); + + sensor_process::spawn().ok(); + + ( + Shared { + dev_accel, + dev_gyro, + }, + Local {}, + ) + } + + // polling task to read out the FIFO. + #[task(priority=1, shared=[dev_accel, dev_gyro])] + async fn sensor_process(mut cx: sensor_process::Context) { + loop { + let now = Mono::now(); + + // burst read into the queue + let mut buf = [0u8; 1024]; + cx.shared.dev_accel.lock(|a| { + a.acc_fifo_data(&mut buf).unwrap(); + }); + + let mut header: bmi088::AccFifoFrameHeader; + let mut cursor = 0; + let mut frames_read: usize = 0; + while cursor + 7 < buf.len() && frames_read < 40 { + header = bmi088::AccFifoFrameHeader::from(buf[cursor]); + + match header { + bmi088::AccFifoFrameHeader::Acceleration(_tags) => { + frames_read += 1; + let d = bmi088::Sensor3DData::from_le_slice( + buf[cursor + 1..cursor + 7].try_into().unwrap(), + ); + defmt::debug!("[{},{},{}]", d.x, d.y, d.z); + } + bmi088::AccFifoFrameHeader::End => { + break; + } + _ => {} + } + + let n = header.bytes_to_read(); + cursor += n + 1; + } + defmt::warn!("frames read: {}", frames_read); + + // Adjust based on the number of frames read out and the sample + // rate. At 400Hz, we have 2.5ms per sample, therefore we should + // see 10 frames read out every time this loop runs. + Mono::delay_until(now + 25.millis()).await; + } + } + + #[idle(shared=[dev_accel])] + fn idle(mut cx: idle::Context) -> ! { + loop { + let len = cx + .shared + .dev_accel + .lock(|a| a.acc_fifo_length_read().unwrap()); + defmt::info!("len: {}", len); + Mono::delay_ms(&mut Mono, 10); + cortex_m::asm::nop(); + } + } +} diff --git a/examples/stm32f401-spi.rs b/examples/stm32f401-spi.rs deleted file mode 100644 index ac7f95d..0000000 --- a/examples/stm32f401-spi.rs +++ /dev/null @@ -1,198 +0,0 @@ -#![no_main] -#![no_std] - -use core::cell::RefCell; - -use bmi088::Bmi088; -use cortex_m_rt::entry; -use defmt::{debug, info}; -use defmt_rtt as _; -use embedded_hal_bus::spi::RefCellDevice; -use panic_probe as _; -use stm32f4xx_hal::{prelude::*, spi, spi::Spi3}; - -#[entry] -fn main() -> ! { - let dp = stm32f4xx_hal::pac::Peripherals::take().unwrap(); - let _cp = cortex_m::Peripherals::take().unwrap(); - - let rcc = dp.RCC.constrain(); - let clocks = rcc.cfgr.freeze(); - - let gpiob = dp.GPIOB.split(); - let gpioc = dp.GPIOC.split(); - let gpiod = dp.GPIOD.split(); - - let mut delay = dp.TIM2.delay_ms(&clocks); - - info!("spi"); - - let spi = Spi3::new( - dp.SPI3, - ( - gpioc.pc10.into_alternate(), - gpioc.pc11.into_alternate(), - gpioc.pc12.into_alternate(), - ), - spi::Mode { - polarity: spi::Polarity::IdleLow, - phase: spi::Phase::CaptureOnFirstTransition, - }, - 1.MHz(), - &clocks, - ); - - let rc_spi = RefCell::new(spi); - let gyro_cs = gpiob.pb5.into_push_pull_output(); - let acc_cs = gpiod.pd2.into_push_pull_output(); - - let delay1 = dp.TIM10.delay_ms(&clocks); - let delay2 = dp.TIM11.delay_ms(&clocks); - - let gyr_dev = RefCellDevice::new(&rc_spi, gyro_cs, delay1); - let acc_dev = RefCellDevice::new(&rc_spi, acc_cs, delay2); - - // initialize the sensor - let mut gyr_dev = Bmi088::new_with_spi(gyr_dev); - let mut acc_dev = Bmi088::new_with_spi(acc_dev); - - // Perform resets - info!("Accelerometer soft reset"); - gyr_dev.acc_soft_reset().unwrap(); - delay.delay_ms(5); - - info!("Gyro soft reset"); - gyr_dev.gyro_soft_reset().unwrap(); - delay.delay_ms(50); - - loop { - // TEST: successful read of chip ID - // -------------------------------- - info!("Reading acc chip ID"); - let id = acc_dev.acc_chipid().unwrap(); - debug!("Chip ID: {:02x}", id); - - // TEST: successful write and read back of configuration - // ---------------------------------------------------- - info!("Writing and reading back configuration"); - - // Configuration - let configuration = bmi088::AccelerometerConfig { - conf: bmi088::AccConf { - acc_bwp: bmi088::AccBandwidth::X1, - acc_odr: bmi088::AccDataRate::Hz100, - }, - acc_range: bmi088::AccRange::G3, - }; - acc_dev.acc_configuration_write(configuration).unwrap(); - - // Read back configuration - let readback_conf = acc_dev.acc_configuration_read().unwrap(); - - // Success - assert!(configuration == readback_conf); - - // Test: successful write and read of "enable" register - // ---------------------------------------------------- - info!("Writing and reading back enable register"); - - let enable = bmi088::AccOffOn::On; - acc_dev.acc_enable_write(enable).unwrap(); - let readback_enable = acc_dev.acc_enable_read().unwrap(); - assert!(enable == readback_enable); - - // Test: successful write and read of "power" register - // --------------------------------------------------- - info!("Writing and reading back power register"); - - let power = bmi088::AccWakeSuspend::Active; - acc_dev.acc_wake_suspend_write(power).unwrap(); - let readback_power = acc_dev.acc_wake_suspend_read().unwrap(); - assert!(power == readback_power); - - // Test: successful read of values - // ------------------------------- - info!("Reading accelerometer data"); - - let values = acc_dev.acc_data().unwrap(); - debug!( - "Accelerometer data: [{:?} {:?} {:?}]", - values.x, values.y, values.z - ); - - // Test: successful read of positively incrementing sensortime - // ----------------------------------------------------------- - info!("Reading sensortime"); - - let sensortime24_0 = acc_dev.sensor_time_24bit().unwrap(); - debug!("Sensortime: {:?}", sensortime24_0); - let sensortime24_1 = acc_dev.sensor_time_24bit().unwrap(); - debug!("Sensortime: {:?}", sensortime24_1); - assert!(sensortime24_1 > sensortime24_0); - - // Test: successful read of temperature - // ------------------------------------ - info!("Reading temperature"); - - let temp = acc_dev.temperature().unwrap(); - debug!("Temperature: {:?}", temp); - - // Test: successful read of error register - // --------------------------------------- - info!("Reading error register"); - - let err = acc_dev.acc_err_reg().unwrap(); - debug!( - "Error register: code={:?}, fatal={:?}", - err.error_code, err.fatal_error - ); - - // Test: successful read of status register - // ---------------------------------------- - info!("Reading status register"); - - let status = acc_dev.acc_status().unwrap(); - debug!("Status register: status={:?}", status); - - info!("Chip ID"); - let id = gyr_dev.gyro_chipid().unwrap(); - debug!("Chip ID: {:x}", id); - - info!("Bandwidth"); - let bw = bmi088::GyroBandwidth::Hz32; - gyr_dev.gyro_bandwidth_write(bw).unwrap(); - let readback_bw = gyr_dev.gyro_bandwidth_read().unwrap(); - assert!(bw == readback_bw); - - info!("Range"); - let range = bmi088::GyroRange::Dps2000; - gyr_dev.gyro_range_write(range).unwrap(); - let readback_range = gyr_dev.gyro_range_read().unwrap(); - assert!(range == readback_range); - - info!("Power mode"); - let power = bmi088::GyroPowerMode::Normal; - gyr_dev.gyro_power_mode_write(power).unwrap(); - let readback_power = gyr_dev.gyro_power_mode_read().unwrap(); - assert!(power == readback_power); - - info!("Data ready interrupt mapping"); - let pin_active = bmi088::PinActive::ActiveHigh; - let pin_behavior = bmi088::PinBehavior::PushPull; - - gyr_dev - .gyro_conf_int3_write(pin_active, pin_behavior) - .unwrap(); - let (rb_active, rb_beh) = gyr_dev.gyro_conf_int3_read().unwrap(); - assert!(pin_active == rb_active); - assert!(pin_behavior == rb_beh); - - info!("Reading gyroscope data"); - let values = gyr_dev.gyro_read_rate().unwrap(); - debug!( - "Gyroscope data: [{:?} {:?} {:?}]", - values.x, values.y, values.z - ); - delay.delay_ms(1000); - } -} diff --git a/examples/stm32f411-i2c.rs b/examples/stm32f411-i2c.rs deleted file mode 100644 index ffba59b..0000000 --- a/examples/stm32f411-i2c.rs +++ /dev/null @@ -1,169 +0,0 @@ -#![no_main] -#![no_std] - -use bmi088::Bmi088; -use cortex_m_rt::entry; -use defmt::{debug, info}; -use defmt_rtt as _; -use panic_probe as _; -use stm32f4xx_hal::{i2c::I2c1, prelude::*}; - -#[entry] -fn main() -> ! { - let dp = stm32f4xx_hal::pac::Peripherals::take().unwrap(); - let _cp = cortex_m::Peripherals::take().unwrap(); - - let rcc = dp.RCC.constrain(); - let clocks = rcc.cfgr.freeze(); - - let gpiob = dp.GPIOB.split(); - let _gpioc = dp.GPIOC.split(); // for interrupts, when those are implemented - let mut delay = dp.TIM2.delay_ms(&clocks); - - info!("i2c"); - let i2c1_scl = gpiob.pb6.into_alternate_open_drain(); - let i2c1_sda = gpiob.pb7.into_alternate_open_drain(); - let i2c1 = I2c1::new(dp.I2C1, (i2c1_scl, i2c1_sda), 100.kHz(), &clocks); - - // initialize the sensor - let mut sensor = Bmi088::new_with_i2c(i2c1, false, false); - - // Perform resets - info!("Accelerometer soft reset"); - sensor.acc_soft_reset().unwrap(); - delay.delay_ms(5); - - info!("Gyro soft reset"); - sensor.gyro_soft_reset().unwrap(); - delay.delay_ms(50); - - loop { - // TEST: successful read of chip ID - // -------------------------------- - info!("Reading chip ID"); - let id = sensor.acc_chip_id().unwrap(); - debug!("Chip ID: {:x}", id); - - // TEST: successful write and read back of configuration - // ---------------------------------------------------- - info!("Writing and reading back configuration"); - - // Configuration - let configuration = bmi088::AccelerometerConfig { - conf: bmi088::AccConf { - acc_bwp: bmi088::AccBandwidth::X1, - acc_odr: bmi088::AccDataRate::Hz100, - }, - acc_range: bmi088::AccRange::G3, - }; - sensor.acc_configuration_write(configuration).unwrap(); - - // Read back configuration - let readback_conf = sensor.acc_configuration_read().unwrap(); - - // Success - assert!(configuration == readback_conf); - - // Test: successful write and read of "enable" register - // ---------------------------------------------------- - info!("Writing and reading back enable register"); - - let enable = bmi088::AccOffOn::On; - sensor.acc_enable_write(enable).unwrap(); - let readback_enable = sensor.acc_enable_read().unwrap(); - assert!(enable == readback_enable); - - // Test: successful write and read of "power" register - // --------------------------------------------------- - info!("Writing and reading back power register"); - - let power = bmi088::AccWakeSuspend::Active; - sensor.acc_wake_suspend_write(power).unwrap(); - let readback_power = sensor.acc_wake_suspend_read().unwrap(); - assert!(power == readback_power); - - // Test: successful read of values - // ------------------------------- - info!("Reading accelerometer data"); - - let values = sensor.acc_data().unwrap(); - debug!( - "Accelerometer data: [{:?} {:?} {:?}]", - values.x, values.y, values.z - ); - - // Test: successful read of positively incrementing sensortime - // ----------------------------------------------------------- - info!("Reading sensortime"); - - let sensortime24_0 = sensor.sensor_time_24bit().unwrap(); - debug!("Sensortime: {:?}", sensortime24_0); - let sensortime24_1 = sensor.sensor_time_24bit().unwrap(); - debug!("Sensortime: {:?}", sensortime24_1); - assert!(sensortime24_1 > sensortime24_0); - - // Test: successful read of temperature - // ------------------------------------ - info!("Reading temperature"); - - let temp = sensor.temperature().unwrap(); - debug!("Temperature: {:?}", temp); - - // Test: successful read of error register - // --------------------------------------- - info!("Reading error register"); - - let err = sensor.acc_err_reg().unwrap(); - debug!( - "Error register: code={:?}, fatal={:?}", - err.error_code, err.fatal_error - ); - - // Test: successful read of status register - // ---------------------------------------- - info!("Reading status register"); - - let status = sensor.acc_status().unwrap(); - debug!("Status register: status={:?}", status); - - info!("Chip ID"); - let id = sensor.gyro_chipid().unwrap(); - debug!("Chip ID: {:x}", id); - - info!("Bandwidth"); - let bw = bmi088::GyroBandwidth::Hz32; - sensor.gyro_bandwidth_write(bw).unwrap(); - let readback_bw = sensor.gyro_bandwidth_read().unwrap(); - assert!(bw == readback_bw); - - info!("Range"); - let range = bmi088::GyroRange::Dps2000; - sensor.gyro_range_write(range).unwrap(); - let readback_range = sensor.gyro_range_read().unwrap(); - assert!(range == readback_range); - - info!("Power mode"); - let power = bmi088::GyroPowerMode::Normal; - sensor.gyro_power_mode_write(power).unwrap(); - let readback_power = sensor.gyro_power_mode_read().unwrap(); - assert!(power == readback_power); - - info!("Data ready interrupt mapping"); - let pin_active = bmi088::PinActive::ActiveHigh; - let pin_behavior = bmi088::PinBehavior::PushPull; - - sensor - .gyro_conf_int3_write(pin_active, pin_behavior) - .unwrap(); - let (rb_active, rb_beh) = sensor.gyro_conf_int3_read().unwrap(); - assert!(pin_active == rb_active); - assert!(pin_behavior == rb_beh); - - info!("Reading gyroscope data"); - let values = sensor.gyro_read_rate().unwrap(); - debug!( - "Gyroscope data: [{:?} {:?} {:?}]", - values.x, values.y, values.z - ); - } -} diff --git a/src/builder.rs b/src/builder.rs new file mode 100644 index 0000000..059a24e --- /dev/null +++ b/src/builder.rs @@ -0,0 +1,164 @@ +use super::types; + +#[derive(Default)] +pub struct AccConfiguration { + pub int1: Option, + pub int2: Option, + pub bandwidth: Option<(types::acc::AccBandwidth, types::acc::AccDataRate)>, + pub range: Option, + pub int_map: Option, + pub power_conf: Option, + pub power_ctrl: Option, + pub fifo_mode: Option, + pub fifo_conf1: Option, + pub fifo_downs: Option, + pub fifo_wtm: Option, +} + +pub struct AccConfigurationBuilder { + config: AccConfiguration, +} + +impl AccConfigurationBuilder { + pub fn new() -> Self { + Self { + config: AccConfiguration::default(), + } + } + + pub fn acc_int1(mut self, value: types::acc::AccIntConfiguration) -> Self { + self.config.int1 = Some(value); + self + } + + pub fn acc_int2(mut self, value: types::acc::AccIntConfiguration) -> Self { + self.config.int2 = Some(value); + self + } + + pub fn acc_bandwidth( + mut self, + value: (types::acc::AccBandwidth, types::acc::AccDataRate), + ) -> Self { + self.config.bandwidth = Some(value); + self + } + + pub fn acc_range(mut self, value: types::acc::AccRange) -> Self { + self.config.range = Some(value); + self + } + + pub fn acc_int_map(mut self, value: types::acc::AccIntMap) -> Self { + self.config.int_map = Some(value); + self + } + + pub fn acc_power_conf(mut self, value: types::acc::AccPowerConf) -> Self { + self.config.power_conf = Some(value); + self + } + + pub fn acc_power_ctrl(mut self, value: types::acc::AccPowerCtrl) -> Self { + self.config.power_ctrl = Some(value); + self + } + + pub fn acc_fifo_mode(mut self, value: types::acc::AccFifoMode) -> Self { + self.config.fifo_mode = Some(value); + self + } + + pub fn acc_fifo_conf1(mut self, value: types::acc::AccFifoConfig1) -> Self { + self.config.fifo_conf1 = Some(value); + self + } + + pub fn acc_fifo_downs(mut self, value: u8) -> Self { + self.config.fifo_downs = Some(value); + self + } + + pub fn acc_fifo_wtm(mut self, value: u16) -> Self { + self.config.fifo_wtm = Some(value); + self + } + + pub fn build(self) -> AccConfiguration { + self.config + } +} + +impl AccConfiguration { + pub fn builder() -> AccConfigurationBuilder { + AccConfigurationBuilder::new() + } +} + +#[derive(Default)] +pub struct GyroConfiguration { + pub interrupt: Option, + pub bandwidth: Option, + pub range: Option, + pub power_conf: Option, + pub fifo_mode: Option, + pub ext_s: Option, + pub fifo_wtm: Option, +} + +impl GyroConfiguration { + pub fn builder() -> GyroConfigurationBuilder { + GyroConfigurationBuilder::new() + } +} + +pub struct GyroConfigurationBuilder { + config: GyroConfiguration, +} + +impl GyroConfigurationBuilder { + pub fn new() -> Self { + Self { + config: GyroConfiguration::default(), + } + } + + pub fn interrupt(mut self, value: types::gyro::GyroIntConfiguration) -> Self { + self.config.interrupt = Some(value); + self + } + + pub fn bandwidth(mut self, value: types::gyro::GyroBandwidth) -> Self { + self.config.bandwidth = Some(value); + self + } + + pub fn range(mut self, value: types::gyro::GyroRange) -> Self { + self.config.range = Some(value); + self + } + + pub fn power_conf(mut self, value: types::gyro::GyroPowerConf) -> Self { + self.config.power_conf = Some(value); + self + } + + pub fn fifo_mode(mut self, value: types::gyro::GyroFifoModeConf) -> Self { + self.config.fifo_mode = Some(value); + self + } + + pub fn ext_s(mut self, value: types::gyro::GyroExtIntS) -> Self { + self.config.ext_s = Some(value); + self + } + + pub fn fifo_wtm(mut self, value: u8) -> Self { + self.config.fifo_wtm = Some(value); + self + } + + pub fn build(self) -> GyroConfiguration { + self.config + } +} diff --git a/src/interface.rs b/src/interface.rs index 236d003..4545e70 100644 --- a/src/interface.rs +++ b/src/interface.rs @@ -1,6 +1,6 @@ //! I2C interfaces -use crate::{private, Error}; +use crate::{private, types::Error}; use embedded_hal::i2c; use embedded_hal::spi; use embedded_hal::spi::Operation; @@ -185,7 +185,7 @@ where type Error = Error; fn read_register_acc(&mut self, register: u8) -> Result { - let mut data = [0xFF, 0xFF]; + let mut data = [0xFF; 2]; self.spi .transaction(&mut [ Operation::Write(&[register | 0x80]), diff --git a/src/lib.rs b/src/lib.rs index 633e355..c7b857c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,7 +4,7 @@ //! //! -#![deny(unsafe_code, missing_docs)] +#![deny(unsafe_code)] #![no_std] const I2C_ACC_BASE_ADDR: u8 = 0x18; @@ -12,23 +12,23 @@ const I2C_ACC_ALT_ADDR: u8 = 0x19; const I2C_GYRO_BASE_ADDR: u8 = 0x68; const I2C_GYRO_ALT_ADDR: u8 = 0x69; +mod builder; pub mod interface; mod reg; -mod sensor; -mod types; +pub mod sensor; +pub mod types; -pub use crate::interface::Addr; -pub use crate::types::{ - AccBandwidth, AccConf, AccDataRate, AccDrdyMap, AccOffOn, AccRange, AccWakeSuspend, - AccelerometerConfig, ErrCode, Error, GyroBandwidth, GyroDrdyMap, GyroPowerMode, GyroRange, - IntConfiguration, IntPin, PinActive, PinBehavior, Sensor3DData, -}; +pub use builder::{AccConfiguration, GyroConfiguration}; +pub use interface::{Addr, I2cInterface, SpiInterface}; +pub use types::acc::*; +pub use types::gyro::*; +pub use types::{IntPin, PinActive, PinBehavior, Sensor3DData}; /// BMI088 device object. #[derive(Debug)] pub struct Bmi088 { /// Digital interface (i2c) - iface: DI, + pub iface: DI, } mod private { diff --git a/src/reg.rs b/src/reg.rs index 0e142e7..d82ba81 100644 --- a/src/reg.rs +++ b/src/reg.rs @@ -1,13 +1,14 @@ // ignore case warnings + #![allow(non_camel_case_types)] #![allow(clippy::upper_case_acronyms)] -//#[allow(dead_code)] #[derive(Debug, Clone, Copy, PartialEq)] pub enum AccRegisters { ACC_SOFTRESET = 0x7E, ACC_PWR_CTRL = 0x7D, ACC_PWR_CONF = 0x7C, + #[allow(dead_code)] ACC_SELF_TEST = 0x6D, INT_MAP_DATA = 0x58, INT2_IO_CTRL = 0x54, @@ -23,15 +24,24 @@ pub enum AccRegisters { FIFO_LENGTH_1 = 0x25, FIFO_LENGTH_0 = 0x24, TEMP_LSB = 0x23, + #[allow(dead_code)] TEMP_MSB = 0x22, ACC_INT_STAT_1 = 0x1D, + #[allow(dead_code)] SENSORTIME_2 = 0x1A, + #[allow(dead_code)] SENSORTIME_1 = 0x19, + #[allow(dead_code)] SENSORTIME_0 = 0x18, + #[allow(dead_code)] ACC_Z_MSB = 0x17, + #[allow(dead_code)] ACC_Z_LSB = 0x16, + #[allow(dead_code)] ACC_Y_MSB = 0x15, + #[allow(dead_code)] ACC_Y_LSB = 0x14, + #[allow(dead_code)] ACC_X_MSB = 0x13, ACC_X_LSB = 0x12, ACC_STATUS = 0x03, @@ -39,11 +49,11 @@ pub enum AccRegisters { ACC_CHIP_ID = 0x00, } -//#[allow(dead_code)] pub enum GyroRegisters { FIFO_DATA = 0x3F, FIFO_CONFIG_1 = 0x3E, FIFO_CONFIG_0 = 0x3D, + #[allow(dead_code)] GYRO_SELF_TEST = 0x3C, FIFO_EXT_INT_S = 0x34, FIFO_WM_EN = 0x1E, @@ -56,10 +66,15 @@ pub enum GyroRegisters { GYRO_RANGE = 0x0F, FIFO_STATUS = 0x0E, GYRO_INT_STAT_1 = 0x0A, + #[allow(dead_code)] RATE_Z_MSB = 0x07, + #[allow(dead_code)] RATE_Z_LSB = 0x06, + #[allow(dead_code)] RATE_Y_MSB = 0x05, + #[allow(dead_code)] RATE_Y_LSB = 0x04, + #[allow(dead_code)] RATE_X_MSB = 0x03, RATE_X_LSB = 0x02, GYRO_CHIP_ID = 0x00, diff --git a/src/sensor.rs b/src/sensor.rs index 8fc916f..b22c1af 100644 --- a/src/sensor.rs +++ b/src/sensor.rs @@ -1,68 +1,40 @@ use crate::{ + builder, interface::{ReadData, WriteData}, - reg, types, Bmi088, Error, + reg, types, + types::Error, + Bmi088, }; impl Bmi088 where DI: ReadData> + WriteData>, { - /// Accelerometer chip ID. (0x00) - /// - /// Reads the chip ID and returns the chip ID value. - /// - /// # Returns - /// - /// - `Ok(u8)`: The chip ID value. - /// - `Err(Error)`: Read failure - /// + pub fn acc_fifo_clear(&mut self) -> Result<(), Error> { + self.iface + .write_register_acc(reg::AccRegisters::ACC_SOFTRESET as u8, 0xB0)?; + Ok(()) + } + pub fn acc_chip_id_read(&mut self) -> Result> { self.iface .read_register_acc(reg::AccRegisters::ACC_CHIP_ID as u8) } - /// Accelerometer error register. (0x02) - /// - /// Reads the error register and returns the error code. - /// - /// # Returns - /// - /// - `Ok(ErrCode)`: The error code. - /// - `Err(Error)`: Read failure - /// - pub fn acc_err_reg(&mut self) -> Result> { + pub fn acc_err_reg(&mut self) -> Result> { let err = self .iface .read_register_acc(reg::AccRegisters::ACC_ERR_REG as u8)?; - Ok(types::ErrCode::from_u8(err)) + Ok(types::acc::ErrCode::from_u8(err)) } - /// Accelerometer status register. (0x03) - /// - /// Read drdy. True if data ready. Also clears. - /// - /// # Returns - /// - /// - `Ok(bool)`: `true` if data is ready, `false` otherwise. - /// - `Err(Error)`: Read failure - /// - pub fn acc_status(&mut self) -> Result> { - let drdy = self - .iface - .read_register_acc(reg::AccRegisters::ACC_STATUS as u8)?; + pub fn acc_status_read(&mut self) -> Result> { + let reg = reg::AccRegisters::ACC_STATUS as u8; + let status = self.iface.read_register_acc(reg)?; - Ok(((drdy & 0b1000_0000) >> 7) != 0) + Ok((status & 0b1000_0000) != 0) } - /// Accelerometer data. (0x12) - /// - /// Reads the accelerometer data registers and returns the 3D sensor data. - /// - /// # Returns - /// - /// - `Ok(Sensor3DData)`: The 3D sensor data. - /// - `Err(Error)`: Read failure - /// pub fn acc_data(&mut self) -> Result> { let mut data = [0xFF; 6]; let reg = reg::AccRegisters::ACC_X_LSB as u8; @@ -74,15 +46,6 @@ where }) } - /// Sensor time register. (24-bit, 0x18-0x1A) - /// - /// Sensor time, in 24-bit counts. Overflows after 2^23 - 1 counts. - /// - /// # Returns - /// - /// - `Ok(u32)`: The time value. - /// - `Err(Error)`: Read failure - /// pub fn sensor_time(&mut self) -> Result> { let mut data = [0xFF; 4]; let reg = reg::AccRegisters::SENSORTIME_0 as u8; @@ -91,30 +54,11 @@ where Ok(u32::from_be_bytes(sensortime)) } - /// Converts sensor time (counts) to microseconds. - /// - /// # Arguments - /// - /// - `counts`: The sensor time in counts. - /// - /// # Returns - /// - /// - `u32`: The converted time in microseconds. - /// pub fn sensor_time_counts_to_us(counts: u32) -> u32 { let time = counts as u64 * 655360000 / ((1 << 23) - 1) as u64; time as u32 } - /// Accelerometer drdy status. (0x1D) - /// - /// Read and return acc drdy. Also clear the interrupt status register - /// - /// # Returns - /// - /// - `Ok(bool)`: `true` if an interrupt is active - /// - `Err(Error)`: Read failure - /// pub fn acc_int_stat_1_read(&mut self) -> Result> { let reg = self .iface @@ -122,15 +66,6 @@ where Ok((reg & 0b1000_0000) != 0) } - /// Temperature register. (0x22) - /// - /// Temperature value in degrees Celsius. - /// - /// # Returns - /// - /// - `Ok(f32)`: The temperature value in degrees Celsius. - /// - `Err(Error)`: Read failure - /// pub fn temp(&mut self) -> Result> { let mut data = [0xFF, 0xFF]; let reg = reg::AccRegisters::TEMP_LSB as u8; @@ -146,76 +81,40 @@ where Ok(temperature) } - /// Writes ACC_CONF. (0x40) - /// - /// NOTE:Does not write the whole AccelerometerConfig, just the subset - /// in the ACC_CONF register. - /// - /// # Arguments - /// - /// - `conf`: The accelerometer configuration to write. - /// - /// # Returns - /// - /// - `Ok(())`: Success - /// - `Err(Error)`: Write failure - /// - fn acc_conf_write(&mut self, conf: types::AccConf) -> Result<(), Error> { - let reg = reg::AccRegisters::ACC_CONF as u8; - let res: u8 = 0b1000_0000; // reserved - let bwp: u8 = ((conf.acc_bwp as u8) << 4) & 0b0110_0000; // [6:4] - let odr: u8 = conf.acc_odr as u8 & 0b0000_1111; // [3:0] - let set = res | bwp | odr; - - let mut data = [reg, set]; - - self.iface.write_data_acc(&mut data)?; + pub fn acc_conf_write( + &mut self, + bandwidth: types::acc::AccBandwidth, + data_rate: types::acc::AccDataRate, + ) -> Result<(), Error> { + let bw = bandwidth as u8; + let odr = data_rate as u8; + self.iface + .write_register_acc(reg::AccRegisters::ACC_CONF as u8, bw | odr)?; Ok(()) } - /// Reads the ACC_CONF (0x40) register - /// - /// # Returns - /// - /// - `Ok(AccConf)`: Accelerometer conf value - /// - `Err(Error)`: Read Failure - /// - fn acc_conf_read(&mut self) -> Result> { - let mut data = [0xFF]; - let reg = reg::AccRegisters::ACC_CONF as u8; - self.iface.read_data_acc(reg, &mut data)?; - let conf = types::AccConf::try_from(data[0]).map_err(|_| Error::InvalidInputData)?; - Ok(conf) + pub fn acc_conf_read( + &mut self, + ) -> Result<(types::acc::AccBandwidth, types::acc::AccDataRate), Error> { + let conf = self + .iface + .read_register_acc(reg::AccRegisters::ACC_CONF as u8)?; + + let bw = types::acc::AccBandwidth::try_from(conf).map_err(|_e| Error::InvalidOutputData)?; + let odr = types::acc::AccDataRate::try_from(conf).map_err(|_e| Error::InvalidOutputData)?; + Ok((bw, odr)) } - /// Read ACC_RANGE (0x41) - /// - /// # Returns - /// - /// - `Ok(AccRange)`: Range value - /// - `Err(Error)`: Read failure - /// - fn acc_range_read(&mut self) -> Result> { + pub fn acc_range_read(&mut self) -> Result> { let mut data = [0xFF]; let reg = reg::AccRegisters::ACC_RANGE as u8; self.iface.read_data_acc(reg, &mut data)?; - let range = types::AccRange::try_from(data[0]).map_err(|_| Error::InvalidInputData)?; + let range = types::acc::AccRange::try_from(data[0]).map_err(|_| Error::InvalidInputData)?; Ok(range) } - /// Write ACC_RANGE. (0x41) - /// - /// # Arguments - /// - /// - `range`: The accelerometer range to write. - /// - /// # Returns - /// - /// - `Ok(())`: Success - /// - `Err(Error)`: Write failure - /// - fn acc_range_write(&mut self, range: types::AccRange) -> Result<(), Error> { + pub fn acc_range_write(&mut self, range: types::acc::AccRange) -> Result<(), Error> { let reg = reg::AccRegisters::ACC_RANGE as u8; // [7:2] are reserved. let set = (range as u8) & 0b0000_0011; // [1:0] @@ -224,107 +123,33 @@ where Ok(()) } - /// Reads the accelerometer configuration. Utility method for both - /// ACC_CONF and ACC_RANGE. - /// - /// # Returns - /// - /// - `Ok(AccelerometerConfig)`: Accelerometer configuration - /// - `Err(Error)`: Read failure - /// - pub fn acc_configuration_read(&mut self) -> Result> { - let conf = self.acc_conf_read()?; - let range = self.acc_range_read()?; - Ok(types::AccelerometerConfig { - conf, - acc_range: range, - }) - } - - /// Writes the accelerometer configuration. Utility method for both - /// ACC_CONF and ACC_RANGE. - /// - /// # Arguments - /// - /// - `config`: The accelerometer configuration to write. - /// - /// # Returns - /// - /// - `Ok(())`: Write success - /// - `Err(Error)`: Write failure - /// - pub fn acc_configuration_write( - &mut self, - config: types::AccelerometerConfig, - ) -> Result<(), Error> { - self.acc_conf_write(config.conf)?; - self.acc_range_write(config.acc_range)?; - Ok(()) - } - - /// Reads the ACC_PWR_CONF (0x1E) register to figure out if the sensor - /// is suspended or active. - /// - /// # Returns - /// - /// - `Ok(AccPowerConf)`: Power conf value - /// - `Err(Error)`: Read failure - /// - pub fn acc_pwr_conf_read(&mut self) -> Result> { + pub fn acc_pwr_conf_read(&mut self) -> Result> { let reg = self .iface .read_register_acc(reg::AccRegisters::ACC_PWR_CONF as u8)?; - types::AccWakeSuspend::try_from(reg).map_err(|_| Error::InvalidInputData) + types::acc::AccPowerConf::try_from(reg).map_err(|_| Error::InvalidInputData) } - /// Writes the ACC_PWR_CONF (0x7C) register to suspend or wake the - /// sensor. - /// - /// # Arguments - /// - /// - `conf`: The power configuration to write. - /// - /// # Returns - /// - /// - `Ok(())`: Write success - /// - `Err(Error)`: Write failure - /// - pub fn acc_pwr_conf_write(&mut self, conf: types::AccWakeSuspend) -> Result<(), Error> { + pub fn acc_pwr_conf_write( + &mut self, + conf: types::acc::AccPowerConf, + ) -> Result<(), Error> { let reg = reg::AccRegisters::ACC_PWR_CONF as u8; let set = conf as u8; - let mut data = [reg, set]; - self.iface.write_data_acc(&mut data)?; - Ok(()) + self.iface.write_register_acc(reg, set) } - /// Reads the ACC_PWR_CTRL (0x7C) register to figure out if the sensor - /// is enabled or disabled. - /// - /// # Returns - /// - /// - `Ok(AccPowerEnable)`: Power enable value - /// - `Err(Error)`: Read failure - /// - pub fn acc_pwr_ctrl_read(&mut self) -> Result> { + pub fn acc_pwr_ctrl_read(&mut self) -> Result> { let reg = self .iface .read_register_acc(reg::AccRegisters::ACC_PWR_CTRL as u8)?; - types::AccOffOn::try_from(reg).map_err(|_| Error::InvalidInputData) + types::acc::AccPowerCtrl::try_from(reg).map_err(|_| Error::InvalidInputData) } - /// Writes the ACC_PWR_CTRL (0x7D) register to enable or disable the sensor. - /// Must be called after startup to enable the sensor. - /// - /// # Arguments - /// - /// - `enable`: Power enable - /// - /// # Returns - /// - /// - `Ok(())`: Write success - /// - `Err(Error)`: Write failure - /// - pub fn acc_pwr_ctrl_write(&mut self, enable: types::AccOffOn) -> Result<(), Error> { + pub fn acc_pwr_ctrl_write( + &mut self, + enable: types::acc::AccPowerCtrl, + ) -> Result<(), Error> { let reg = reg::AccRegisters::ACC_PWR_CTRL as u8; let set = enable as u8; let mut data = [reg, set]; @@ -332,47 +157,19 @@ where Ok(()) } - /// Soft Reset (0x7E) - /// - /// Sensortec recommends a delay of 1ms after a soft reset of the sensor. - /// - /// # Returns - /// - /// - `Ok(())`: Write success - /// - `Err(Error)`: Write failure - /// - pub fn acc_softreset(&mut self) -> Result<(), Error> { + pub fn acc_softreset( + &mut self, + delay: &mut dyn embedded_hal::delay::DelayNs, + ) -> Result<(), Error> { let reg = 0xB6; self.iface .write_register_acc(reg::AccRegisters::ACC_SOFTRESET as u8, reg)?; + delay.delay_ms(1); + // first byte after reset is 0xFF so read that to flush. + self.acc_chip_id_read()?; Ok(()) } - /// Utility method for initializing the accelerometer after a power-on - /// reset or startup. - /// - /// # Returns - /// - /// - `Ok(())`: Write success - /// - `Err(Error)`: Write failure - /// - pub fn acc_init( - &mut self, - configuration: types::AccelerometerConfig, - ) -> Result<(), Error> { - self.acc_configuration_write(configuration)?; - self.acc_pwr_ctrl_write(types::AccOffOn::On)?; - self.acc_pwr_conf_write(types::AccWakeSuspend::Active)?; - Ok(()) - } - - /// Accelerometer drdy status. (0x1D) Clears drdy interrupt - /// - /// # Returns - /// - /// - `Ok(bool)`: `true` if an interrupt is active - /// - `Err(Error)`: Read failure - /// pub fn acc_int_stat_1_read_drdy(&mut self) -> Result> { let reg = self .iface @@ -380,20 +177,9 @@ where Ok((reg & 0b1000_0000) != 0) } - /// Write INT1 pin configuration - /// - /// # Arguments - /// - /// - `conf`: The interrupt configuration to write. - /// - /// # Returns - /// - /// - `Ok(())`: Write success - /// - `Err(Error)`: Write failure - /// pub fn acc_int1_io_ctrl_write( &mut self, - conf: types::IntConfiguration, + conf: types::acc::AccIntConfiguration, ) -> Result<(), Error> { let reg = reg::AccRegisters::INT1_IO_CTRL as u8; let set = conf.into(); @@ -402,157 +188,175 @@ where Ok(()) } - /// Read configuration of INT1 pin. - /// - /// # Returns - /// - /// - `Ok(IntConfiguration)`: Interrupt configuration - /// - `Err(Error)`: Write failure - /// - pub fn acc_int1_io_ctrl_read(&mut self) -> Result> { + pub fn acc_int1_io_ctrl_read( + &mut self, + ) -> Result> { let reg = reg::AccRegisters::INT1_IO_CTRL as u8; let data = self.iface.read_register_acc(reg)?; - Ok(types::IntConfiguration::from(data)) + Ok(data.into()) } - /// Configure INT2 pin. - /// - /// # Arguments - /// - /// - `conf`: The interrupt configuration to write. - /// - /// # Returns - /// - /// - `Ok(())`: Write success - /// - `Err(Error)`: Write failure - /// pub fn acc_int2_io_ctrl_write( &mut self, - conf: types::IntConfiguration, + conf: types::acc::AccIntConfiguration, ) -> Result<(), Error> { - let reg = reg::AccRegisters::INT2_IO_CTRL as u8; - let set = conf.into(); - let mut data = [reg, set]; - self.iface.write_data_acc(&mut data)?; + self.iface + .write_register_acc(reg::AccRegisters::INT2_IO_CTRL as u8, conf.into())?; Ok(()) } - /// Read configuration of INT2 pin. - /// - /// # Returns - /// - /// - `Ok(IntConfiguration)`: Interrupt configuration - /// - `Err(Error)`: Write failure - /// - pub fn acc_int2_io_ctrl_read(&mut self) -> Result> { + pub fn acc_int2_io_ctrl_read( + &mut self, + ) -> Result> { let reg = reg::AccRegisters::INT2_IO_CTRL as u8; let data = self.iface.read_register_acc(reg)?; - Ok(types::IntConfiguration::from(data)) + Ok(data.into()) } - /// Write Map data ready interrupt to output pin INT1 and/or INT2. (0x58) - /// - /// # Arguments - /// - /// - `map`: The interrupt configuration to write. - /// - /// # Returns - /// - /// - `Ok(())`: Write success - /// - `Err(Error)`: Write failure - /// - pub fn acc_int_map_data_write(&mut self, map: types::AccDrdyMap) -> Result<(), Error> { - let reg = reg::AccRegisters::INT_MAP_DATA as u8; - let set = map as u8; - let mut data = [reg, set]; - self.iface.write_data_acc(&mut data)?; + pub fn acc_int1_int2_map_data_write( + &mut self, + map: types::acc::AccIntMap, + ) -> Result<(), Error> { + self.iface + .write_register_acc(reg::AccRegisters::INT_MAP_DATA as u8, map.into())?; Ok(()) } - /// Read Map data ready interrupt to output pin INT1 and/or INT2. (0x58) - /// - /// # Returns - /// - /// - `Ok(AccDrdyMap)`: The interrupt configuration - /// - `Err(Error)`: Read failure - /// - pub fn acc_int_map_data_read(&mut self) -> Result> { + pub fn acc_int1_int2_map_data_read(&mut self) -> Result> { let reg = reg::AccRegisters::INT_MAP_DATA as u8; let data = self.iface.read_register_acc(reg)?; - Ok(types::AccDrdyMap::from(data)) + Ok(types::acc::AccIntMap::from(data)) + } + + pub fn acc_fifo_length_read(&mut self) -> Result> { + let l0 = reg::AccRegisters::FIFO_LENGTH_0 as u8; + let l1 = reg::AccRegisters::FIFO_LENGTH_1 as u8; + let l0_read = self.iface.read_register_acc(l0)?; + let l1_read = self.iface.read_register_acc(l1)?; + + let fifo_length = ((l1_read as u16) << 8) | l0_read as u16; + Ok(fifo_length) + } + + pub fn acc_fifo_downs_write(&mut self, downs: u8) -> Result<(), Error> { + let reg = reg::AccRegisters::FIFO_DOWNS as u8; + let downs: u8 = ((downs & 0b111) << 4) | 0b1000_0000; + self.iface.write_register_acc(reg, downs) + } + + pub fn acc_fifo_downs_read(&mut self) -> Result> { + let reg = reg::AccRegisters::FIFO_DOWNS as u8; + let downs = self.iface.read_register_acc(reg)?; + Ok((downs & 0b0111_0000) >> 4) + } + + pub fn acc_fifo_wtm_write(&mut self, watermark: u16) -> Result<(), Error> { + // buffer[0] = (uint8_t) wml & 0xff; + // buffer[1] = (uint8_t) (wml >> 8) & 0xff; + + let mut payload = [ + reg::AccRegisters::FIFO_WTM_0 as u8, + //0xFF, // dummy + watermark as u8, + (watermark >> 8) as u8, + ]; + + self.iface.write_data_acc(&mut payload)?; + + // let watermark_lsb = watermark as u8; + // let watermark_msb = (watermark >> 8) as u8; + // let wtm0 = reg::AccRegisters::FIFO_WTM_0 as u8; + // let wtm1 = reg::AccRegisters::FIFO_WTM_1 as u8; + // self.iface.write_register_acc(wtm0, watermark_lsb)?; + // self.iface.write_register_acc(wtm1, watermark_msb)?; + Ok(()) + } + + pub fn acc_fifo_wtm_read(&mut self) -> Result> { + let wtm0 = reg::AccRegisters::FIFO_WTM_0 as u8; + let wtm1 = reg::AccRegisters::FIFO_WTM_1 as u8; + let wtm0_read = self.iface.read_register_acc(wtm0)?; + let wtm1_read = self.iface.read_register_acc(wtm1)?; + let watermark = ((wtm1_read as u16) << 8) | wtm0_read as u16; + Ok(watermark) + } + + pub fn acc_fifo_config1_read(&mut self) -> Result> { + let reg = reg::AccRegisters::FIFO_CONFIG_1 as u8; + let data = self.iface.read_register_acc(reg)?; + Ok(types::acc::AccFifoConfig1::from(data)) + } + + pub fn acc_fifo_config1_write( + &mut self, + set: types::acc::AccFifoConfig1, + ) -> Result<(), Error> { + let reg = reg::AccRegisters::FIFO_CONFIG_1 as u8; + self.iface.write_register_acc(reg, u8::from(set))?; + Ok(()) + } + + pub fn acc_fifo_mode_read(&mut self) -> Result> { + let reg = reg::AccRegisters::FIFO_CONFIG_0 as u8; + let data = self.iface.read_register_acc(reg)?; + Ok(types::acc::AccFifoMode::from(data)) + } + + pub fn acc_fifo_mode_write(&mut self, watermark: u8) -> Result<(), Error> { + let reg = reg::AccRegisters::FIFO_CONFIG_0 as u8; + + if watermark > 128 { + self.iface.write_register_acc(reg, 128u8)?; + } else { + self.iface.write_register_acc(reg, watermark)?; + } + + Ok(()) + } + + /// blocking fifo data read. read out the entire buffer at &buf; incomplete + /// frames are dropped. The FIFO queue is drained in proportion to the + /// amount of data read. + pub fn acc_fifo_data(&mut self, buf: &mut [u8]) -> Result<(), Error> { + self.iface + .read_data_acc(reg::AccRegisters::FIFO_DATA as u8, buf)?; + Ok(()) } /////////////////////////////////////// /* GYRO REGISTERS */ /////////////////////////////////////// - - /// Read FIFO configuration register. (0x49) - /// - /// Stream - sampling continues when buffer is full (i.e. filled with 99 - /// frames); old is discarded - /// - /// Fifo - data collection stops once buffer is full (i.e. filled with 100 - /// frames) - /// - /// # Returns /// - /// - `Ok(FifoModeConf)`: The FIFO configuration - /// - `Err(Error)`: Read failure - /// - pub fn gyro_fifo_config1_read(&mut self) -> Result> { + + pub fn gyro_fifo_data(&mut self, data: &mut [u8]) -> Result<(), Error> { + self.iface + .read_data_gyro(reg::GyroRegisters::FIFO_DATA as u8, data)?; + Ok(()) + } + + pub fn gyro_fifo_config1_read( + &mut self, + ) -> Result> { let reg = reg::GyroRegisters::FIFO_CONFIG_1 as u8; let data = self.iface.read_register_gyro(reg)?; - Ok(types::GyroFifoModeConf::from(data)) + Ok(types::gyro::GyroFifoModeConf::from(data)) } - /// Write FIFO configuration register. (0x49) - /// - /// Stream - sampling continues when buffer is full (i.e. filled with 99 - /// frames); old is discarded - /// - /// Fifo - data collection stops once buffer is full (i.e. filled with 100 - /// frames) - /// - /// # Returns - /// - /// - `Ok(())`: Written successfully - /// - `Err(Error)`: Write failure - /// pub fn gyro_fifo_config1_write( &mut self, - set: types::GyroFifoModeConf, + set: types::gyro::GyroFifoModeConf, ) -> Result<(), Error> { let reg = reg::GyroRegisters::FIFO_CONFIG_1 as u8; self.iface.write_register_gyro(reg, set as u8)?; Ok(()) } - /// Read FIFO configuration register. (0x48) - /// - /// # Returns - /// - /// - `Ok(u8)`: The FIFO configuration - /// - `Err(Error)`: Read failure - /// pub fn gyro_fifo_config0_read(&mut self) -> Result> { let reg = reg::GyroRegisters::FIFO_CONFIG_0 as u8; let data = self.iface.read_register_gyro(reg)?; Ok(data) } - /// Write FIFO configuration register. (0x48) - /// - /// # Arguments - /// - /// - `set`: The FIFO configuration to write. If the requested watermark - /// level is greater than 128, the value is clamped to the maximum value of - /// 128. - /// - /// # Returns - /// - /// - `Ok(())`: Written successfully - /// - `Err(Error)`: Write failure - /// pub fn gyro_fifo_config0_write(&mut self, watermark: u8) -> Result<(), Error> { let reg = reg::GyroRegisters::FIFO_CONFIG_0 as u8; @@ -565,46 +369,21 @@ where Ok(()) } - /// Read FIFO external interrupt register. (0x34) - /// - /// # Returns - /// - /// - `Ok(FifoExtIntS)`: The FIFO interrupt source configuration - /// - `Err(Error)`: Read failure - /// - pub fn gyro_fifo_ext_int_source_read(&mut self) -> Result> { + pub fn gyro_fifo_ext_int_s_read(&mut self) -> Result> { let reg = reg::GyroRegisters::FIFO_EXT_INT_S as u8; let data = self.iface.read_register_gyro(reg)?; - Ok(types::FifoExtIntS::from(data)) + Ok(types::gyro::GyroExtIntS::from(data)) } - /// Write FIFO external interrupt register. (0x34) - /// - /// # Arguments - /// - /// - `set`: The FIFO interrupt source configuration to write. - /// - /// # Returns - /// - /// - `Ok(())`: Written successfully - /// - `Err(Error)`: Write failure - /// - pub fn gyro_fifo_ext_int_source_write( + pub fn gyro_fifo_ext_int_s_write( &mut self, - set: types::FifoExtIntS, + set: types::gyro::GyroExtIntS, ) -> Result<(), Error> { let reg = reg::GyroRegisters::FIFO_EXT_INT_S as u8; self.iface.write_register_gyro(reg, set.into())?; Ok(()) } - /// Read FIFO watermark enable register. (0x1E) - /// - /// # Returns - /// - /// - `Ok(bool)`: `true` if watermark interrupt is enabled - /// - `Err(Error)`: Read failure - /// pub fn gyro_fifo_wm_enable_read(&mut self) -> Result> { let reg = reg::GyroRegisters::FIFO_WM_EN as u8; let data = self.iface.read_register_gyro(reg)?; @@ -615,17 +394,6 @@ where } } - /// Write FIFO watermark enable register. (0x1E) - /// - /// # Arguments - /// - /// - `enable`: `true` to enable watermark interrupt, `false` to disable - /// - /// # Returns - /// - /// - `Ok(())`: Written successfully - /// - `Err(Error)`: Write failure - /// pub fn gyro_fifo_wm_enable_write(&mut self, enable: bool) -> Result<(), Error> { let reg = reg::GyroRegisters::FIFO_WM_EN as u8; let set = if enable { 0x88 } else { 0x08 }; @@ -633,44 +401,19 @@ where Ok(()) } - /// Read FIFO watermark level register. (0x0E) - /// - /// If an overrun has occurred, the overrun bit is set until a write to - /// FIFO_CONFIG_1 clears it. - /// - /// # Returns - /// - /// - `Ok((bool, u8))`: Tuple of whether overrun has occurred and the - /// current watermark level. - /// - `Err(Error)`: Read failure - /// pub fn gyro_fifo_status_read(&mut self) -> Result<(bool, u8), Error> { let reg = reg::GyroRegisters::FIFO_STATUS as u8; let data = self.iface.read_register_gyro(reg)?; - let watermark = (data & 0b1000_0000) != 0; + let overrun = (data & 0b1000_0000) != 0; let count = data & 0b0111_1111; - Ok((watermark, count)) + Ok((overrun, count)) } - /// Read the gyro chip ID (0x00) - /// - /// # Returns - /// - /// - `Ok(u8)`: The chip ID value. - /// - `Err(Error)`: Read failure - /// pub fn gyro_chip_id_read(&mut self) -> Result> { self.iface .read_register_gyro(reg::GyroRegisters::GYRO_CHIP_ID as u8) } - /// Read the gyro rate data (0x02-0x07) - /// - /// # Returns - /// - /// - `Ok(Sensor3DData)`: The 3D sensor data. - /// - `Err(Error)`: Read failure - /// pub fn gyro_rate_read(&mut self) -> Result> { let mut data = [0xFF; 6]; let reg = reg::GyroRegisters::RATE_X_LSB as u8; @@ -682,46 +425,24 @@ where }) } - /// Read gyro drdy status (0x0A) - /// - /// # Returns - /// - /// - `Ok(bool)`: `true` if an interrupt is active - /// - `Err(Error)`: Read failure - /// - pub fn gyro_drdy(&mut self) -> Result> { + /// returns a bool pair (drdy, fifo_int) + pub fn gyro_drdy(&mut self) -> Result<(bool, bool), Error> { let reg = self .iface .read_register_gyro(reg::GyroRegisters::GYRO_INT_STAT_1 as u8)?; - Ok((reg & 0b1000_0000) != 0) + let drdy = (reg & 0b1000_0000) != 0; + let fifo_int = (reg & 0b0001_0000) != 0; + Ok((drdy, fifo_int)) } - /// Read gyro range (0x0F) - /// - /// # Returns - /// - /// - `Ok(GyroRange)`: Range value - /// - `Err(Error)`: Read failure - /// - pub fn gyro_range_read(&mut self) -> Result> { + pub fn gyro_range_read(&mut self) -> Result> { let reg = reg::GyroRegisters::GYRO_RANGE as u8; let data = self.iface.read_register_gyro(reg)?; - let range = types::GyroRange::try_from(data).map_err(|_| Error::InvalidInputData)?; + let range = types::gyro::GyroRange::try_from(data).map_err(|_| Error::InvalidInputData)?; Ok(range) } - /// Write gyro range (0x0F) - /// - /// # Arguments - /// - /// - `range`: The gyro range to write. - /// - /// # Returns - /// - /// - `Ok(())`: Write success - /// - `Err(Error)`: Write failure - /// - pub fn gyro_range_write(&mut self, range: types::GyroRange) -> Result<(), Error> { + pub fn gyro_range_write(&mut self, range: types::gyro::GyroRange) -> Result<(), Error> { let reg = reg::GyroRegisters::GYRO_RANGE as u8; let set = range as u8; self.iface.write_register_gyro(reg, set)?; @@ -729,253 +450,195 @@ where Ok(()) } - /// Read gyro bandwidth (0x10) - /// - /// # Returns - /// - /// - `Ok(GyroBandwidth)`: Bandwidth value - /// - `Err(Error)`: Read failure - /// - pub fn gyro_bandwidth_read(&mut self) -> Result> { + pub fn gyro_bandwidth_read(&mut self) -> Result> { let reg = reg::GyroRegisters::GYRO_BANDWIDTH as u8; let data = self.iface.read_register_gyro(reg)?; // Note: bit #7 is read-only and always ‚1‘, but has no function and can safely be ignored. let data_masked = data & 0b0111_1111; - let bw = - types::GyroBandwidth::try_from(data_masked).map_err(|_| Error::InvalidInputData)?; + let bw = types::gyro::GyroBandwidth::try_from(data_masked) + .map_err(|_| Error::InvalidInputData)?; Ok(bw) } - /// Write gyro bandwidth (0x10) - /// - /// # Arguments - /// - /// - `bw`: The gyro bandwidth to write. - /// - /// # Returns - /// - /// - `Ok(())`: Write success - /// - `Err(Error)`: Write failure - /// - pub fn gyro_bandwidth_write(&mut self, bw: types::GyroBandwidth) -> Result<(), Error> { + pub fn gyro_bandwidth_write( + &mut self, + bw: types::gyro::GyroBandwidth, + ) -> Result<(), Error> { let reg = reg::GyroRegisters::GYRO_BANDWIDTH as u8; let set = bw as u8; self.iface.write_register_gyro(reg, set)?; - Ok(()) } - /// Read gyro power mode (0x11) - /// - /// # Returns - /// - /// - `Ok(GyroPowerMode)`: Power mode value - /// - `Err(Error)`: Read failure - /// - pub fn gyro_power_mode_read(&mut self) -> Result> { + pub fn gyro_lpm_read(&mut self) -> Result> { let reg = reg::GyroRegisters::GYRO_LPM1 as u8; let data = self.iface.read_register_gyro(reg)?; - let mode = types::GyroPowerMode::try_from(data).map_err(|_| Error::InvalidInputData)?; + let mode = + types::gyro::GyroPowerConf::try_from(data).map_err(|_| Error::InvalidInputData)?; Ok(mode) } - /// Write gyro power mode (0x11) - /// - /// # Arguments - /// - /// - `mode`: The gyro power mode to write. - /// - /// # Returns - /// - /// - `Ok(())`: Write success - /// - `Err(Error)`: Write failure - /// - pub fn gyro_power_mode_write( - &mut self, - mode: types::GyroPowerMode, - ) -> Result<(), Error> { + pub fn gyro_lpm_write(&mut self, mode: types::gyro::GyroPowerConf) -> Result<(), Error> { let reg = reg::GyroRegisters::GYRO_LPM1 as u8; let set = mode as u8; self.iface.write_register_gyro(reg, set)?; - Ok(()) } - /// Perform a soft reset of the gyroscope. (0x14) - /// Sensortec recommends a delay of 30ms after resetting the gyro. - /// - /// # Returns - /// - /// - `Ok(())`: Write success - /// - `Err(Error)`: Write failure - /// - pub fn gyro_soft_reset(&mut self) -> Result<(), Error> { + pub fn gyro_softreset( + &mut self, + delay: &mut dyn embedded_hal::delay::DelayNs, + ) -> Result<(), Error> { let reg = 0xB6; self.iface .write_register_gyro(reg::GyroRegisters::GYRO_SOFTRESET as u8, reg)?; + delay.delay_ms(30); + // first byte read after reset seems to be 0xFF so read that to flush. + self.acc_chip_id_read()?; Ok(()) } - /// Enable data ready interrupt on the gyro. (0x15) - /// - /// # Arguments - /// - /// - `enable`: `true` to enable, `false` to disable - /// - /// # Returns - /// - /// - `Ok(())`: Write success - /// - `Err(Error)`: Write failure - /// - pub fn gyro_int_ctrl_enable(&mut self, enable: bool) -> Result<(), Error> { + pub fn gyro_int_ctrl_enable_write( + &mut self, + enable: types::gyro::GyroIntEnable, + ) -> Result<(), Error> { let reg = reg::GyroRegisters::GYRO_INT_CTRL as u8; - let set = if enable { 0x80 } else { 0x00 }; - self.iface.write_register_gyro(reg, set)?; + self.iface.write_register_gyro(reg, u8::from(enable))?; Ok(()) } - /// Read interrupt 4 configuration (0x16) - /// - /// # Returns - /// - /// - `Ok((PinActive, PinBehavior))`: The pin active and behavior - /// - `Err(Error)`: Read failure - /// - pub fn gyro_int4_io_conf_read( + pub fn gyro_int_ctrl_enable_read( &mut self, - ) -> Result<(types::PinActive, types::PinBehavior), Error> { - let reg = reg::GyroRegisters::INT3_INT4_IO_CONF as u8; + ) -> Result> { + let reg = reg::GyroRegisters::GYRO_INT_CTRL as u8; let data = self.iface.read_register_gyro(reg)?; + Ok(types::gyro::GyroIntEnable::from(data)) + } - let active = if data & 0b0000_0100 != 0 { - types::PinActive::ActiveHigh - } else { - types::PinActive::ActiveLow - }; - let behavior = if data & 0b0000_1000 != 0 { - types::PinBehavior::OpenDrain - } else { - types::PinBehavior::PushPull - }; - Ok((active, behavior)) + pub fn gyro_int3_int4_io_conf_read( + &mut self, + ) -> Result> { + let reg = reg::GyroRegisters::INT3_INT4_IO_CONF as u8; + let data = self.iface.read_register_gyro(reg)?; + Ok(types::gyro::GyroIntPinConfiguration::from(data)) } - /// Write interrupt 4 configuration (0x16) - /// - /// # Arguments - /// - /// - `active`: The pin active state - /// - `behavior`: The pin behavior - /// - /// # Returns - /// - /// - `Ok(())`: Write success - /// - `Err(Error)`: Write failure - /// - pub fn gyro_int4_io_conf_write( + pub fn gyro_int3_int4_io_conf_write( &mut self, - active: types::PinActive, - behavior: types::PinBehavior, + conf: types::gyro::GyroIntPinConfiguration, ) -> Result<(), Error> { let reg = reg::GyroRegisters::INT3_INT4_IO_CONF as u8; - let set = match active { - types::PinActive::ActiveHigh => 0b0000_0100, - types::PinActive::ActiveLow => 0b0000_0000, - } | match behavior { - types::PinBehavior::PushPull => 0b0000_0000, - types::PinBehavior::OpenDrain => 0b0000_1000, - }; - self.iface.write_register_gyro(reg, set)?; + self.iface.write_register_gyro(reg, conf.into())?; Ok(()) } - /// Read interrupt 3 configuration (0x16) - /// This is the same register as interrupt 3. - /// - /// # Returns - /// - /// - `Ok((PinActive, PinBehavior))`: The pin active and behavior - /// - `Err(Error)`: Read failure - /// - pub fn gyro_int3_io_conf_read( + pub fn gyro_int3_int4_io_map_read( &mut self, - ) -> Result<(types::PinActive, types::PinBehavior), Error> { - let reg = reg::GyroRegisters::INT3_INT4_IO_CONF as u8; + ) -> Result> { + let reg = reg::GyroRegisters::INT3_INT4_IO_MAP as u8; let data = self.iface.read_register_gyro(reg)?; - let active = if data & 0b0000_0001 != 0 { - types::PinActive::ActiveHigh - } else { - types::PinActive::ActiveLow - }; - let behavior = if data & 0b0000_0010 != 0 { - types::PinBehavior::OpenDrain - } else { - types::PinBehavior::PushPull - }; - Ok((active, behavior)) + Ok(types::gyro::GyroIntMapping::from(data)) } - /// Write interrupt 3 configuration (0x16) - /// This is the same register as interrupt 3. - /// - /// # Arguments - /// - /// - `active`: The pin active state - /// - `behavior`: The pin behavior - /// - /// # Returns - /// - /// - `Ok(())`: Write success - /// - `Err(Error)`: Write failure - /// - pub fn gyro_int3_io_conf_write( + pub fn gyro_int3_int4_io_map_write( &mut self, - active: types::PinActive, - behavior: types::PinBehavior, + map: types::gyro::GyroIntMapping, ) -> Result<(), Error> { - let reg = reg::GyroRegisters::INT3_INT4_IO_CONF as u8; - let set = match active { - types::PinActive::ActiveHigh => 0b0000_0001, - types::PinActive::ActiveLow => 0b0000_0000, - } | match behavior { - types::PinBehavior::PushPull => 0b0000_0000, - types::PinBehavior::OpenDrain => 0b0000_0010, - }; - self.iface.write_register_gyro(reg, set)?; + let reg = reg::GyroRegisters::INT3_INT4_IO_MAP as u8; + self.iface.write_register_gyro(reg, map.into())?; Ok(()) } - /// Read interrupt 3 and 4 IO map (0x18) - /// - /// # Returns - /// - /// - `Ok(GyroDrdyMap)`: The pin mapping - /// - `Err(Error)`: Read failure - /// - pub fn gyro_int3_int4_io_map_read(&mut self) -> Result> { - let reg = reg::GyroRegisters::INT3_INT4_IO_MAP as u8; - let data = self.iface.read_register_gyro(reg)?; - types::GyroDrdyMap::try_from(data).map_err(|_| Error::InvalidInputData) + /////////////////////////////////////// + /* Utility Methods */ + /////////////////////////////////////// + + /// Write the accelerometer configuration. This method combines every + pub fn configure_accelerometer( + &mut self, + config: builder::AccConfiguration, + ) -> Result<(), Error> { + if let Some(acc_int1) = config.int1 { + self.acc_int1_io_ctrl_write(acc_int1)?; + } + + if let Some(acc_int2) = config.int2 { + self.acc_int2_io_ctrl_write(acc_int2)?; + } + + if let Some(acc_int_map) = config.int_map { + self.acc_int1_int2_map_data_write(acc_int_map)?; + } + + if let Some((bw, odr)) = config.bandwidth { + self.acc_conf_write(bw, odr)?; + } + + if let Some(acc_range) = config.range { + self.acc_range_write(acc_range)?; + } + + if let Some(acc_power_conf) = config.power_conf { + self.acc_pwr_conf_write(acc_power_conf)?; + } + + if let Some(acc_power_ctrl) = config.power_ctrl { + self.acc_pwr_ctrl_write(acc_power_ctrl)?; + } + + if let Some(fifo_mode) = config.fifo_mode { + self.acc_fifo_mode_write(fifo_mode as u8)?; + } + + if let Some(fifo_conf1) = config.fifo_conf1 { + self.acc_fifo_config1_write(fifo_conf1)?; + } + + if let Some(fifo_downs) = config.fifo_downs { + self.acc_fifo_downs_write(fifo_downs)?; + } + + if let Some(fifo_wtm) = config.fifo_wtm { + self.acc_fifo_wtm_write(fifo_wtm)?; + } + + Ok(()) } - /// Write interrupt 3 and 4 IO map (0x18) - /// - /// # Arguments - /// - /// - `map`: The pin mapping - /// - /// # Returns - /// - /// - `Ok(())`: Write success - /// - `Err(Error)`: Write failure - /// - pub fn gyro_int3_int4_io_map_write( + pub fn configure_gyro( &mut self, - map: types::GyroDrdyMap, + config: builder::GyroConfiguration, ) -> Result<(), Error> { - let reg = reg::GyroRegisters::INT3_INT4_IO_MAP as u8; - let set = map as u8; - self.iface.write_register_gyro(reg, set)?; + if let Some(interrupt) = config.interrupt { + self.gyro_int_ctrl_enable_write(interrupt.enable)?; + self.gyro_int3_int4_io_map_write(interrupt.map)?; + self.gyro_int3_int4_io_conf_write(interrupt.conf)?; + } + + if let Some(bandwidth) = config.bandwidth { + self.gyro_bandwidth_write(bandwidth)?; + } + + if let Some(range) = config.range { + self.gyro_range_write(range)?; + } + + if let Some(conf) = config.power_conf { + self.gyro_lpm_write(conf)?; + } + + if let Some(fifo_mode) = config.fifo_mode { + self.gyro_fifo_config1_write(fifo_mode)?; + } + + if let Some(ext_s) = config.ext_s { + self.gyro_fifo_ext_int_s_write(ext_s)?; + } + + if let Some(fifo_wtm) = config.fifo_wtm { + self.gyro_fifo_config0_write(fifo_wtm & 0x7F)?; + } + Ok(()) } } diff --git a/src/types.rs b/src/types.rs deleted file mode 100644 index 69adc22..0000000 --- a/src/types.rs +++ /dev/null @@ -1,543 +0,0 @@ -/// BMI088 errors. -#[derive(Debug)] -pub enum Error { - /// Interface communication error - Comm(CommE), - /// Invalid input data - InvalidInputData, - /// Invalid output data - InvalidOutputData, - /// Bad write, if a write fails - BadWrite, -} - -/// Accelerometer Power Mode -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum AccWakeSuspend { - /// Normal Mode - Active = 0x00, - /// Suspend Mode - Suspend = 0x03, -} - -impl TryFrom for AccWakeSuspend { - type Error = Error<()>; - - fn try_from(item: u8) -> Result { - match item { - 0x00 => Ok(AccWakeSuspend::Active), - 0x03 => Ok(AccWakeSuspend::Suspend), - _ => Err(Error::InvalidInputData), - } - } -} - -/// Accelerometer Power Enable -/// 0x00 Accelerometer off -// 0x04 Accelerometer on -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum AccOffOn { - /// Power off - Off = 0x00, - /// Power on - On = 0x04, -} - -impl TryFrom for AccOffOn { - type Error = Error<()>; - - fn try_from(item: u8) -> Result { - match item { - 0x00 => Ok(AccOffOn::Off), - 0x04 => Ok(AccOffOn::On), - _ => Err(Error::InvalidInputData), - } - } -} - -/// Accelerometer Error codes -#[derive(Debug, Clone, Copy, PartialEq)] -pub struct ErrCode { - /// Error code - pub error_code: bool, - /// Fatal error - pub fatal_error: bool, -} - -impl ErrCode { - /// ErrCode from a raw u8 register value - pub fn from_u8(value: u8) -> Self { - ErrCode { - error_code: (value & 0b001_1100) != 0, - fatal_error: (value & 0b000_0001) != 0, - } - } -} - -/// Sensor data read selector -#[derive(Debug, Clone, Copy, PartialEq)] -pub struct Sensor3DData { - /// X axis data - pub x: i16, - /// Y axis data - pub y: i16, - /// Z axis data - pub z: i16, -} - -/// Accelerometer bandwidth -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum AccBandwidth { - /// osr4 - X4 = 0x00, - /// osr2 - X2 = 0x01, - /// none - X1 = 0x02, -} - -impl TryFrom for AccBandwidth { - type Error = Error<()>; - - fn try_from(item: u8) -> Result { - match item { - 0x00 => Ok(AccBandwidth::X4), - 0x01 => Ok(AccBandwidth::X2), - 0x02 => Ok(AccBandwidth::X1), - _ => Err(Error::InvalidInputData), - } - } -} - -/// Accelerometer Data Rate -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum AccDataRate { - /// 12.5 Hz - Hz12_5 = 0x05, - /// 25 Hz - Hz25 = 0x06, - /// 50 Hz - Hz50 = 0x07, - /// 100 Hz - Hz100 = 0x08, - /// 200 Hz - Hz200 = 0x09, - /// 400 Hz - Hz400 = 0x0A, - /// 800 Hz - Hz800 = 0x0B, - /// 1600 Hz - Hz1600 = 0x0C, -} - -impl TryFrom for AccDataRate { - type Error = Error<()>; - - fn try_from(item: u8) -> Result { - match item { - 0x05 => Ok(AccDataRate::Hz12_5), - 0x06 => Ok(AccDataRate::Hz25), - 0x07 => Ok(AccDataRate::Hz50), - 0x08 => Ok(AccDataRate::Hz100), - 0x09 => Ok(AccDataRate::Hz200), - 0x0A => Ok(AccDataRate::Hz400), - 0x0B => Ok(AccDataRate::Hz800), - 0x0C => Ok(AccDataRate::Hz1600), - _ => Err(Error::InvalidInputData), - } - } -} - -/// Stuct to hold Conf register values -#[derive(Debug, Clone, Copy, PartialEq)] -pub struct AccConf { - /// Bandwidth - pub acc_bwp: AccBandwidth, - /// Data Rate - pub acc_odr: AccDataRate, -} - -impl TryFrom for AccConf { - type Error = Error<()>; - - fn try_from(item: u8) -> Result { - let acc_bwp = AccBandwidth::try_from((item & 0b0111_0000) >> 4)?; - let acc_odr = AccDataRate::try_from(item & 0b0000_1111)?; - Ok(AccConf { acc_bwp, acc_odr }) - } -} - -/// Accelerometer range configuration -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum AccRange { - /// ±3g - G3 = 0x00, - /// ±6g - G6 = 0x01, - /// ±12g - G12 = 0x02, - /// ±24g - G24 = 0x03, -} - -impl TryFrom for AccRange { - type Error = Error<()>; - - fn try_from(item: u8) -> Result { - match item & 0b0000_0011 { - // mask out the reserved bits - 0x00 => Ok(AccRange::G3), - 0x01 => Ok(AccRange::G6), - 0x02 => Ok(AccRange::G12), - 0x03 => Ok(AccRange::G24), - _ => Err(Error::InvalidInputData), - } - } -} - -/// Struct to hold a complete accelerometer configuration -#[derive(Debug, Clone, Copy, PartialEq)] - -pub struct AccelerometerConfig { - /// Bandwidth of the low pass filter - pub conf: AccConf, - /// Range - pub acc_range: AccRange, -} - -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum AccFifoModeConf { - Fifo = 0b0000_0010, - Stream = 0b0000_0011, -} - -impl From for AccFifoModeConf { - fn from(item: u8) -> Self { - match item { - 0b0000_0010 => AccFifoModeConf::Fifo, - 0b0000_0011 => AccFifoModeConf::Stream, - _ => AccFifoModeConf::Fifo, - } - } -} - -impl From for u8 { - fn from(item: AccFifoModeConf) -> Self { - match item { - AccFifoModeConf::Fifo => 0b0000_0010, - AccFifoModeConf::Stream => 0b0000_0011, - } - } -} - -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum GyroFifoModeConf { - Fifo = 0x40, - Stream = 0x80, -} - -impl From for GyroFifoModeConf { - fn from(item: u8) -> Self { - match item { - 0x40 => GyroFifoModeConf::Fifo, - 0x80 => GyroFifoModeConf::Stream, - _ => GyroFifoModeConf::Fifo, - } - } -} - -impl From for u8 { - fn from(item: GyroFifoModeConf) -> Self { - match item { - GyroFifoModeConf::Fifo => 0x40, - GyroFifoModeConf::Stream => 0x80, - } - } -} - -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum FifoExtIntPin { - /// INT3 - Int3 = 0x00, - /// INT4 - Int4 = 0x01, -} - -#[derive(Debug, Clone, Copy, PartialEq)] -pub struct FifoExtIntS { - pub ext_sync_mode: bool, - pub ext_sync_sel: FifoExtIntPin, -} - -impl From for FifoExtIntS { - fn from(item: u8) -> Self { - FifoExtIntS { - ext_sync_mode: (item & 0b0010_0000) != 0, - ext_sync_sel: if (item & 0b0001_0000) != 0 { - FifoExtIntPin::Int4 - } else { - FifoExtIntPin::Int3 - }, - } - } -} - -impl From for u8 { - fn from(item: FifoExtIntS) -> Self { - let mut value = 0b0000_0000; - if item.ext_sync_mode { - value |= 0b0010_0000; - } - match item.ext_sync_sel { - FifoExtIntPin::Int3 => (), - FifoExtIntPin::Int4 => value |= 0b0001_0000, - } - value - } -} - -/// Input pin configuration -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum IntPin { - /// Input - Input = 0b0001_0000, - /// Output - Output = 0b0000_1000, -} - -/// Pin behavior configuration -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum PinBehavior { - /// Push-pull - PushPull = 0b0000_0000, - /// Open-drain - OpenDrain = 0b0000_0100, -} - -/// Pin active configuration -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum PinActive { - /// Active high - ActiveHigh = 0b0000_0000, - /// Active low - ActiveLow = 0b0000_0010, -} - -/// Struct to hold the configuration of an input pin -#[derive(Debug, Clone, Copy, PartialEq)] -pub struct IntConfiguration { - /// Input or output - pub int_pin: IntPin, - /// Push-pull or open-drain - pub int_od: PinBehavior, - /// Active high or active low - pub int_lvl: PinActive, -} - -impl From for IntConfiguration { - fn from(item: u8) -> Self { - let int_pin = if (item & 0b0001_0000) != 0 { - IntPin::Input - } else { - IntPin::Output - }; - let int_od = if (item & 0b0000_0100) != 0 { - PinBehavior::OpenDrain - } else { - PinBehavior::PushPull - }; - let int_lvl = if (item & 0b0000_0010) != 0 { - PinActive::ActiveLow - } else { - PinActive::ActiveHigh - }; - IntConfiguration { - int_pin, - int_od, - int_lvl, - } - } -} - -impl From<&IntConfiguration> for u8 { - fn from(item: &IntConfiguration) -> Self { - let mut value = 0; - match item.int_pin { - IntPin::Input => value |= 0b0001_0000, - IntPin::Output => value |= 0b0000_1000, - } - match item.int_od { - PinBehavior::PushPull => value |= 0b0000_0000, - PinBehavior::OpenDrain => value |= 0b0000_0100, - } - match item.int_lvl { - PinActive::ActiveHigh => value |= 0b0000_0000, - PinActive::ActiveLow => value |= 0b0000_0010, - } - value - } -} - -impl From for u8 { - fn from(val: IntConfiguration) -> Self { - let mut value = 0; - match val.int_pin { - IntPin::Input => value |= 0b0001_0000, - IntPin::Output => value |= 0b0000_1000, - } - match val.int_od { - PinBehavior::PushPull => value |= 0b0000_0000, - PinBehavior::OpenDrain => value |= 0b0000_0100, - } - match val.int_lvl { - PinActive::ActiveHigh => value |= 0b0000_0000, - PinActive::ActiveLow => value |= 0b0000_0010, - } - value - } -} - -/// Data ready map for accelerometer interrupt -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum AccDrdyMap { - /// Map data ready to neither pin - None = 0b0000_0000, - /// Map data ready interrupt to output pin INT1 - Int1 = 0b0000_0100, - /// Map data ready interrupt to output pin INT2 - Int2 = 0b0100_0000, - /// Map data ready interrupt to output pin INT1 and INT2 - Int1Int2 = 0b0100_0100, -} - -impl From for AccDrdyMap { - fn from(item: u8) -> Self { - match item { - 0b0000_0000 => AccDrdyMap::None, - 0b0000_0100 => AccDrdyMap::Int1, - 0b0100_0000 => AccDrdyMap::Int2, - 0b0100_0100 => AccDrdyMap::Int1Int2, - _ => AccDrdyMap::None, - } - } -} - -/// Gyroscope range configuration -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum GyroRange { - /// ±2000°/s - Dps2000 = 0x00, - /// ±1000°/s - Dps1000 = 0x01, - /// ±500°/s - Dps500 = 0x02, - /// ±250°/s - Dps250 = 0x03, - /// ±125°/s - Dps125 = 0x04, -} - -impl TryFrom for GyroRange { - type Error = Error<()>; - - fn try_from(item: u8) -> Result { - match item { - 0x00 => Ok(GyroRange::Dps2000), - 0x01 => Ok(GyroRange::Dps1000), - 0x02 => Ok(GyroRange::Dps500), - 0x03 => Ok(GyroRange::Dps250), - 0x04 => Ok(GyroRange::Dps125), - _ => Err(Error::InvalidInputData), - } - } -} - -/// Gyroscope bandwidth configuration -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum GyroBandwidth { - /// 532 Hz, ODR 2000 Hz - Hz532 = 0x00, - /// 230 Hz, ODR 2000 Hz - Hz230 = 0x01, - /// 116 Hz, ODR 1000 Hz - Hz116 = 0x02, - /// 47 Hz, ODR 400 Hz - Hz47 = 0x03, - /// 23 Hz, ODR 200 Hz - Hz23 = 0x04, - /// 12 Hz, ODR 100 Hz - Hz12 = 0x05, - /// 64 Hz, ODR 200 Hz - Hz64 = 0x06, - /// 32 Hz, ODR 100 Hz - Hz32 = 0x07, -} - -impl TryFrom for GyroBandwidth { - type Error = Error<()>; - - fn try_from(item: u8) -> Result { - match item { - 0x00 => Ok(GyroBandwidth::Hz532), - 0x01 => Ok(GyroBandwidth::Hz230), - 0x02 => Ok(GyroBandwidth::Hz116), - 0x03 => Ok(GyroBandwidth::Hz47), - 0x04 => Ok(GyroBandwidth::Hz23), - 0x05 => Ok(GyroBandwidth::Hz12), - 0x06 => Ok(GyroBandwidth::Hz64), - 0x07 => Ok(GyroBandwidth::Hz32), - _ => Err(Error::InvalidInputData), - } - } -} - -/// Gyroscope power mode -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum GyroPowerMode { - /// Normal mode - Normal = 0x00, - /// Deep suspend mode - DeepSuspend = 0x20, - /// Suspend mode - Suspend = 0x80, -} - -impl TryFrom for GyroPowerMode { - type Error = Error<()>; - - fn try_from(item: u8) -> Result { - match item { - 0x00 => Ok(GyroPowerMode::Normal), - 0x20 => Ok(GyroPowerMode::DeepSuspend), - 0x80 => Ok(GyroPowerMode::Suspend), - _ => Err(Error::InvalidInputData), - } - } -} - -/// Gyroscope data ready interrupt mapping -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum GyroDrdyMap { - /// Map data ready to neither pin - None = 0x00, - /// Map data ready interrupt to output pin INT1 - Int3 = 0x01, - /// Map data ready interrupt to output pin INT2 - Int4 = 0x80, - /// Map data ready interrupt to output pin INT1 and INT2 - Int4Int5 = 0x81, -} - -impl TryFrom for GyroDrdyMap { - type Error = Error<()>; - - fn try_from(item: u8) -> Result { - match item { - 0x00 => Ok(GyroDrdyMap::None), - 0x01 => Ok(GyroDrdyMap::Int3), - 0x80 => Ok(GyroDrdyMap::Int4), - 0x81 => Ok(GyroDrdyMap::Int4Int5), - _ => Err(Error::InvalidInputData), - } - } -} diff --git a/src/types/acc.rs b/src/types/acc.rs new file mode 100644 index 0000000..e25e676 --- /dev/null +++ b/src/types/acc.rs @@ -0,0 +1,463 @@ +/// Accelerometer Power Conf +/// +/// ACC_PWR_CONF 0x7C +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum AccPowerConf { + Active = 0x00, + Suspend = 0x03, +} + +impl TryFrom for AccPowerConf { + type Error = crate::types::Error<()>; + + fn try_from(item: u8) -> Result { + match item { + 0x00 => Ok(AccPowerConf::Active), + 0x03 => Ok(AccPowerConf::Suspend), + _ => Err(crate::types::Error::InvalidInputData), + } + } +} + +/// Accelerometer Power Ctrl +/// +/// ACC_PWR_CTRL 0x7D +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum AccPowerCtrl { + Off = 0x00, + On = 0x04, +} + +impl TryFrom for AccPowerCtrl { + type Error = crate::types::Error<()>; + + fn try_from(item: u8) -> Result { + match item { + 0x00 => Ok(AccPowerCtrl::Off), + 0x04 => Ok(AccPowerCtrl::On), + _ => Err(crate::types::Error::InvalidInputData), + } + } +} + +/// Accelerometer Error codes +/// +/// ACC_ERR_REG 0x02 +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct ErrCode { + pub error_code: bool, + pub fatal_error: bool, +} + +impl ErrCode { + /// ErrCode from a raw u8 register value + pub fn from_u8(value: u8) -> Self { + ErrCode { + error_code: (value & 0b001_1100) != 0, + fatal_error: (value & 0b000_0001) != 0, + } + } +} + +/// Accelerometer bandwidth +/// +/// ACC_CONF 0x40 +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum AccBandwidth { + X4 = 0b1000_0000, + X2 = 0b1001_0000, + X1 = 0b1010_0000, +} + +impl TryFrom for AccBandwidth { + type Error = crate::types::Error<()>; + + fn try_from(item: u8) -> Result { + match item & 0b1111_0000 { + 0b1000_0000 => Ok(AccBandwidth::X4), + 0b1001_0000 => Ok(AccBandwidth::X2), + 0b1010_0000 => Ok(AccBandwidth::X1), + _ => Err(crate::types::Error::InvalidInputData), + } + } +} + +/// Accelerometer Data Rate +/// +/// ACC_CONF 0x40 +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum AccDataRate { + /// 12.5 Hz + Hz12_5 = 0b0000_0101, + /// 25 Hz + Hz25 = 0b0000_0110, + /// 50 Hz + Hz50 = 0b0000_0111, + /// 100 Hz + Hz100 = 0b0000_1000, + /// 200 Hz + Hz200 = 0b0000_1001, + /// 400 Hz + Hz400 = 0b0000_1010, + /// 800 Hz + Hz800 = 0b0000_1011, + /// 1600 Hz + Hz1600 = 0b0000_1100, +} + +impl TryFrom for AccDataRate { + type Error = crate::types::Error<()>; + + fn try_from(item: u8) -> Result { + match item & 0b0000_1111 { + 0b0000_0101 => Ok(AccDataRate::Hz12_5), + 0b0000_0110 => Ok(AccDataRate::Hz25), + 0b0000_0111 => Ok(AccDataRate::Hz50), + 0b0000_1000 => Ok(AccDataRate::Hz100), + 0b0000_1001 => Ok(AccDataRate::Hz200), + 0b0000_1010 => Ok(AccDataRate::Hz400), + 0b0000_1011 => Ok(AccDataRate::Hz800), + 0b0000_1100 => Ok(AccDataRate::Hz1600), + _ => Err(crate::types::Error::InvalidInputData), + } + } +} + +/// Accelerometer range configuration +/// +/// ACC_CONF 0x41 +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum AccRange { + /// ±3g + G3 = 0x00, + /// ±6g + G6 = 0x01, + /// ±12g + G12 = 0x02, + /// ±24g + G24 = 0x03, +} + +impl TryFrom for AccRange { + type Error = crate::types::Error<()>; + + fn try_from(item: u8) -> Result { + match item & 0b0000_0011 { + // mask out the reserved bits + 0x00 => Ok(AccRange::G3), + 0x01 => Ok(AccRange::G6), + 0x02 => Ok(AccRange::G12), + 0x03 => Ok(AccRange::G24), + _ => Err(crate::types::Error::InvalidInputData), + } + } +} + +/// FIFO configuration +/// +/// FIFO_CONFIG_0 0x48 +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum AccFifoMode { + Fifo = 0b0000_0010, + Stream = 0b0000_0011, +} + +impl From for AccFifoMode { + fn from(item: u8) -> Self { + match item { + 0b0000_0010 => AccFifoMode::Fifo, + 0b0000_0011 => AccFifoMode::Stream, + _ => AccFifoMode::Fifo, + } + } +} + +impl From for u8 { + fn from(item: AccFifoMode) -> Self { + match item { + AccFifoMode::Fifo => 0b0000_0010, + AccFifoMode::Stream => 0b0000_0011, + } + } +} + +/// FIFO configuration +/// +/// FIFO_CONFIG_1 0x49 +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct AccFifoConfig1 { + pub acc_en: bool, + pub int1_input_en: bool, + pub int2_input_en: bool, +} + +impl From for AccFifoConfig1 { + fn from(item: u8) -> Self { + AccFifoConfig1 { + acc_en: (item & 0b0100_0000) != 0, + int1_input_en: (item & 0b0000_1000) != 0, + int2_input_en: (item & 0b0000_0100) != 0, + } + } +} + +impl From for u8 { + fn from(item: AccFifoConfig1) -> u8 { + let mut value = 0b0001_0000; + if item.acc_en { + value |= 0b0100_0000; + } + if item.int1_input_en { + value |= 0b0000_1000; + } + if item.int2_input_en { + value |= 0b0000_0100; + } + value + } +} + +/// FIFO configuration +/// +/// FIFO_CONFIG_1 0x49 +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum FifoExtIntPin { + /// INT3 + Int3 = 0x00, + /// INT4 + Int4 = 0x01, +} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct FifoExtIntS { + pub ext_sync_mode: bool, + pub ext_sync_sel: FifoExtIntPin, +} + +impl From for FifoExtIntS { + fn from(item: u8) -> Self { + FifoExtIntS { + ext_sync_mode: (item & 0b0010_0000) != 0, + ext_sync_sel: if (item & 0b0001_0000) != 0 { + FifoExtIntPin::Int4 + } else { + FifoExtIntPin::Int3 + }, + } + } +} + +impl From for u8 { + fn from(item: FifoExtIntS) -> Self { + let mut value = 0b0000_0000; + if item.ext_sync_mode { + value |= 0b0010_0000; + } + match item.ext_sync_sel { + FifoExtIntPin::Int3 => (), + FifoExtIntPin::Int4 => value |= 0b0001_0000, + } + value + } +} + +/// Struct to hold the configuration of an input pin +/// +/// INT1_IO_CTRL 0x53 +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct AccIntConfiguration { + /// Input or output + pub int_pin: crate::types::IntPin, + /// Push-pull or open-drain + pub int_od: crate::types::PinBehavior, + /// Active high or active low + pub int_lvl: crate::types::PinActive, +} + +impl From for AccIntConfiguration { + fn from(item: u8) -> Self { + let int_pin = if (item & 0b0001_0000) != 0 { + crate::types::IntPin::Input + } else { + crate::types::IntPin::Output + }; + let int_od = if (item & 0b0000_0100) != 0 { + crate::types::PinBehavior::OpenDrain + } else { + crate::types::PinBehavior::PushPull + }; + let int_lvl = if (item & 0b0000_0010) != 0 { + crate::types::PinActive::ActiveLow + } else { + crate::types::PinActive::ActiveHigh + }; + AccIntConfiguration { + int_pin, + int_od, + int_lvl, + } + } +} + +impl From<&AccIntConfiguration> for u8 { + fn from(item: &AccIntConfiguration) -> Self { + let mut value = 0; + match item.int_pin { + crate::types::IntPin::Input => value |= 0b0001_0000, + crate::types::IntPin::Output => value |= 0b0000_1000, + } + match item.int_od { + crate::types::PinBehavior::PushPull => value |= 0b0000_0000, + crate::types::PinBehavior::OpenDrain => value |= 0b0000_0100, + } + match item.int_lvl { + crate::types::PinActive::ActiveHigh => value |= 0b0000_0000, + crate::types::PinActive::ActiveLow => value |= 0b0000_0010, + } + value + } +} + +impl From for u8 { + fn from(val: AccIntConfiguration) -> Self { + let mut value = 0; + match val.int_pin { + crate::types::IntPin::Input => value |= 0b0001_0000, + crate::types::IntPin::Output => value |= 0b0000_1000, + } + match val.int_od { + crate::types::PinBehavior::PushPull => value |= 0b0000_0000, + crate::types::PinBehavior::OpenDrain => value |= 0b0000_0100, + } + match val.int_lvl { + crate::types::PinActive::ActiveHigh => value |= 0b0000_0000, + crate::types::PinActive::ActiveLow => value |= 0b0000_0010, + } + value + } +} + +/// Interrupt mapping +/// +/// INT_MAP_DATA 0x58 +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum IntMap { + Drdy, + Fwm, + Ffull, + None, +} + +/// Interrupt mapping +/// +/// INT_MAP_DATA 0x58 +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct AccIntMap { + pub int1: IntMap, + pub int2: IntMap, +} + +impl From for AccIntMap { + fn from(item: u8) -> Self { + let int1 = match item & 0b0000_0111 { + 0b0000_0100 => IntMap::Drdy, + 0b0000_0010 => IntMap::Fwm, + 0b0000_0001 => IntMap::Ffull, + _ => IntMap::None, + }; + + let int2 = match item & 0b0111_0000 { + 0b0100_0000 => IntMap::Drdy, + 0b0010_0000 => IntMap::Fwm, + 0b0001_0000 => IntMap::Ffull, + _ => IntMap::None, + }; + + Self { int1, int2 } + } +} + +impl From for u8 { + fn from(item: AccIntMap) -> u8 { + let mut val = 0; + val |= match item.int1 { + IntMap::Drdy => 0b0000_0100, + IntMap::Fwm => 0b0000_0010, + IntMap::Ffull => 0b0000_0001, + IntMap::None => 0b0000_0000, + }; + val |= match item.int2 { + IntMap::Drdy => 0b0100_0000, + IntMap::Fwm => 0b0010_0000, + IntMap::Ffull => 0b0001_0000, + IntMap::None => 0b0000_0000, + }; + defmt::debug!("int map: {=u8:#04b}", val); + val + } +} + +/// Accelerometer FIFO Frame header +/// +/// FIFO_DATA 0x26 +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct AccIntFrameHeader { + pub int1: bool, + pub int2: bool, +} + +impl From for AccIntFrameHeader { + fn from(item: u8) -> Self { + AccIntFrameHeader { + int1: (item & 0b0000_0001) != 0, + int2: (item & 0b0000_0010) != 0, + } + } +} + +/// Accelerometer FIFO Frame header +/// +/// FIFO_DATA 0x26 +#[derive(Debug, Clone, Copy, PartialEq)] +#[repr(u8)] +pub enum AccFifoFrameHeader { + Acceleration(AccIntFrameHeader) = 0b1000_0100, + Skip = 0b0100_0000, + SensorTime = 0b0100_0100, + InputConfig = 0b0100_1000, + SampleSkip = 0b0101_0000, + Invalid = 0b0000_0000, + End = 0b1000_0000, +} + +impl From for AccFifoFrameHeader { + fn from(item: u8) -> Self { + match item & 0b1111_1100 { + 0b1000_0100 => { + let status = AccIntFrameHeader::from(item); + AccFifoFrameHeader::Acceleration(status) + } + 0b0100_0000 => AccFifoFrameHeader::Skip, + 0b0100_0100 => AccFifoFrameHeader::SensorTime, + 0b0100_1000 => AccFifoFrameHeader::InputConfig, + 0b0101_0000 => AccFifoFrameHeader::SampleSkip, + 0b1000_0000 => AccFifoFrameHeader::End, + _ => AccFifoFrameHeader::Invalid, + } + } +} + +impl AccFifoFrameHeader { + /// number of bytes to read subsequently for the given frame type + pub fn bytes_to_read(&self) -> usize { + match self { + AccFifoFrameHeader::Acceleration(_) => 6, + AccFifoFrameHeader::Skip => 1, + AccFifoFrameHeader::SensorTime => 3, + AccFifoFrameHeader::InputConfig => 1, + AccFifoFrameHeader::SampleSkip => 1, + AccFifoFrameHeader::Invalid => 1, + AccFifoFrameHeader::End => 1, + } + } +} diff --git a/src/types/gyro.rs b/src/types/gyro.rs new file mode 100644 index 0000000..3f75b24 --- /dev/null +++ b/src/types/gyro.rs @@ -0,0 +1,297 @@ +/// Gyroscope range configuration +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum GyroRange { + /// ±2000°/s + Dps2000 = 0x00, + /// ±1000°/s + Dps1000 = 0x01, + /// ±500°/s + Dps500 = 0x02, + /// ±250°/s + Dps250 = 0x03, + /// ±125°/s + Dps125 = 0x04, +} + +impl TryFrom for GyroRange { + type Error = crate::types::Error<()>; + + fn try_from(item: u8) -> Result { + match item { + 0x00 => Ok(GyroRange::Dps2000), + 0x01 => Ok(GyroRange::Dps1000), + 0x02 => Ok(GyroRange::Dps500), + 0x03 => Ok(GyroRange::Dps250), + 0x04 => Ok(GyroRange::Dps125), + _ => Err(crate::types::Error::InvalidInputData), + } + } +} + +/// Gyroscope bandwidth configuration +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum GyroBandwidth { + /// 532 Hz, ODR 2000 Hz + Hz532 = 0x00, + /// 230 Hz, ODR 2000 Hz + Hz230 = 0x01, + /// 116 Hz, ODR 1000 Hz + Hz116 = 0x02, + /// 47 Hz, ODR 400 Hz + Hz47 = 0x03, + /// 23 Hz, ODR 200 Hz + Hz23 = 0x04, + /// 12 Hz, ODR 100 Hz + Hz12 = 0x05, + /// 64 Hz, ODR 200 Hz + Hz64 = 0x06, + /// 32 Hz, ODR 100 Hz + Hz32 = 0x07, +} + +impl TryFrom for GyroBandwidth { + type Error = crate::types::Error<()>; + + fn try_from(item: u8) -> Result { + match item { + 0x00 => Ok(GyroBandwidth::Hz532), + 0x01 => Ok(GyroBandwidth::Hz230), + 0x02 => Ok(GyroBandwidth::Hz116), + 0x03 => Ok(GyroBandwidth::Hz47), + 0x04 => Ok(GyroBandwidth::Hz23), + 0x05 => Ok(GyroBandwidth::Hz12), + 0x06 => Ok(GyroBandwidth::Hz64), + 0x07 => Ok(GyroBandwidth::Hz32), + _ => Err(crate::types::Error::InvalidInputData), + } + } +} + +/// Gyroscope power mode +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum GyroPowerConf { + /// Normal mode + Normal = 0x00, + /// Deep suspend mode + DeepSuspend = 0x20, + /// Suspend mode + Suspend = 0x80, +} + +impl TryFrom for GyroPowerConf { + type Error = crate::types::Error<()>; + + fn try_from(item: u8) -> Result { + match item { + 0x00 => Ok(GyroPowerConf::Normal), + 0x20 => Ok(GyroPowerConf::DeepSuspend), + 0x80 => Ok(GyroPowerConf::Suspend), + _ => Err(crate::types::Error::InvalidInputData), + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum GyroIntMap { + Drdy, + Fifo, + DrdyFifo, + None, +} + +/// Gyroscope data ready interrupt mapping +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct GyroIntMapping { + pub int3: GyroIntMap, + pub int4: GyroIntMap, +} + +impl From for GyroIntMapping { + fn from(item: u8) -> Self { + Self { + int3: match item & 0b0000_0101 { + 0x05 => GyroIntMap::DrdyFifo, + 0x01 => GyroIntMap::Drdy, + 0x04 => GyroIntMap::Fifo, + 0x00 => GyroIntMap::None, + _ => GyroIntMap::None, + }, + int4: match item & 0b1010_0000 { + 0xA0 => GyroIntMap::DrdyFifo, + 0x80 => GyroIntMap::Drdy, + 0x20 => GyroIntMap::Fifo, + 0x00 => GyroIntMap::None, + _ => GyroIntMap::None, + }, + } + } +} + +impl From for u8 { + fn from(item: GyroIntMapping) -> Self { + let mut val = 0; + match item.int3 { + GyroIntMap::Drdy => val |= 0x01, + GyroIntMap::Fifo => val |= 0x04, + GyroIntMap::DrdyFifo => val |= 0x05, + GyroIntMap::None => {} + } + match item.int4 { + GyroIntMap::Drdy => val |= 0x80, + GyroIntMap::Fifo => val |= 0x20, + GyroIntMap::DrdyFifo => val |= 0xA0, + GyroIntMap::None => {} + } + val + } +} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum GyroFifoModeConf { + Fifo = 0x40, + Stream = 0x80, +} + +impl From for GyroFifoModeConf { + fn from(item: u8) -> Self { + match item { + 0x40 => GyroFifoModeConf::Fifo, + 0x80 => GyroFifoModeConf::Stream, + _ => GyroFifoModeConf::Fifo, + } + } +} + +impl From for u8 { + fn from(item: GyroFifoModeConf) -> Self { + match item { + GyroFifoModeConf::Fifo => 0x40, + GyroFifoModeConf::Stream => 0x80, + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct GyroIntEnable { + data: bool, + fifo: bool, +} + +impl From for GyroIntEnable { + fn from(item: u8) -> Self { + GyroIntEnable { + data: (item & 0b1000_0000) != 0, + fifo: (item & 0b0100_0000) != 0, + } + } +} + +impl From for u8 { + fn from(item: GyroIntEnable) -> Self { + let mut val = 0; + if item.data { + val |= 0b1000_0000; + } + if item.fifo { + val |= 0b0100_0000; + } + val + } +} + +pub struct GyroIntPinConfiguration { + int3_od: crate::types::PinBehavior, + int3_lvl: crate::types::PinActive, + int4_od: crate::types::PinBehavior, + int4_lvl: crate::types::PinActive, +} + +impl From for GyroIntPinConfiguration { + fn from(item: u8) -> Self { + GyroIntPinConfiguration { + int3_od: if (item & 0b0000_0010) != 0 { + crate::types::PinBehavior::OpenDrain + } else { + crate::types::PinBehavior::PushPull + }, + int3_lvl: if (item & 0b0000_0001) != 0 { + crate::types::PinActive::ActiveLow + } else { + crate::types::PinActive::ActiveHigh + }, + int4_od: if (item & 0b0000_1000) != 0 { + crate::types::PinBehavior::OpenDrain + } else { + crate::types::PinBehavior::PushPull + }, + int4_lvl: if (item & 0b0000_0100) != 0 { + crate::types::PinActive::ActiveLow + } else { + crate::types::PinActive::ActiveHigh + }, + } + } +} + +impl From for u8 { + fn from(item: GyroIntPinConfiguration) -> Self { + let mut val = 0; + if item.int3_od == crate::types::PinBehavior::OpenDrain { + val |= 0b0000_0010; + } + if item.int3_lvl == crate::types::PinActive::ActiveLow { + val |= 0b0000_0001; + } + if item.int4_od == crate::types::PinBehavior::OpenDrain { + val |= 0b0000_1000; + } + if item.int4_lvl == crate::types::PinActive::ActiveLow { + val |= 0b0000_0100; + } + val + } +} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum GyroIntPin { + Int3 = 0x00, + Int4 = 0x01, +} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct GyroExtIntS { + enable: bool, + source: GyroIntPin, +} + +impl From for GyroExtIntS { + fn from(item: u8) -> Self { + GyroExtIntS { + enable: (item & 0b0010_0000) != 0, + source: if (item & 0b0001_0000) != 0 { + GyroIntPin::Int4 + } else { + GyroIntPin::Int3 + }, + } + } +} + +impl From for u8 { + fn from(item: GyroExtIntS) -> Self { + let mut val = 0; + if item.enable { + val |= 0b0010_0000; + } + if item.source == GyroIntPin::Int4 { + val |= 0b0001_0000; + } + val + } +} + +pub struct GyroIntConfiguration { + pub map: GyroIntMapping, + pub conf: GyroIntPinConfiguration, + pub enable: GyroIntEnable, +} diff --git a/src/types/mod.rs b/src/types/mod.rs new file mode 100644 index 0000000..fd8121e --- /dev/null +++ b/src/types/mod.rs @@ -0,0 +1,59 @@ +pub mod acc; +pub mod gyro; + +/// Input pin configuration +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum IntPin { + /// Input + Input = 0b0001_0000, + /// Output + Output = 0b0000_1000, +} + +/// Pin behavior configuration +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum PinBehavior { + /// Push-pull + PushPull = 0b0000_0000, + /// Open-drain + OpenDrain = 0b0000_0100, +} + +/// Pin active configuration +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum PinActive { + /// Active high + ActiveHigh = 0b0000_0000, + /// Active low + ActiveLow = 0b0000_0010, +} + +/// BMI088 errors. +#[derive(Debug)] +pub enum Error { + /// Interface communication error + Comm(CommE), + /// Invalid input data + InvalidInputData, + /// Invalid output data + InvalidOutputData, + /// Bad write, if a write fails + BadWrite, +} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct Sensor3DData { + pub x: i16, + pub y: i16, + pub z: i16, +} + +impl Sensor3DData { + /// from a 6-byte slice, construct a Sensor3DData + pub fn from_le_slice(bytes: &[u8]) -> Self { + let x = i16::from_le_bytes(bytes[0..2].try_into().unwrap()); + let y = i16::from_le_bytes(bytes[2..4].try_into().unwrap()); + let z = i16::from_le_bytes(bytes[4..6].try_into().unwrap()); + Self { x, y, z } + } +}