#![allow(async_fn_in_trait)]
#![no_std]
pub mod config;
pub mod errors;
pub mod interface;
pub mod registers;
pub mod types;
pub use config::*;
pub use errors::Error;
pub use interface::*;
pub use registers::{
AccelBandwidth,
AccelDataRate,
AccelHpfMode,
AccelMagRegisters,
AccelScale,
AccelSelfTest,
ActiveLevel,
ActiveLevelInverted,
BlockDataUpdate,
ClickSign,
ClickSrc,
Enable,
Endianness,
FifoMode,
FifoSrcReg,
FifoSrcRegG,
GyroBandwidth,
GyroDataRate,
GyroHpfCutoff,
GyroHpfMode,
GyroOutputSel,
GyroPowerMode,
GyroRegisters,
GyroScale,
GyroSelfTest,
Int1SrcG,
IntGenSrc,
IntSrcRegM,
InterruptCombination,
LatchInterrupt,
MagDataRate,
MagLowPower,
MagMode,
MagResolution,
MagScale,
OutputType,
PowerMode,
SpiMode,
StatusRegA,
StatusRegG,
StatusRegM,
device_constants,
};
pub use types::*;
use embedded_hal_async::delay::DelayNs;
pub struct Lsm9ds0<I>
where
I: Interface,
{
interface: I,
config: Lsm9ds0Config,
}
impl<I> Lsm9ds0<I>
where
I: Interface,
{
pub fn new(interface: I) -> Self {
Self {
interface,
config: Lsm9ds0Config::default(),
}
}
pub fn new_with_config(interface: I, config: Lsm9ds0Config) -> Self {
Self { interface, config }
}
pub async fn init<D: DelayNs>(&mut self, delay: &mut D) -> Result<(), Error<I::BusError>> {
self.verify_device_ids().await?;
self.apply_configs().await?;
if let Some(orientation) = self.config.auto_calibration {
self.calibrate_bias(delay, orientation).await?;
}
Ok(())
}
pub async fn begin<D: DelayNs>(&mut self, delay: &mut D) -> Result<(), Error<I::BusError>> {
self.init(delay).await
}
pub async fn software_reset<D: DelayNs>(
&mut self,
delay: &mut D,
) -> Result<(), Error<I::BusError>> {
self.config.ctrl_reg5_g.set_boot(Enable::Enabled);
self.interface
.write_byte_gyro(
GyroRegisters::CTRL_REG5_G.addr(),
self.config.ctrl_reg5_g.into(),
)
.await?;
self.config.ctrl_reg0_xm.set_boot(Enable::Enabled);
self.interface
.write_byte_xm(
AccelMagRegisters::CTRL_REG0_XM.addr(),
self.config.ctrl_reg0_xm.into(),
)
.await?;
delay.delay_ms(50).await;
self.config = Lsm9ds0Config::default();
self.verify_device_ids().await?;
Ok(())
}
pub async fn set_config(&mut self, config: Lsm9ds0Config) -> Result<(), Error<I::BusError>> {
self.config = config;
self.apply_configs().await
}
pub fn config(&self) -> &Lsm9ds0Config {
&self.config
}
async fn verify_device_ids(&mut self) -> Result<(), Error<I::BusError>> {
let mut id = [0u8];
self.interface
.read_gyro(GyroRegisters::WHO_AM_I_G.addr(), &mut id)
.await?;
if id[0] != registers::device_constants::gyro::DEVICE_ID {
return Err(Error::InvalidGyroId {
expected: registers::device_constants::gyro::DEVICE_ID,
actual: id[0],
});
}
self.interface
.read_xm(AccelMagRegisters::WHO_AM_I_XM.addr(), &mut id)
.await?;
if id[0] != registers::device_constants::xm::DEVICE_ID {
return Err(Error::InvalidXmId {
expected: registers::device_constants::xm::DEVICE_ID,
actual: id[0],
});
}
Ok(())
}
async fn apply_configs(&mut self) -> Result<(), Error<I::BusError>> {
self.interface
.write_gyro(
GyroRegisters::CTRL_REG1_G.addr(),
&[
self.config.ctrl_reg1_g.into(),
self.config.ctrl_reg2_g.into(),
self.config.ctrl_reg3_g.into(),
self.config.ctrl_reg4_g.into(),
self.config.ctrl_reg5_g.into(),
],
)
.await?;
self.interface
.write_byte_gyro(
GyroRegisters::FIFO_CTRL_REG_G.addr(),
self.config.fifo_ctrl_reg_g.into(),
)
.await?;
self.interface
.write_byte_gyro(
GyroRegisters::INT1_CFG_G.addr(),
self.config.int1_cfg_g.into(),
)
.await?;
self.interface
.write_gyro(
GyroRegisters::INT1_THS_XH_G.addr(),
&[
((self.config.gyro_int_ths_x >> 8) & 0x7F) as u8,
(self.config.gyro_int_ths_x & 0xFF) as u8,
((self.config.gyro_int_ths_y >> 8) & 0x7F) as u8,
(self.config.gyro_int_ths_y & 0xFF) as u8,
((self.config.gyro_int_ths_z >> 8) & 0x7F) as u8,
(self.config.gyro_int_ths_z & 0xFF) as u8,
self.config.int1_duration_g.into(),
],
)
.await?;
self.interface
.write_byte_xm(
AccelMagRegisters::INT_CTRL_REG_M.addr(),
self.config.int_ctrl_reg_m.into(),
)
.await?;
self.interface
.write_xm(
AccelMagRegisters::INT_THS_L_M.addr(),
&[
(self.config.mag_int_ths & 0xFF) as u8,
((self.config.mag_int_ths >> 8) & 0x7F) as u8,
],
)
.await?;
self.interface
.write_xm(
AccelMagRegisters::OFFSET_X_L_M.addr(),
&[
(self.config.mag_offset_x & 0xFF) as u8,
((self.config.mag_offset_x >> 8) & 0xFF) as u8,
(self.config.mag_offset_y & 0xFF) as u8,
((self.config.mag_offset_y >> 8) & 0xFF) as u8,
(self.config.mag_offset_z & 0xFF) as u8,
((self.config.mag_offset_z >> 8) & 0xFF) as u8,
],
)
.await?;
self.interface
.write_xm(
AccelMagRegisters::CTRL_REG0_XM.addr(),
&[
self.config.ctrl_reg0_xm.into(),
self.config.ctrl_reg1_xm.into(),
self.config.ctrl_reg2_xm.into(),
self.config.ctrl_reg3_xm.into(),
self.config.ctrl_reg4_xm.into(),
self.config.ctrl_reg5_xm.into(),
self.config.ctrl_reg6_xm.into(),
self.config.ctrl_reg7_xm.into(),
],
)
.await?;
self.interface
.write_byte_xm(
AccelMagRegisters::FIFO_CTRL_REG.addr(),
self.config.fifo_ctrl_reg.into(),
)
.await?;
self.interface
.write_byte_xm(
AccelMagRegisters::INT_GEN_1_REG.addr(),
self.config.int_gen_1_reg.into(),
)
.await?;
self.interface
.write_xm(
AccelMagRegisters::INT_GEN_1_THS.addr(),
&[
self.config.int_gen_1_ths & 0x7F,
self.config.int_gen_1_duration & 0x7F,
],
)
.await?;
self.interface
.write_byte_xm(
AccelMagRegisters::INT_GEN_2_REG.addr(),
self.config.int_gen_2_reg.into(),
)
.await?;
self.interface
.write_xm(
AccelMagRegisters::INT_GEN_2_THS.addr(),
&[
self.config.int_gen_2_ths & 0x7F,
self.config.int_gen_2_duration & 0x7F,
],
)
.await?;
self.interface
.write_byte_xm(
AccelMagRegisters::CLICK_CFG.addr(),
self.config.click_cfg.into(),
)
.await?;
self.interface
.write_xm(
AccelMagRegisters::CLICK_THS.addr(),
&[
self.config.click_ths & 0x7F,
self.config.time_limit_ms,
self.config.time_latency_ms,
self.config.time_window_ms,
],
)
.await?;
self.interface
.write_xm(
AccelMagRegisters::ACT_THS.addr(),
&[self.config.act_ths & 0x7F, self.config.act_dur],
)
.await?;
Ok(())
}
pub async fn read_gyro_fifo_level(&mut self) -> Result<u8, Error<I::BusError>> {
let status = self.read_gyro_fifo_status().await?;
Ok(status.fss())
}
pub async fn read_gyro_fifo(&mut self) -> Result<(i16, i16, i16), Error<I::BusError>> {
self.read_gyro_raw().await
}
pub async fn read_gyro_raw(&mut self) -> Result<(i16, i16, i16), Error<I::BusError>> {
let mut bytes = [0u8; 6];
self.interface
.read_gyro(GyroRegisters::OUT_X_L_G.addr(), &mut bytes)
.await?;
let x = i16::from_le_bytes([bytes[0], bytes[1]]);
let y = i16::from_le_bytes([bytes[2], bytes[3]]);
let z = i16::from_le_bytes([bytes[4], bytes[5]]);
Ok((x, y, z))
}
pub async fn read_gyro(
&mut self,
) -> Result<
(
types::DegreesPerSecond,
types::DegreesPerSecond,
types::DegreesPerSecond,
),
Error<I::BusError>,
> {
let (x, y, z) = self.read_gyro_raw().await?;
let sensitivity = self.config.gyro_sensitivity() / 1000.0;
let (bias_x, bias_y, bias_z) = self.config.gyro_bias;
Ok((
types::DegreesPerSecond::new(x as f32 * sensitivity - bias_x),
types::DegreesPerSecond::new(y as f32 * sensitivity - bias_y),
types::DegreesPerSecond::new(z as f32 * sensitivity - bias_z),
))
}
pub async fn read_accel_fifo_level(&mut self) -> Result<u8, Error<I::BusError>> {
let status = self.read_accel_fifo_status().await?;
Ok(status.fss())
}
pub async fn read_accel_fifo(&mut self) -> Result<(i16, i16, i16), Error<I::BusError>> {
self.read_accel_raw().await
}
pub async fn read_accel_raw(&mut self) -> Result<(i16, i16, i16), Error<I::BusError>> {
let mut bytes = [0u8; 6];
self.interface
.read_xm(AccelMagRegisters::OUT_X_L_A.addr(), &mut bytes)
.await?;
let x = i16::from_le_bytes([bytes[0], bytes[1]]);
let y = i16::from_le_bytes([bytes[2], bytes[3]]);
let z = i16::from_le_bytes([bytes[4], bytes[5]]);
Ok((x, y, z))
}
pub async fn read_accel(
&mut self,
) -> Result<(types::GForce, types::GForce, types::GForce), Error<I::BusError>> {
let (x, y, z) = self.read_accel_raw().await?;
let sensitivity = self.config.accel_sensitivity() / 1000.0;
let (bias_x, bias_y, bias_z) = self.config.accel_bias;
Ok((
types::GForce::new(x as f32 * sensitivity - bias_x),
types::GForce::new(y as f32 * sensitivity - bias_y),
types::GForce::new(z as f32 * sensitivity - bias_z),
))
}
pub async fn read_mag_raw(&mut self) -> Result<(i16, i16, i16), Error<I::BusError>> {
let mut bytes = [0u8; 6];
self.interface
.read_xm(AccelMagRegisters::OUT_X_L_M.addr(), &mut bytes)
.await?;
let x = i16::from_le_bytes([bytes[0], bytes[1]]);
let y = i16::from_le_bytes([bytes[2], bytes[3]]);
let z = i16::from_le_bytes([bytes[4], bytes[5]]);
Ok((x, y, z))
}
pub async fn read_mag(
&mut self,
) -> Result<(types::Gauss, types::Gauss, types::Gauss), Error<I::BusError>> {
let (x, y, z) = self.read_mag_raw().await?;
let sensitivity = self.config.mag_sensitivity() / 1000.0;
Ok((
types::Gauss::new(x as f32 * sensitivity),
types::Gauss::new(y as f32 * sensitivity),
types::Gauss::new(z as f32 * sensitivity),
))
}
pub async fn read_temp(&mut self) -> Result<types::Celsius, Error<I::BusError>> {
let mut bytes = [0u8; 2];
self.interface
.read_xm(AccelMagRegisters::OUT_TEMP_L_XM.addr(), &mut bytes)
.await?;
let raw = i16::from_le_bytes([bytes[0], bytes[1]]);
let result: i16 = (raw << 4) >> 4;
Ok(types::Celsius::new(
(result as f32) / TEMP_SCALE + self.config.temp_offset,
))
}
pub async fn set_gyro_scale(&mut self, scale: GyroScale) -> Result<(), Error<I::BusError>> {
self.config.ctrl_reg4_g.set_fs(scale);
self.interface
.write_byte_gyro(
GyroRegisters::CTRL_REG4_G.addr(),
self.config.ctrl_reg4_g.into(),
)
.await?;
Ok(())
}
pub async fn set_gyro_data_rate(
&mut self,
rate: GyroDataRate,
) -> Result<(), Error<I::BusError>> {
self.config.ctrl_reg1_g.set_dr(rate);
self.interface
.write_byte_gyro(
GyroRegisters::CTRL_REG1_G.addr(),
self.config.ctrl_reg1_g.into(),
)
.await?;
Ok(())
}
pub async fn set_gyro_enabled(&mut self, enabled: bool) -> Result<(), Error<I::BusError>> {
self.config.ctrl_reg1_g.set_pd(if enabled {
PowerMode::Normal
} else {
PowerMode::PowerDown
});
self.interface
.write_byte_gyro(
GyroRegisters::CTRL_REG1_G.addr(),
self.config.ctrl_reg1_g.into(),
)
.await?;
Ok(())
}
pub async fn set_gyro_power_mode(
&mut self,
mode: GyroPowerMode,
) -> Result<(), Error<I::BusError>> {
match mode {
GyroPowerMode::PowerDown => {
self.config.ctrl_reg1_g.set_pd(PowerMode::PowerDown);
}
GyroPowerMode::Sleep => {
self.config.ctrl_reg1_g.set_pd(PowerMode::Normal);
self.config.ctrl_reg1_g.set_xen(Enable::Disabled);
self.config.ctrl_reg1_g.set_yen(Enable::Disabled);
self.config.ctrl_reg1_g.set_zen(Enable::Disabled);
}
GyroPowerMode::Normal => {
self.config.ctrl_reg1_g.set_pd(PowerMode::Normal);
let (x, y, z) = self.config.gyro_axes_enabled;
self.config.ctrl_reg1_g.set_xen(Enable::from(x));
self.config.ctrl_reg1_g.set_yen(Enable::from(y));
self.config.ctrl_reg1_g.set_zen(Enable::from(z));
}
}
self.interface
.write_byte_gyro(
GyroRegisters::CTRL_REG1_G.addr(),
self.config.ctrl_reg1_g.into(),
)
.await?;
Ok(())
}
pub async fn set_gyro_axes(
&mut self,
x: bool,
y: bool,
z: bool,
) -> Result<(), Error<I::BusError>> {
self.config.ctrl_reg1_g.set_xen(Enable::from(x));
self.config.ctrl_reg1_g.set_yen(Enable::from(y));
self.config.ctrl_reg1_g.set_zen(Enable::from(z));
self.interface
.write_byte_gyro(
GyroRegisters::CTRL_REG1_G.addr(),
self.config.ctrl_reg1_g.into(),
)
.await?;
Ok(())
}
pub async fn set_gyro_bandwidth(
&mut self,
bw: GyroBandwidth,
) -> Result<(), Error<I::BusError>> {
self.config.ctrl_reg1_g.set_bw(bw);
self.interface
.write_byte_gyro(
GyroRegisters::CTRL_REG1_G.addr(),
self.config.ctrl_reg1_g.into(),
)
.await?;
Ok(())
}
pub async fn set_gyro_reference(&mut self, value: u8) -> Result<(), Error<I::BusError>> {
self.interface
.write_byte_gyro(GyroRegisters::REFERENCE_G.addr(), value)
.await?;
Ok(())
}
pub async fn set_accel_reference(
&mut self,
x: u8,
y: u8,
z: u8,
) -> Result<(), Error<I::BusError>> {
self.interface
.write_xm(AccelMagRegisters::REFERENCE_X.addr(), &[x, y, z])
.await?;
Ok(())
}
pub async fn set_accel_scale(&mut self, scale: AccelScale) -> Result<(), Error<I::BusError>> {
self.config.ctrl_reg2_xm.set_afs(scale);
self.interface
.write_byte_xm(
AccelMagRegisters::CTRL_REG2_XM.addr(),
self.config.ctrl_reg2_xm.into(),
)
.await?;
Ok(())
}
pub async fn set_accel_data_rate(
&mut self,
rate: AccelDataRate,
) -> Result<(), Error<I::BusError>> {
self.config.ctrl_reg1_xm.set_aodr(rate);
self.interface
.write_byte_xm(
AccelMagRegisters::CTRL_REG1_XM.addr(),
self.config.ctrl_reg1_xm.into(),
)
.await?;
Ok(())
}
pub async fn set_accel_axes(
&mut self,
x: bool,
y: bool,
z: bool,
) -> Result<(), Error<I::BusError>> {
self.config.ctrl_reg1_xm.set_axen(Enable::from(x));
self.config.ctrl_reg1_xm.set_ayen(Enable::from(y));
self.config.ctrl_reg1_xm.set_azen(Enable::from(z));
self.interface
.write_byte_xm(
AccelMagRegisters::CTRL_REG1_XM.addr(),
self.config.ctrl_reg1_xm.into(),
)
.await?;
Ok(())
}
pub async fn set_accel_bandwidth(
&mut self,
bw: AccelBandwidth,
) -> Result<(), Error<I::BusError>> {
self.config.ctrl_reg2_xm.set_abw(bw);
self.interface
.write_byte_xm(
AccelMagRegisters::CTRL_REG2_XM.addr(),
self.config.ctrl_reg2_xm.into(),
)
.await?;
Ok(())
}
pub async fn set_mag_scale(&mut self, scale: MagScale) -> Result<(), Error<I::BusError>> {
self.config.ctrl_reg6_xm.set_mfs(scale);
self.interface
.write_byte_xm(
AccelMagRegisters::CTRL_REG6_XM.addr(),
self.config.ctrl_reg6_xm.into(),
)
.await?;
Ok(())
}
pub async fn set_mag_data_rate(&mut self, rate: MagDataRate) -> Result<(), Error<I::BusError>> {
self.config.ctrl_reg5_xm.set_m_odr(rate);
self.interface
.write_byte_xm(
AccelMagRegisters::CTRL_REG5_XM.addr(),
self.config.ctrl_reg5_xm.into(),
)
.await?;
Ok(())
}
pub async fn set_mag_mode(&mut self, mode: MagMode) -> Result<(), Error<I::BusError>> {
self.config.ctrl_reg7_xm.set_md(mode);
self.interface
.write_byte_xm(
AccelMagRegisters::CTRL_REG7_XM.addr(),
self.config.ctrl_reg7_xm.into(),
)
.await?;
Ok(())
}
pub async fn set_mag_resolution(
&mut self,
res: MagResolution,
) -> Result<(), Error<I::BusError>> {
self.config.ctrl_reg5_xm.set_m_res(res);
self.interface
.write_byte_xm(
AccelMagRegisters::CTRL_REG5_XM.addr(),
self.config.ctrl_reg5_xm.into(),
)
.await?;
Ok(())
}
pub async fn set_temperature_enabled(
&mut self,
enabled: bool,
) -> Result<(), Error<I::BusError>> {
self.config.ctrl_reg5_xm.set_temp_en(if enabled {
Enable::Enabled
} else {
Enable::Disabled
});
self.interface
.write_byte_xm(
AccelMagRegisters::CTRL_REG5_XM.addr(),
self.config.ctrl_reg5_xm.into(),
)
.await?;
Ok(())
}
pub async fn set_block_data_update(&mut self, enabled: bool) -> Result<(), Error<I::BusError>> {
let bdu = if enabled {
BlockDataUpdate::WaitForRead
} else {
BlockDataUpdate::Continuous
};
self.config.ctrl_reg4_g.set_bdu(bdu);
self.config.ctrl_reg1_xm.set_bdu(bdu);
self.interface
.write_byte_gyro(
GyroRegisters::CTRL_REG4_G.addr(),
self.config.ctrl_reg4_g.into(),
)
.await?;
self.interface
.write_byte_xm(
AccelMagRegisters::CTRL_REG1_XM.addr(),
self.config.ctrl_reg1_xm.into(),
)
.await?;
Ok(())
}
pub fn get_gyro_hpf_cutoff_hz(&self) -> f32 {
self.config.gyro_hpf_cutoff_hz()
}
pub fn get_gyro_lpf_cutoff_hz(&self) -> f32 {
self.config.gyro_lpf_cutoff_hz()
}
pub async fn read_gyro_fifo_status(&mut self) -> Result<FifoSrcRegG, Error<I::BusError>> {
let mut data = [0u8];
self.interface
.read_gyro(GyroRegisters::FIFO_SRC_REG_G.addr(), &mut data)
.await?;
Ok(FifoSrcRegG::from(data[0]))
}
pub async fn read_accel_fifo_status(&mut self) -> Result<FifoSrcReg, Error<I::BusError>> {
let mut data = [0u8];
self.interface
.read_xm(AccelMagRegisters::FIFO_SRC_REG.addr(), &mut data)
.await?;
Ok(FifoSrcReg::from(data[0]))
}
pub async fn read_gyro_status(&mut self) -> Result<StatusRegG, Error<I::BusError>> {
let mut data = [0u8];
self.interface
.read_gyro(GyroRegisters::STATUS_REG_G.addr(), &mut data)
.await?;
Ok(StatusRegG::from(data[0]))
}
pub async fn read_accel_status(&mut self) -> Result<StatusRegA, Error<I::BusError>> {
let mut data = [0u8];
self.interface
.read_xm(AccelMagRegisters::STATUS_REG_A.addr(), &mut data)
.await?;
Ok(StatusRegA::from(data[0]))
}
pub async fn read_mag_status(&mut self) -> Result<StatusRegM, Error<I::BusError>> {
let mut data = [0u8];
self.interface
.read_xm(AccelMagRegisters::STATUS_REG_M.addr(), &mut data)
.await?;
Ok(StatusRegM::from(data[0]))
}
pub async fn gyro_data_ready(&mut self) -> Result<bool, Error<I::BusError>> {
let status = self.read_gyro_status().await?;
Ok(status.zyxda())
}
pub async fn accel_data_ready(&mut self) -> Result<bool, Error<I::BusError>> {
let status = self.read_accel_status().await?;
Ok(status.zyxada())
}
pub async fn mag_data_ready(&mut self) -> Result<bool, Error<I::BusError>> {
let status = self.read_mag_status().await?;
Ok(status.zyxmda())
}
pub async fn gyro_data_overrun(&mut self) -> Result<bool, Error<I::BusError>> {
let status = self.read_gyro_status().await?;
Ok(status.zyxor())
}
pub async fn accel_data_overrun(&mut self) -> Result<bool, Error<I::BusError>> {
let status = self.read_accel_status().await?;
Ok(status.zyxaor())
}
pub async fn mag_data_overrun(&mut self) -> Result<bool, Error<I::BusError>> {
let status = self.read_mag_status().await?;
Ok(status.zyxmor())
}
pub async fn read_gyro_interrupt_source(&mut self) -> Result<Int1SrcG, Error<I::BusError>> {
let mut data = [0u8];
self.interface
.read_gyro(GyroRegisters::INT1_SRC_G.addr(), &mut data)
.await?;
Ok(Int1SrcG::from(data[0]))
}
pub async fn read_accel_int1_source(&mut self) -> Result<IntGenSrc, Error<I::BusError>> {
let mut data = [0u8];
self.interface
.read_xm(AccelMagRegisters::INT_GEN_1_SRC.addr(), &mut data)
.await?;
Ok(IntGenSrc::from(data[0]))
}
pub async fn read_accel_int2_source(&mut self) -> Result<IntGenSrc, Error<I::BusError>> {
let mut data = [0u8];
self.interface
.read_xm(AccelMagRegisters::INT_GEN_2_SRC.addr(), &mut data)
.await?;
Ok(IntGenSrc::from(data[0]))
}
pub async fn read_mag_interrupt_source(&mut self) -> Result<IntSrcRegM, Error<I::BusError>> {
let mut data = [0u8];
self.interface
.read_xm(AccelMagRegisters::INT_SRC_REG_M.addr(), &mut data)
.await?;
Ok(IntSrcRegM::from(data[0]))
}
pub async fn read_click_source(&mut self) -> Result<ClickSrc, Error<I::BusError>> {
let mut data = [0u8];
self.interface
.read_xm(AccelMagRegisters::CLICK_SRC.addr(), &mut data)
.await?;
Ok(ClickSrc::from(data[0]))
}
pub async fn calibrate_bias<D: DelayNs>(
&mut self,
delay: &mut D,
orientation: types::Orientation,
) -> Result<(), Error<I::BusError>> {
let saved_gyro_fifo_en = self.config.ctrl_reg5_g.fifo_en();
let saved_gyro_fifo_mode = self.config.fifo_ctrl_reg_g.fm();
let saved_gyro_fifo_wtm = self.config.fifo_ctrl_reg_g.wtm();
let saved_accel_fifo_en = self.config.ctrl_reg0_xm.fifo_en();
let saved_accel_fifo_mode = self.config.fifo_ctrl_reg.fm();
let saved_accel_fifo_wtm = self.config.fifo_ctrl_reg.fth();
self.config.ctrl_reg5_g.set_fifo_en(Enable::Enabled);
self.config.fifo_ctrl_reg_g.set_wtm(0x1F);
self.config.ctrl_reg0_xm.set_fifo_en(Enable::Enabled);
self.config.fifo_ctrl_reg.set_fth(0x1F);
self.interface
.write_byte_gyro(
GyroRegisters::CTRL_REG5_G.addr(),
self.config.ctrl_reg5_g.into(),
)
.await?;
self.config.fifo_ctrl_reg_g.set_fm(FifoMode::Bypass);
self.interface
.write_byte_gyro(
GyroRegisters::FIFO_CTRL_REG_G.addr(),
self.config.fifo_ctrl_reg_g.into(),
)
.await?;
self.config.fifo_ctrl_reg_g.set_fm(FifoMode::Stream);
self.interface
.write_byte_gyro(
GyroRegisters::FIFO_CTRL_REG_G.addr(),
self.config.fifo_ctrl_reg_g.into(),
)
.await?;
self.interface
.write_byte_xm(
AccelMagRegisters::CTRL_REG0_XM.addr(),
self.config.ctrl_reg0_xm.into(),
)
.await?;
self.config.fifo_ctrl_reg.set_fm(FifoMode::Bypass);
self.interface
.write_byte_xm(
AccelMagRegisters::FIFO_CTRL_REG.addr(),
self.config.fifo_ctrl_reg.into(),
)
.await?;
self.config.fifo_ctrl_reg.set_fm(FifoMode::Stream);
self.interface
.write_byte_xm(
AccelMagRegisters::FIFO_CTRL_REG.addr(),
self.config.fifo_ctrl_reg.into(),
)
.await?;
const NUM_FIFO_SAMPLES: f32 = 32.0;
const MARGIN: f32 = 1.1;
const MIN_DELAY_MS: u32 = 10;
let gyro_hz = self.config.ctrl_reg1_g.dr().hz();
let accel_hz = self.config.ctrl_reg1_xm.aodr().hz();
let gyro_fill_ms = NUM_FIFO_SAMPLES * 1000.0 / gyro_hz;
let accel_fill_ms = if accel_hz > 0.0 {
NUM_FIFO_SAMPLES * 1000.0 / accel_hz
} else {
0.0
};
let fill_ms = gyro_fill_ms.max(accel_fill_ms);
let delay_ms = ((fill_ms * MARGIN) as u32).max(MIN_DELAY_MS);
delay.delay_ms(delay_ms).await;
let gyro_samples = self.read_gyro_fifo_level().await?;
let accel_samples = self.read_accel_fifo_level().await?;
let mut gyro_sum: (i32, i32, i32) = (0, 0, 0);
for _ in 0..gyro_samples {
let (x, y, z) = self.read_gyro_fifo().await?;
gyro_sum.0 += x as i32;
gyro_sum.1 += y as i32;
gyro_sum.2 += z as i32;
}
let mut accel_sum: (i32, i32, i32) = (0, 0, 0);
for _ in 0..accel_samples {
let (x, y, z) = self.read_accel_fifo().await?;
accel_sum.0 += x as i32;
accel_sum.1 += y as i32;
accel_sum.2 += z as i32;
}
let gyro_sensitivity = self.config.gyro_sensitivity() / 1000.0;
let gyro_bias = if gyro_samples > 0 {
let n = gyro_samples as f32;
(
(gyro_sum.0 as f32 / n) * gyro_sensitivity,
(gyro_sum.1 as f32 / n) * gyro_sensitivity,
(gyro_sum.2 as f32 / n) * gyro_sensitivity,
)
} else {
(0.0, 0.0, 0.0)
};
let accel_sensitivity = self.config.accel_sensitivity() / 1000.0;
let (grav_x, grav_y, grav_z) = orientation.gravity_vector();
let accel_bias = if accel_samples > 0 {
let n = accel_samples as f32;
(
(accel_sum.0 as f32 / n) * accel_sensitivity - grav_x,
(accel_sum.1 as f32 / n) * accel_sensitivity - grav_y,
(accel_sum.2 as f32 / n) * accel_sensitivity - grav_z,
)
} else {
(0.0, 0.0, 0.0)
};
self.config.gyro_bias = gyro_bias;
self.config.accel_bias = accel_bias;
self.config.ctrl_reg5_g.set_fifo_en(saved_gyro_fifo_en);
self.config.fifo_ctrl_reg_g.set_fm(saved_gyro_fifo_mode);
self.config.fifo_ctrl_reg_g.set_wtm(saved_gyro_fifo_wtm);
self.config.ctrl_reg0_xm.set_fifo_en(saved_accel_fifo_en);
self.config.fifo_ctrl_reg.set_fm(saved_accel_fifo_mode);
self.config.fifo_ctrl_reg.set_fth(saved_accel_fifo_wtm);
self.interface
.write_byte_gyro(
GyroRegisters::CTRL_REG5_G.addr(),
self.config.ctrl_reg5_g.into(),
)
.await?;
self.interface
.write_byte_gyro(
GyroRegisters::FIFO_CTRL_REG_G.addr(),
self.config.fifo_ctrl_reg_g.into(),
)
.await?;
self.interface
.write_byte_xm(
AccelMagRegisters::CTRL_REG0_XM.addr(),
self.config.ctrl_reg0_xm.into(),
)
.await?;
self.interface
.write_byte_xm(
AccelMagRegisters::FIFO_CTRL_REG.addr(),
self.config.fifo_ctrl_reg.into(),
)
.await?;
Ok(())
}
pub fn gyro_bias(&self) -> (f32, f32, f32) {
self.config.gyro_bias
}
pub fn accel_bias(&self) -> (f32, f32, f32) {
self.config.accel_bias
}
pub fn set_gyro_bias(&mut self, x: f32, y: f32, z: f32) {
self.config.gyro_bias = (x, y, z);
}
pub fn set_accel_bias(&mut self, x: f32, y: f32, z: f32) {
self.config.accel_bias = (x, y, z);
}
pub fn release(self) -> I {
self.interface
}
}
#[cfg(test)]
mod tests {
extern crate std;
use super::*;
use embedded_hal_mock::eh1::i2c::{Mock as I2cMock, Transaction as I2cTransaction};
use std::vec;
const GYRO_ADDR: u8 = registers::device_constants::gyro::I2C_ADDR_0;
const XM_ADDR: u8 = registers::device_constants::xm::I2C_ADDR_0;
#[tokio::test]
async fn test_read_gyro_raw() {
const AUTO_INCREMENT: u8 = 0x80;
let expectations = [I2cTransaction::write_read(
GYRO_ADDR,
vec![GyroRegisters::OUT_X_L_G.addr() | AUTO_INCREMENT],
vec![0x02, 0x01, 0x04, 0x03, 0x06, 0x05],
)];
let i2c = I2cMock::new(&expectations);
let interface = I2cInterface::init(i2c);
let mut driver = Lsm9ds0::new(interface);
let (x, y, z) = driver.read_gyro_raw().await.unwrap();
assert_eq!(x, 0x0102); assert_eq!(y, 0x0304); assert_eq!(z, 0x0506);
driver.release().release().done();
}
#[tokio::test]
async fn test_read_gyro_raw_negative_values() {
const AUTO_INCREMENT: u8 = 0x80;
let expectations = [I2cTransaction::write_read(
GYRO_ADDR,
vec![GyroRegisters::OUT_X_L_G.addr() | AUTO_INCREMENT],
vec![0xFF, 0xFF, 0x00, 0xFF, 0x00, 0x80],
)];
let i2c = I2cMock::new(&expectations);
let interface = I2cInterface::init(i2c);
let mut driver = Lsm9ds0::new(interface);
let (x, y, z) = driver.read_gyro_raw().await.unwrap();
assert_eq!(x, -1i16);
assert_eq!(y, -256i16);
assert_eq!(z, -32768i16);
driver.release().release().done();
}
#[tokio::test]
async fn test_read_accel_raw() {
const AUTO_INCREMENT: u8 = 0x80;
let expectations = [I2cTransaction::write_read(
XM_ADDR,
vec![AccelMagRegisters::OUT_X_L_A.addr() | AUTO_INCREMENT],
vec![0x64, 0x00, 0xC8, 0x00, 0x2C, 0x01],
)];
let i2c = I2cMock::new(&expectations);
let interface = I2cInterface::init(i2c);
let mut driver = Lsm9ds0::new(interface);
let (x, y, z) = driver.read_accel_raw().await.unwrap();
assert_eq!(x, 100);
assert_eq!(y, 200);
assert_eq!(z, 300);
driver.release().release().done();
}
#[tokio::test]
async fn test_read_accel_raw_negative_values() {
const AUTO_INCREMENT: u8 = 0x80;
let expectations = [I2cTransaction::write_read(
XM_ADDR,
vec![AccelMagRegisters::OUT_X_L_A.addr() | AUTO_INCREMENT],
vec![0x9C, 0xFF, 0x38, 0xFF, 0xD4, 0xFE],
)];
let i2c = I2cMock::new(&expectations);
let interface = I2cInterface::init(i2c);
let mut driver = Lsm9ds0::new(interface);
let (x, y, z) = driver.read_accel_raw().await.unwrap();
assert_eq!(x, -100i16);
assert_eq!(y, -200i16);
assert_eq!(z, -300i16);
driver.release().release().done();
}
#[tokio::test]
async fn test_read_mag_raw() {
const AUTO_INCREMENT: u8 = 0x80;
let expectations = [I2cTransaction::write_read(
XM_ADDR,
vec![AccelMagRegisters::OUT_X_L_M.addr() | AUTO_INCREMENT],
vec![0xE8, 0x03, 0xD0, 0x07, 0xB8, 0x0B],
)];
let i2c = I2cMock::new(&expectations);
let interface = I2cInterface::init(i2c);
let mut driver = Lsm9ds0::new(interface);
let (x, y, z) = driver.read_mag_raw().await.unwrap();
assert_eq!(x, 1000);
assert_eq!(y, 2000);
assert_eq!(z, 3000);
driver.release().release().done();
}
#[tokio::test]
async fn test_read_mag_raw_negative_values() {
const AUTO_INCREMENT: u8 = 0x80;
let expectations = [I2cTransaction::write_read(
XM_ADDR,
vec![AccelMagRegisters::OUT_X_L_M.addr() | AUTO_INCREMENT],
vec![0x18, 0xFC, 0x30, 0xF8, 0x48, 0xF4],
)];
let i2c = I2cMock::new(&expectations);
let interface = I2cInterface::init(i2c);
let mut driver = Lsm9ds0::new(interface);
let (x, y, z) = driver.read_mag_raw().await.unwrap();
assert_eq!(x, -1000i16);
assert_eq!(y, -2000i16);
assert_eq!(z, -3000i16);
driver.release().release().done();
}
#[test]
fn assert_send() {
fn is_send<T: Send>() {}
is_send::<Lsm9ds0<I2cInterface<I2cMock>>>();
}
fn default_init_expectations() -> std::vec::Vec<I2cTransaction> {
let config = Lsm9ds0Config::default();
vec![
I2cTransaction::write_read(
GYRO_ADDR,
vec![GyroRegisters::WHO_AM_I_G.addr()],
vec![registers::device_constants::gyro::DEVICE_ID],
),
I2cTransaction::write_read(
XM_ADDR,
vec![AccelMagRegisters::WHO_AM_I_XM.addr()],
vec![registers::device_constants::xm::DEVICE_ID],
),
I2cTransaction::write(
GYRO_ADDR,
vec![
GyroRegisters::CTRL_REG1_G.addr() | 0x80,
config.ctrl_reg1_g.into(),
config.ctrl_reg2_g.into(),
config.ctrl_reg3_g.into(),
config.ctrl_reg4_g.into(),
config.ctrl_reg5_g.into(),
],
),
I2cTransaction::write(
GYRO_ADDR,
vec![
GyroRegisters::FIFO_CTRL_REG_G.addr(),
config.fifo_ctrl_reg_g.into(),
],
),
I2cTransaction::write(
GYRO_ADDR,
vec![GyroRegisters::INT1_CFG_G.addr(), config.int1_cfg_g.into()],
),
I2cTransaction::write(
GYRO_ADDR,
vec![
GyroRegisters::INT1_THS_XH_G.addr() | 0x80,
0x00,
0x00, 0x00,
0x00, 0x00,
0x00, config.int1_duration_g.into(),
],
),
I2cTransaction::write(
XM_ADDR,
vec![
AccelMagRegisters::INT_CTRL_REG_M.addr(),
config.int_ctrl_reg_m.into(),
],
),
I2cTransaction::write(
XM_ADDR,
vec![AccelMagRegisters::INT_THS_L_M.addr() | 0x80, 0x00, 0x00],
),
I2cTransaction::write(
XM_ADDR,
vec![
AccelMagRegisters::OFFSET_X_L_M.addr() | 0x80,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
],
),
I2cTransaction::write(
XM_ADDR,
vec![
AccelMagRegisters::CTRL_REG0_XM.addr() | 0x80,
config.ctrl_reg0_xm.into(),
config.ctrl_reg1_xm.into(),
config.ctrl_reg2_xm.into(),
config.ctrl_reg3_xm.into(),
config.ctrl_reg4_xm.into(),
config.ctrl_reg5_xm.into(),
config.ctrl_reg6_xm.into(),
config.ctrl_reg7_xm.into(),
],
),
I2cTransaction::write(
XM_ADDR,
vec![
AccelMagRegisters::FIFO_CTRL_REG.addr(),
config.fifo_ctrl_reg.into(),
],
),
I2cTransaction::write(
XM_ADDR,
vec![
AccelMagRegisters::INT_GEN_1_REG.addr(),
config.int_gen_1_reg.into(),
],
),
I2cTransaction::write(
XM_ADDR,
vec![AccelMagRegisters::INT_GEN_1_THS.addr() | 0x80, 0x00, 0x00],
),
I2cTransaction::write(
XM_ADDR,
vec![
AccelMagRegisters::INT_GEN_2_REG.addr(),
config.int_gen_2_reg.into(),
],
),
I2cTransaction::write(
XM_ADDR,
vec![AccelMagRegisters::INT_GEN_2_THS.addr() | 0x80, 0x00, 0x00],
),
I2cTransaction::write(
XM_ADDR,
vec![AccelMagRegisters::CLICK_CFG.addr(), config.click_cfg.into()],
),
I2cTransaction::write(
XM_ADDR,
vec![
AccelMagRegisters::CLICK_THS.addr() | 0x80,
0x00,
0x00,
0x00,
0x00,
],
),
I2cTransaction::write(
XM_ADDR,
vec![AccelMagRegisters::ACT_THS.addr() | 0x80, 0x00, 0x00],
),
]
}
struct MockDelay;
impl embedded_hal_async::delay::DelayNs for MockDelay {
async fn delay_ns(&mut self, _ns: u32) {}
}
#[tokio::test]
async fn test_init_sequence() {
let expectations = default_init_expectations();
let i2c = I2cMock::new(&expectations);
let interface = I2cInterface::init(i2c);
let mut driver = Lsm9ds0::new(interface);
driver.init(&mut MockDelay).await.unwrap();
driver.release().release().done();
}
#[tokio::test]
async fn test_set_gyro_scale_writes_register() {
let mut expectations = default_init_expectations();
let expected_reg4: u8 = registers::CtrlReg4G::new()
.with_fs(GyroScale::Dps2000)
.into();
expectations.push(I2cTransaction::write(
GYRO_ADDR,
vec![GyroRegisters::CTRL_REG4_G.addr(), expected_reg4],
));
let i2c = I2cMock::new(&expectations);
let interface = I2cInterface::init(i2c);
let mut driver = Lsm9ds0::new(interface);
driver.init(&mut MockDelay).await.unwrap();
driver.set_gyro_scale(GyroScale::Dps2000).await.unwrap();
driver.release().release().done();
}
#[tokio::test]
async fn test_read_gyro_scaled() {
const AUTO_INCREMENT: u8 = 0x80;
let expectations = [I2cTransaction::write_read(
GYRO_ADDR,
vec![GyroRegisters::OUT_X_L_G.addr() | AUTO_INCREMENT],
vec![0xE8, 0x03, 0x0C, 0xFE, 0xFA, 0x00], )];
let i2c = I2cMock::new(&expectations);
let interface = I2cInterface::init(i2c);
let mut driver = Lsm9ds0::new(interface);
driver.config.gyro_bias = (1.0, -0.5, 0.0);
let (gx, gy, gz) = driver.read_gyro().await.unwrap();
let epsilon = 0.001;
assert!((gx.as_f32() - 7.75).abs() < epsilon, "gx={}", gx.as_f32());
assert!(
(gy.as_f32() - (-3.875)).abs() < epsilon,
"gy={}",
gy.as_f32()
);
assert!((gz.as_f32() - 2.1875).abs() < epsilon, "gz={}", gz.as_f32());
driver.release().release().done();
}
#[tokio::test]
async fn test_error_propagation() {
use embedded_hal_async::i2c::ErrorKind;
const AUTO_INCREMENT: u8 = 0x80;
let expectations = [I2cTransaction::write_read(
GYRO_ADDR,
vec![GyroRegisters::OUT_X_L_G.addr() | AUTO_INCREMENT],
vec![0u8; 6],
)
.with_error(ErrorKind::Other)];
let i2c = I2cMock::new(&expectations);
let interface = I2cInterface::init(i2c);
let mut driver = Lsm9ds0::new(interface);
let result = driver.read_gyro_raw().await;
assert!(matches!(result, Err(Error::GyroBus(_))));
driver.release().release().done();
}
#[tokio::test]
async fn test_read_temp() {
const AUTO_INCREMENT: u8 = 0x80;
let expectations = [I2cTransaction::write_read(
XM_ADDR,
vec![AccelMagRegisters::OUT_TEMP_L_XM.addr() | AUTO_INCREMENT],
vec![0xC8, 0x00], )];
let i2c = I2cMock::new(&expectations);
let interface = I2cInterface::init(i2c);
let mut driver = Lsm9ds0::new(interface);
let temp = driver.read_temp().await.unwrap();
let epsilon = 0.01;
assert!(
(temp.as_f32() - 50.0).abs() < epsilon,
"positive temp={}",
temp.as_f32()
);
driver.release().release().done();
}
#[tokio::test]
async fn test_read_temp_negative() {
const AUTO_INCREMENT: u8 = 0x80;
let expectations = [I2cTransaction::write_read(
XM_ADDR,
vec![AccelMagRegisters::OUT_TEMP_L_XM.addr() | AUTO_INCREMENT],
vec![0xB0, 0x0F], )];
let i2c = I2cMock::new(&expectations);
let interface = I2cInterface::init(i2c);
let mut driver = Lsm9ds0::new(interface);
let temp = driver.read_temp().await.unwrap();
let epsilon = 0.01;
assert!(
(temp.as_f32() - 15.0).abs() < epsilon,
"negative temp={}",
temp.as_f32()
);
driver.release().release().done();
}
use embedded_hal_mock::eh1::spi::{Mock as SpiMock, Transaction as SpiTransaction};
const SPI_READ: u8 = 0x80;
const MS_BIT: u8 = 0x40;
#[tokio::test]
async fn test_spi_read_gyro_raw() {
let gyro_expectations = [
SpiTransaction::transaction_start(),
SpiTransaction::write_vec(vec![SPI_READ | MS_BIT | GyroRegisters::OUT_X_L_G.addr()]),
SpiTransaction::read_vec(vec![0x02, 0x01, 0x04, 0x03, 0x06, 0x05]),
SpiTransaction::transaction_end(),
];
let xm_expectations: [SpiTransaction<u8>; 0] = [];
let gyro_spi = SpiMock::new(&gyro_expectations);
let xm_spi = SpiMock::new(&xm_expectations);
let interface = SpiInterface::init(gyro_spi, xm_spi);
let mut driver = Lsm9ds0::new(interface);
let (x, y, z) = driver.read_gyro_raw().await.unwrap();
assert_eq!(x, 0x0102);
assert_eq!(y, 0x0304);
assert_eq!(z, 0x0506);
let (mut gyro_spi, mut xm_spi) = driver.release().release();
gyro_spi.done();
xm_spi.done();
}
#[tokio::test]
async fn test_spi_read_accel_raw() {
let gyro_expectations: [SpiTransaction<u8>; 0] = [];
let xm_expectations = [
SpiTransaction::transaction_start(),
SpiTransaction::write_vec(vec![
SPI_READ | MS_BIT | AccelMagRegisters::OUT_X_L_A.addr(),
]),
SpiTransaction::read_vec(vec![0x64, 0x00, 0xC8, 0x00, 0x2C, 0x01]),
SpiTransaction::transaction_end(),
];
let gyro_spi = SpiMock::new(&gyro_expectations);
let xm_spi = SpiMock::new(&xm_expectations);
let interface = SpiInterface::init(gyro_spi, xm_spi);
let mut driver = Lsm9ds0::new(interface);
let (x, y, z) = driver.read_accel_raw().await.unwrap();
assert_eq!(x, 100);
assert_eq!(y, 200);
assert_eq!(z, 300);
let (mut gyro_spi, mut xm_spi) = driver.release().release();
gyro_spi.done();
xm_spi.done();
}
#[tokio::test]
async fn test_spi_read_mag_raw() {
let gyro_expectations: [SpiTransaction<u8>; 0] = [];
let xm_expectations = [
SpiTransaction::transaction_start(),
SpiTransaction::write_vec(vec![
SPI_READ | MS_BIT | AccelMagRegisters::OUT_X_L_M.addr(),
]),
SpiTransaction::read_vec(vec![0xE8, 0x03, 0xD0, 0x07, 0xB8, 0x0B]),
SpiTransaction::transaction_end(),
];
let gyro_spi = SpiMock::new(&gyro_expectations);
let xm_spi = SpiMock::new(&xm_expectations);
let interface = SpiInterface::init(gyro_spi, xm_spi);
let mut driver = Lsm9ds0::new(interface);
let (x, y, z) = driver.read_mag_raw().await.unwrap();
assert_eq!(x, 1000);
assert_eq!(y, 2000);
assert_eq!(z, 3000);
let (mut gyro_spi, mut xm_spi) = driver.release().release();
gyro_spi.done();
xm_spi.done();
}
#[tokio::test]
async fn test_i2c_multi_byte_write() {
const AUTO_INCREMENT: u8 = 0x80;
let test_addr = 0x20; let test_data: [u8; 9] = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09];
let mut expected_write = vec![test_addr | AUTO_INCREMENT];
expected_write.extend_from_slice(&test_data);
let expectations = [I2cTransaction::write(GYRO_ADDR, expected_write)];
let i2c = I2cMock::new(&expectations);
let mut interface = I2cInterface::init(i2c);
interface.write_gyro(test_addr, &test_data).await.unwrap();
interface.release().done();
}
#[tokio::test]
#[should_panic(expected = "exceeds MAX_WRITE_LEN")]
#[cfg(debug_assertions)]
async fn test_i2c_write_exceeds_max_length_panics_in_debug() {
let test_addr = 0x20;
let test_data: [u8; 20] = [0u8; 20];
let expectations: [I2cTransaction; 0] = [];
let i2c = I2cMock::new(&expectations);
let mut interface = I2cInterface::init(i2c);
let _ = interface.write_gyro(test_addr, &test_data).await;
}
}