#![no_std]
use core::convert::{TryFrom, TryInto};
use core::fmt::Debug;
pub use accelerometer;
use accelerometer::error::Error as AccelerometerError;
use accelerometer::vector::{F32x3, I16x3};
use accelerometer::{Accelerometer, RawAccelerometer};
use embedded_hal::blocking::i2c::{self, WriteRead};
use embedded_hal::blocking::spi::{self, Transfer};
use embedded_hal::digital::v2::OutputPin;
mod interrupts;
mod register;
use interrupts::*;
pub use interrupts::{
Detect4D, Interrupt1, Interrupt2, InterruptConfig, InterruptMode, InterruptSource, IrqPin,
IrqPin1Config, IrqPin2Config, LatchInterruptRequest,
};
use register::*;
pub use register::{
DataRate, DataStatus, Duration, Range, Register, SlaveAddr, Threshold,
};
#[derive(Debug)]
pub enum Error<BusError, PinError> {
Bus(BusError),
Pin(PinError),
InvalidDataRate,
InvalidRange,
WriteToReadOnly,
WrongAddress,
}
pub struct Lis331<CORE> {
core: CORE,
}
impl<I2C, E> Lis331<Lis331I2C<I2C>>
where
I2C: WriteRead<Error = E> + i2c::Write<Error = E>,
{
pub fn new_i2c(
i2c: I2C,
address: SlaveAddr,
) -> Result<Self, Error<E, core::convert::Infallible>> {
Self::new_i2c_with_config(i2c, address, Configuration::default())
}
pub fn new_i2c_with_config(
i2c: I2C,
address: SlaveAddr,
config: Configuration,
) -> Result<Self, Error<E, core::convert::Infallible>> {
let core = Lis331I2C {
i2c,
address: address.addr(),
};
let mut lis3dh = Lis331 { core };
lis3dh.configure(config)?;
Ok(lis3dh)
}
}
impl<SPI, NSS, ESPI, ENSS> Lis331<Lis331SPI<SPI, NSS>>
where
SPI: spi::Write<u8, Error = ESPI> + Transfer<u8, Error = ESPI>,
NSS: OutputPin<Error = ENSS>,
{
pub fn new_spi(spi: SPI, nss: NSS) -> Result<Self, Error<ESPI, ENSS>> {
Self::new_spi_with_config(spi, nss, Configuration::default())
}
pub fn new_spi_with_config(
spi: SPI,
nss: NSS,
config: Configuration,
) -> Result<Self, Error<ESPI, ENSS>> {
let core = Lis331SPI { spi, nss };
let mut lis3dh = Lis331 { core };
lis3dh.configure(config)?;
Ok(lis3dh)
}
}
impl<CORE> Lis331<CORE>
where
CORE: Lis331Core,
{
pub fn configure(
&mut self,
conf: Configuration,
) -> Result<(), Error<CORE::BusError, CORE::PinError>> {
if conf.block_data_update {
self.write_register(Register::CTRL4, BDU)?;
}
self.set_datarate(conf.datarate)?;
self.enable_axis((conf.enable_x_axis, conf.enable_y_axis, conf.enable_z_axis))
}
fn enable_axis(
&mut self,
(x, y, z): (bool, bool, bool),
) -> Result<(), Error<CORE::BusError, CORE::PinError>> {
self.modify_register(Register::CTRL1, |mut ctrl1| {
ctrl1 &= !(X_EN | Y_EN | Z_EN);
ctrl1 |= if x { X_EN } else { 0 };
ctrl1 |= if y { Y_EN } else { 0 };
ctrl1 |= if z { Z_EN } else { 0 };
ctrl1
})
}
pub fn set_datarate(
&mut self,
datarate: DataRate,
) -> Result<(), Error<CORE::BusError, CORE::PinError>> {
self.modify_register(Register::CTRL1, |mut ctrl1| {
ctrl1 &= !ODR_MASK;
ctrl1 |= datarate.bits() << 4;
ctrl1
})
}
pub fn get_datarate(&mut self) -> Result<DataRate, Error<CORE::BusError, CORE::PinError>> {
let ctrl1 = self.read_register(Register::CTRL1)?;
let odr = (ctrl1 >> 4) & 0x0F;
DataRate::try_from(odr).map_err(|_| Error::InvalidDataRate)
}
pub fn set_range(&mut self, range: Range) -> Result<(), Error<CORE::BusError, CORE::PinError>> {
self.modify_register(Register::CTRL4, |mut ctrl4| {
ctrl4 &= !FS_MASK;
ctrl4 |= range.bits() << 4;
ctrl4
})
}
pub fn get_range(&mut self) -> Result<Range, Error<CORE::BusError, CORE::PinError>> {
let ctrl4 = self.read_register(Register::CTRL4)?;
let fs = (ctrl4 >> 4) & 0b0011;
Range::try_from(fs).map_err(|_| Error::InvalidRange)
}
pub fn set_ref(&mut self, reference: u8) -> Result<(), Error<CORE::BusError, CORE::PinError>> {
self.write_register(Register::REFERENCE, reference)
}
pub fn get_ref(&mut self) -> Result<u8, Error<CORE::BusError, CORE::PinError>> {
self.read_register(Register::REFERENCE)
}
pub fn get_status(&mut self) -> Result<DataStatus, Error<CORE::BusError, CORE::PinError>> {
let stat = self.read_register(Register::STATUS)?;
Ok(DataStatus {
zyxor: (stat & ZYXOR) != 0,
xyzor: ((stat & XOR) != 0, (stat & YOR) != 0, (stat & ZOR) != 0),
zyxda: (stat & ZYXDA) != 0,
xyzda: ((stat & XDA) != 0, (stat & YDA) != 0, (stat & ZDA) != 0),
})
}
pub fn is_data_ready(&mut self) -> Result<bool, Error<CORE::BusError, CORE::PinError>> {
let value = self.get_status()?;
Ok(value.zyxda)
}
fn modify_register<F>(
&mut self,
register: Register,
f: F,
) -> Result<(), Error<CORE::BusError, CORE::PinError>>
where
F: FnOnce(u8) -> u8,
{
let value = self.read_register(register)?;
self.write_register(register, f(value))
}
pub fn register_clear_bits(
&mut self,
reg: Register,
bits: u8,
) -> Result<(), Error<CORE::BusError, CORE::PinError>> {
self.modify_register(reg, |v| v & !bits)
}
pub fn register_set_bits(
&mut self,
reg: Register,
bits: u8,
) -> Result<(), Error<CORE::BusError, CORE::PinError>> {
self.modify_register(reg, |v| v | bits)
}
fn register_xset_bits(
&mut self,
reg: Register,
bits: u8,
set: bool,
) -> Result<(), Error<CORE::BusError, CORE::PinError>> {
if set {
self.register_set_bits(reg, bits)
} else {
self.register_clear_bits(reg, bits)
}
}
pub fn configure_interrupt_pin<P: IrqPin>(
&mut self,
pin: P,
) -> Result<(), Error<CORE::BusError, CORE::PinError>> {
self.write_register(P::ctrl_reg(), pin.bits())
}
pub fn configure_irq_src<I: Interrupt>(
&mut self,
int: I,
interrupt_mode: InterruptMode,
interrupt_config: InterruptConfig,
) -> Result<(), Error<CORE::BusError, CORE::PinError>> {
self.configure_irq_src_and_control(
int,
interrupt_mode,
interrupt_config,
LatchInterruptRequest::default(),
Detect4D::default(),
)
}
pub fn configure_irq_src_and_control<I: Interrupt>(
&mut self,
_int: I,
interrupt_mode: InterruptMode,
interrupt_config: InterruptConfig,
latch_interrupt_request: LatchInterruptRequest,
detect_4d: Detect4D,
) -> Result<(), Error<CORE::BusError, CORE::PinError>> {
let latch_interrupt_request =
matches!(latch_interrupt_request, LatchInterruptRequest::Enable);
let detect_4d = matches!(detect_4d, Detect4D::Enable);
if latch_interrupt_request || detect_4d {
let latch = (latch_interrupt_request as u8) << I::lir_int_bit();
let d4d = (detect_4d as u8) << I::d4d_int_bit();
self.register_set_bits(Register::CTRL5, latch | d4d)?;
}
self.write_register(I::cfg_reg(), interrupt_config.to_bits(interrupt_mode))
}
#[doc(alias = "INT1_DURATION")]
#[doc(alias = "INT2_DURATION")]
pub fn configure_irq_duration<I: Interrupt>(
&mut self,
_int: I,
duration: Duration,
) -> Result<(), Error<CORE::BusError, CORE::PinError>> {
self.write_register(I::duration_reg(), duration.0)
}
#[doc(alias = "INT1_THS")]
#[doc(alias = "INT2_THS")]
pub fn configure_irq_threshold<I: Interrupt>(
&mut self,
_int: I,
threshold: Threshold,
) -> Result<(), Error<CORE::BusError, CORE::PinError>> {
self.write_register(I::ths_reg(), threshold.0)
}
pub fn get_irq_src<I: Interrupt>(
&mut self,
_int: I,
) -> Result<InterruptSource, Error<CORE::BusError, CORE::PinError>> {
let irq_src = self.read_register(I::src_reg())?;
Ok(InterruptSource::from_bits(irq_src))
}
pub fn reboot_memory_content(&mut self) -> Result<(), Error<CORE::BusError, CORE::PinError>> {
self.register_set_bits(Register::CTRL5, 0b1000_0000)
}
}
impl<CORE> Accelerometer for Lis331<CORE>
where
CORE: Lis331Core,
CORE::PinError: Debug,
CORE::BusError: Debug,
{
type Error = Error<CORE::BusError, CORE::PinError>;
fn accel_norm(&mut self) -> Result<F32x3, AccelerometerError<Self::Error>> {
let range = self.get_range()?;
let scale = match range {
Range::G6 => 0.003,
Range::G12 => 0.006,
Range::G24 => 0.0012,
};
let shift: u8 = 4;
let acc_raw = self.accel_raw()?;
let x = (acc_raw.x >> shift) as f32 * scale;
let y = (acc_raw.y >> shift) as f32 * scale;
let z = (acc_raw.z >> shift) as f32 * scale;
Ok(F32x3::new(x, y, z))
}
fn sample_rate(&mut self) -> Result<f32, AccelerometerError<Self::Error>> {
Ok(self.get_datarate()?.sample_rate())
}
}
impl<CORE> RawAccelerometer<I16x3> for Lis331<CORE>
where
CORE: Lis331Core,
CORE::PinError: Debug,
CORE::BusError: Debug,
{
type Error = Error<CORE::BusError, CORE::PinError>;
fn accel_raw(&mut self) -> Result<I16x3, AccelerometerError<Self::Error>> {
let accel_bytes = self.read_accel_bytes()?;
let x = i16::from_le_bytes(accel_bytes[0..2].try_into().unwrap());
let y = i16::from_le_bytes(accel_bytes[2..4].try_into().unwrap());
let z = i16::from_le_bytes(accel_bytes[4..6].try_into().unwrap());
Ok(I16x3::new(x, y, z))
}
}
pub trait Lis331Core {
type BusError;
type PinError;
fn write_register(
&mut self,
register: Register,
value: u8,
) -> Result<(), Error<Self::BusError, Self::PinError>>;
fn read_register(
&mut self,
register: Register,
) -> Result<u8, Error<Self::BusError, Self::PinError>>;
fn read_accel_bytes(&mut self) -> Result<[u8; 6], Error<Self::BusError, Self::PinError>>;
}
impl<CORE> Lis331Core for Lis331<CORE>
where
CORE: Lis331Core,
{
type BusError = CORE::BusError;
type PinError = CORE::PinError;
fn write_register(
&mut self,
register: Register,
value: u8,
) -> Result<(), Error<Self::BusError, Self::PinError>> {
self.core.write_register(register, value)
}
fn read_register(
&mut self,
register: Register,
) -> Result<u8, Error<Self::BusError, Self::PinError>> {
self.core.read_register(register)
}
fn read_accel_bytes(&mut self) -> Result<[u8; 6], Error<Self::BusError, Self::PinError>> {
self.core.read_accel_bytes()
}
}
pub struct Lis331I2C<I2C> {
i2c: I2C,
address: u8,
}
impl<I2C, E> Lis331Core for Lis331I2C<I2C>
where
I2C: WriteRead<Error = E> + i2c::Write<Error = E>,
{
type BusError = E;
type PinError = core::convert::Infallible;
fn read_accel_bytes(&mut self) -> Result<[u8; 6], Error<Self::BusError, Self::PinError>> {
let mut data = [0u8; 6];
self.i2c
.write_read(self.address, &[Register::OUT_X_L.addr() | 0x80], &mut data)
.map_err(Error::Bus)
.and(Ok(data))
}
fn write_register(
&mut self,
register: Register,
value: u8,
) -> Result<(), Error<Self::BusError, Self::PinError>> {
if register.read_only() {
return Err(Error::WriteToReadOnly);
}
self.i2c
.write(self.address, &[register.addr(), value])
.map_err(Error::Bus)
}
fn read_register(
&mut self,
register: Register,
) -> Result<u8, Error<Self::BusError, Self::PinError>> {
let mut data = [0];
self.i2c
.write_read(self.address, &[register.addr()], &mut data)
.map_err(Error::Bus)
.and(Ok(data[0]))
}
}
pub struct Lis331SPI<SPI, NSS> {
spi: SPI,
nss: NSS,
}
impl<SPI, NSS, ESPI, ENSS> Lis331SPI<SPI, NSS>
where
SPI: spi::Write<u8, Error = ESPI> + Transfer<u8, Error = ESPI>,
NSS: OutputPin<Error = ENSS>,
{
fn nss_turn_on(&mut self) -> Result<(), Error<ESPI, ENSS>> {
self.nss.set_low().map_err(Error::Pin)
}
fn nss_turn_off(&mut self) -> Result<(), Error<ESPI, ENSS>> {
self.nss.set_high().map_err(Error::Pin)
}
unsafe fn write_multiple_regs(
&mut self,
start_register: Register,
data: &[u8],
) -> Result<(), Error<ESPI, ENSS>> {
self.nss_turn_on()?;
let res = self
.spi
.write(&[start_register.addr() | 0x40])
.and_then(|_| self.spi.write(data))
.map_err(Error::Bus);
self.nss_turn_off()?;
res
}
fn read_multiple_regs(
&mut self,
start_register: Register,
buf: &mut [u8],
) -> Result<(), Error<ESPI, ENSS>> {
self.nss_turn_on()?;
self.spi
.write(&[start_register.addr() | 0xC0])
.and_then(|_| self.spi.transfer(buf))
.map_err(Error::Bus)?;
self.nss_turn_off()
}
}
impl<SPI, NSS, ESPI, ENSS> Lis331Core for Lis331SPI<SPI, NSS>
where
SPI: spi::Write<u8, Error = ESPI> + Transfer<u8, Error = ESPI>,
NSS: OutputPin<Error = ENSS>,
{
type BusError = ESPI;
type PinError = ENSS;
fn read_accel_bytes(&mut self) -> Result<[u8; 6], Error<ESPI, ENSS>> {
let mut data = [0u8; 6];
self.read_multiple_regs(Register::OUT_X_L, &mut data)?;
Ok(data)
}
fn write_register(&mut self, register: Register, value: u8) -> Result<(), Error<ESPI, ENSS>> {
if register.read_only() {
return Err(Error::WriteToReadOnly);
}
unsafe { self.write_multiple_regs(register, &[value]) }
}
fn read_register(&mut self, register: Register) -> Result<u8, Error<ESPI, ENSS>> {
let mut data = [0];
self.nss_turn_on()?;
self.spi
.write(&[register.addr() | 0x80])
.and_then(|_| self.spi.transfer(&mut data))
.map_err(Error::Bus)?;
self.nss_turn_off()?;
Ok(data[0])
}
}
#[derive(Debug, Clone, Copy)]
pub struct Configuration {
pub datarate: DataRate,
pub enable_x_axis: bool,
pub enable_y_axis: bool,
pub enable_z_axis: bool,
pub block_data_update: bool,
}
impl Default for Configuration {
fn default() -> Self {
Self {
block_data_update: true,
datarate: DataRate::Hz_400,
enable_x_axis: true,
enable_y_axis: true,
enable_z_axis: true,
}
}
}