#![no_std]
#[cfg(feature = "accel")]
use accelerometer::{vector::F32x3, Accelerometer};
use bitmask_enum::bitmask;
use embedded_hal::{delay::DelayNs, i2c::I2c};
use num_enum::{FromPrimitive, IntoPrimitive};
mod config;
pub mod features;
use features::{EditFeatures, FEATURE_SIZE};
#[allow(dead_code)]
#[repr(u8)]
#[derive(Debug, Clone, Copy, IntoPrimitive)]
enum Reg {
ChipId = 0x00,
Error = 0x02,
Status = 0x03,
AccXLSB = 0x12,
AccXMSB = 0x13,
AccYLSB = 0x14,
AccYMSB = 0x15,
AccZLSB = 0x16,
AccZMSB = 0x17,
FeatureInterruptStatus = 0x1c,
HardwareInterruptStatus = 0x1d,
InternalStatus = 0x2a,
AccelConfig = 0x40,
AccelRange = 0x41,
Interrupt1IOCtl = 0x53,
Interrupt2IOCtl = 0x54,
InterruptConfig = 0x55,
FeatureInterrupt1Mapping = 0x56,
FeatureInterrupt2Mapping = 0x57,
HardwareInterruptMapping = 0x58,
StartInitialization = 0x59,
Bma4Reserved5BAddr = 0x5b,
Bma4Reserved5CAddr = 0x5c,
FeatureConfig = 0x5e,
InternalError = 0x5f,
NvmConfig = 0x6a,
SerialIfConfig = 0x6b,
AccelSelfTest = 0x6d,
NvmBackendConfig = 0x70,
OffsetX = 0x71,
OffsetY = 0x72,
OffsetZ = 0x73,
PowerConfiguration = 0x7c,
PowerControl = 0x7d,
Command = 0x7e,
}
#[repr(u8)]
#[derive(Copy, Clone, Debug, IntoPrimitive)]
pub enum AccelConfigOdr {
Odr0p78 = 0x01,
Odr1p5 = 0x02,
Odr3p1 = 0x03,
Odr6p25 = 0x04,
Odr12p5 = 0x05,
Odr25 = 0x06,
Odr50 = 0x07,
Odr100 = 0x08,
Odr200 = 0x09,
Odr400 = 0x0a,
Odr800 = 0x0b,
Odr1k6 = 0x0c,
}
#[repr(u8)]
#[derive(Copy, Clone, Debug, IntoPrimitive)]
pub enum AccelConfigBandwidth {
Osr4Avg1 = 0x00,
Osr2Avg2 = 0x10,
NormAvg4 = 0x20,
CicAvg8 = 0x30,
ResAvg16 = 0x40,
ResAvg32 = 0x50,
ResAvg64 = 0x60,
ResAvg128 = 0x70,
}
#[repr(u8)]
#[derive(Copy, Clone, Debug, Eq, PartialEq, IntoPrimitive)]
pub enum AccelConfigPerfMode {
CicAvg = 0x00,
Continuous = 0x80,
}
#[repr(u8)]
#[derive(Copy, Clone, Debug, IntoPrimitive)]
pub enum AccelRange {
Range2g = 0x00,
Range4g = 0x01,
Range8g = 0x02,
Range16g = 0x03,
}
impl AccelRange {
pub fn as_float(&self) -> f32 {
match self {
AccelRange::Range2g => 2.0,
AccelRange::Range4g => 4.0,
AccelRange::Range8g => 8.0,
AccelRange::Range16g => 16.0,
}
}
}
#[bitmask(u8)]
pub enum FeatureInterruptStatus {
SingleTap = 0b0000_0001,
StepCounter = 0b0000_0010,
Activity = 0b0000_0100,
WristWear = 0b0000_1000,
DoubleTap = 0b0001_0000,
AnyMotion = 0b0010_0000,
NoMotion = 0b0100_0000,
Error = 0b1000_0000,
}
#[bitmask(u8)]
pub enum HardwareInterruptStatus {
FifoFull = 0x01,
FifoWatermark = 0x02,
DataReady = 0x04,
AuxiliaryDataReady = 0x20,
AcceleratorDataReady = 0x80,
}
#[derive(Copy, Clone, Debug)]
pub struct InterruptStatus {
pub feature: FeatureInterruptStatus,
pub hardware: HardwareInterruptStatus,
}
#[repr(u8)]
#[derive(Copy, Clone, Debug, IntoPrimitive)]
pub enum InterruptLine {
Line1 = 0,
Line2 = 1,
}
#[repr(u8)]
#[derive(Copy, Clone, Debug, IntoPrimitive)]
pub enum InterruptTriggerCondition {
Level = 0x00,
Edge = 0x01,
}
#[repr(u8)]
#[derive(Copy, Clone, Debug, IntoPrimitive)]
pub enum InterruptLevel {
ActiveLow = 0x00,
ActiveHigh = 0x02,
}
#[repr(u8)]
#[derive(Copy, Clone, Debug, IntoPrimitive)]
pub enum InterruptOutputBehavior {
PushPull = 0x00,
OpenDrain = 0x04,
}
#[derive(Clone, Debug)]
pub enum InterruptDirection {
Input(InterruptTriggerCondition),
Output(InterruptOutputBehavior, InterruptLevel),
}
impl InterruptDirection {
fn bit_mask(&self) -> u8 {
match self {
InterruptDirection::Input(_) => 0x10,
InterruptDirection::Output(_, _) => 0x08,
}
}
}
#[repr(u8)]
#[derive(Copy, Clone, Debug, IntoPrimitive, FromPrimitive)]
enum Activity {
Stationary = 0x00,
Walking = 0x01,
Running = 0x02,
#[default]
Invalid = 0x03,
}
#[allow(dead_code)]
#[repr(u8)]
#[derive(Copy, Clone, Debug, IntoPrimitive)]
enum Command {
NvmProg = 0xa0,
FifoFlush = 0xb0,
SoftReset = 0xb6,
}
#[bitmask(u8)]
pub enum PowerControlFlag {
Accelerometer = 0b0000_0100,
Auxiliary = 0b0000_0001,
}
#[bitmask(u8)]
enum PowerConfigurationFlag {
AdvancedPowerSave = 0b0000_0001,
FifoSelfWakeUp = 0b0000_0010,
}
const DEFAULT_ADDRESS: u8 = 0x18;
const READ_WRITE_LEN: usize = 0x08;
const GRAVITY_EARTH: f32 = 9.80665;
#[derive(Clone, Copy, Debug)]
pub enum Error<E> {
BusError(E),
ConfigError,
BadInternal(u8),
BadArgument,
}
impl<E> core::convert::From<E> for Error<E> {
fn from(error: E) -> Self {
Error::BusError(error)
}
}
#[repr(u8)]
#[derive(Copy, Clone, Debug, IntoPrimitive, FromPrimitive)]
pub enum ChipId {
#[default]
Unknown = 0x00,
Bma423 = 0x13,
}
pub struct Uninitialized;
pub struct FullPower;
pub struct PowerSave;
pub trait Initialized {}
impl Initialized for FullPower {}
impl Initialized for PowerSave {}
pub struct Bma423<I2C, S> {
address: u8,
i2c: I2C,
config: Config,
#[allow(dead_code)]
state: S,
}
#[allow(dead_code)]
#[derive(Copy, Clone, Debug)]
pub struct Config {
pub bandwidth: AccelConfigBandwidth,
pub range: AccelRange,
pub performance_mode: AccelConfigPerfMode,
pub sample_rate: AccelConfigOdr,
}
impl Default for Config {
fn default() -> Self {
Self {
bandwidth: AccelConfigBandwidth::NormAvg4,
range: AccelRange::Range4g,
performance_mode: AccelConfigPerfMode::CicAvg,
sample_rate: AccelConfigOdr::Odr50,
}
}
}
impl<I2C: I2c, S> Bma423<I2C, S> {
fn write(&mut self, data: &[u8]) -> Result<(), Error<I2C::Error>> {
self.i2c.write(self.address, data)?;
Ok(())
}
fn read_register_bytes(&mut self, reg: Reg, data: &mut [u8]) -> Result<(), Error<I2C::Error>> {
self.i2c.write_read(self.address, &[reg.into()], data)?;
Ok(())
}
fn read_register(&mut self, reg: Reg) -> Result<u8, Error<I2C::Error>> {
let mut data = [0; 1];
self.read_register_bytes(reg, &mut data)?;
Ok(data[0])
}
fn set_power_config(
&mut self,
value: PowerConfigurationFlag,
set: bool,
) -> Result<(), Error<I2C::Error>> {
let mut reg = self.read_register(Reg::PowerConfiguration)?;
if set {
reg |= u8::from(value);
} else {
reg &= !u8::from(value);
}
self.write(&[Reg::PowerConfiguration.into(), reg])
}
}
impl<I2C: I2c> Bma423<I2C, Uninitialized> {
fn stream_write(&mut self, reg: Reg, data: &[u8]) -> Result<(), Error<I2C::Error>> {
let inc: usize = READ_WRITE_LEN;
let mut index: usize = 0;
loop {
if index >= data.len() {
break;
}
let mut buf: [u8; 9] = [0; 9];
buf[0] = reg.into();
buf[1..9].copy_from_slice(&data[index..index + inc]);
let asic_msb: u8 = ((index / 2) >> 4) as u8;
let asic_lsb: u8 = ((index / 2) & 0x0F) as u8;
self.write(&[Reg::Bma4Reserved5BAddr.into(), asic_lsb])?;
self.write(&[Reg::Bma4Reserved5CAddr.into(), asic_msb])?;
self.write(&buf)?;
index += inc;
}
Ok(())
}
#[inline(always)]
pub fn new(i2c: I2C, config: Config) -> Self {
Self::new_with_address(i2c, config, DEFAULT_ADDRESS)
}
pub fn new_with_address(i2c: I2C, config: Config, address: u8) -> Self {
Self {
address,
i2c,
config,
state: Uninitialized,
}
}
pub fn init(
mut self,
delay: &mut impl DelayNs,
) -> Result<Bma423<I2C, FullPower>, Error<I2C::Error>> {
self.write(&[Reg::Command.into(), Command::SoftReset.into()])?;
delay.delay_ms(1);
self.set_power_config(PowerConfigurationFlag::all_bits(), false)?;
delay.delay_us(500);
self.write(&[Reg::StartInitialization.into(), 0])?;
self.stream_write(Reg::FeatureConfig, &config::BMA423_CONFIG_FILE)?;
self.write(&[Reg::StartInitialization.into(), 1u8])?;
let mut time_ms: usize = 200;
while time_ms > 0 && (self.read_register(Reg::InternalStatus)? & 0x0F) != 0x01 {
delay.delay_us(1000);
time_ms -= 1;
}
if time_ms == 0 {
return Err(Error::BadInternal(self.read_register(Reg::InternalStatus)?));
}
let mut driver = Bma423 {
address: self.address,
i2c: self.i2c,
config: self.config,
state: FullPower,
};
driver.set_power_control(PowerControlFlag::Accelerometer)?;
driver.set_accel_config(delay, driver.config)?;
Ok(driver)
}
}
impl<I2C: I2c> Bma423<I2C, FullPower> {
pub fn edit_features(&mut self) -> Result<EditFeatures<'_, I2C>, Error<I2C::Error>> {
let mut register = [0; FEATURE_SIZE + 1];
self.read_register_bytes(Reg::FeatureConfig, &mut register[1..FEATURE_SIZE + 1])?;
Ok(EditFeatures {
register,
driver: self,
})
}
pub fn power_save_mode(self) -> Result<Bma423<I2C, PowerSave>, Error<I2C::Error>> {
let mut driver = Bma423 {
address: self.address,
i2c: self.i2c,
config: self.config,
state: PowerSave,
};
driver.set_power_config(PowerConfigurationFlag::AdvancedPowerSave, true)?;
Ok(driver)
}
}
impl<I2C: I2c> Bma423<I2C, PowerSave> {
pub fn full_power_mode(self) -> Result<Bma423<I2C, FullPower>, Error<I2C::Error>> {
let mut driver = Bma423 {
address: self.address,
i2c: self.i2c,
config: self.config,
state: FullPower,
};
driver.set_power_config(PowerConfigurationFlag::AdvancedPowerSave, false)?;
Ok(driver)
}
}
impl<I2C: I2c, S: Initialized> Bma423<I2C, S> {
pub fn set_power_control(&mut self, value: PowerControlFlag) -> Result<(), Error<I2C::Error>> {
self.write(&[Reg::PowerControl.into(), value.into()])
}
pub fn read_chip_id(&mut self) -> Result<ChipId, Error<I2C::Error>> {
Ok(ChipId::from(self.read_register(Reg::ChipId)?))
}
pub fn set_accel_config(
&mut self,
delay: &mut impl DelayNs,
config: Config,
) -> Result<(), Error<I2C::Error>> {
if config.performance_mode == AccelConfigPerfMode::Continuous {
if (config.bandwidth as u8) > (AccelConfigBandwidth::NormAvg4 as u8) {
return Err(Error::ConfigError);
}
} else if config.performance_mode == AccelConfigPerfMode::CicAvg {
if (config.bandwidth as u8) > (AccelConfigBandwidth::ResAvg128 as u8) {
return Err(Error::ConfigError);
}
} else {
return Err(Error::ConfigError);
}
let accel_config: u8 =
config.sample_rate as u8 | config.bandwidth as u8 | config.performance_mode as u8;
let accel_range: u8 = config.range as u8;
self.write(&[Reg::AccelConfig.into(), accel_config])?;
self.write(&[Reg::AccelRange.into(), accel_range])?;
self.config = config;
delay.delay_ms(50);
Ok(())
}
pub fn set_interrupt_config(
&mut self,
line: InterruptLine,
direction: InterruptDirection,
) -> Result<(), Error<I2C::Error>> {
let reg = direction.bit_mask()
| match direction {
InterruptDirection::Input(tc) => u8::from(tc),
InterruptDirection::Output(ob, l) => u8::from(ob) | u8::from(l),
};
let addr = match line {
InterruptLine::Line1 => Reg::Interrupt1IOCtl,
InterruptLine::Line2 => Reg::Interrupt2IOCtl,
};
self.write(&[addr.into(), reg])?;
Ok(())
}
pub fn map_feature_interrupt(
&mut self,
line: InterruptLine,
interrupts: FeatureInterruptStatus,
enable: bool,
) -> Result<(), Error<I2C::Error>> {
let addr = match line {
InterruptLine::Line1 => Reg::FeatureInterrupt1Mapping,
InterruptLine::Line2 => Reg::FeatureInterrupt2Mapping,
};
let mut reg = self.read_register(addr)?;
if enable {
reg |= u8::from(interrupts);
} else {
reg &= u8::from(interrupts.not());
}
self.write(&[addr.into(), reg])?;
Ok(())
}
pub fn map_hardware_interrupt(
&mut self,
interrupts: HardwareInterruptStatus,
enable: bool,
) -> Result<(), Error<I2C::Error>> {
let mut reg = self.read_register(Reg::HardwareInterruptMapping)?;
if enable {
reg |= u8::from(interrupts);
} else {
reg &= u8::from(interrupts.not());
}
self.write(&[Reg::HardwareInterruptMapping.into(), reg])?;
Ok(())
}
pub fn read_interrupt_status(&mut self) -> Result<InterruptStatus, Error<I2C::Error>> {
let mut data: [u8; 2] = [0; 2];
self.read_register_bytes(Reg::FeatureInterruptStatus, &mut data)?;
Ok(InterruptStatus {
feature: data[0].into(),
hardware: data[1].into(),
})
}
pub fn read_status(&mut self) -> Result<u8, Error<I2C::Error>> {
self.read_register(Reg::Status)
}
pub fn set_fifo_self_wakeup(&mut self, enable: bool) -> Result<(), Error<I2C::Error>> {
self.set_power_config(PowerConfigurationFlag::FifoSelfWakeUp, enable)
}
pub fn accel_norm_int(&mut self) -> Result<(f32, f32, f32), Error<I2C::Error>> {
let mut data: [u8; 6] = [0; 6];
self.read_register_bytes(Reg::AccXLSB, &mut data)?;
let x: i16 = (((data[1] as i16) << 8) | (data[0] as i16)) / 0x10;
let y: i16 = (((data[3] as i16) << 8) | (data[2] as i16)) / 0x10;
let z: i16 = (((data[5] as i16) << 8) | (data[4] as i16)) / 0x10;
let range = self.config.range.as_float();
Ok((
lsb_to_ms2(x, range, 12),
lsb_to_ms2(y, range, 12),
lsb_to_ms2(z, range, 12),
))
}
pub fn accel_abs(&mut self) -> Result<(f32, f32, f32), Error<I2C::Error>> {
let accel = self.accel_norm_int()?;
Ok((
GRAVITY_EARTH * accel.0,
GRAVITY_EARTH * accel.1,
GRAVITY_EARTH * accel.2,
))
}
}
#[cfg(feature = "accel")]
impl<I2C: I2c, S: Initialized> Accelerometer for Bma423<I2C, S> {
type Error = Error<I2C::Error>;
fn accel_norm(&mut self) -> Result<F32x3, accelerometer::Error<Error<I2C::Error>>> {
Ok(self.accel_norm_int()?.into())
}
fn sample_rate(&mut self) -> Result<f32, accelerometer::Error<Error<I2C::Error>>> {
match self.config.sample_rate {
AccelConfigOdr::Odr0p78 => Ok(25.0 / 32.0),
AccelConfigOdr::Odr1p5 => Ok(25.0 / 16.0),
AccelConfigOdr::Odr3p1 => Ok(25.0 / 8.0),
AccelConfigOdr::Odr6p25 => Ok(25.0 / 4.0),
AccelConfigOdr::Odr12p5 => Ok(25.0 / 2.0),
AccelConfigOdr::Odr25 => Ok(25.0),
AccelConfigOdr::Odr50 => Ok(50.0),
AccelConfigOdr::Odr100 => Ok(100.0),
AccelConfigOdr::Odr200 => Ok(200.0),
AccelConfigOdr::Odr400 => Ok(400.0),
AccelConfigOdr::Odr800 => Ok(800.0),
AccelConfigOdr::Odr1k6 => Ok(1600.0),
}
}
}
#[inline(always)]
fn lsb_to_ms2(val: i16, g_range: f32, bit_width: u8) -> f32 {
let half_scale: f32 = (1 << bit_width) as f32 / 2.0;
val as f32 * g_range / half_scale
}