use crate::{Error, FullPower, Reg};
use bitmask_enum::bitmask;
use core::ops::RangeInclusive;
pub(crate) const FEATURE_SIZE: usize = 70;
#[repr(u8)]
#[derive(Copy, Clone, Debug)]
enum FeatureOffset {
AnyMotion = 0x00,
NoMotion = 0x04,
SingleTap = 0x3C,
DoubleTap = 0x3E,
AxesRemap = 0x44,
}
#[derive(Copy, Clone, Debug)]
pub enum MotionFeature {
AnyMotion,
NoMotion,
}
const MOTION_THRESHOLD_RANGE: RangeInclusive<u16> = 0..=0x7FF;
const MOTION_DURATION_RANGE: RangeInclusive<u16> = 0..=0x1FFF;
#[bitmask(u8)]
pub enum MotionAxes {
AxisX = 0b0010_0000,
AxisY = 0b0100_0000,
AxisZ = 0b1000_0000,
}
#[repr(u8)]
#[derive(Copy, Clone, Debug)]
pub enum RemapFromAxis {
AxisX = 0b000,
AxisY = 0b001,
AxisZ = 0b010,
AxisXMinus = 0b100,
AxisYMinus = 0b101,
AxisZMinus = 0b110,
}
impl core::ops::Neg for RemapFromAxis {
type Output = Self;
fn neg(self) -> Self::Output {
match self {
Self::AxisX => Self::AxisXMinus,
Self::AxisY => Self::AxisYMinus,
Self::AxisZ => Self::AxisZMinus,
Self::AxisXMinus => Self::AxisX,
Self::AxisYMinus => Self::AxisY,
Self::AxisZMinus => Self::AxisZ,
}
}
}
const TAP_SENSITIVITY_RANGE: RangeInclusive<u8> = 0..=7;
#[derive(Copy, Clone, Debug)]
pub enum TapFeature {
SingleTap,
DoubleTap,
}
pub struct EditFeatures<'a, I2C> {
pub(crate) register: [u8; FEATURE_SIZE + 1],
pub(crate) driver: &'a mut crate::Bma423<I2C, FullPower>,
}
impl<I2C: embedded_hal::i2c::I2c> EditFeatures<'_, I2C> {
fn edit_register(&mut self, f: impl FnOnce(&mut [u8])) {
f(&mut self.register[1..FEATURE_SIZE + 1]);
}
pub fn set_motion_config(
&mut self,
which: MotionFeature,
threshold: u16,
duration: u16,
enabled_axes: MotionAxes,
) -> Result<(), Error<I2C::Error>> {
if !MOTION_THRESHOLD_RANGE.contains(&threshold)
|| !MOTION_DURATION_RANGE.contains(&duration)
{
return Err(Error::BadArgument);
}
self.edit_register(|reg| {
let offset = match which {
MotionFeature::AnyMotion => FeatureOffset::AnyMotion,
MotionFeature::NoMotion => FeatureOffset::NoMotion,
} as usize;
let threshold = threshold.to_le_bytes();
reg[offset] = threshold[0];
reg[offset + 1] = threshold[1];
let duration = duration.to_le_bytes();
reg[offset + 2] = duration[0];
reg[offset + 3] = duration[1];
reg[offset + 3] = u8::from(enabled_axes) & 0xE0;
});
Ok(())
}
pub fn set_tap_config(
&mut self,
which: TapFeature,
sensitivity: u8,
enable: bool,
) -> Result<(), Error<I2C::Error>> {
if !TAP_SENSITIVITY_RANGE.contains(&sensitivity) {
return Err(Error::BadArgument);
}
self.edit_register(|reg| {
let offset = match which {
TapFeature::SingleTap => FeatureOffset::SingleTap,
TapFeature::DoubleTap => FeatureOffset::DoubleTap,
} as usize;
reg[offset] = sensitivity << 1;
if enable {
reg[offset] |= 1;
}
});
Ok(())
}
pub fn remap_axes(
&mut self,
x_axis: RemapFromAxis,
y_axis: RemapFromAxis,
z_axis: RemapFromAxis,
) {
let val = (x_axis as u16) | (y_axis as u16) << 3 | (z_axis as u16) << 6;
self.edit_register(|reg| {
let bytes = val.to_le_bytes();
reg[FeatureOffset::AxesRemap as usize] = bytes[0];
reg[FeatureOffset::AxesRemap as usize + 1] = bytes[1];
})
}
pub fn write(mut self) -> Result<(), Error<I2C::Error>> {
self.register[0] = Reg::FeatureConfig.into();
self.driver.write(&self.register)
}
}