#![no_std]
use bitflags::bitflags;
use core::fmt::Debug;
use core::marker::PhantomData;
use maybe_async_cfg::maybe;
#[cfg(feature = "async")]
use embedded_hal_async::{delay::DelayNs, i2c, i2c::Error as I2cError};
#[cfg(feature = "blocking")]
use embedded_hal::{delay::DelayNs, i2c, i2c::Error as I2cError};
#[cfg(feature = "std")]
extern crate std;
#[cfg(feature = "defmt")]
#[allow(unused_imports)]
#[macro_use]
extern crate defmt;
pub struct Tlv493d<I2c, I2cErr, Delay> {
i2c: I2c,
delay: Delay,
addr: u8,
initial: [u8; 10],
last_frm: Option<u8>,
_e_i2c: PhantomData<I2cErr>,
}
pub const ADDRESS_BASE: u8 = 0b1011110;
#[derive(Debug, PartialEq, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum ReadRegisters {
Rx = 0x00, By = 0x01, Bz = 0x02, Temp = 0x03, Bx2 = 0x04, Bz2 = 0x05, Temp2 = 0x06,
FactSet1 = 0x07,
FactSet2 = 0x08,
FactSet3 = 0x09,
}
#[derive(Debug, PartialEq, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum WriteRegisters {
Res = 0x00, Mode1 = 0x01, Res2 = 0x02, Mode2 = 0x03, }
#[derive(Debug, PartialEq, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Values {
pub x: f32, pub y: f32, pub z: f32, pub temp: f32, }
#[derive(Debug, PartialEq, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Mode {
Disabled, Master, Fast, LowPower, UltraLowPower, }
bitflags! {
pub struct Mode1: u8 {
const PARITY = 0b1000_0000; const I2C_ADDR_1 = 0b0100_0000; const I2C_ADDR_0 = 0b0010_0000; const IRQ_EN = 0b0000_0100; const FAST = 0b0000_0010; const LOW = 0b0000_0001; }
}
bitflags! {
pub struct Mode2: u8 {
const TEMP_DISABLE = 0b1000_0000; const LOW_POW_PERIOD = 0b0100_0000; const PARITY_TEST_EN = 0b0010_0000; }
}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[cfg_attr(feature = "std", derive(thiserror::Error))]
pub enum Error<I2cErr: I2cError + Debug> {
#[cfg_attr(
feature = "std",
error("No device found with specified i2c bus and address")
)]
NoDevice,
#[cfg_attr(feature = "std", error("Device ADC lockup, reset required"))]
AdcLockup,
#[cfg_attr(feature = "std", error("I2C device error: {0:?}"))]
I2c(I2cErr),
}
maybe_async_cfg::content! {
impl<I2c, I2cErr, Delay> Tlv493d<I2c, I2cErr, Delay>
where
I2c: i2c::I2c,
I2cErr: I2cError + Debug,
Error<I2cErr>: From<Error<<I2c as i2c::ErrorType>::Error>>,
Delay: DelayNs,
{
#[maybe(
sync(feature = "blocking"),
async(feature = "async")
)]
pub async fn new(
i2c: I2c,
delay: Delay,
addr: u8,
mode: Mode,
) -> Result<Self, (Error<I2cErr>, I2c)> {
#[cfg(feature = "defmt")]
debug!("New Tlv493d with address: 0x{:02x}", addr);
let mut s = Self {
i2c,
delay,
addr,
initial: [0u8; 10],
last_frm: None,
_e_i2c: PhantomData,
};
#[cfg(feature = "async")]
if let Err(err) = s.configure_async(mode, true).await {
return Err((err, s.i2c));
};
#[cfg(feature = "blocking")]
if let Err(err) = s.configure_sync(mode, true) {
return Err((err, s.i2c));
};
Ok(s)
}
pub fn into_i2c(self) -> I2c {
self.i2c
}
#[maybe(
sync(feature = "blocking"),
async(feature = "async")
)]
pub async fn configure(&mut self, mode: Mode, reset: bool) -> Result<(), Error<I2cErr>> {
if reset {
#[cfg(feature = "defmt")]
debug!("Resetting device");
#[cfg(feature = "async")]
self.delay.delay_ms(1).await;
#[cfg(feature = "blocking")]
self.delay.delay_ms(1);
#[cfg(feature = "async")]
self.i2c.write(0xff, &[]).await
.map_err(Error::I2c).ok();
#[cfg(feature = "blocking")]
self.i2c.write(0xff, &[])
.map_err(Error::I2c).ok();
#[cfg(feature = "defmt")]
debug!("Setting device address");
#[cfg(feature = "async")]
self.i2c.write(0x00, &[0xff]).await
.map_err(Error::I2c).ok();
#[cfg(feature = "blocking")]
self.i2c.write(0x00, &[0xff])
.map_err(Error::I2c).ok();
#[cfg(feature = "defmt")]
debug!("Read device initial state");
#[cfg(feature = "async")]
self.i2c.read(self.addr, &mut self.initial[..]).await
.map_err(Error::I2c)?;
#[cfg(feature = "blocking")]
self.i2c.read(self.addr, &mut self.initial[..])
.map_err(Error::I2c)?;
#[cfg(feature = "defmt")]
debug!("Initial state: {:02x}", self.initial);
}
let Some(mut mod1) = Mode1::from_bits(self.initial[7]) else {
panic!("implementation error");
};
let Some(mod2) = Mode2::from_bits(self.initial[9]) else {
panic!("implementation error");
};
mod1.remove(Mode1::PARITY);
mod1.remove(Mode1::FAST | Mode1::LOW);
match mode {
Mode::Disabled => (),
Mode::Master => mod1 |= Mode1::FAST | Mode1::LOW,
Mode::Fast => mod1 |= Mode1::FAST | Mode1::IRQ_EN,
Mode::LowPower => mod1 |= Mode1::LOW | Mode1::IRQ_EN,
Mode::UltraLowPower => mod1 |= Mode1::IRQ_EN,
}
let mut cfg = [0x00, mod1.bits(), self.initial[8], mod2.bits()];
self.initial[7] = mod1.bits();
self.initial[9] = mod2.bits();
let mut parity = 0;
for v in &cfg {
for i in 0..8 {
if v & (1 << i) != 0 {
parity += 1;
}
}
}
if parity % 2 == 0 {
mod1 |= Mode1::PARITY;
cfg[1] = mod1.bits();
}
#[cfg(feature = "async")]
self.i2c.write(self.addr, &cfg).await
.map_err(Error::I2c)?;
#[cfg(feature = "blocking")]
self.i2c.write(self.addr, &cfg)
.map_err(Error::I2c)?;
Ok(())
}
#[maybe(
sync(feature = "blocking"),
async(feature = "async")
)]
pub async fn read_raw(&mut self) -> Result<[i16; 4], Error<I2cErr>> {
let mut v = [0i16; 4];
let mut b = [0u8; 7];
#[cfg(feature = "async")]
self.i2c.read(self.addr, &mut b[..]).await
.map_err(Error::I2c)?;
#[cfg(feature = "blocking")]
self.i2c.read(self.addr, &mut b[..])
.map_err(Error::I2c)?;
let frm = b[3] & 0b0000_1100;
if let Some(last_frm) = self.last_frm
&& last_frm == frm
{
return Err(Error::AdcLockup);
}
self.last_frm = Some(frm);
v[0] = (b[0] as i8 as i16) << 4 | ((b[4] & 0xf0) >> 4) as i16;
v[1] = (b[1] as i8 as i16) << 4 | (b[4] & 0x0f) as i16;
v[2] = (b[2] as i8 as i16) << 4 | (b[5] & 0x0f) as i16;
v[3] = (b[3] as i8 as i16 & 0xf0) << 4 | (b[6] as i16 & 0xff);
#[cfg(feature = "defmt")]
debug!("Read data {:02x} values: {:04x}", b, v);
Ok(v)
}
#[maybe(
sync(feature = "blocking"),
async(feature = "async")
)]
pub async fn read(&mut self) -> Result<Values, Error<I2cErr>> {
#[cfg(feature = "async")]
let raw = self.read_raw_async().await?;
#[cfg(feature = "blocking")]
let raw = self.read_raw_sync()?;
Ok(Values {
x: raw[0] as f32 * 0.098f32,
y: raw[1] as f32 * 0.098f32,
z: raw[2] as f32 * 0.098f32,
temp: (raw[3] - 340) as f32 * 1.1f32 + 24.2f32,
})
}
#[cfg(feature = "math")]
#[maybe(
sync(feature = "blocking"),
async(feature = "async")
)]
pub async fn read_angle_f32(&mut self) -> Result<f32, Error<I2cErr>> {
#[cfg(feature = "async")]
let v = self.read_async().await?;
#[cfg(feature = "blocking")]
let v = self.read_sync()?;
Ok(libm::Libm::<f32>::atan2(v.x, v.y))
}
}
}