stk8ba58 1.0.2

Driver for the Sensortek STK8BA58 3-axis MEMS Accelerometer
Documentation
use core::fmt::Debug;

use accelerometer_hal::{
    vector::{F32x3, I16x3},
    Accelerometer, Error, RawAccelerometer,
};
use embedded_hal::i2c::I2c;

use crate::{
    registers::{Axis, BwSel, Read},
    Stk8ba58,
};

impl<I2C, E> Accelerometer for Stk8ba58<I2C>
where
    I2C: I2c<Error = E>,
    E: Debug,
{
    type Error = E;

    /// Get normalized ±g reading from the accelerometer.
    ///
    /// **Note**: it is expected that you call this when you know
    /// data is ready and valid, like in response to an
    /// interrupt or polling far slower than your data rate.
    ///
    /// Usage
    /// ```no_run
    /// let accel = accelerometer.accel_norm().unwrap();
    /// let (x, y, z) = (accel.x, accel.y, accel.z)
    /// println!("X = {x}\nY = {y},\nZ = [z]");
    /// ```
    ///
    /// # Errors
    ///  - Any I²C errors will cause the function to fail.
    fn accel_norm(&mut self) -> Result<F32x3, Error<E>> {
        const I12_MAX: f32 = 2048.0;
        let raw_data: I16x3 = self.accel_raw()?;
        let range: f32 = self.range.into();

        let x = (raw_data.x as f32 / I12_MAX) * range;
        let y = (raw_data.y as f32 / I12_MAX) * range;
        let z = (raw_data.z as f32 / I12_MAX) * range;

        Ok(F32x3::new(x, y, z))
    }

    /// Read the rample rate from the `BWSEL` register and convert it to Hz.
    ///
    /// **Note**: You likely want to use `.read_mode(BwSel::default())` from `registers::Read`
    ///
    /// # Errors
    ///  - Any I²C errors will cause the function to fail.
    fn sample_rate(&mut self) -> Result<f32, Error<Self::Error>> {
        Ok(self.read_mode(BwSel::default())?.into())
    }
}

impl<I2C, E> RawAccelerometer<I16x3> for Stk8ba58<I2C>
where
    I2C: I2c<Error = E>,
    E: Debug,
{
    type Error = E;

    /// Get raw acceleration reading from the accelerometer.
    /// Keep in mind readings are 12 bits.
    ///
    /// **Note**: it is expected that you call this when you know
    /// data is ready and valid, like in response to an
    /// interrupt or polling far slower than your data rate.
    ///
    /// # Errors
    ///  - Any I²C errors will cause the function to fail.
    fn accel_raw(&mut self) -> Result<I16x3, Error<E>> {
        const NEGATIVE: i16 = 0b1000_0000_0000; // Two's complement negative sign for 12 bits

        let mut xyz = [0, 0, 0];
        let axis = [Axis::X, Axis::Y, Axis::Z];

        for (i, axis) in axis.iter().enumerate() {
            // LSB should be read first due to data protection
            let lsb = self.axis_lsb(axis)?;
            let lsb = (lsb as i16) >> 4; // Move the bits to occupy positions 3-0 instead of 7-4

            let msb = self.axis_msb(axis)?;
            let msb = (msb as i16) << 4; // Move the bits to occupy positions 11-4 instead of 7-0

            let sign = if (msb & NEGATIVE) == NEGATIVE {
                0b1111_1111_0000_0000u16 as i16 // Correct for two's complement negative sign
            } else {
                0
            };

            let accel_raw = msb | lsb | sign;

            xyz[i] = accel_raw;
        }

        Ok(I16x3::new(xyz[0], xyz[1], xyz[2]))
    }
}