#![no_std]
use core::marker::PhantomData;
use log::{debug};
use bitflags::bitflags;
use embedded_hal::blocking::i2c;
#[cfg(feature = "std")]
extern crate std;
pub struct Tlv493d<I2c, E> {
i2c: I2c,
addr: u8,
initial: [u8; 10],
last_frm: u8,
_e: PhantomData<E>,
}
pub const ADDRESS_BASE: u8 = 0b1011110;
pub enum ReadRegisters {
Rx = 0x00, By = 0x01, Bz = 0x02, Temp = 0x03, Bx2 = 0x04, Bz2 = 0x05, Temp2 = 0x06,
FactSet1 = 0x07,
FactSet2 = 0x08,
FactSet3 = 0x09,
}
pub enum WriteRegisters {
Res = 0x00, Mode1 = 0x01, Res2 = 0x02, Mode2 = 0x03, }
#[derive(Debug, PartialEq, Clone)]
pub struct Values {
x: f32, y: f32, z: f32, temp: f32, }
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 = "std", derive(thiserror::Error))]
pub enum Error<E: core::fmt::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(E),
}
impl <I2c, E> Tlv493d<I2c, E>
where
I2c: i2c::Read<Error = E> + i2c::Write<Error = E> + i2c::WriteRead<Error = E>,
E: core::fmt::Debug,
{
pub fn new(i2c: I2c, addr: u8, mode: Mode) -> Result<Self, Error<E>> {
debug!("New Tlv493d with address: 0x{:02x}", addr);
let mut s = Self {
i2c, addr, initial: [0u8; 10], last_frm: 0xff, _e: PhantomData,
};
s.i2c.write(s.addr, &[0xFF]).map_err(Error::I2c)?;
s.i2c.write(s.addr, &[0x00]).map_err(Error::I2c)?;
let _ = s.i2c.read(s.addr, &mut s.initial[..]).map_err(Error::I2c)?;
debug!("initial read: {:02x?}", s.initial);
s.configure(mode)?;
Ok(s)
}
pub fn configure(&mut self, mode: Mode) -> Result<(), Error<E>> {
let mut m1 = unsafe { Mode1::from_bits_unchecked(self.initial[7]) };
let m2 = unsafe { Mode2::from_bits_unchecked(self.initial[9]) };
debug!("Factory config: {:?} ({:02x?})", m1, self.initial);
m1.remove(Mode1::PARITY);
m1.remove(Mode1::FAST | Mode1::LOW);
match mode {
Mode::Disabled => (),
Mode::Master => m1 |= Mode1::FAST | Mode1::LOW,
Mode::Fast => m1 |= Mode1::FAST | Mode1::IRQ_EN,
Mode::LowPower => m1 |= Mode1::LOW | Mode1::IRQ_EN,
Mode::UltraLowPower => m1 |= Mode1::IRQ_EN,
}
let mut cfg = [
0x00,
m1.bits(),
self.initial[8],
m2.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 {
m1 |= Mode1::PARITY;
cfg[1] = m1.bits();
}
debug!("Writing config: Mode1: {:?} Mode2: {:?} (cfg: {:02x?}", m1, m2, cfg);
self.i2c.write(self.addr, &cfg).map_err(Error::I2c)?;
Ok(())
}
pub fn read_raw(&mut self) -> Result<[i16; 4], Error<E>> {
let mut v = [0i16; 4];
let mut b = [0u8; 7];
self.i2c.read(self.addr, &mut b[..]).map_err(Error::I2c)?;
let frm = b[3] & 0b0000_1100;
if self.last_frm == frm {
return Err(Error::AdcLockup)
} else {
self.last_frm = 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);
debug!("Read data {:02x?} values: {:04x?}", b, v);
Ok(v)
}
pub fn read(&mut self) -> Result<Values, Error<E>> {
let raw = self.read_raw()?;
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,
})
}
}