use crate::registers::*;
pub(crate) const TEMP_SCALE: f32 = 8.0;
pub const DEFAULT_TEMP_BIAS: f32 = 25.0;
#[derive(Debug, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Lsm9ds0Config {
pub(crate) ctrl_reg1_g: CtrlReg1G,
pub(crate) ctrl_reg2_g: CtrlReg2G,
pub(crate) ctrl_reg3_g: CtrlReg3G,
pub(crate) ctrl_reg4_g: CtrlReg4G,
pub(crate) ctrl_reg5_g: CtrlReg5G,
pub(crate) fifo_ctrl_reg_g: FifoCtrlRegG,
pub(crate) int1_cfg_g: Int1CfgG,
pub(crate) int1_duration_g: Int1DurationG,
pub(crate) gyro_int_ths_x: u16, pub(crate) gyro_int_ths_y: u16,
pub(crate) gyro_int_ths_z: u16,
pub(crate) ctrl_reg0_xm: CtrlReg0Xm,
pub(crate) ctrl_reg1_xm: CtrlReg1Xm,
pub(crate) ctrl_reg2_xm: CtrlReg2Xm,
pub(crate) ctrl_reg3_xm: CtrlReg3Xm,
pub(crate) ctrl_reg4_xm: CtrlReg4Xm,
pub(crate) ctrl_reg5_xm: CtrlReg5Xm,
pub(crate) ctrl_reg6_xm: CtrlReg6Xm,
pub(crate) ctrl_reg7_xm: CtrlReg7Xm,
pub(crate) fifo_ctrl_reg: FifoCtrlReg,
pub(crate) int_gen_1_reg: IntGenReg,
pub(crate) int_gen_1_ths: u8, pub(crate) int_gen_1_duration: u8, pub(crate) int_gen_2_reg: IntGenReg,
pub(crate) int_gen_2_ths: u8,
pub(crate) int_gen_2_duration: u8,
pub(crate) int_ctrl_reg_m: IntCtrlRegM,
pub(crate) click_cfg: ClickCfg,
pub(crate) click_ths: u8, pub(crate) time_limit_ms: u8,
pub(crate) time_latency_ms: u8,
pub(crate) time_window_ms: u8,
pub(crate) act_ths: u8, pub(crate) act_dur: u8,
pub(crate) mag_int_ths: u16, pub(crate) mag_offset_x: i16,
pub(crate) mag_offset_y: i16,
pub(crate) mag_offset_z: i16,
pub(crate) gyro_axes_enabled: (bool, bool, bool),
pub(crate) temp_offset: f32,
pub(crate) gyro_bias: (f32, f32, f32),
pub(crate) accel_bias: (f32, f32, f32),
pub(crate) auto_calibration: Option<crate::types::Orientation>,
}
impl Default for Lsm9ds0Config {
fn default() -> Self {
Self {
ctrl_reg1_g: CtrlReg1G::new()
.with_dr(GyroDataRate::Hz95)
.with_bw(GyroBandwidth::Bw0)
.with_pd(PowerMode::PowerDown) .with_zen(Enable::Enabled)
.with_yen(Enable::Enabled)
.with_xen(Enable::Enabled),
ctrl_reg2_g: CtrlReg2G::new()
.with_hpm(GyroHpfMode::NormalReset)
.with_hpcf(GyroHpfCutoff::Cutoff0),
ctrl_reg3_g: CtrlReg3G::new(),
ctrl_reg4_g: CtrlReg4G::new()
.with_fs(GyroScale::Dps245)
.with_bdu(BlockDataUpdate::Continuous)
.with_ble(Endianness::Little)
.with_st(GyroSelfTest::Disabled)
.with_sim(SpiMode::FourWire),
ctrl_reg5_g: CtrlReg5G::new(),
fifo_ctrl_reg_g: FifoCtrlRegG::new().with_fm(FifoMode::Bypass).with_wtm(0),
int1_cfg_g: Int1CfgG::new(),
int1_duration_g: Int1DurationG::new(),
gyro_int_ths_x: 0,
gyro_int_ths_y: 0,
gyro_int_ths_z: 0,
ctrl_reg0_xm: CtrlReg0Xm::new(),
ctrl_reg1_xm: CtrlReg1Xm::new()
.with_aodr(AccelDataRate::PowerDown)
.with_bdu(BlockDataUpdate::Continuous)
.with_azen(Enable::Enabled)
.with_ayen(Enable::Enabled)
.with_axen(Enable::Enabled),
ctrl_reg2_xm: CtrlReg2Xm::new()
.with_abw(AccelBandwidth::Hz773)
.with_afs(AccelScale::G2)
.with_ast(AccelSelfTest::Normal)
.with_sim(SpiMode::FourWire),
ctrl_reg3_xm: CtrlReg3Xm::new(),
ctrl_reg4_xm: CtrlReg4Xm::new(),
ctrl_reg5_xm: CtrlReg5Xm::new()
.with_temp_en(Enable::Disabled)
.with_m_res(MagResolution::Low)
.with_m_odr(MagDataRate::Hz50)
.with_lir1(LatchInterrupt::NotLatched)
.with_lir2(LatchInterrupt::NotLatched),
ctrl_reg6_xm: CtrlReg6Xm::new().with_mfs(MagScale::Gauss4),
ctrl_reg7_xm: CtrlReg7Xm::new()
.with_md(MagMode::PowerDown)
.with_mlp(MagLowPower::Normal)
.with_afds(Enable::Disabled)
.with_ahpm(AccelHpfMode::NormalReset),
fifo_ctrl_reg: FifoCtrlReg::new().with_fm(FifoMode::Bypass).with_fth(0),
int_gen_1_reg: IntGenReg::new(),
int_gen_1_ths: 0,
int_gen_1_duration: 0,
int_gen_2_reg: IntGenReg::new(),
int_gen_2_ths: 0,
int_gen_2_duration: 0,
int_ctrl_reg_m: IntCtrlRegM::new()
.with_iea(ActiveLevelInverted::ActiveHigh)
.with_zmien(Enable::Enabled)
.with_ymien(Enable::Enabled)
.with_xmien(Enable::Enabled),
click_cfg: ClickCfg::new(),
click_ths: 0,
time_limit_ms: 0,
time_latency_ms: 0,
time_window_ms: 0,
act_ths: 0,
act_dur: 0,
mag_int_ths: 0,
mag_offset_x: 0,
mag_offset_y: 0,
mag_offset_z: 0,
temp_offset: DEFAULT_TEMP_BIAS,
gyro_axes_enabled: (true, true, true),
gyro_bias: (0.0, 0.0, 0.0),
accel_bias: (0.0, 0.0, 0.0),
auto_calibration: None,
}
}
}
impl Lsm9ds0Config {
pub fn new() -> Self {
Self::default()
}
}
impl Lsm9ds0Config {
pub fn gyro_sensitivity(&self) -> f32 {
self.ctrl_reg4_g.fs().sensitivity()
}
pub fn accel_sensitivity(&self) -> f32 {
self.ctrl_reg2_xm.afs().sensitivity()
}
pub fn mag_sensitivity(&self) -> f32 {
self.ctrl_reg6_xm.mfs().sensitivity()
}
pub fn gyro_hpf_cutoff_hz(&self) -> f32 {
const CUTOFF_TABLE: [[f32; 10]; 4] = [
[7.2, 3.5, 1.8, 0.9, 0.45, 0.18, 0.09, 0.045, 0.018, 0.009],
[13.5, 7.2, 3.5, 1.8, 0.9, 0.45, 0.18, 0.09, 0.045, 0.018],
[27.0, 13.5, 7.2, 3.5, 1.8, 0.9, 0.45, 0.18, 0.09, 0.045],
[51.4, 27.0, 13.5, 7.2, 3.5, 1.8, 0.9, 0.45, 0.18, 0.09],
];
let odr_idx = self.ctrl_reg1_g.dr() as usize;
let cutoff_idx = self.ctrl_reg2_g.hpcf() as usize;
CUTOFF_TABLE[odr_idx][cutoff_idx.min(9)]
}
pub fn gyro_lpf_cutoff_hz(&self) -> f32 {
const CUTOFF_TABLE: [[f32; 4]; 4] = [
[12.5, 25.0, 25.0, 25.0],
[12.5, 25.0, 50.0, 70.0],
[20.0, 25.0, 50.0, 100.0],
[30.0, 35.0, 50.0, 100.0],
];
let odr_idx = self.ctrl_reg1_g.dr() as usize;
let bw_idx = self.ctrl_reg1_g.bw() as usize;
CUTOFF_TABLE[odr_idx][bw_idx]
}
pub fn temperature_offset(&self) -> f32 {
self.temp_offset
}
pub fn gyro_scale(&self) -> GyroScale {
self.ctrl_reg4_g.fs()
}
pub fn gyro_data_rate(&self) -> GyroDataRate {
self.ctrl_reg1_g.dr()
}
pub fn accel_scale(&self) -> AccelScale {
self.ctrl_reg2_xm.afs()
}
pub fn accel_data_rate(&self) -> AccelDataRate {
self.ctrl_reg1_xm.aodr()
}
pub fn mag_scale(&self) -> MagScale {
self.ctrl_reg6_xm.mfs()
}
pub fn mag_data_rate(&self) -> MagDataRate {
self.ctrl_reg5_xm.m_odr()
}
}
impl Lsm9ds0Config {
pub fn with_gyro_enabled(mut self, enabled: bool) -> Self {
if enabled {
self.ctrl_reg1_g.set_pd(PowerMode::Normal);
let (x, y, z) = self.gyro_axes_enabled;
self.ctrl_reg1_g.set_xen(Enable::from(x));
self.ctrl_reg1_g.set_yen(Enable::from(y));
self.ctrl_reg1_g.set_zen(Enable::from(z));
} else {
self.ctrl_reg1_g.set_pd(PowerMode::PowerDown);
}
self
}
pub fn with_gyro_power_mode(mut self, mode: GyroPowerMode) -> Self {
match mode {
GyroPowerMode::PowerDown => {
self.ctrl_reg1_g.set_pd(PowerMode::PowerDown);
}
GyroPowerMode::Sleep => {
self.ctrl_reg1_g.set_pd(PowerMode::Normal);
self.ctrl_reg1_g.set_xen(Enable::Disabled);
self.ctrl_reg1_g.set_yen(Enable::Disabled);
self.ctrl_reg1_g.set_zen(Enable::Disabled);
}
GyroPowerMode::Normal => {
self.ctrl_reg1_g.set_pd(PowerMode::Normal);
let (x, y, z) = self.gyro_axes_enabled;
self.ctrl_reg1_g.set_xen(Enable::from(x));
self.ctrl_reg1_g.set_yen(Enable::from(y));
self.ctrl_reg1_g.set_zen(Enable::from(z));
}
}
self
}
pub fn with_gyro_scale(mut self, scale: GyroScale) -> Self {
self.ctrl_reg4_g.set_fs(scale);
self
}
pub fn with_gyro_data_rate(mut self, rate: GyroDataRate) -> Self {
self.ctrl_reg1_g.set_dr(rate);
self
}
pub fn with_gyro_bandwidth(mut self, bw: GyroBandwidth) -> Self {
self.ctrl_reg1_g.set_bw(bw);
self
}
pub fn with_gyro_axes(mut self, x: bool, y: bool, z: bool) -> Self {
self.gyro_axes_enabled = (x, y, z);
self.ctrl_reg1_g.set_xen(Enable::from(x));
self.ctrl_reg1_g.set_yen(Enable::from(y));
self.ctrl_reg1_g.set_zen(Enable::from(z));
self
}
pub fn with_gyro_hpf(
mut self,
enabled: bool,
mode: GyroHpfMode,
cutoff: GyroHpfCutoff,
) -> Self {
self.ctrl_reg5_g.set_hpen(if enabled {
Enable::Enabled
} else {
Enable::Disabled
});
self.ctrl_reg2_g.set_hpm(mode);
self.ctrl_reg2_g.set_hpcf(cutoff);
self
}
pub fn with_gyro_fifo(mut self, enabled: bool, mode: FifoMode, watermark: u8) -> Self {
self.ctrl_reg5_g.set_fifo_en(if enabled {
Enable::Enabled
} else {
Enable::Disabled
});
self.fifo_ctrl_reg_g.set_fm(mode);
self.fifo_ctrl_reg_g.set_wtm(watermark & 0x1F);
self
}
pub fn with_gyro_self_test(mut self, mode: GyroSelfTest) -> Self {
self.ctrl_reg4_g.set_st(mode);
self
}
pub fn with_gyro_bdu(mut self, enabled: bool) -> Self {
self.ctrl_reg4_g.set_bdu(if enabled {
BlockDataUpdate::WaitForRead
} else {
BlockDataUpdate::Continuous
});
self
}
}
impl Lsm9ds0Config {
pub fn with_gyro_int_enabled(mut self, enabled: bool) -> Self {
self.ctrl_reg3_g.set_i1_int1(if enabled {
Enable::Enabled
} else {
Enable::Disabled
});
self
}
pub fn with_gyro_int_pin_config(mut self, active_low: bool, open_drain: bool) -> Self {
self.ctrl_reg3_g.set_h_lactive(if active_low {
ActiveLevel::ActiveLow
} else {
ActiveLevel::ActiveHigh
});
self.ctrl_reg3_g.set_pp_od(if open_drain {
OutputType::OpenDrain
} else {
OutputType::PushPull
});
self
}
pub fn with_gyro_int_axes(
mut self,
x_high: bool,
x_low: bool,
y_high: bool,
y_low: bool,
z_high: bool,
z_low: bool,
) -> Self {
self.int1_cfg_g.set_xhie(if x_high {
Enable::Enabled
} else {
Enable::Disabled
});
self.int1_cfg_g.set_xlie(if x_low {
Enable::Enabled
} else {
Enable::Disabled
});
self.int1_cfg_g.set_yhie(if y_high {
Enable::Enabled
} else {
Enable::Disabled
});
self.int1_cfg_g.set_ylie(if y_low {
Enable::Enabled
} else {
Enable::Disabled
});
self.int1_cfg_g.set_zhie(if z_high {
Enable::Enabled
} else {
Enable::Disabled
});
self.int1_cfg_g.set_zlie(if z_low {
Enable::Enabled
} else {
Enable::Disabled
});
self
}
pub fn with_gyro_int_mode(mut self, and_combination: bool, latch: bool) -> Self {
self.int1_cfg_g.set_and_or(if and_combination {
InterruptCombination::And
} else {
InterruptCombination::Or
});
self.int1_cfg_g.set_lir(LatchInterrupt::from(latch));
self
}
pub fn with_gyro_int_threshold(mut self, threshold: u16) -> Self {
debug_assert!(
threshold <= 0x7FFF,
"gyro interrupt threshold {threshold:#06X} exceeds 15-bit max (0x7FFF)"
);
let masked = threshold & 0x7FFF;
self.gyro_int_ths_x = masked;
self.gyro_int_ths_y = masked;
self.gyro_int_ths_z = masked;
self
}
pub fn with_gyro_int_threshold_xyz(mut self, x: u16, y: u16, z: u16) -> Self {
debug_assert!(
x <= 0x7FFF,
"gyro X interrupt threshold {x:#06X} exceeds 15-bit max (0x7FFF)"
);
debug_assert!(
y <= 0x7FFF,
"gyro Y interrupt threshold {y:#06X} exceeds 15-bit max (0x7FFF)"
);
debug_assert!(
z <= 0x7FFF,
"gyro Z interrupt threshold {z:#06X} exceeds 15-bit max (0x7FFF)"
);
self.gyro_int_ths_x = x & 0x7FFF;
self.gyro_int_ths_y = y & 0x7FFF;
self.gyro_int_ths_z = z & 0x7FFF;
self
}
pub fn with_gyro_int_wait(mut self, wait: bool) -> Self {
self.int1_duration_g.set_wait(wait);
self
}
pub fn with_gyro_int_duration(mut self, duration: u8) -> Self {
debug_assert!(
duration <= 0x7F,
"gyro interrupt duration {duration:#04X} exceeds 7-bit max (0x7F)"
);
self.int1_duration_g.set_duration(duration & 0x7F);
self
}
pub fn with_gyro_motion_threshold(mut self, threshold: u16) -> Self {
debug_assert!(
threshold <= 0x7FFF,
"gyro motion threshold {threshold:#06X} exceeds 15-bit max (0x7FFF)"
);
self.ctrl_reg3_g.set_i1_int1(Enable::Enabled);
self.int1_cfg_g.set_and_or(InterruptCombination::Or); self.int1_cfg_g.set_lir(LatchInterrupt::NotLatched);
self.int1_cfg_g.set_xhie(Enable::Enabled);
self.int1_cfg_g.set_xlie(Enable::Enabled);
self.int1_cfg_g.set_yhie(Enable::Enabled);
self.int1_cfg_g.set_ylie(Enable::Enabled);
self.int1_cfg_g.set_zhie(Enable::Enabled);
self.int1_cfg_g.set_zlie(Enable::Enabled);
let masked = threshold & 0x7FFF;
self.gyro_int_ths_x = masked;
self.gyro_int_ths_y = masked;
self.gyro_int_ths_z = masked;
self.int1_duration_g = Int1DurationG::new();
self
}
pub fn with_gyro_drdy_int(
mut self,
data_ready: bool,
fifo_wtm: bool,
fifo_overrun: bool,
fifo_empty: bool,
) -> Self {
self.ctrl_reg3_g.set_i2_drdy(if data_ready {
Enable::Enabled
} else {
Enable::Disabled
});
self.ctrl_reg3_g.set_i2_wtm(if fifo_wtm {
Enable::Enabled
} else {
Enable::Disabled
});
self.ctrl_reg3_g.set_i2_orun(if fifo_overrun {
Enable::Enabled
} else {
Enable::Disabled
});
self.ctrl_reg3_g.set_i2_empty(if fifo_empty {
Enable::Enabled
} else {
Enable::Disabled
});
self
}
}
impl Lsm9ds0Config {
pub fn with_accel_data_rate(mut self, rate: AccelDataRate) -> Self {
let mag_odr = self.ctrl_reg5_xm.m_odr();
if mag_odr == MagDataRate::Hz100 {
debug_assert!(
matches!(
rate,
AccelDataRate::PowerDown
| AccelDataRate::Hz100
| AccelDataRate::Hz200
| AccelDataRate::Hz400
| AccelDataRate::Hz800
| AccelDataRate::Hz1600
),
"AccelDataRate must be > 50 Hz or PowerDown when MagDataRate is Hz100, got {:?}",
rate
);
}
self.ctrl_reg1_xm.set_aodr(rate);
self
}
pub fn with_accel_scale(mut self, scale: AccelScale) -> Self {
self.ctrl_reg2_xm.set_afs(scale);
self
}
pub fn with_accel_bandwidth(mut self, bw: AccelBandwidth) -> Self {
self.ctrl_reg2_xm.set_abw(bw);
self
}
pub fn with_accel_axes(mut self, x: bool, y: bool, z: bool) -> Self {
self.ctrl_reg1_xm.set_axen(Enable::from(x));
self.ctrl_reg1_xm.set_ayen(Enable::from(y));
self.ctrl_reg1_xm.set_azen(Enable::from(z));
self
}
pub fn with_accel_bdu(mut self, enabled: bool) -> Self {
self.ctrl_reg1_xm.set_bdu(if enabled {
BlockDataUpdate::WaitForRead
} else {
BlockDataUpdate::Continuous
});
self
}
pub fn with_accel_self_test(mut self, mode: AccelSelfTest) -> Self {
self.ctrl_reg2_xm.set_ast(mode);
self
}
pub fn with_accel_hpf(mut self, enabled: bool, mode: AccelHpfMode) -> Self {
self.ctrl_reg7_xm.set_afds(if enabled {
Enable::Enabled
} else {
Enable::Disabled
});
self.ctrl_reg7_xm.set_ahpm(mode);
self
}
pub fn with_accel_hpf_functions(
mut self,
for_click: bool,
for_int1: bool,
for_int2: bool,
) -> Self {
self.ctrl_reg0_xm.set_hp_click(if for_click {
Enable::Enabled
} else {
Enable::Disabled
});
self.ctrl_reg0_xm.set_hpis1(if for_int1 {
Enable::Enabled
} else {
Enable::Disabled
});
self.ctrl_reg0_xm.set_hpis2(if for_int2 {
Enable::Enabled
} else {
Enable::Disabled
});
self
}
pub fn with_accel_fifo(mut self, enabled: bool, mode: FifoMode, watermark: u8) -> Self {
self.ctrl_reg0_xm.set_fifo_en(if enabled {
Enable::Enabled
} else {
Enable::Disabled
});
self.fifo_ctrl_reg.set_fm(mode);
self.fifo_ctrl_reg.set_fth(watermark & 0x1F);
self
}
}
impl Lsm9ds0Config {
pub fn with_accel_int1_axes(
mut self,
x_high: bool,
x_low: bool,
y_high: bool,
y_low: bool,
z_high: bool,
z_low: bool,
) -> Self {
self.int_gen_1_reg.set_xhie(if x_high {
Enable::Enabled
} else {
Enable::Disabled
});
self.int_gen_1_reg.set_xlie(if x_low {
Enable::Enabled
} else {
Enable::Disabled
});
self.int_gen_1_reg.set_yhie(if y_high {
Enable::Enabled
} else {
Enable::Disabled
});
self.int_gen_1_reg.set_ylie(if y_low {
Enable::Enabled
} else {
Enable::Disabled
});
self.int_gen_1_reg.set_zhie(if z_high {
Enable::Enabled
} else {
Enable::Disabled
});
self.int_gen_1_reg.set_zlie(if z_low {
Enable::Enabled
} else {
Enable::Disabled
});
self
}
pub fn with_accel_int1_mode(mut self, and_combination: bool, six_d: bool) -> Self {
self.int_gen_1_reg.set_aoi(if and_combination {
InterruptCombination::And
} else {
InterruptCombination::Or
});
self.int_gen_1_reg.set_six_d(if six_d {
Enable::Enabled
} else {
Enable::Disabled
});
self
}
pub fn with_accel_int1_threshold(mut self, threshold: u8) -> Self {
debug_assert!(
threshold <= 0x7F,
"accel int1 threshold {threshold:#04X} exceeds 7-bit max (0x7F)"
);
self.int_gen_1_ths = threshold & 0x7F;
self
}
pub fn with_accel_int1_duration(mut self, duration: u8) -> Self {
debug_assert!(
duration <= 0x7F,
"accel int1 duration {duration:#04X} exceeds 7-bit max (0x7F)"
);
self.int_gen_1_duration = duration & 0x7F;
self
}
pub fn with_accel_int1_latch(mut self, latch: bool) -> Self {
self.ctrl_reg5_xm.set_lir1(LatchInterrupt::from(latch));
self
}
pub fn with_accel_motion_threshold(mut self, threshold: u8) -> Self {
debug_assert!(
threshold <= 0x7F,
"accel motion threshold {threshold:#04X} exceeds 7-bit max (0x7F)"
);
self.ctrl_reg3_xm.set_p1_int1(Enable::Enabled);
self.int_gen_1_reg.set_aoi(InterruptCombination::Or); self.int_gen_1_reg.set_six_d(Enable::Disabled);
self.int_gen_1_reg.set_xhie(Enable::Enabled);
self.int_gen_1_reg.set_xlie(Enable::Enabled);
self.int_gen_1_reg.set_yhie(Enable::Enabled);
self.int_gen_1_reg.set_ylie(Enable::Enabled);
self.int_gen_1_reg.set_zhie(Enable::Enabled);
self.int_gen_1_reg.set_zlie(Enable::Enabled);
self.int_gen_1_ths = threshold & 0x7F;
self.int_gen_1_duration = 0;
self
}
pub fn with_accel_int2_axes(
mut self,
x_high: bool,
x_low: bool,
y_high: bool,
y_low: bool,
z_high: bool,
z_low: bool,
) -> Self {
self.int_gen_2_reg.set_xhie(if x_high {
Enable::Enabled
} else {
Enable::Disabled
});
self.int_gen_2_reg.set_xlie(if x_low {
Enable::Enabled
} else {
Enable::Disabled
});
self.int_gen_2_reg.set_yhie(if y_high {
Enable::Enabled
} else {
Enable::Disabled
});
self.int_gen_2_reg.set_ylie(if y_low {
Enable::Enabled
} else {
Enable::Disabled
});
self.int_gen_2_reg.set_zhie(if z_high {
Enable::Enabled
} else {
Enable::Disabled
});
self.int_gen_2_reg.set_zlie(if z_low {
Enable::Enabled
} else {
Enable::Disabled
});
self
}
pub fn with_accel_int2_mode(mut self, and_combination: bool, six_d: bool) -> Self {
self.int_gen_2_reg.set_aoi(if and_combination {
InterruptCombination::And
} else {
InterruptCombination::Or
});
self.int_gen_2_reg.set_six_d(if six_d {
Enable::Enabled
} else {
Enable::Disabled
});
self
}
pub fn with_accel_int2_threshold(mut self, threshold: u8) -> Self {
debug_assert!(
threshold <= 0x7F,
"accel int2 threshold {threshold:#04X} exceeds 7-bit max (0x7F)"
);
self.int_gen_2_ths = threshold & 0x7F;
self
}
pub fn with_accel_int2_duration(mut self, duration: u8) -> Self {
debug_assert!(
duration <= 0x7F,
"accel int2 duration {duration:#04X} exceeds 7-bit max (0x7F)"
);
self.int_gen_2_duration = duration & 0x7F;
self
}
pub fn with_accel_int2_latch(mut self, latch: bool) -> Self {
self.ctrl_reg5_xm.set_lir2(LatchInterrupt::from(latch));
self
}
#[allow(clippy::too_many_arguments)]
pub fn with_int1_xm_routing(
mut self,
boot: bool,
tap: bool,
int1: bool,
int2: bool,
mag_int: bool,
accel_drdy: bool,
mag_drdy: bool,
fifo_empty: bool,
) -> Self {
self.ctrl_reg3_xm.set_p1_boot(if boot {
Enable::Enabled
} else {
Enable::Disabled
});
self.ctrl_reg3_xm.set_p1_tap(if tap {
Enable::Enabled
} else {
Enable::Disabled
});
self.ctrl_reg3_xm.set_p1_int1(if int1 {
Enable::Enabled
} else {
Enable::Disabled
});
self.ctrl_reg3_xm.set_p1_int2(if int2 {
Enable::Enabled
} else {
Enable::Disabled
});
self.ctrl_reg3_xm.set_p1_intm(if mag_int {
Enable::Enabled
} else {
Enable::Disabled
});
self.ctrl_reg3_xm.set_p1_drdya(if accel_drdy {
Enable::Enabled
} else {
Enable::Disabled
});
self.ctrl_reg3_xm.set_p1_drdym(if mag_drdy {
Enable::Enabled
} else {
Enable::Disabled
});
self.ctrl_reg3_xm.set_p1_empty(if fifo_empty {
Enable::Enabled
} else {
Enable::Disabled
});
self
}
#[allow(clippy::too_many_arguments)]
pub fn with_int2_xm_routing(
mut self,
tap: bool,
int1: bool,
int2: bool,
mag_int: bool,
accel_drdy: bool,
mag_drdy: bool,
fifo_overrun: bool,
fifo_wtm: bool,
) -> Self {
self.ctrl_reg4_xm.set_p2_tap(if tap {
Enable::Enabled
} else {
Enable::Disabled
});
self.ctrl_reg4_xm.set_p2_int1(if int1 {
Enable::Enabled
} else {
Enable::Disabled
});
self.ctrl_reg4_xm.set_p2_int2(if int2 {
Enable::Enabled
} else {
Enable::Disabled
});
self.ctrl_reg4_xm.set_p2_intm(if mag_int {
Enable::Enabled
} else {
Enable::Disabled
});
self.ctrl_reg4_xm.set_p2_drdya(if accel_drdy {
Enable::Enabled
} else {
Enable::Disabled
});
self.ctrl_reg4_xm.set_p2_drdym(if mag_drdy {
Enable::Enabled
} else {
Enable::Disabled
});
self.ctrl_reg4_xm.set_p2_overrun(if fifo_overrun {
Enable::Enabled
} else {
Enable::Disabled
});
self.ctrl_reg4_xm.set_p2_wtm(if fifo_wtm {
Enable::Enabled
} else {
Enable::Disabled
});
self
}
pub fn with_click_axes(
mut self,
x_single: bool,
x_double: bool,
y_single: bool,
y_double: bool,
z_single: bool,
z_double: bool,
) -> Self {
self.click_cfg.set_xs(if x_single {
Enable::Enabled
} else {
Enable::Disabled
});
self.click_cfg.set_xd(if x_double {
Enable::Enabled
} else {
Enable::Disabled
});
self.click_cfg.set_ys(if y_single {
Enable::Enabled
} else {
Enable::Disabled
});
self.click_cfg.set_yd(if y_double {
Enable::Enabled
} else {
Enable::Disabled
});
self.click_cfg.set_zs(if z_single {
Enable::Enabled
} else {
Enable::Disabled
});
self.click_cfg.set_zd(if z_double {
Enable::Enabled
} else {
Enable::Disabled
});
self
}
pub fn with_click_threshold(mut self, threshold: u8) -> Self {
debug_assert!(
threshold <= 0x7F,
"click threshold {threshold:#04X} exceeds 7-bit max (0x7F)"
);
self.click_ths = threshold & 0x7F;
self
}
pub fn with_click_time_limit(mut self, limit: u8) -> Self {
self.time_limit_ms = limit;
self
}
pub fn with_click_time_latency(mut self, latency: u8) -> Self {
self.time_latency_ms = latency;
self
}
pub fn with_click_time_window(mut self, window: u8) -> Self {
self.time_window_ms = window;
self
}
pub fn with_click_detection(mut self, threshold: u8) -> Self {
debug_assert!(
threshold <= 0x7F,
"click detection threshold {threshold:#04X} exceeds 7-bit max (0x7F)"
);
self.click_cfg.set_xs(Enable::Enabled);
self.click_cfg.set_ys(Enable::Enabled);
self.click_cfg.set_zs(Enable::Enabled);
self.click_ths = threshold & 0x7F;
self.time_limit_ms = 10;
self.time_latency_ms = 20;
self.time_window_ms = 50;
self
}
pub fn with_activity_threshold(mut self, threshold: u8) -> Self {
debug_assert!(
threshold <= 0x7F,
"activity threshold {threshold:#04X} exceeds 7-bit max (0x7F)"
);
self.act_ths = threshold & 0x7F;
self
}
pub fn with_activity_duration(mut self, duration: u8) -> Self {
self.act_dur = duration;
self
}
pub fn with_activity_detection(mut self, threshold: u8, duration: u8) -> Self {
debug_assert!(
threshold <= 0x7F,
"activity threshold {threshold:#04X} exceeds 7-bit max (0x7F)"
);
self.act_ths = threshold & 0x7F;
self.act_dur = duration;
self
}
}
impl Lsm9ds0Config {
pub fn with_mag_mode(mut self, mode: MagMode) -> Self {
self.ctrl_reg7_xm.set_md(mode);
self
}
pub fn with_mag_data_rate(mut self, rate: MagDataRate) -> Self {
if rate == MagDataRate::Hz100 {
let accel_odr = self.ctrl_reg1_xm.aodr();
debug_assert!(
matches!(
accel_odr,
AccelDataRate::PowerDown
| AccelDataRate::Hz100
| AccelDataRate::Hz200
| AccelDataRate::Hz400
| AccelDataRate::Hz800
| AccelDataRate::Hz1600
),
"MagDataRate::Hz100 requires AccelDataRate > 50 Hz or PowerDown, got {:?}",
accel_odr
);
}
self.ctrl_reg5_xm.set_m_odr(rate);
self
}
pub fn with_mag_scale(mut self, scale: MagScale) -> Self {
self.ctrl_reg6_xm.set_mfs(scale);
self
}
pub fn with_mag_resolution(mut self, res: MagResolution) -> Self {
self.ctrl_reg5_xm.set_m_res(res);
self
}
pub fn with_mag_low_power(mut self, enabled: bool) -> Self {
self.ctrl_reg7_xm.set_mlp(if enabled {
MagLowPower::LowPower
} else {
MagLowPower::Normal
});
self
}
pub fn with_mag_int(
mut self,
enabled: bool,
x: bool,
y: bool,
z: bool,
active_high: bool,
latch: bool,
) -> Self {
self.int_ctrl_reg_m.set_mien(if enabled {
Enable::Enabled
} else {
Enable::Disabled
});
self.int_ctrl_reg_m.set_xmien(Enable::from(x));
self.int_ctrl_reg_m.set_ymien(Enable::from(y));
self.int_ctrl_reg_m.set_zmien(Enable::from(z));
self.int_ctrl_reg_m.set_iea(if active_high {
ActiveLevelInverted::ActiveHigh
} else {
ActiveLevelInverted::ActiveLow
});
self.int_ctrl_reg_m.set_iel(LatchInterrupt::from(latch));
self
}
pub fn with_mag_int_threshold(mut self, threshold: u16) -> Self {
debug_assert!(
threshold <= 0x7FFF,
"mag interrupt threshold {threshold:#06X} exceeds 15-bit max (0x7FFF)"
);
self.mag_int_ths = threshold & 0x7FFF;
self
}
pub fn with_mag_offset(mut self, x: i16, y: i16, z: i16) -> Self {
self.mag_offset_x = x;
self.mag_offset_y = y;
self.mag_offset_z = z;
self
}
pub fn with_mag_offset_x(mut self, offset: i16) -> Self {
self.mag_offset_x = offset;
self
}
pub fn with_mag_offset_y(mut self, offset: i16) -> Self {
self.mag_offset_y = offset;
self
}
pub fn with_mag_offset_z(mut self, offset: i16) -> Self {
self.mag_offset_z = offset;
self
}
}
impl Lsm9ds0Config {
pub fn with_temperature_enabled(mut self, enabled: bool) -> Self {
self.ctrl_reg5_xm.set_temp_en(if enabled {
Enable::Enabled
} else {
Enable::Disabled
});
self
}
pub fn with_temperature_offset(mut self, offset: f32) -> Self {
self.temp_offset = offset;
self
}
}
impl Lsm9ds0Config {
pub fn with_block_data_update(mut self, enabled: bool) -> Self {
let bdu = if enabled {
BlockDataUpdate::WaitForRead
} else {
BlockDataUpdate::Continuous
};
self.ctrl_reg4_g.set_bdu(bdu);
self.ctrl_reg1_xm.set_bdu(bdu);
self
}
pub fn with_spi_mode(mut self, mode: SpiMode) -> Self {
self.ctrl_reg4_g.set_sim(mode);
self.ctrl_reg2_xm.set_sim(mode);
self
}
}
impl Lsm9ds0Config {
pub fn with_gyro_bias(mut self, x: f32, y: f32, z: f32) -> Self {
self.gyro_bias = (x, y, z);
self
}
pub fn with_accel_bias(mut self, x: f32, y: f32, z: f32) -> Self {
self.accel_bias = (x, y, z);
self
}
pub fn with_auto_calibration(mut self, orientation: crate::types::Orientation) -> Self {
self.auto_calibration = Some(orientation);
self
}
pub fn gyro_bias(&self) -> (f32, f32, f32) {
self.gyro_bias
}
pub fn accel_bias(&self) -> (f32, f32, f32) {
self.accel_bias
}
pub fn auto_calibration(&self) -> Option<crate::types::Orientation> {
self.auto_calibration
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_config() {
let config = Lsm9ds0Config::default();
assert_eq!(config.ctrl_reg1_g.pd(), PowerMode::PowerDown);
assert_eq!(config.ctrl_reg1_g.dr(), GyroDataRate::Hz95);
assert_eq!(config.ctrl_reg4_g.fs(), GyroScale::Dps245);
assert_eq!(config.ctrl_reg4_g.bdu(), BlockDataUpdate::Continuous);
assert_eq!(config.ctrl_reg1_xm.aodr(), AccelDataRate::PowerDown);
assert_eq!(config.ctrl_reg2_xm.afs(), AccelScale::G2);
assert_eq!(config.ctrl_reg6_xm.mfs(), MagScale::Gauss4);
assert_eq!(config.ctrl_reg7_xm.md(), MagMode::PowerDown);
assert_eq!(config.gyro_int_ths_x, 0);
assert_eq!(config.mag_offset_x, 0);
}
#[test]
fn test_builder_pattern() {
let config = Lsm9ds0Config::new()
.with_gyro_scale(GyroScale::Dps2000)
.with_accel_scale(AccelScale::G8)
.with_mag_scale(MagScale::Gauss12)
.with_block_data_update(true)
.with_temperature_enabled(true);
assert_eq!(config.ctrl_reg4_g.fs(), GyroScale::Dps2000);
assert_eq!(config.ctrl_reg2_xm.afs(), AccelScale::G8);
assert_eq!(config.ctrl_reg6_xm.mfs(), MagScale::Gauss12);
assert_eq!(config.ctrl_reg4_g.bdu(), BlockDataUpdate::WaitForRead);
assert_eq!(config.ctrl_reg1_xm.bdu(), BlockDataUpdate::WaitForRead);
assert_eq!(config.ctrl_reg5_xm.temp_en(), Enable::Enabled);
}
#[test]
fn test_gyro_motion_threshold() {
let config = Lsm9ds0Config::new().with_gyro_motion_threshold(1000);
assert_eq!(config.ctrl_reg3_g.i1_int1(), Enable::Enabled);
assert_eq!(config.int1_cfg_g.and_or(), InterruptCombination::Or);
assert_eq!(config.int1_cfg_g.xhie(), Enable::Enabled);
assert_eq!(config.int1_cfg_g.xlie(), Enable::Enabled);
assert_eq!(config.gyro_int_ths_x, 1000);
assert_eq!(config.gyro_int_ths_y, 1000);
assert_eq!(config.gyro_int_ths_z, 1000);
}
#[test]
fn test_per_axis_gyro_threshold() {
let config = Lsm9ds0Config::new().with_gyro_int_threshold_xyz(100, 200, 300);
assert_eq!(config.gyro_int_ths_x, 100);
assert_eq!(config.gyro_int_ths_y, 200);
assert_eq!(config.gyro_int_ths_z, 300);
}
#[test]
fn test_threshold_at_max_value() {
let config = Lsm9ds0Config::new()
.with_gyro_int_threshold(0x7FFF) .with_accel_int1_threshold(0x7F);
assert_eq!(config.gyro_int_ths_x, 0x7FFF);
assert_eq!(config.int_gen_1_ths, 0x7F);
}
#[test]
#[should_panic(expected = "exceeds 15-bit max")]
#[cfg(debug_assertions)]
fn test_gyro_threshold_overflow_panics_in_debug() {
let _ = Lsm9ds0Config::new().with_gyro_int_threshold(0xFFFF);
}
#[test]
#[should_panic(expected = "exceeds 7-bit max")]
#[cfg(debug_assertions)]
fn test_accel_threshold_overflow_panics_in_debug() {
let _ = Lsm9ds0Config::new().with_accel_int1_threshold(0xFF);
}
#[test]
fn test_sensitivity_values() {
let config = Lsm9ds0Config::new()
.with_gyro_scale(GyroScale::Dps500)
.with_accel_scale(AccelScale::G4)
.with_mag_scale(MagScale::Gauss8);
assert_eq!(config.gyro_sensitivity(), 17.5);
assert_eq!(config.accel_sensitivity(), 0.122);
assert_eq!(config.mag_sensitivity(), 0.32);
}
#[test]
fn test_click_detection() {
let config = Lsm9ds0Config::new().with_click_detection(32);
assert_eq!(config.click_cfg.xs(), Enable::Enabled);
assert_eq!(config.click_cfg.ys(), Enable::Enabled);
assert_eq!(config.click_cfg.zs(), Enable::Enabled);
assert_eq!(config.click_ths, 32);
}
#[test]
fn test_register_to_u8_conversion() {
let config = Lsm9ds0Config::new()
.with_gyro_enabled(true)
.with_gyro_data_rate(GyroDataRate::Hz380)
.with_gyro_axes(true, true, true);
let reg_value: u8 = config.ctrl_reg1_g.into();
assert_eq!(reg_value, 0x8F);
}
}