commit b368f6200c3ec9a84114e8ca137eb5fe0aa08e29 Author: baldeau Date: Sun Feb 23 21:24:37 2025 +0100 initial commit diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..70f9eae --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,2 @@ +[registries.crates-io] +protocol = "sparse" diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..b634d85 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.pdf filter=lfs diff=lfs merge=lfs -text diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3803319 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/target +/Cargo.lock +.vscode/ diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..604f267 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,34 @@ +default: + # https://gitlab.com/mtczekajlo/lsm6ds3tr-rs-docker + image: registry.gitlab.com/mtczekajlo/lsm6ds3tr-rs-docker:f3598e64faee1b7e66b2bb792fce3290 + +stages: + - check + - build + - test + +check: + stage: check + dependencies: [] + script: + - ci/jobs/check + +doc: + stage: check + dependencies: [] + script: + - ci/jobs/doc + +build: + stage: build + needs: ["check"] + dependencies: ["check"] + script: + - ci/jobs/build + +test: + stage: test + needs: ["build"] + dependencies: ["build"] + script: + - ci/jobs/test diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..f644cf9 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "lsm6ds3tr" +version = "0.3.0" +edition = "2021" +description = "LSM6DS3TR 6-axis (DOF) IMU accelerometer & gyroscope rust driver library" +authors = ["Marcin Czekajło "] +repository = "https://gitlab.com/mtczekajlo/lsm6ds3tr-rs" +readme = "readme.md" +license = "MIT" +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +defmt = { version = "0.3.6", optional = true } +embedded-hal = "1.0.0" +heapless = "0.8.0" + +[features] +default = [] +defmt = ["dep:defmt"] diff --git a/ci/build b/ci/build new file mode 100755 index 0000000..30f8289 --- /dev/null +++ b/ci/build @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +set -xEeuo pipefail + +cd "$(git rev-parse --show-toplevel)" + +cargo build "$@" diff --git a/ci/cargo-sort b/ci/cargo-sort new file mode 100755 index 0000000..a2d4d99 --- /dev/null +++ b/ci/cargo-sort @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +set -xEeuo pipefail + +cd "$(git rev-parse --show-toplevel)" + +cargo sort --check "$@" diff --git a/ci/check b/ci/check new file mode 100755 index 0000000..78e9ebb --- /dev/null +++ b/ci/check @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +set -xEeuo pipefail + +cd "$(git rev-parse --show-toplevel)" + +cargo check "$@" diff --git a/ci/clippy b/ci/clippy new file mode 100755 index 0000000..01072a5 --- /dev/null +++ b/ci/clippy @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +set -xEeuo pipefail + +cd "$(git rev-parse --show-toplevel)" + +cargo clippy "$@" -- --deny warnings diff --git a/ci/doc b/ci/doc new file mode 100755 index 0000000..7f88c39 --- /dev/null +++ b/ci/doc @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +set -xEeuo pipefail + +cd "$(git rev-parse --show-toplevel)" + +cargo doc "$@" diff --git a/ci/format b/ci/format new file mode 100755 index 0000000..de5f997 --- /dev/null +++ b/ci/format @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +set -xEeuo pipefail + +cd "$(git rev-parse --show-toplevel)" + +cargo fmt -- --check diff --git a/ci/jobs/build b/ci/jobs/build new file mode 100755 index 0000000..074918e --- /dev/null +++ b/ci/jobs/build @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +set -xEeuo pipefail + +cd "$(git rev-parse --show-toplevel)" + +ci/build +ci/build --features defmt +ci/build --release +ci/build --features defmt --release diff --git a/ci/jobs/check b/ci/jobs/check new file mode 100755 index 0000000..f434df0 --- /dev/null +++ b/ci/jobs/check @@ -0,0 +1,15 @@ +#!/usr/bin/env bash +set -xEeuo pipefail + +cd "$(git rev-parse --show-toplevel)" + +ci/format +ci/check +ci/check --features defmt +ci/check --release +ci/check --features defmt --release +ci/clippy +ci/clippy --release +ci/cargo-sort +ci/udeps +ci/udeps --release diff --git a/ci/jobs/doc b/ci/jobs/doc new file mode 100755 index 0000000..d1722a7 --- /dev/null +++ b/ci/jobs/doc @@ -0,0 +1,7 @@ +#!/usr/bin/env bash +set -xEeuo pipefail + +cd "$(git rev-parse --show-toplevel)" + +ci/doc +ci/doc --features defmt diff --git a/ci/jobs/test b/ci/jobs/test new file mode 100755 index 0000000..1634922 --- /dev/null +++ b/ci/jobs/test @@ -0,0 +1,7 @@ +#!/usr/bin/env bash +set -xEeuo pipefail + +cd "$(git rev-parse --show-toplevel)" + +ci/test +ci/test --release diff --git a/ci/pipeline b/ci/pipeline new file mode 100755 index 0000000..19f0800 --- /dev/null +++ b/ci/pipeline @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +set -xEeuo pipefail + +cd "$(git rev-parse --show-toplevel)" + +./ci/jobs/check +./ci/jobs/build +./ci/jobs/test +./ci/jobs/doc diff --git a/ci/test b/ci/test new file mode 100755 index 0000000..a7a8a35 --- /dev/null +++ b/ci/test @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +set -xEeuo pipefail + +cd "$(git rev-parse --show-toplevel)" + +cargo test "$@" diff --git a/ci/udeps b/ci/udeps new file mode 100755 index 0000000..eac9164 --- /dev/null +++ b/ci/udeps @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +set -xEeuo pipefail + +cd "$(git rev-parse --show-toplevel)" + +cargo +nightly udeps "$@" diff --git a/docs/1point-or-3point-tumble-sensor-calibration-DT0105.pdf b/docs/1point-or-3point-tumble-sensor-calibration-DT0105.pdf new file mode 100644 index 0000000..2493979 --- /dev/null +++ b/docs/1point-or-3point-tumble-sensor-calibration-DT0105.pdf @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0e2089de13ab537736e174c76eaee17fc6ac164c69324862716859688db9e963 +size 135994 diff --git a/docs/LSM6DS3TR-C-application-note-AN5130.pdf b/docs/LSM6DS3TR-C-application-note-AN5130.pdf new file mode 100644 index 0000000..85bcd96 --- /dev/null +++ b/docs/LSM6DS3TR-C-application-note-AN5130.pdf @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:15b90b0ccca09f1cd6f561ecc67c7620714f452ad3f2c07e0f1cbce7e1ebb1c6 +size 1577264 diff --git a/docs/LSM6DS3TR-C-datasheet-DS11937.pdf b/docs/LSM6DS3TR-C-datasheet-DS11937.pdf new file mode 100644 index 0000000..73e1db1 --- /dev/null +++ b/docs/LSM6DS3TR-C-datasheet-DS11937.pdf @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:251737db70e3b6cdc217a5681dd4634a05faffd5288aff6a576078a6464a1b2b +size 1839683 diff --git a/docs/computing-tilt-measurement-and-tiltcompensated-ecompass-DT0058.pdf b/docs/computing-tilt-measurement-and-tiltcompensated-ecompass-DT0058.pdf new file mode 100644 index 0000000..439255f --- /dev/null +++ b/docs/computing-tilt-measurement-and-tiltcompensated-ecompass-DT0058.pdf @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:541e1501d15cc3a1be1e45f4e98a91d4005c9e94b471407ed3b9775fe4023c5d +size 220256 diff --git a/docs/exploiting-the-gyroscope-to-update-tilt-measurement-and-ecompass-DT0060.pdf b/docs/exploiting-the-gyroscope-to-update-tilt-measurement-and-ecompass-DT0060.pdf new file mode 100644 index 0000000..9aa7f1c --- /dev/null +++ b/docs/exploiting-the-gyroscope-to-update-tilt-measurement-and-ecompass-DT0060.pdf @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0837f80f41f215a1ee9556bdb0c3637f0c36e23e422320832dfa223b3bd87ea0 +size 202544 diff --git a/docs/noise-analysis-and-identification-in-mems-sensors-allan-time-hadamard-overlapping-modified-total-variance-DT0064.pdf b/docs/noise-analysis-and-identification-in-mems-sensors-allan-time-hadamard-overlapping-modified-total-variance-DT0064.pdf new file mode 100644 index 0000000..1f9c205 --- /dev/null +++ b/docs/noise-analysis-and-identification-in-mems-sensors-allan-time-hadamard-overlapping-modified-total-variance-DT0064.pdf @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:21ad5fa9ea2ae4076585c569bd8ffc502221fe9b700b2a7a04767b412535ff48 +size 631742 diff --git a/docs/residual-linear-acceleration-by-gravity-subtraction-to-enable-deadreckoning-DT0106.pdf b/docs/residual-linear-acceleration-by-gravity-subtraction-to-enable-deadreckoning-DT0106.pdf new file mode 100644 index 0000000..e9f572c --- /dev/null +++ b/docs/residual-linear-acceleration-by-gravity-subtraction-to-enable-deadreckoning-DT0106.pdf @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f6eb4483119f6fadc3088f7318078743a58c6bca5738383ea98f37c3d0066e19 +size 197066 diff --git a/license b/license new file mode 100644 index 0000000..6dfe8a6 --- /dev/null +++ b/license @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Marcin Czekajło + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..3ec2f77 --- /dev/null +++ b/readme.md @@ -0,0 +1,5 @@ +# LSM6DS3TR-C Rust + +LSM6DS3TR-C 6-axis (DOF) IMU accelerometer & gyroscope rust driver library. + +_Inspired by [LSM9DS1 rust driver](https://gitlab.com/mtczekajlo/lsm6ds3tr-rs)._ diff --git a/src/consts.rs b/src/consts.rs new file mode 100644 index 0000000..829e53e --- /dev/null +++ b/src/consts.rs @@ -0,0 +1,8 @@ +/// LSM6DS3TR-C's ID +pub const LSM6DS3TR_ID: u8 = 0x6A; + +/// Temperature scale = 256 LSB/°C +pub const TEMP_SCALE: f32 = 256.0; + +/// The output of the temperature sensor is 0 (typ.) at 25 °C +pub const TEMP_BIAS: f32 = 25.0; diff --git a/src/data.rs b/src/data.rs new file mode 100644 index 0000000..f6e8b5c --- /dev/null +++ b/src/data.rs @@ -0,0 +1,28 @@ +use core::fmt::{Display, Result}; + +#[allow(clippy::upper_case_acronyms)] +#[derive(Default)] +pub struct XYZ { + pub x: T, + pub y: T, + pub z: T, +} + +impl Display for XYZ +where + T: Display, +{ + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result { + write!(f, "X:{} Y:{} Z:{}", self.x, self.y, self.z) + } +} + +#[cfg(feature = "defmt")] +impl defmt::Format for XYZ +where + T: defmt::Format, +{ + fn format(&self, f: defmt::Formatter) { + defmt::write!(f, "X:{} Y:{} Z:{}", self.x, self.y, self.z) + } +} diff --git a/src/interface/i2c.rs b/src/interface/i2c.rs new file mode 100644 index 0000000..18c7c69 --- /dev/null +++ b/src/interface/i2c.rs @@ -0,0 +1,34 @@ +use embedded_hal::i2c::I2c; + +use super::Interface; + +const I2C_ADDRESS: u8 = 0x6A; + +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(Debug)] +pub enum InterfaceE { + Comm(CommE), +} + +/// I2C communication interface +pub struct I2cInterface { + i2c: I2C, +} + +impl I2cInterface { + pub fn new(i2c: I2C) -> Self { + Self { i2c } + } +} + +impl Interface for I2cInterface { + type Error = I2C::Error; + + fn write(&mut self, addr: u8, value: u8) -> Result<(), Self::Error> { + self.i2c.write(I2C_ADDRESS, &[addr, value]) + } + + fn read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { + self.i2c.write_read(I2C_ADDRESS, &[addr], buffer) + } +} diff --git a/src/interface/mod.rs b/src/interface/mod.rs new file mode 100644 index 0000000..48ca474 --- /dev/null +++ b/src/interface/mod.rs @@ -0,0 +1,13 @@ +pub mod i2c; +pub mod spi; +pub use self::i2c::I2cInterface; +pub use self::spi::SpiInterface; + +/// Communication interface +pub trait Interface { + type Error; + + fn write(&mut self, addr: u8, value: u8) -> Result<(), Self::Error>; + + fn read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Self::Error>; +} diff --git a/src/interface/spi.rs b/src/interface/spi.rs new file mode 100644 index 0000000..7535726 --- /dev/null +++ b/src/interface/spi.rs @@ -0,0 +1,34 @@ +use embedded_hal::spi::{Operation, SpiDevice}; + +use super::Interface; + +/// SPI communication interface +pub struct SpiInterface { + spi: SPI, +} + +impl SpiInterface { + pub fn new(spi: SPI) -> Self { + Self { spi } + } +} + +impl Interface for SpiInterface +where + SPI: SpiDevice, +{ + type Error = SPI::Error; + + fn write(&mut self, addr: u8, value: u8) -> Result<(), Self::Error> { + let bytes = [addr, value]; + self.spi.write(&bytes) + } + + fn read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { + self.spi.transaction(&mut [ + Operation::Write(&[0b1000_0000 | addr]), + Operation::Read(buffer), + ])?; + Ok(()) + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..5461e8f --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,220 @@ +#![cfg_attr(not(test), no_std)] +#![allow(dead_code)] +#![allow(async_fn_in_trait)] + +mod consts; +mod data; +pub mod interface; +pub mod registers; +mod settings; + +use heapless::Vec; + +use consts::*; +pub use data::XYZ; +use interface::Interface; +pub use registers::{AccelSampleRate, AccelScale, InactivityGyroMode}; +use registers::{ + Ctrl3C, GyroSampleRate, GyroScale, RegisterAddress, RegisterBits, RegisterConfig, + RegisterValue, TapSrc, WakeUpSrc, +}; +pub use settings::{ + irq::{InterruptRoute, TapIrqSettings, TapRecognitionMode}, + AccelSettings, GyroSettings, IrqSettings, LsmSettings, +}; + +/// Device driver +pub struct LSM6DS3TR +where + I: Interface, +{ + interface: I, + pub settings: LsmSettings, +} + +impl LSM6DS3TR +where + I: Interface, +{ + /// Returns uninitialized device object with default settings + pub fn new(interface: I) -> Self { + Self { + interface, + settings: Default::default(), + } + } + + /// Returns uninitialized device object with provided settings + pub fn with_settings(mut self, settings: LsmSettings) -> Self { + self.settings = settings; + self + } + + /// Initializes device with stored settings + pub fn init(&mut self) -> Result<(), I::Error> { + self.init_accel()?; + self.init_gyro()?; + self.init_irqs()?; + self.init_other()?; + Ok(()) + } + + /// Returns if device is reachable + pub fn is_reachable(&mut self) -> Result { + Ok(self.read_register(RegisterAddress::WHO_AM_I.address())? == LSM6DS3TR_ID) + } + + /// Performs a software reset + pub fn software_reset(&mut self) -> Result<(), I::Error> { + let ctrl3_c = Ctrl3C { + software_reset: RegisterBits::new(1), + ..Default::default() + }; + self.write_register_config(ctrl3_c.config())?; + Ok(()) + } + + /// Initializes accelerometer with stored settings + pub fn init_accel(&mut self) -> Result<(), I::Error> { + self.write_register_config(self.settings.accel.config())?; + Ok(()) + } + + /// Initializes gyroscope with stored settings + pub fn init_gyro(&mut self) -> Result<(), I::Error> { + self.write_register_config(self.settings.gyro.config())?; + Ok(()) + } + + /// Initializes interrupts with stored settings + pub fn init_irqs(&mut self) -> Result<(), I::Error> { + for config in self.settings.irq.configs() { + self.write_register_config(config)?; + } + Ok(()) + } + + /// Initializes other options with stored settings + pub fn init_other(&mut self) -> Result<(), I::Error> { + if self.settings.low_performance_mode { + self.write_register(RegisterAddress::CTRL6_C.address(), 1 << 4)?; // TODO make it right like the others + self.write_register(RegisterAddress::CTRL7_G.address(), 1 << 7)?; // TODO make it right like the others + } + Ok(()) + } + + /// Returns accelerometer raw readings + pub fn read_accel_raw(&mut self) -> Result, I::Error> { + self.read_sensor_raw(RegisterAddress::OUTX_L_XL.address()) + } + + /// Returns accelerometer scaled readings \[g] + pub fn read_accel(&mut self) -> Result, I::Error> { + let xyz = self.read_accel_raw()?; + let sensitivity = self.settings.accel.scale.sensitivity(); + Ok(XYZ { + x: xyz.x as f32 * sensitivity, + y: xyz.y as f32 * sensitivity, + z: xyz.z as f32 * sensitivity, + }) + } + + /// Returns gyroscope raw readings + pub fn read_gyro_raw(&mut self) -> Result, I::Error> { + self.read_sensor_raw(RegisterAddress::OUTX_L_G.address()) + } + + /// Returns gyroscope scaled readings [°/s] + pub fn read_gyro(&mut self) -> Result, I::Error> { + let xyz = self.read_gyro_raw()?; + let sensitivity = self.settings.gyro.scale.sensitivity(); + Ok(XYZ { + x: xyz.x as f32 * sensitivity, + y: xyz.y as f32 * sensitivity, + z: xyz.z as f32 * sensitivity, + }) + } + + /// Returns temperature sensor raw reading + pub fn read_temp_raw(&mut self) -> Result { + let mut bytes = [0u8; 2]; + self.interface + .read(RegisterAddress::OUT_TEMP_L.address(), &mut bytes)?; + let temp: i16 = (bytes[1] as i16) << 8 | bytes[0] as i16; + Ok(temp) + } + + /// Returns temperature sensor scaled reading [°C] + pub fn read_temp(&mut self) -> Result { + let temp = self.read_temp_raw()?; + Ok(temp as f32 / TEMP_SCALE + TEMP_BIAS) + } + + /// Returns last interrupt sources + pub fn read_interrupt_sources(&mut self) -> Result, I::Error> { + let mut wake_up_src = WakeUpSrc::default(); + let mut tap_src = TapSrc::default(); + // TODO add FUNC_SRC1 reading + // TODO add FUNC_SRC2 reading + wake_up_src = self.read_register(wake_up_src.address())?.into(); + tap_src = self.read_register(tap_src.address())?.into(); + let mut irq_sources = Vec::new(); + for source in wake_up_src.get_irq_sources() { + irq_sources.push(source).unwrap(); + } + for source in tap_src.get_irq_sources() { + irq_sources.push(source).unwrap(); + } + Ok(irq_sources) + } + + fn read_sensor_raw(&mut self, addr: u8) -> Result, I::Error> { + let mut bytes = [0u8; 6]; + self.interface.read(addr, &mut bytes)?; + let x: i16 = (bytes[1] as i16) << 8 | bytes[0] as i16; + let y: i16 = (bytes[3] as i16) << 8 | bytes[2] as i16; + let z: i16 = (bytes[5] as i16) << 8 | bytes[4] as i16; + Ok(XYZ { x, y, z }) + } + + fn read_register(&mut self, address: u8) -> Result { + let mut value = [0u8]; + self.interface.read(address, &mut value)?; + Ok(value[0]) + } + + fn write_register(&mut self, address: u8, value: u8) -> Result<(), I::Error> { + self.interface.write(address, value)?; + Ok(()) + } + + fn read_register_config(&mut self, address: u8) -> Result { + let mut value = [0u8]; + self.interface.read(address, &mut value)?; + let value = value[0]; + Ok(RegisterConfig { address, value }) + } + + fn write_register_config(&mut self, register_config: RegisterConfig) -> Result<(), I::Error> { + self.write_register(register_config.address, register_config.value)?; + Ok(()) + } +} + +/// Interrupt sources +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum IrqSource { + FreeFall, + Sleep, + WakeUp, + WakeUpOnX, + WakeUpOnY, + WakeUpOnZ, + Tap, + SingleTap, + DoubleTap, + TapOnX, + TapOnY, + TapOnZ, +} diff --git a/src/registers/addresses.rs b/src/registers/addresses.rs new file mode 100644 index 0000000..5f54c9d --- /dev/null +++ b/src/registers/addresses.rs @@ -0,0 +1,540 @@ +/// Registers list +#[allow(non_camel_case_types)] +#[derive(Clone, Copy)] +pub enum RegisterAddress { + // RESERVED = 0x00, + /// Embedded functions configuration register + FUNC_CFG_ACCESS = 0x01, + // RESERVED = [0x02..0x03], + /// Sensor sync configuration register + SENSOR_SYNC_TIME_FRAME = 0x04, + /// Sensor sync configuration register + SENSOR_SYNC_RES_RATIO = 0x05, + /// FIFO configuration registers + FIFO_CTRL1 = 0x06, + /// FIFO configuration registers + FIFO_CTRL2 = 0x07, + /// FIFO configuration registers + FIFO_CTRL3 = 0x08, + /// FIFO configuration registers + FIFO_CTRL4 = 0x09, + /// FIFO configuration registers + FIFO_CTRL5 = 0x0A, + /// (no comment) + DRDY_PULSE_CFG_G = 0x0B, + // RESERVED = 0x0C, + /// INT1 pin control + INT1_CTRL = 0x0D, + /// INT2 pin control + INT2_CTRL = 0x0E, + /// Who I am ID + WHO_AM_I = 0x0F, + /// Accelerometer and gyroscope control registers + CTRL1_XL = 0x10, + /// Accelerometer and gyroscope control registers + CTRL2_G = 0x11, + /// Accelerometer and gyroscope control registers + CTRL3_C = 0x12, + /// Accelerometer and gyroscope control registers + CTRL4_C = 0x13, + /// Accelerometer and gyroscope control registers + CTRL5_C = 0x14, + /// Accelerometer and gyroscope control registers + CTRL6_C = 0x15, + /// Accelerometer and gyroscope control registers + CTRL7_G = 0x16, + /// Accelerometer and gyroscope control registers + CTRL8_XL = 0x17, + /// Accelerometer and gyroscope control registers + CTRL9_XL = 0x18, + /// Accelerometer and gyroscope control registers + CTRL10_C = 0x19, + /// I2C master configuration register + MASTER_CONFIG = 0x1A, + /// Interrupt registers + WAKE_UP_SRC = 0x1B, + /// Interrupt registers + TAP_SRC = 0x1C, + /// Interrupt registers + D6D_SRC = 0x1D, + /// Status data register for user interface + STATUS_REG = 0x1E, + // RESERVED = 0x1F, + /// Temperature output data registers + OUT_TEMP_L = 0x20, + /// Temperature output data registers + OUT_TEMP_H = 0x21, + /// Gyroscope output registers for user interface + OUTX_L_G = 0x22, + /// Gyroscope output registers for user interface + OUTX_H_G = 0x23, + /// Gyroscope output registers for user interface + OUTY_L_G = 0x24, + /// Gyroscope output registers for user interface + OUTY_H_G = 0x25, + /// Gyroscope output registers for user interface + OUTZ_L_G = 0x26, + /// Gyroscope output registers for user interface + OUTZ_H_G = 0x27, + /// Accelerometer output registers + OUTX_L_XL = 0x28, + /// Accelerometer output registers + OUTX_H_XL = 0x29, + /// Accelerometer output registers + OUTY_L_XL = 0x2A, + /// Accelerometer output registers + OUTY_H_XL = 0x2B, + /// Accelerometer output registers + OUTZ_L_XL = 0x2C, + /// Accelerometer output registers + OUTZ_H_XL = 0x2D, + /// Sensor hub output registers + SENSORHUB1_REG = 0x2E, + /// Sensor hub output registers + SENSORHUB2_REG = 0x2F, + /// Sensor hub output registers + SENSORHUB3_REG = 0x30, + /// Sensor hub output registers + SENSORHUB4_REG = 0x31, + /// Sensor hub output registers + SENSORHUB5_REG = 0x32, + /// Sensor hub output registers + SENSORHUB6_REG = 0x33, + /// Sensor hub output registers + SENSORHUB7_REG = 0x34, + /// Sensor hub output registers + SENSORHUB8_REG = 0x35, + /// Sensor hub output registers + SENSORHUB9_REG = 0x36, + /// Sensor hub output registers + SENSORHUB10_REG = 0x37, + /// Sensor hub output registers + SENSORHUB11_REG = 0x38, + /// Sensor hub output registers + SENSORHUB12_REG = 0x39, + /// FIFO status registers + FIFO_STATUS1 = 0x3A, + /// FIFO status registers + FIFO_STATUS2 = 0x3B, + /// FIFO status registers + FIFO_STATUS3 = 0x3C, + /// FIFO status registers + FIFO_STATUS4 = 0x3D, + /// FIFO data output registers + FIFO_DATA_OUT_L = 0x3E, + /// FIFO data output registers + FIFO_DATA_OUT_H = 0x3F, + /// Timestamp output registers + TIMESTAMP0_REG = 0x40, + /// Timestamp output registers + TIMESTAMP1_REG = 0x41, + /// Timestamp output registers + TIMESTAMP2_REG = 0x42, + // RESERVED = [0x43..0x48], + /// Step counter timestamp registers + STEP_TIMESTAMP_L = 0x49, + /// Step counter timestamp registers + STEP_TIMESTAMP_H = 0x4A, + /// Step counter output registers + STEP_COUNTER_L = 0x4B, + /// Step counter output registers + STEP_COUNTER_H = 0x4C, + /// Sensor hub output registers + SENSORHUB13_REG = 0x4D, + /// Sensor hub output registers + SENSORHUB14_REG = 0x4E, + /// Sensor hub output registers + SENSORHUB15_REG = 0x4F, + /// Sensor hub output registers + SENSORHUB16_REG = 0x50, + /// Sensor hub output registers + SENSORHUB17_REG = 0x51, + /// Sensor hub output registers + SENSORHUB18_REG = 0x52, + /// Interrupt registers + FUNC_SRC1 = 0x53, + /// Interrupt registers + FUNC_SRC2 = 0x54, + /// Interrupt register + WRIST_TILT_IA = 0x55, + // RESERVED = [0x56..0x57], + /// Interrupt registers + TAP_CFG = 0x58, + /// Interrupt registers + TAP_THS_6D = 0x59, + /// Interrupt registers + INT_DUR2 = 0x5A, + /// Interrupt registers + WAKE_UP_THS = 0x5B, + /// Interrupt registers + WAKE_UP_DUR = 0x5C, + /// Interrupt registers + FREE_FALL = 0x5D, + /// Interrupt registers + MD1_CFG = 0x5E, + /// Interrupt registers + MD2_CFG = 0x5F, + /// (no comment) + MASTER_CMD_CODE = 0x60, + /// (no comment) + SENS_SYNC_SPI_ERROR_CODE = 0x61, + // RESERVED = [0x62..0x65], + /// External magnetometer raw data output registers + OUT_MAG_RAW_X_L = 0x66, + /// External magnetometer raw data output registers + OUT_MAG_RAW_X_H = 0x67, + /// External magnetometer raw data output registers + OUT_MAG_RAW_Y_L = 0x68, + /// External magnetometer raw data output registers + OUT_MAG_RAW_Y_H = 0x69, + /// External magnetometer raw data output registers + OUT_MAG_RAW_Z_L = 0x6A, + /// External magnetometer raw data output registers + OUT_MAG_RAW_Z_H = 0x6B, + // RESERVED = [0x6C..0x72], + /// Accelerometer user offset correction + X_OFS_USR = 0x73, + /// Accelerometer user offset correction + Y_OFS_USR = 0x74, + /// Accelerometer user offset correction + Z_OFS_USR = 0x75, + // RESERVED = [0x76..0x7F], +} + +impl RegisterAddress { + pub fn address(self) -> u8 { + self as u8 + } +} + +/// Register address+value container +pub struct RegisterConfig { + pub address: u8, + pub value: u8, +} + +/// Simple bit-field structure +#[derive(Default)] +pub struct RegisterBits { + value: u8, +} + +impl RegisterBits { + /// New object with (unshifted) value set + pub fn new(value: u8) -> Self { + Self::from(value) + } + + /// New object with value set by extracting (shifting) relevant bits from register value + pub fn from_reg(value: u8) -> Self { + let mut s = Self::default(); + s.set_from_reg(value); + s + } + + /// Sets value by extracting (shifting) relevant bits from register value + pub fn set_from_reg(&mut self, value: u8) { + self.value = (value >> BITS_POS) & Self::bit_mask(); + } + + /// Returns unshifted value + pub fn value(&self) -> u8 { + self.value + } + + /// Returns unshifted bit mask + pub fn bit_mask() -> u8 { + (1 << BITS_NUM) - 1 + } + + /// Returns shifted bit mask + pub fn bit_shifted_mask() -> u8 { + Self::bit_mask() << BITS_POS + } +} + +pub trait RegisterValue { + fn shifted(&self) -> u8; +} + +impl RegisterValue for RegisterBits { + /// Returns shifted value to be OR-ed into register value + fn shifted(&self) -> u8 { + (self.value & Self::bit_mask()) << BITS_POS + } +} + +impl From for RegisterBits { + fn from(value: u8) -> Self { + Self { + value: value & Self::bit_mask(), + } + } +} + +impl From for RegisterBits { + fn from(value: bool) -> Self { + Self::from(value as u8) + } +} + +#[cfg(test)] +mod tests { + use super::{RegisterBits, RegisterValue}; + + #[test] + fn new_1_0() { + const BITS: u8 = 1; + const POS: u8 = 0; + let rb = RegisterBits::::new(0b11111111); + assert_eq!(rb.value, 0b1); + } + + #[test] + fn new_1_1() { + const BITS: u8 = 1; + const POS: u8 = 1; + let rb = RegisterBits::::new(0b11111111); + assert_eq!(rb.value, 0b1); + } + + #[test] + fn new_1_2() { + const BITS: u8 = 1; + const POS: u8 = 2; + let rb = RegisterBits::::new(0b11111111); + assert_eq!(rb.value, 0b1); + } + + #[test] + fn new_2_0() { + const BITS: u8 = 2; + const POS: u8 = 0; + let rb = RegisterBits::::new(0b11111111); + assert_eq!(rb.value, 0b11); + } + + #[test] + fn new_2_1() { + const BITS: u8 = 2; + const POS: u8 = 1; + let rb = RegisterBits::::new(0b11111111); + assert_eq!(rb.value, 0b11); + } + + #[test] + fn new_2_2() { + const BITS: u8 = 2; + const POS: u8 = 2; + let rb = RegisterBits::::new(0b11111111); + assert_eq!(rb.value, 0b11); + } + + #[test] + fn shifted_1_0() { + const BITS: u8 = 1; + const POS: u8 = 0; + let rb = RegisterBits::::new(0b11111111); + assert_eq!(rb.value, 0b1); + assert_eq!(rb.shifted(), 0b1); + } + + #[test] + fn shifted_1_1() { + const BITS: u8 = 1; + const POS: u8 = 1; + let rb = RegisterBits::::new(0b11111111); + assert_eq!(rb.value, 0b1); + assert_eq!(rb.shifted(), 0b10); + } + + #[test] + fn shifted_1_2() { + const BITS: u8 = 1; + const POS: u8 = 2; + let rb = RegisterBits::::new(0b11111111); + assert_eq!(rb.value, 0b1); + assert_eq!(rb.shifted(), 0b100); + } + + #[test] + fn shifted_2_1() { + const BITS: u8 = 2; + const POS: u8 = 1; + let rb = RegisterBits::::new(0b11111111); + assert_eq!(rb.value, 0b11); + assert_eq!(rb.shifted(), 0b110); + } + + #[test] + fn shifted_2_2() { + const BITS: u8 = 2; + const POS: u8 = 2; + let rb = RegisterBits::::new(0b11111111); + assert_eq!(rb.value, 0b11); + assert_eq!(rb.shifted(), 0b1100); + } + + #[test] + fn new_from_reg_1_0() { + const BITS: u8 = 1; + const POS: u8 = 0; + let rb = RegisterBits::::from_reg(0b11111111); + assert_eq!(rb.value, 0b1); + assert_eq!(rb.shifted(), 0b1); + } + + #[test] + fn new_from_reg_1_1() { + const BITS: u8 = 1; + const POS: u8 = 1; + let rb = RegisterBits::::from_reg(0b11111111); + assert_eq!(rb.value, 0b1); + assert_eq!(rb.shifted(), 0b10); + } + + #[test] + fn new_from_reg_2_0() { + const BITS: u8 = 2; + const POS: u8 = 0; + let rb = RegisterBits::::from_reg(0b11111111); + assert_eq!(rb.value, 0b11); + assert_eq!(rb.shifted(), 0b11); + } + + #[test] + fn new_from_reg_2_1() { + const BITS: u8 = 2; + const POS: u8 = 1; + let rb = RegisterBits::::from_reg(0b11111111); + assert_eq!(rb.value, 0b11); + assert_eq!(rb.shifted(), 0b110); + } + + #[test] + fn new_from_reg_2_2() { + const BITS: u8 = 2; + const POS: u8 = 2; + let rb = RegisterBits::::from_reg(0b11111111); + assert_eq!(rb.value, 0b11); + assert_eq!(rb.shifted(), 0b1100); + } + + #[test] + fn from_reg_1_0() { + const BITS: u8 = 1; + const POS: u8 = 0; + let mut rb = RegisterBits::::default(); + let reg: u8 = 0b11111111; + rb.set_from_reg(reg); + assert_eq!(rb.value, 0b1); + assert_eq!(rb.shifted(), 0b1); + } + + #[test] + fn from_reg_1_1() { + const BITS: u8 = 1; + const POS: u8 = 1; + let mut rb = RegisterBits::::default(); + let reg: u8 = 0b11111111; + rb.set_from_reg(reg); + assert_eq!(rb.value, 0b1); + assert_eq!(rb.shifted(), 0b10); + } + + #[test] + fn from_reg_2_0() { + const BITS: u8 = 2; + const POS: u8 = 0; + let mut rb = RegisterBits::::default(); + let reg: u8 = 0b11111111; + rb.set_from_reg(reg); + assert_eq!(rb.value, 0b11); + assert_eq!(rb.shifted(), 0b11); + } + + #[test] + fn from_reg_2_1() { + const BITS: u8 = 2; + const POS: u8 = 1; + let mut rb = RegisterBits::::default(); + let reg: u8 = 0b11111111; + rb.set_from_reg(reg); + assert_eq!(rb.value, 0b11); + assert_eq!(rb.shifted(), 0b110); + } + + #[test] + fn from_reg_2_2() { + const BITS: u8 = 2; + const POS: u8 = 2; + let mut rb = RegisterBits::::default(); + let reg: u8 = 0b11111111; + rb.set_from_reg(reg); + assert_eq!(rb.value, 0b11); + assert_eq!(rb.shifted(), 0b1100); + } + + #[test] + fn bit_mask_1_0() { + const BITS: u8 = 1; + const POS: u8 = 0; + assert_eq!(RegisterBits::::bit_mask(), 0b1); + } + + #[test] + fn bit_mask_2_0() { + const BITS: u8 = 2; + const POS: u8 = 0; + assert_eq!(RegisterBits::::bit_mask(), 0b11); + } + + #[test] + fn bit_mask_3_0() { + const BITS: u8 = 3; + const POS: u8 = 0; + assert_eq!(RegisterBits::::bit_mask(), 0b111); + } + + #[test] + fn bit_shifted_mask_1_0() { + const BITS: u8 = 1; + const POS: u8 = 0; + assert_eq!(RegisterBits::::bit_shifted_mask(), 0b1); + } + + #[test] + fn bit_shifted_mask_1_1() { + const BITS: u8 = 1; + const POS: u8 = 1; + assert_eq!(RegisterBits::::bit_shifted_mask(), 0b10); + } + + #[test] + fn bit_shifted_mask_2_0() { + const BITS: u8 = 2; + const POS: u8 = 0; + assert_eq!(RegisterBits::::bit_shifted_mask(), 0b11); + } + + #[test] + fn bit_shifted_mask_2_1() { + const BITS: u8 = 2; + const POS: u8 = 1; + assert_eq!(RegisterBits::::bit_shifted_mask(), 0b110); + } + + #[test] + fn bit_shifted_mask_3_0() { + const BITS: u8 = 3; + const POS: u8 = 0; + assert_eq!(RegisterBits::::bit_shifted_mask(), 0b111); + } + + #[test] + fn bit_shifted_mask_3_1() { + const BITS: u8 = 3; + const POS: u8 = 1; + assert_eq!(RegisterBits::::bit_shifted_mask(), 0b1110); + } +} diff --git a/src/registers/ctrl1_xl.rs b/src/registers/ctrl1_xl.rs new file mode 100644 index 0000000..46fd5f7 --- /dev/null +++ b/src/registers/ctrl1_xl.rs @@ -0,0 +1,103 @@ +//! Linear acceleration sensor control register 1 (r/w). + +#![allow(non_camel_case_types)] + +use crate::{RegisterAddress, RegisterBits, RegisterConfig, RegisterValue}; + +/// Linear acceleration sensor control register 1 (r/w). +#[derive(Default)] +pub struct Ctrl1Xl { + /// Output data rate and power mode selection. Default value: 0000 (see Table 52). + pub sample_rate: AccelSampleRate, + /// Accelerometer full-scale selection. Default value: 00. + /// (00: ±2 g; 01: ±16 g; 10: ±4 g; 11: ±8 g) + pub scale: AccelScale, + /// Accelerometer digital LPF (LPF1) bandwidth selection. For bandwidth selection refer to CTRL8_XL (17h). + pub low_pass_filter: RegisterBits<1, 6>, // TODO + /// Accelerometer analog chain bandwidth selection (only for accelerometer ODR ≥ 1.67 kHz). + /// (0: BW @ 1.5 kHz; 1: BW @ 400 Hz) + pub analog_chain_bandwidth: RegisterBits<1, 7>, // TODO +} + +impl Ctrl1Xl { + pub fn address(&self) -> u8 { + RegisterAddress::CTRL1_XL.address() + } + + pub fn value(&self) -> u8 { + self.sample_rate.shifted() + | self.scale.shifted() + | self.low_pass_filter.shifted() + | self.analog_chain_bandwidth.shifted() + } + + pub fn config(&self) -> RegisterConfig { + RegisterConfig { + address: self.address(), + value: self.value(), + } + } +} + +#[derive(Default, Clone, Copy)] +pub enum AccelScale { + #[default] + _2G = 0b00, + _16G = 0b01, + _4G = 0b10, + _8G = 0b11, +} + +impl AccelScale { + pub fn sensitivity(self) -> f32 { + use AccelScale::*; + match self { + _2G => 0.000_061, + _4G => 0.000_122, + _8G => 0.000_244, + _16G => 0.000_488, + } + } +} + +impl RegisterValue for AccelScale { + fn shifted(&self) -> u8 { + (*self as u8) << 2 + } +} + +#[derive(Default, Clone, Copy)] +pub enum AccelSampleRate { + /// Power Down (disabled) + #[default] + PowerDown = 0b0000, + /// 12.5 Hz + _12_5Hz = 0b0001, + /// 26 Hz + _26Hz = 0b0010, + /// 52 Hz + _52Hz = 0b0011, + /// 104 Hz + _104Hz = 0b0100, + /// 208 Hz + _208Hz = 0b0101, + /// 416 Hz + _416Hz = 0b0110, + /// 833 Hz + _833Hz = 0b0111, + /// 1.66 kHz + _1660Hz = 0b1000, + /// 3.33 kHz + _3330Hz = 0b1001, + /// 6.66 kHz + _6660Hz = 0b1010, + /// 1.6 Hz in low power mode; 12.5 Hz in high performance mode + _1_6Hz_LP_or_12_5Hz_HP = 0b1011, + // Not allowed = [0b1100..0b1111] +} + +impl RegisterValue for AccelSampleRate { + fn shifted(&self) -> u8 { + (*self as u8) << 4 + } +} diff --git a/src/registers/ctrl2_g.rs b/src/registers/ctrl2_g.rs new file mode 100644 index 0000000..f76dbb5 --- /dev/null +++ b/src/registers/ctrl2_g.rs @@ -0,0 +1,98 @@ +//! Angular rate sensor control register 2 (r/w). + +#![allow(non_camel_case_types)] + +use crate::registers::RegisterConfig; +use crate::RegisterAddress; + +use super::RegisterValue; + +#[derive(Default)] +/// Angular rate sensor control register 2 (r/w). +pub struct Ctrl2G { + /// Gyroscope output data rate selection. Default value: 0000 (Refer to Table 55) + pub sample_rate: GyroSampleRate, + /// Gyroscope full-scale selection. Default value: 00 (00: 245 dps; 01: 500 dps; 10: 1000 dps; 11: 2000 dps) + /// Gyroscope full-scale at 125 dps. Default value: 0 (0: disabled; 1: enabled) + pub scale: GyroScale, +} + +impl Ctrl2G { + pub fn address(&self) -> u8 { + RegisterAddress::CTRL2_G.address() + } + + pub fn value(&self) -> u8 { + self.sample_rate.shifted() | self.scale.shifted() + } + + pub fn config(&self) -> RegisterConfig { + RegisterConfig { + address: self.address(), + value: self.value(), + } + } +} + +#[derive(Default, Debug, Clone, Copy)] +pub enum GyroScale { + _125DPS = 0b001, + #[default] + _250DPS = 0b000, + _500DPS = 0b010, + _1000DPS = 0b100, + _2000DPS = 0b110, +} + +impl GyroScale { + pub fn sensitivity(self) -> f32 { + use GyroScale::*; + match self { + _125DPS => 0.004_375, + _250DPS => 0.008_750, + _500DPS => 0.017_500, + _1000DPS => 0.035_000, + _2000DPS => 0.070_000, + } + } +} + +impl RegisterValue for GyroScale { + fn shifted(&self) -> u8 { + (*self as u8) << 1 + } +} + +#[derive(Default, Debug, Clone, Copy)] +pub enum GyroSampleRate { + /// Power Down (disabled) + #[default] + PowerDown = 0b0000, + /// 12.5 Hz + _12_5Hz = 0b0001, + /// 26 Hz + _26Hz = 0b0010, + /// 52 Hz + _52Hz = 0b0011, + /// 104 Hz + _104Hz = 0b0100, + /// 208 Hz + _208Hz = 0b0101, + /// 416 Hz + _416Hz = 0b0110, + /// 833 Hz + _833Hz = 0b0111, + /// 1.66 kHz + _1660Hz = 0b1000, + /// 3.33 kHz + _3330Hz = 0b1001, + /// 6.66 kHz + _6660Hz = 0b1010, + // Not allowed = [0b1011..0b1111] +} + +impl RegisterValue for GyroSampleRate { + fn shifted(&self) -> u8 { + (*self as u8) << 4 + } +} diff --git a/src/registers/ctrl3_c.rs b/src/registers/ctrl3_c.rs new file mode 100644 index 0000000..63f640a --- /dev/null +++ b/src/registers/ctrl3_c.rs @@ -0,0 +1,73 @@ +//! Control register 3 (r/w). + +#![allow(non_camel_case_types)] + +use crate::{RegisterAddress, RegisterBits, RegisterConfig, RegisterValue}; + +/// Control register 3 (r/w). +pub struct Ctrl3C { + /// Reboot memory content. Default value: 0 + /// (0: normal mode; 1: reboot memory content) + pub reboot_memory_content: RegisterBits<1, 7>, + /// Block Data Update. Default value: 0 + /// (0: continuous update; 1: output registers not updated until MSB and LSB have been read) + pub block_data_update: RegisterBits<1, 6>, + /// Interrupt activation level. Default value: 0 + /// (0: interrupt output pads active high; 1: interrupt output pads active low) + pub high_low_active: RegisterBits<1, 5>, + /// Push-pull/open-drain selection on INT1 and INT2 pads. Default value: 0 + /// (0: push-pull mode; 1: open-drain mode) + pub push_pull_open_drain: RegisterBits<1, 4>, + /// SPI Serial Interface Mode selection. Default value: 0 + /// (0: 4-wire interface; 1: 3-wire interface). + pub spi_interface_mode: RegisterBits<1, 3>, + /// Register address automatically incremented during a multiple byte access with a serial interface (I2C or SPI). Default value: 1 + /// (0: disabled; 1: enabled) + pub interface_auto_address_increment: RegisterBits<1, 2>, + /// Big/Little Endian Data selection. Default value 0 + /// (0: data LSB @ lower address; 1: data MSB @ lower address) + pub big_little_endian: RegisterBits<1, 1>, + /// Software reset. Default value: 0 + /// (0: normal mode; 1: reset device) + /// This bit is automatically cleared. + pub software_reset: RegisterBits<1, 0>, +} + +impl Default for Ctrl3C { + fn default() -> Self { + Self { + interface_auto_address_increment: RegisterBits::new(1), + reboot_memory_content: RegisterBits::default(), + block_data_update: RegisterBits::default(), + high_low_active: RegisterBits::default(), + push_pull_open_drain: RegisterBits::default(), + spi_interface_mode: RegisterBits::default(), + big_little_endian: RegisterBits::default(), + software_reset: RegisterBits::default(), + } + } +} + +impl Ctrl3C { + pub fn address(&self) -> u8 { + RegisterAddress::CTRL3_C.address() + } + + pub fn value(&self) -> u8 { + self.reboot_memory_content.shifted() + | self.block_data_update.shifted() + | self.high_low_active.shifted() + | self.push_pull_open_drain.shifted() + | self.spi_interface_mode.shifted() + | self.interface_auto_address_increment.shifted() + | self.big_little_endian.shifted() + | self.software_reset.shifted() + } + + pub fn config(&self) -> RegisterConfig { + RegisterConfig { + address: self.address(), + value: self.value(), + } + } +} diff --git a/src/registers/ctrl4_c.rs b/src/registers/ctrl4_c.rs new file mode 100644 index 0000000..5a3b798 --- /dev/null +++ b/src/registers/ctrl4_c.rs @@ -0,0 +1,56 @@ +//! Control register 4 (r/w). + +#![allow(non_camel_case_types)] + +use crate::{RegisterAddress, RegisterBits, RegisterConfig, RegisterValue}; + +/// Control register 4 (r/w). +#[derive(Default)] +pub struct Ctrl4C { + /// Extend DEN functionality to accelerometer sensor. Default value: 0 + /// (0: disabled; 1: enabled) + pub extend_den: RegisterBits<1, 7>, + /// Gyroscope sleep mode enable. Default value: 0 + /// (0: disabled; 1: enabled) + pub gyroscope_sleep: RegisterBits<1, 6>, + /// DEN DRDY signal on INT1 pad. Default value: 0 + /// (0: disabled; 1: enabled) + pub den_data_ready_int1: RegisterBits<1, 5>, + /// All interrupt signals available on INT1 pad enable. Default value: 0 + /// (0: interrupt signals divided between INT1 and INT2 pads; + /// 1: all interrupt signals in logic or on INT1 pad) + pub int2_on_int1: RegisterBits<1, 4>, + /// Configuration 1 data available enable bit. Default value: 0 + /// (0: DA timer disabled; 1: DA timer enabled) + pub data_ready_mask: RegisterBits<1, 3>, + /// Disable I2C interface. Default value: 0 + /// (0: both I2C and SPI enabled; 1: I2C disabled, SPI only enabled) + pub i2c_disable: RegisterBits<1, 2>, + /// Enable gyroscope digital LPF1. The bandwidth can be selected through + /// FTYPE[1\:0] in FUNC_CFG_ACCESS (01h). + /// (0: disabled; 1: enabled) + pub gyroscope_low_pass_filter_selection: RegisterBits<1, 1>, +} + +impl Ctrl4C { + pub fn address(&self) -> u8 { + RegisterAddress::CTRL4_C.address() + } + + pub fn value(&self) -> u8 { + self.extend_den.shifted() + | self.gyroscope_sleep.shifted() + | self.den_data_ready_int1.shifted() + | self.int2_on_int1.shifted() + | self.data_ready_mask.shifted() + | self.i2c_disable.shifted() + | self.gyroscope_low_pass_filter_selection.shifted() + } + + pub fn config(&self) -> RegisterConfig { + RegisterConfig { + address: self.address(), + value: self.value(), + } + } +} diff --git a/src/registers/drdy_pulse_cfg_g.rs b/src/registers/drdy_pulse_cfg_g.rs new file mode 100644 index 0000000..6f34d2a --- /dev/null +++ b/src/registers/drdy_pulse_cfg_g.rs @@ -0,0 +1,34 @@ +//! DataReady configuration register (r/w). + +#![allow(non_camel_case_types)] + +use crate::{RegisterAddress, RegisterBits, RegisterConfig, RegisterValue}; + +/// DataReady configuration register (r/w). +#[derive(Default)] +pub struct DrdyPulseCfgG { + /// Enable pulsed DataReady mode. Default value: 0 + /// (0: DataReady latched mode. Returns to 0 only after output data have been read; + /// 1: DataReady pulsed mode. The DataReady pulses are 75 μs long.) + pub pulsed_data_ready: RegisterBits<1, 7>, + /// Wrist tilt interrupt on INT2 pad. Default value: 0 + /// (0: disabled; 1: enabled) + pub int2_wrist_tilt: RegisterBits<1, 0>, +} + +impl DrdyPulseCfgG { + pub fn address(&self) -> u8 { + RegisterAddress::DRDY_PULSE_CFG_G.address() + } + + pub fn value(&self) -> u8 { + self.pulsed_data_ready.shifted() | self.int2_wrist_tilt.shifted() + } + + pub fn config(&self) -> RegisterConfig { + RegisterConfig { + address: self.address(), + value: self.value(), + } + } +} diff --git a/src/registers/free_fall.rs b/src/registers/free_fall.rs new file mode 100644 index 0000000..ce6d839 --- /dev/null +++ b/src/registers/free_fall.rs @@ -0,0 +1,52 @@ +//! Free-fall function duration setting register (r/w). + +use crate::{RegisterAddress, RegisterBits, RegisterConfig, RegisterValue}; + +/// Free-fall function duration setting register (r/w). +#[derive(Default)] +pub struct FreeFall { + /// Free-fall duration event. Default: 0 + /// For the complete configuration of the free fall duration, refer to FF_DUR5 in WAKE_UP_DUR (5Ch) configuration + pub duration_event: RegisterBits<5, 3>, + /// Free fall threshold setting. Default: 000 + /// For details refer to Table 196. + pub threshold: FreeFallThreshold, +} + +impl FreeFall { + pub fn address(&self) -> u8 { + RegisterAddress::FREE_FALL.address() + } + + pub fn value(&self) -> u8 { + self.duration_event.shifted() | self.threshold.shifted() + } + + pub fn config(&self) -> RegisterConfig { + RegisterConfig { + address: self.address(), + value: self.value(), + } + } +} + +#[allow(non_camel_case_types)] +#[repr(u8)] +#[derive(Default, Clone, Copy)] +pub enum FreeFallThreshold { + #[default] + _156_mg = 0b000, + _219_mg = 0b001, + _250_mg = 0b010, + _312_mg = 0b011, + _344_mg = 0b100, + _406_mg = 0b101, + _469_mg = 0b110, + _500_mg = 0b111, +} + +impl RegisterValue for FreeFallThreshold { + fn shifted(&self) -> u8 { + *self as u8 + } +} diff --git a/src/registers/int1_ctrl.rs b/src/registers/int1_ctrl.rs new file mode 100644 index 0000000..1cefc99 --- /dev/null +++ b/src/registers/int1_ctrl.rs @@ -0,0 +1,57 @@ +//! INT1 pad control register (r/w). + +use crate::{RegisterAddress, RegisterBits, RegisterConfig, RegisterValue}; + +/// INT1 pad control register (r/w). +/// Each bit in this register enables a signal to be carried through INT1. The pad’s output will supply the OR combination of the selected signals. +#[derive(Default)] +pub struct Int1Ctrl { + /// Pedometer step recognition interrupt enable on INT1 pad. Default value: 0 + /// (0: disabled; 1: enabled) + pub pedometer_step_recognition: RegisterBits<1, 7>, + /// Significant motion interrupt enable on INT1 pad. Default value: 0 + /// (0: disabled; 1: enabled) + pub significant_motion: RegisterBits<1, 6>, + /// FIFO full flag interrupt enable on INT1 pad. Default value: 0 + /// (0: disabled; 1: enabled) + pub fifo_full: RegisterBits<1, 5>, + /// FIFO overrun interrupt on INT1 pad. Default value: 0 + /// (0: disabled; 1: enabled) + pub fifo_overrun: RegisterBits<1, 4>, + /// FIFO threshold interrupt on INT1 pad. Default value: 0 + /// (0: disabled; 1: enabled) + pub fifo_threshold: RegisterBits<1, 3>, + /// Boot status available on INT1 pad. Default value: 0 + /// (0: disabled; 1: enabled) + pub boot_status_available: RegisterBits<1, 2>, + /// Gyroscope Data Ready on INT1 pad. Default value: 0 + /// (0: disabled; 1: enabled) + pub gyroscope_data_ready: RegisterBits<1, 1>, + /// Accelerometer Data Ready on INT1 pad. Default value: 0 + /// (0: disabled; 1: enabled) + pub accelerometer_data_ready: RegisterBits<1, 0>, +} + +impl Int1Ctrl { + pub fn address(&self) -> u8 { + RegisterAddress::INT1_CTRL.address() + } + + pub fn value(&self) -> u8 { + self.pedometer_step_recognition.shifted() + | self.significant_motion.shifted() + | self.fifo_full.shifted() + | self.fifo_overrun.shifted() + | self.fifo_threshold.shifted() + | self.boot_status_available.shifted() + | self.gyroscope_data_ready.shifted() + | self.accelerometer_data_ready.shifted() + } + + pub fn config(&self) -> RegisterConfig { + RegisterConfig { + address: self.address(), + value: self.value(), + } + } +} diff --git a/src/registers/int2_ctrl.rs b/src/registers/int2_ctrl.rs new file mode 100644 index 0000000..0355249 --- /dev/null +++ b/src/registers/int2_ctrl.rs @@ -0,0 +1,57 @@ +//! INT2 pad control register (r/w). + +use crate::{RegisterAddress, RegisterBits, RegisterConfig, RegisterValue}; + +/// INT2 pad control register (r/w). +/// Each bit in this register enables a signal to be carried through INT2. The pad’s output will supply the OR combination of the selected signals. +#[derive(Default)] +pub struct Int2Ctrl { + /// Pedometer step recognition interrupt on delta time(1) enable on INT2 pad. Default value: 0 + /// (0: disabled; 1: enabled) + pub pedometer_step_recognition_delta_time: RegisterBits<1, 7>, + /// Step counter overflow interrupt enable on INT2 pad. Default value: 0 + /// (0: disabled; 1: enabled) + pub step_counter_overflow: RegisterBits<1, 6>, + /// FIFO full flag interrupt enable on INT2 pad. Default value: 0 + /// (0: disabled; 1: enabled) + pub fifo_full: RegisterBits<1, 5>, + /// FIFO overrun interrupt on INT2 pad. Default value: 0 + /// (0: disabled; 1: enabled) + pub fifo_overrun: RegisterBits<1, 4>, + /// FIFO threshold interrupt on INT2 pad. Default value: 0 + /// (0: disabled; 1: enabled) + pub fifo_threshold: RegisterBits<1, 3>, + /// Temperature Data Ready on INT2 pad. Default value: 0 + /// (0: disabled; 1: enabled) + pub temperature_data_ready: RegisterBits<1, 2>, + /// Gyroscope Data Ready on INT2 pad. Default value: 0 + /// (0: disabled; 1: enabled) + pub gyroscope_data_ready: RegisterBits<1, 1>, + /// Accelerometer Data Ready on INT2 pad. Default value: 0 + /// (0: disabled; 1: enabled) + pub accelerometer_data_ready: RegisterBits<1, 0>, +} + +impl Int2Ctrl { + pub fn address(&self) -> u8 { + RegisterAddress::INT2_CTRL.address() + } + + pub fn value(&self) -> u8 { + self.pedometer_step_recognition_delta_time.shifted() + | self.step_counter_overflow.shifted() + | self.fifo_full.shifted() + | self.fifo_overrun.shifted() + | self.fifo_threshold.shifted() + | self.temperature_data_ready.shifted() + | self.gyroscope_data_ready.shifted() + | self.accelerometer_data_ready.shifted() + } + + pub fn config(&self) -> RegisterConfig { + RegisterConfig { + address: self.address(), + value: self.value(), + } + } +} diff --git a/src/registers/int_dur2.rs b/src/registers/int_dur2.rs new file mode 100644 index 0000000..e7d51da --- /dev/null +++ b/src/registers/int_dur2.rs @@ -0,0 +1,34 @@ +//! Tap recognition function setting register (r/w). + +use crate::{RegisterAddress, RegisterBits, RegisterConfig, RegisterValue}; + +/// Tap recognition function setting register (r/w). +#[derive(Default)] +pub struct IntDur2 { + /// Duration of maximum time gap for double tap recognition. Default: 0000 + /// When double tap recognition is enabled, this register expresses the maximum time between two consecutive detected taps to determine a double tap event. The default value of these bits is 0000b which corresponds to 16*ODR_XL time. If the DUR[3\:0] bits are set to a different value, 1LSB corresponds to 32*ODR_XL time. + pub duration: RegisterBits<4, 4>, + /// Expected quiet time after a tap detection. Default value: 00 + /// Quiet time is the time after the first detected tap in which there must not be any overthreshold event. The default value of these bits is 00b which corresponds to 2*ODR_XL time. If the QUIET[1\:0] bits are set to a different value, 1LSB corresponds to 4*ODR_XL time. + pub quiet: RegisterBits<2, 2>, + /// Maximum duration of overthreshold event. Default value: 00 + /// Maximum duration is the maximum time of an overthreshold signal detection to be recognized as a tap event. The default value of these bits is 00b which corresponds to 4*ODR_XL time. If the SHOCK[1\:0] bits are set to a different value, 1LSB corresponds to 8*ODR_XL time. + pub shock: RegisterBits<2, 0>, +} + +impl IntDur2 { + pub fn address(&self) -> u8 { + RegisterAddress::INT_DUR2.address() + } + + pub fn value(&self) -> u8 { + self.duration.shifted() | self.quiet.shifted() | self.shock.shifted() + } + + pub fn config(&self) -> RegisterConfig { + RegisterConfig { + address: self.address(), + value: self.value(), + } + } +} diff --git a/src/registers/md1_cfg.rs b/src/registers/md1_cfg.rs new file mode 100644 index 0000000..bde4439 --- /dev/null +++ b/src/registers/md1_cfg.rs @@ -0,0 +1,61 @@ +//! Function routing on INT1 register (r/w). + +use crate::{RegisterAddress, RegisterBits, RegisterConfig, RegisterValue}; + +/// Function routing on INT1 register (r/w). +#[derive(Default)] +pub struct Md1Cfg { + /// Routing on INT1 of inactivity mode. Default: 0 + /// (0: routing on INT1 of inactivity disabled; 1: routing on INT1 of inactivity enabled) + pub inactivity_event: RegisterBits<1, 7>, + /// Single-tap recognition routing on INT1. Default: 0 + /// (0: routing of single-tap event on INT1 disabled; + /// 1: routing of single-tap event on INT1 enabled) + pub single_tap_event: RegisterBits<1, 6>, + /// Routing of wakeup event on INT1. Default value: 0 + /// (0: routing of wakeup event on INT1 disabled; + /// 1: routing of wakeup event on INT1 enabled) + pub wake_up_event: RegisterBits<1, 5>, + /// Routing of free-fall event on INT1. Default value: 0 + /// (0: routing of free-fall event on INT1 disabled; + /// 1: routing of free-fall event on INT1 enabled) + pub free_fall_event: RegisterBits<1, 4>, + /// Routing of tap event on INT1. Default value: 0 + /// (0: routing of double-tap event on INT1 disabled; + /// 1: routing of double-tap event on INT1 enabled) + pub double_tap_event: RegisterBits<1, 3>, + /// Routing of 6D event on INT1. Default value: 0 + /// (0: routing of 6D event on INT1 disabled; 1: routing of 6D event on INT1 enabled) + pub six_degrees_event: RegisterBits<1, 2>, + /// Routing of tilt event on INT1. Default value: 0 + /// (0: routing of tilt event on INT1 disabled; 1: routing of tilt event on INT1 enabled) + pub tilt_event: RegisterBits<1, 1>, + /// Routing of end counter event of timer on INT1. Default value: 0 + /// (0: routing of end counter event of timer on INT1 disabled; + /// 1: routing of end counter event of timer event on INT1 enabled) + pub timer_end_counter_event: RegisterBits<1, 0>, +} + +impl Md1Cfg { + pub fn address(&self) -> u8 { + RegisterAddress::MD1_CFG.address() + } + + pub fn value(&self) -> u8 { + self.inactivity_event.shifted() + | self.single_tap_event.shifted() + | self.wake_up_event.shifted() + | self.free_fall_event.shifted() + | self.double_tap_event.shifted() + | self.six_degrees_event.shifted() + | self.tilt_event.shifted() + | self.timer_end_counter_event.shifted() + } + + pub fn config(&self) -> RegisterConfig { + RegisterConfig { + address: self.address(), + value: self.value(), + } + } +} diff --git a/src/registers/md2_cfg.rs b/src/registers/md2_cfg.rs new file mode 100644 index 0000000..edbe3f3 --- /dev/null +++ b/src/registers/md2_cfg.rs @@ -0,0 +1,61 @@ +//! Function routing on INT2 register (r/w). + +use crate::{RegisterAddress, RegisterBits, RegisterConfig, RegisterValue}; + +/// Function routing on INT2 register (r/w). +#[derive(Default)] +pub struct Md2Cfg { + /// Routing on INT2 of inactivity mode. Default: 0 + /// (0: routing on INT2 of inactivity disabled; 1: routing on INT2 of inactivity enabled) + pub inactivity_event: RegisterBits<1, 7>, + /// Single-tap recognition routing on INT2. Default: 0 + /// (0: routing of single-tap event on INT2 disabled; + /// 1: routing of single-tap event on INT2 enabled) + pub single_tap_event: RegisterBits<1, 6>, + /// Routing of wakeup event on INT2. Default value: 0 + /// (0: routing of wakeup event on INT2 disabled; + /// 1: routing of wake-up event on INT2 enabled) + pub wake_up_event: RegisterBits<1, 5>, + /// Routing of free-fall event on INT2. Default value: 0 + /// (0: routing of free-fall event on INT2 disabled; + /// 1: routing of free-fall event on INT2 enabled) + pub free_fall_event: RegisterBits<1, 4>, + /// Routing of tap event on INT2. Default value: 0 + /// (0: routing of double-tap event on INT2 disabled; + /// 1: routing of double-tap event on INT2 enabled) + pub double_tap_event: RegisterBits<1, 3>, + /// Routing of 6D event on INT2. Default value: 0 + /// (0: routing of 6D event on INT2 disabled; 1: routing of 6D event on INT2 enabled) + pub six_degrees_event: RegisterBits<1, 2>, + /// Routing of tilt event on INT2. Default value: 0 + /// (0: routing of tilt event on INT2 disabled; 1: routing of tilt event on INT2 enabled) + pub tilt_event: RegisterBits<1, 1>, + /// Routing of soft-iron/hard-iron algorithm end event on INT2. Default value: 0 + /// (0: routing of soft-iron/hard-iron algorithm end event on INT2 disabled; + /// 1: routing of soft-iron/hard-iron algorithm end event on INT2 enabled) + pub soft_hard_iron_algorithm_end_event: RegisterBits<1, 0>, +} + +impl Md2Cfg { + pub fn address(&self) -> u8 { + RegisterAddress::MD2_CFG.address() + } + + pub fn value(&self) -> u8 { + self.inactivity_event.shifted() + | self.single_tap_event.shifted() + | self.wake_up_event.shifted() + | self.free_fall_event.shifted() + | self.double_tap_event.shifted() + | self.six_degrees_event.shifted() + | self.tilt_event.shifted() + | self.soft_hard_iron_algorithm_end_event.shifted() + } + + pub fn config(&self) -> RegisterConfig { + RegisterConfig { + address: self.address(), + value: self.value(), + } + } +} diff --git a/src/registers/mod.rs b/src/registers/mod.rs new file mode 100644 index 0000000..f99db6c --- /dev/null +++ b/src/registers/mod.rs @@ -0,0 +1,39 @@ +#![allow(unused_imports)] + +pub mod addresses; +pub mod ctrl1_xl; +pub mod ctrl2_g; +pub mod ctrl3_c; +pub mod ctrl4_c; +pub mod drdy_pulse_cfg_g; +pub mod free_fall; +pub mod int1_ctrl; +pub mod int2_ctrl; +pub mod int_dur2; +pub mod md1_cfg; +pub mod md2_cfg; +pub mod tap_cfg; +pub mod tap_src; +pub mod tap_ths_6d; +pub mod wake_up_dur; +pub mod wake_up_src; +pub mod wake_up_ths; + +pub use addresses::*; +pub use ctrl1_xl::*; +pub use ctrl2_g::*; +pub use ctrl3_c::*; +pub use ctrl4_c::*; +pub use drdy_pulse_cfg_g::*; +pub use free_fall::*; +pub use int1_ctrl::*; +pub use int2_ctrl::*; +pub use int_dur2::*; +pub use md1_cfg::*; +pub use md2_cfg::*; +pub use tap_cfg::*; +pub use tap_src::*; +pub use tap_ths_6d::*; +pub use wake_up_dur::*; +pub use wake_up_src::*; +pub use wake_up_ths::*; diff --git a/src/registers/tap_cfg.rs b/src/registers/tap_cfg.rs new file mode 100644 index 0000000..607cad4 --- /dev/null +++ b/src/registers/tap_cfg.rs @@ -0,0 +1,85 @@ +//! Enables interrupt and inactivity functions, configuration of filtering and tap recognition functions (r/w). + +use crate::{RegisterAddress, RegisterBits, RegisterConfig, RegisterValue}; + +/// Enables interrupt and inactivity functions, configuration of filtering and tap recognition functions (r/w). +#[derive(Default)] +pub struct TapCfg { + /// Enable basic interrupts (6D/4D, free-fall, wake-up, tap, inactivity). Default 0. + /// (0: interrupt disabled; 1: interrupt enabled) + pub enable_basic_interrupts: RegisterBits<1, 7>, + /// Enable inactivity function. Default value: 00 + /// (00: disabled + /// 01: sets accelerometer ODR to 12.5 Hz (low-power mode), gyro does not change; + /// 10: sets accelerometer ODR to 12.5 Hz (low-power mode), gyro to sleep mode; + /// 11: sets accelerometer ODR to 12.5 Hz (low-power mode), gyro to power-down mode) + pub enable_inactivity_function: InactivityGyroMode, + /// HPF or SLOPE filter selection on wake-up and Activity/Inactivity functions. Refer to Figure 8. Default value: 0 + /// (0: SLOPE filter applied; 1: HPF applied) + pub slope_fds: FilterSelected, + /// Enable X direction in tap recognition. Default value: 0 + /// (0: X direction disabled; 1: X direction enabled) + pub enable_x_direction_tap_recognition: RegisterBits<1, 3>, + /// Enable Y direction in tap recognition. Default value: 0 + /// (0: Y direction disabled; 1: Y direction enabled) + pub enable_y_direction_tap_recognition: RegisterBits<1, 2>, + /// Enable Z direction in tap recognition. Default value: 0 + /// (0: Z direction disabled; 1: Z direction enabled) + pub enable_z_direction_tap_recognition: RegisterBits<1, 1>, + /// Latched Interrupt. Default value: 0 + /// (0: interrupt request not latched; 1: interrupt request latched) + pub latched_interrupt: RegisterBits<1, 0>, +} + +impl TapCfg { + pub fn address(&self) -> u8 { + RegisterAddress::TAP_CFG.address() + } + + pub fn value(&self) -> u8 { + self.enable_basic_interrupts.shifted() + | self.enable_inactivity_function.shifted() + | self.slope_fds.shifted() + | self.enable_x_direction_tap_recognition.shifted() + | self.enable_y_direction_tap_recognition.shifted() + | self.enable_z_direction_tap_recognition.shifted() + | self.latched_interrupt.shifted() + } + + pub fn config(&self) -> RegisterConfig { + RegisterConfig { + address: self.address(), + value: self.value(), + } + } +} + +#[repr(u8)] +#[derive(Default, Clone, Copy)] +pub enum InactivityGyroMode { + #[default] + Disabled = 0b00, + AccelLowPowerGyroUnchanged = 0b01, + AccelLowPowerGyroSleepMode = 0b10, + AccelLowPowerGyroPowerDown = 0b11, +} + +impl RegisterValue for InactivityGyroMode { + fn shifted(&self) -> u8 { + (*self as u8) << 5 + } +} + +#[repr(u8)] +#[derive(Default, Clone, Copy)] +pub enum FilterSelected { + #[default] + Slope = 0b0, + HighPassFilter = 0b1, +} + +impl RegisterValue for FilterSelected { + fn shifted(&self) -> u8 { + (*self as u8) << 4 + } +} diff --git a/src/registers/tap_src.rs b/src/registers/tap_src.rs new file mode 100644 index 0000000..ea90deb --- /dev/null +++ b/src/registers/tap_src.rs @@ -0,0 +1,75 @@ +//! Tap source register (r). + +use heapless::Vec; + +use crate::{IrqSource, RegisterAddress, RegisterBits}; + +/// Tap source register (r). +#[derive(Default)] +pub struct TapSrc { + /// Tap event detection status. Default: 0 + /// (0: tap event not detected; 1: tap event detected) + pub tap_event: RegisterBits<1, 6>, + /// Single-tap event status. Default value: 0 + /// (0: single tap event not detected; 1: single tap event detected) + pub single_tap_event: RegisterBits<1, 5>, + /// Double-tap event detection status. Default value: 0 + /// (0: double-tap event not detected; 1: double-tap event detected.) + pub double_tap_event: RegisterBits<1, 4>, + /// Sign of acceleration detected by tap event. Default: 0 + /// (0: positive sign of acceleration detected by tap event; + /// 1: negative sign of acceleration detected by tap event) + pub tap_sign_acceleration: RegisterBits<1, 3>, + /// Tap event detection status on X-axis. Default value: 0 + /// (0: tap event on X-axis not detected; 1: tap event on X-axis detected) + pub tap_x_axis: RegisterBits<1, 2>, + /// Tap event detection status on Y-axis. Default value: 0 + /// (0: tap event on Y-axis not detected; 1: tap event on Y-axis detected) + pub tap_y_axis: RegisterBits<1, 1>, + /// Tap event detection status on Z-axis. Default value: 0 + /// (0: tap event on Z-axis not detected; 1: tap event on Z-axis detected) + pub tap_z_axis: RegisterBits<1, 0>, +} + +impl TapSrc { + pub fn address(&self) -> u8 { + RegisterAddress::TAP_SRC.address() + } + + pub fn get_irq_sources(&self) -> Vec { + let mut v: Vec = Default::default(); + if self.tap_event.value() != 0 { + v.push(IrqSource::Tap).unwrap(); + } + if self.single_tap_event.value() != 0 { + v.push(IrqSource::SingleTap).unwrap(); + } + if self.double_tap_event.value() != 0 { + v.push(IrqSource::DoubleTap).unwrap(); + } + if self.tap_x_axis.value() != 0 { + v.push(IrqSource::TapOnX).unwrap(); + } + if self.tap_y_axis.value() != 0 { + v.push(IrqSource::TapOnY).unwrap(); + } + if self.tap_z_axis.value() != 0 { + v.push(IrqSource::TapOnZ).unwrap(); + } + v + } +} + +impl From for TapSrc { + fn from(value: u8) -> Self { + let mut s = Self::default(); + s.tap_event.set_from_reg(value); + s.single_tap_event.set_from_reg(value); + s.double_tap_event.set_from_reg(value); + s.tap_sign_acceleration.set_from_reg(value); + s.tap_x_axis.set_from_reg(value); + s.tap_y_axis.set_from_reg(value); + s.tap_z_axis.set_from_reg(value); + s + } +} diff --git a/src/registers/tap_ths_6d.rs b/src/registers/tap_ths_6d.rs new file mode 100644 index 0000000..58a2858 --- /dev/null +++ b/src/registers/tap_ths_6d.rs @@ -0,0 +1,36 @@ +//! Portrait/landscape position and tap function threshold register (r/w). + +use crate::{RegisterAddress, RegisterBits, RegisterConfig, RegisterValue}; + +/// Portrait/landscape position and tap function threshold register (r/w). +#[derive(Default)] +pub struct TapThs6d { + /// 4D orientation detection enable. Z-axis position detection is disabled. Default value: 0 + /// (0: enabled; 1: disabled) + pub four_degrees_detection_enable: RegisterBits<1, 7>, + /// Threshold for 4D/6D function. Default value: 00 + /// For details, refer to Table 187. + pub six_degrees_threshold: RegisterBits<2, 5>, + /// Threshold for tap recognition. Default value: 00000 + /// 1 LSb corresponds to FS_XL/2^5 + pub tap_threshold: RegisterBits<5, 0>, +} + +impl TapThs6d { + pub fn address(&self) -> u8 { + RegisterAddress::TAP_THS_6D.address() + } + + pub fn value(&self) -> u8 { + self.four_degrees_detection_enable.shifted() + | self.six_degrees_threshold.shifted() + | self.tap_threshold.shifted() + } + + pub fn config(&self) -> RegisterConfig { + RegisterConfig { + address: self.address(), + value: self.value(), + } + } +} diff --git a/src/registers/wake_up_dur.rs b/src/registers/wake_up_dur.rs new file mode 100644 index 0000000..8c97db8 --- /dev/null +++ b/src/registers/wake_up_dur.rs @@ -0,0 +1,41 @@ +//! Free-fall, wakeup, timestamp and sleep mode functions duration setting register (r/w). + +use crate::{RegisterAddress, RegisterBits, RegisterConfig, RegisterValue}; + +/// Free-fall, wakeup, timestamp and sleep mode functions duration setting register (r/w). +#[derive(Default)] +pub struct WakeUpDur { + /// Free fall duration event. Default: 0 + /// For the complete configuration of the free-fall duration, refer to FF_DUR[4\:0] in FREE_FALL (5Dh) configuration. + /// 1 LSB = 1 ODR_time + pub free_fall_duration_event: RegisterBits<1, 7>, + /// Wake up duration event. Default: 00 + /// 1LSB = 1 ODR_time + pub wake_up_duration_event: RegisterBits<2, 5>, + /// Timestamp register resolution setting. Default value: 0 + /// (0: 1LSB = 6.4 ms; 1: 1LSB = 25 μs) + pub timestamp_resolution: RegisterBits<1, 4>, + /// Duration to go in sleep mode. Default value: 0000 (this corresponds to 16 ODR) + /// 1 LSB = 512 ODR + pub sleep_duration_event: RegisterBits<4, 0>, +} + +impl WakeUpDur { + pub fn address(&self) -> u8 { + RegisterAddress::WAKE_UP_DUR.address() + } + + pub fn value(&self) -> u8 { + self.free_fall_duration_event.shifted() + | self.wake_up_duration_event.shifted() + | self.timestamp_resolution.shifted() + | self.sleep_duration_event.shifted() + } + + pub fn config(&self) -> RegisterConfig { + RegisterConfig { + address: self.address(), + value: self.value(), + } + } +} diff --git a/src/registers/wake_up_src.rs b/src/registers/wake_up_src.rs new file mode 100644 index 0000000..b16a786 --- /dev/null +++ b/src/registers/wake_up_src.rs @@ -0,0 +1,70 @@ +//! Wake up interrupt source register (r). + +use heapless::Vec; + +use crate::{IrqSource, RegisterAddress, RegisterBits}; + +/// Wake up interrupt source register (r). +#[derive(Default)] +pub struct WakeUpSrc { + /// Free-fall event detection status. Default: 0 + /// (0: free-fall event not detected; 1: free-fall event detected) + pub free_fall_event: RegisterBits<1, 5>, + /// Sleep event status. Default value: 0 + /// (0: sleep event not detected; 1: sleep event detected) + pub sleep_event: RegisterBits<1, 4>, + /// Wakeup event detection status. Default value: 0 + /// (0: wakeup event not detected; 1: wakeup event detected.) + pub wake_up_event: RegisterBits<1, 3>, + /// Wakeup event detection status on X-axis. Default value: 0 + /// (0: wakeup event on X-axis not detected; 1: wakeup event on X-axis detected) + pub wake_up_event_x: RegisterBits<1, 2>, + /// Wakeup event detection status on Y-axis. Default value: 0 + /// (0: wakeup event on Y-axis not detected; 1: wakeup event on Y-axis detected) + pub wake_up_event_y: RegisterBits<1, 1>, + /// Wakeup event detection status on Z-axis. Default value: 0 + /// (0: wakeup event on Z-axis not detected; 1: wakeup event on Z-axis detected) + pub wake_up_event_z: RegisterBits<1, 0>, +} + +impl WakeUpSrc { + pub fn address(&self) -> u8 { + RegisterAddress::WAKE_UP_SRC.address() + } + + pub fn get_irq_sources(&self) -> Vec { + let mut v = Vec::new(); + if self.free_fall_event.value() != 0 { + v.push(IrqSource::FreeFall).unwrap(); + } + if self.sleep_event.value() != 0 { + v.push(IrqSource::Sleep).unwrap(); + } + if self.wake_up_event.value() != 0 { + v.push(IrqSource::WakeUp).unwrap(); + } + if self.wake_up_event_x.value() != 0 { + v.push(IrqSource::WakeUpOnX).unwrap(); + } + if self.wake_up_event_y.value() != 0 { + v.push(IrqSource::WakeUpOnY).unwrap(); + } + if self.wake_up_event_z.value() != 0 { + v.push(IrqSource::WakeUpOnZ).unwrap(); + } + v + } +} + +impl From for WakeUpSrc { + fn from(value: u8) -> Self { + let mut s = Self::default(); + s.free_fall_event.set_from_reg(value); + s.sleep_event.set_from_reg(value); + s.wake_up_event.set_from_reg(value); + s.wake_up_event_x.set_from_reg(value); + s.wake_up_event_y.set_from_reg(value); + s.wake_up_event_z.set_from_reg(value); + s + } +} diff --git a/src/registers/wake_up_ths.rs b/src/registers/wake_up_ths.rs new file mode 100644 index 0000000..53d2276 --- /dev/null +++ b/src/registers/wake_up_ths.rs @@ -0,0 +1,32 @@ +//! Single and double-tap function threshold register (r/w). + +use crate::{RegisterAddress, RegisterBits, RegisterConfig, RegisterValue}; + +/// Single and double-tap function threshold register (r/w). +#[derive(Default)] +pub struct WakeUpThs { + /// Single/double-tap event enable. Default: 0 + /// (0: only single-tap event enabled; + /// 1: both single and double-tap events enabled) + pub single_double_tap_enabled: RegisterBits<1, 7>, + /// Threshold for wakeup. Default value: 000000 + /// 1 LSb corresponds to FS_XL/2^6 + pub wake_up_threshold: RegisterBits<6, 0>, +} + +impl WakeUpThs { + pub fn address(&self) -> u8 { + RegisterAddress::WAKE_UP_THS.address() + } + + pub fn value(&self) -> u8 { + self.single_double_tap_enabled.shifted() | self.wake_up_threshold.shifted() + } + + pub fn config(&self) -> RegisterConfig { + RegisterConfig { + address: self.address(), + value: self.value(), + } + } +} diff --git a/src/settings/accel.rs b/src/settings/accel.rs new file mode 100644 index 0000000..62aa43c --- /dev/null +++ b/src/settings/accel.rs @@ -0,0 +1,37 @@ +use crate::registers::Ctrl1Xl; +use crate::{AccelSampleRate, AccelScale, RegisterConfig}; + +/// Accelerometer settings +#[derive(Default)] +pub struct AccelSettings { + pub sample_rate: AccelSampleRate, + pub scale: AccelScale, +} + +impl AccelSettings { + pub fn new() -> Self { + Self::default() + .with_sample_rate(AccelSampleRate::_416Hz) + .with_scale(AccelScale::_2G) + } + + pub fn with_sample_rate(mut self, sample_rate: AccelSampleRate) -> Self { + self.sample_rate = sample_rate; + self + } + + pub fn with_scale(mut self, scale: AccelScale) -> Self { + self.scale = scale; + self + } + + /// Returns accelerometer-related register config to be written + pub fn config(&self) -> RegisterConfig { + Ctrl1Xl { + sample_rate: self.sample_rate, + scale: self.scale, + ..Default::default() + } + .config() + } +} diff --git a/src/settings/gyro.rs b/src/settings/gyro.rs new file mode 100644 index 0000000..2762895 --- /dev/null +++ b/src/settings/gyro.rs @@ -0,0 +1,36 @@ +use crate::registers::Ctrl2G; +use crate::{GyroSampleRate, GyroScale, RegisterConfig}; + +/// Gyroscope settings +#[derive(Default)] +pub struct GyroSettings { + pub scale: GyroScale, + pub sample_rate: GyroSampleRate, +} + +impl GyroSettings { + pub fn new() -> Self { + Self::default() + .with_sample_rate(GyroSampleRate::_416Hz) + .with_scale(GyroScale::_250DPS) + } + + pub fn with_sample_rate(mut self, sample_rate: GyroSampleRate) -> Self { + self.sample_rate = sample_rate; + self + } + + pub fn with_scale(mut self, scale: GyroScale) -> Self { + self.scale = scale; + self + } + + /// Returns gyroscope-related register config to be written + pub fn config(&self) -> RegisterConfig { + Ctrl2G { + sample_rate: self.sample_rate, + scale: self.scale, + } + .config() + } +} diff --git a/src/settings/irq.rs b/src/settings/irq.rs new file mode 100644 index 0000000..137d7b3 --- /dev/null +++ b/src/settings/irq.rs @@ -0,0 +1,314 @@ +use crate::{ + data::XYZ, + registers::{ + FreeFall, FreeFallThreshold, InactivityGyroMode, Int1Ctrl, Int2Ctrl, IntDur2, Md1Cfg, + Md2Cfg, RegisterConfig, TapCfg, TapThs6d, WakeUpDur, WakeUpThs, + }, +}; + +/// Interrupt pin/pad routing selection +#[derive(Default, Clone, Copy)] +pub enum InterruptRoute { + #[default] + None, + Int1, + Int2, + Both, +} + +#[derive(Default, Clone, Copy)] +pub enum TapRecognitionMode { + #[default] + None, + Single, + Double, + Both, +} + +#[derive(Default, Clone, Copy)] +pub struct FreeFallIrqSettings { + pub interrupt_route: InterruptRoute, + pub threshold: FreeFallThreshold, + pub duration_samples: u8, +} + +#[derive(Default)] +pub struct WakeUpIrqSettings { + pub interrupt_route: InterruptRoute, + pub threshold: u8, +} + +#[derive(Default)] +pub struct OrientationDetectionIrqSettings { + pub interrupt_route: InterruptRoute, + // TODO +} + +#[derive(Default)] +pub struct TapIrqSettings { + pub interrupt_route: InterruptRoute, + pub recognition_mode: TapRecognitionMode, + pub direction_enable: XYZ, + pub threshold: u8, + pub shock_samples: u8, + pub quiet_samples: u8, + pub duration_samples: u8, +} + +#[derive(Default)] +pub struct InactivityIrqSettings { + pub interrupt_route: InterruptRoute, + pub inactivity_gyro_mode: InactivityGyroMode, + pub threshold: u8, + pub sleep_duration: u8, +} + +/// Interrupt settings +#[derive(Default)] +pub struct IrqSettings { + pub free_fall: FreeFallIrqSettings, + pub wake_up: WakeUpIrqSettings, + pub orientation_detection: OrientationDetectionIrqSettings, + pub tap: TapIrqSettings, + pub inactivity: InactivityIrqSettings, + registers: IrqRegisters, +} + +#[derive(Default)] +struct IrqRegisters { + int1_ctrl: Int1Ctrl, + int2_ctrl: Int2Ctrl, + md1_cfg: Md1Cfg, + md2_cfg: Md2Cfg, + tap_cfg: TapCfg, + tap_ths_6d: TapThs6d, + free_fall: FreeFall, + wake_up_dur: WakeUpDur, + wake_up_ths: WakeUpThs, + int_dur2: IntDur2, +} + +impl IrqSettings { + /// Enables Free Fall interrupt + pub fn enable_free_fall_irq( + &mut self, + threshold: FreeFallThreshold, + duration_samples: u8, + interrupt_route: InterruptRoute, + latched_irq: bool, + ) { + self.enable_irqs(latched_irq); + + self.free_fall.threshold = threshold; + self.free_fall.duration_samples = duration_samples; + self.free_fall.interrupt_route = interrupt_route; + + self.update_registers(); + } + + /// Enables Wake Up interrupt + pub fn enable_wake_up_irq( + &mut self, + threshold: u8, + interrupt_route: InterruptRoute, + latched_irq: bool, + ) { + self.enable_irqs(latched_irq); + + self.wake_up.threshold = threshold; + self.wake_up.interrupt_route = interrupt_route; + + self.update_registers(); + } + + // TODO provide helper function calculating desired tap time to register values + /// Enables Single/Double Tap interrupt + pub fn enable_tap_irq( + &mut self, + recognition_mode: TapRecognitionMode, + direction_enable: XYZ, + // threshold: u8, + // shock_samples: u8, + // quiet_samples: u8, + // duration_samples: u8, + interrupt_route: InterruptRoute, + latched_irq: bool, + ) { + self.enable_irqs(latched_irq); + + self.tap.recognition_mode = recognition_mode; + self.tap.direction_enable = direction_enable; + // self.tap.threshold = threshold; + // self.tap.shock_samples = shock_samples; + // self.tap.quiet_samples = quiet_samples; + // self.tap.duration_samples = duration_samples; + self.tap.interrupt_route = interrupt_route; + + self.update_registers(); + } + + /// Enables Activity/Inactivity interrupt + pub fn enable_inactivity_irq( + &mut self, + inactivity_gyro_mode: InactivityGyroMode, + threshold: u8, + duration_samples: u8, + interrupt_route: InterruptRoute, + latched_irq: bool, + ) { + self.enable_irqs(latched_irq); + + self.inactivity.inactivity_gyro_mode = inactivity_gyro_mode; + self.inactivity.threshold = threshold; + self.inactivity.sleep_duration = duration_samples; + self.inactivity.interrupt_route = interrupt_route; + + self.update_registers(); + } + + /// Enables basic interrupts + pub fn enable_irqs(&mut self, latched_irq: bool) { + self.registers.tap_cfg.enable_basic_interrupts = 1.into(); + self.registers.tap_cfg.latched_interrupt = latched_irq.into(); + + self.update_registers(); + } + + /// Disables basic interrupts + pub fn disable_irqs(&mut self) { + self.registers.tap_cfg.enable_basic_interrupts = 0.into(); + + self.update_registers(); + } + + /// Returns interrupt-related registers configs to be written + pub fn configs(&self) -> [RegisterConfig; 10] { + [ + self.registers.int1_ctrl.config(), + self.registers.int2_ctrl.config(), + self.registers.int_dur2.config(), + self.registers.md1_cfg.config(), + self.registers.md2_cfg.config(), + self.registers.tap_cfg.config(), + self.registers.tap_ths_6d.config(), + self.registers.free_fall.config(), + self.registers.wake_up_dur.config(), + self.registers.wake_up_ths.config(), + ] + } + + fn update_registers(&mut self) { + self.update_free_fall_registers(); + self.update_wake_up_registers(); + self.update_tap_registers(); + self.update_inactivity_registers(); + } + + fn update_free_fall_registers(&mut self) { + ( + self.registers.md1_cfg.free_fall_event, + self.registers.md2_cfg.free_fall_event, + ) = match self.free_fall.interrupt_route { + InterruptRoute::None => (0.into(), 0.into()), + InterruptRoute::Int1 => (1.into(), 0.into()), + InterruptRoute::Int2 => (0.into(), 1.into()), + InterruptRoute::Both => (1.into(), 1.into()), + }; + self.registers.free_fall.threshold = self.free_fall.threshold; + self.registers.wake_up_dur.free_fall_duration_event = + (((self.free_fall.threshold as u8) >> 5) & 0b1).into(); + self.registers.free_fall.duration_event = self.free_fall.duration_samples.into(); + } + + fn update_wake_up_registers(&mut self) { + ( + self.registers.md1_cfg.wake_up_event, + self.registers.md2_cfg.wake_up_event, + ) = match self.wake_up.interrupt_route { + InterruptRoute::None => (0.into(), 0.into()), + InterruptRoute::Int1 => (1.into(), 0.into()), + InterruptRoute::Int2 => (0.into(), 1.into()), + InterruptRoute::Both => (1.into(), 1.into()), + }; + self.registers.wake_up_ths.wake_up_threshold = self.wake_up.threshold.into(); + } + + fn update_tap_registers(&mut self) { + match self.tap.recognition_mode { + TapRecognitionMode::None | TapRecognitionMode::Single => { + self.registers.wake_up_ths.single_double_tap_enabled = 0.into() + } + TapRecognitionMode::Double | TapRecognitionMode::Both => { + self.registers.wake_up_ths.single_double_tap_enabled = 1.into() + } + } + match self.tap.recognition_mode { + TapRecognitionMode::None => { + self.registers.tap_cfg.enable_basic_interrupts = false.into() + } + TapRecognitionMode::Single => { + self.registers.tap_cfg.enable_basic_interrupts = true.into(); + self.update_single_tap_registers() + } + TapRecognitionMode::Double => { + self.registers.tap_cfg.enable_basic_interrupts = true.into(); + self.update_double_tap_registers() + } + TapRecognitionMode::Both => { + self.registers.tap_cfg.enable_basic_interrupts = true.into(); + self.update_single_tap_registers(); + self.update_double_tap_registers(); + } + }; + self.registers.tap_cfg.enable_basic_interrupts = 1.into(); + self.registers.tap_cfg.enable_x_direction_tap_recognition = + self.tap.direction_enable.x.into(); + self.registers.tap_cfg.enable_y_direction_tap_recognition = + self.tap.direction_enable.y.into(); + self.registers.tap_cfg.enable_z_direction_tap_recognition = + self.tap.direction_enable.z.into(); + self.registers.tap_ths_6d.tap_threshold = self.tap.threshold.into(); + self.registers.int_dur2.duration = self.tap.duration_samples.into(); + self.registers.int_dur2.quiet = self.tap.quiet_samples.into(); + self.registers.int_dur2.shock = self.tap.shock_samples.into(); + } + + fn update_single_tap_registers(&mut self) { + ( + self.registers.md1_cfg.single_tap_event, + self.registers.md2_cfg.single_tap_event, + ) = match self.tap.interrupt_route { + InterruptRoute::None => (0.into(), 0.into()), + InterruptRoute::Int1 => (1.into(), 0.into()), + InterruptRoute::Int2 => (0.into(), 1.into()), + InterruptRoute::Both => (1.into(), 1.into()), + }; + } + + fn update_double_tap_registers(&mut self) { + ( + self.registers.md1_cfg.double_tap_event, + self.registers.md2_cfg.double_tap_event, + ) = match self.tap.interrupt_route { + InterruptRoute::None => (0.into(), 0.into()), + InterruptRoute::Int1 => (1.into(), 0.into()), + InterruptRoute::Int2 => (0.into(), 1.into()), + InterruptRoute::Both => (1.into(), 1.into()), + }; + } + + fn update_inactivity_registers(&mut self) { + ( + self.registers.md1_cfg.inactivity_event, + self.registers.md2_cfg.inactivity_event, + ) = match self.inactivity.interrupt_route { + InterruptRoute::None => (0.into(), 0.into()), + InterruptRoute::Int1 => (1.into(), 0.into()), + InterruptRoute::Int2 => (0.into(), 1.into()), + InterruptRoute::Both => (1.into(), 1.into()), + }; + self.registers.wake_up_ths.wake_up_threshold = self.inactivity.threshold.into(); + self.registers.wake_up_dur.sleep_duration_event = self.inactivity.sleep_duration.into(); + self.registers.tap_cfg.enable_inactivity_function = self.inactivity.inactivity_gyro_mode; + } +} diff --git a/src/settings/mod.rs b/src/settings/mod.rs new file mode 100644 index 0000000..f1bdb3b --- /dev/null +++ b/src/settings/mod.rs @@ -0,0 +1,44 @@ +pub mod accel; +pub mod gyro; +pub mod irq; + +pub use accel::*; +pub use gyro::*; +pub use irq::*; + +/// Device settings +#[derive(Default)] +pub struct LsmSettings { + pub accel: AccelSettings, + pub gyro: GyroSettings, + pub irq: IrqSettings, + pub low_performance_mode: bool, +} + +impl LsmSettings { + pub fn basic() -> Self { + Self::default() + .with_accel(AccelSettings::new()) + .with_gyro(GyroSettings::new()) + } + + pub fn with_accel(mut self, accel_settings: AccelSettings) -> Self { + self.accel = accel_settings; + self + } + + pub fn with_gyro(mut self, gyro_settings: GyroSettings) -> Self { + self.gyro = gyro_settings; + self + } + + pub fn with_irq(mut self, irq_settings: IrqSettings) -> Self { + self.irq = irq_settings; + self + } + + pub fn with_low_performance_mode(mut self) -> Self { + self.low_performance_mode = true; + self + } +}