use bitflags::bitflags;
#[derive(Debug)]
pub enum BMA400Error<InterfaceError, PinError> {
IOError(InterfaceError),
ChipSelectPinError(PinError),
ConfigBuildError(ConfigError),
ChipIdReadFailed,
SelfTestFailedError,
}
impl<InterfaceError, PinError> From<ConfigError> for BMA400Error<InterfaceError, PinError> {
fn from(value: ConfigError) -> Self {
Self::ConfigBuildError(value)
}
}
#[derive(Debug)]
pub enum ConfigError {
Filt1InterruptInvalidODR,
TapIntEnabledInvalidODR,
FifoReadWhilePwrDisable,
}
pub struct Status {
bits: u8,
}
impl Status {
pub(crate) fn new(status_byte: u8) -> Self {
Status {
bits: status_byte,
}
}
pub fn drdy_stat(&self) -> bool {
(self.bits & 0b1000_0000) != 0
}
pub fn cmd_rdy(&self) -> bool {
(self.bits & 0b0001_0000) != 0
}
pub fn power_mode(&self) -> PowerMode {
match (self.bits & 0b0000_0110) >> 1 {
0 => PowerMode::Sleep,
1 => PowerMode::LowPower,
_ => PowerMode::Normal,
}
}
pub fn int_active(&self) -> bool {
(self.bits & 0b0000_0001) != 0
}
}
pub enum StepIntStatus {
None,
OneStepDetect,
ManyStepDetect,
}
pub struct IntStatus0 {
bits: u8,
}
impl IntStatus0 {
pub(crate) fn new(status_byte: u8) -> Self {
IntStatus0 {
bits: status_byte,
}
}
pub fn drdy_stat(&self) -> bool {
(self.bits & 0b1000_0000) != 0
}
pub fn fwm_stat(&self) -> bool {
(self.bits & 0b0100_0000) != 0
}
pub fn ffull_stat(&self) -> bool {
(self.bits & 0b0010_0000) != 0
}
pub fn ieng_overrun_stat(&self) -> bool {
(self.bits & 0b0001_0000) != 0
}
pub fn gen2_stat(&self) -> bool {
(self.bits & 0b0000_1000) != 0
}
pub fn gen1_stat(&self) -> bool {
(self.bits & 0b0000_0100) != 0
}
pub fn orientch_stat(&self) -> bool {
(self.bits & 0b0000_0010) != 0
}
pub fn wkup_stat(&self) -> bool {
(self.bits & 0b0000_0001) != 0
}
}
pub struct IntStatus1 {
bits: u8,
}
impl IntStatus1 {
pub(crate) fn new(status_byte: u8) -> Self {
IntStatus1 {
bits: status_byte,
}
}
pub fn ieng_overrun_stat(&self) -> bool {
(self.bits & 0b0001_0000) != 0
}
pub fn d_tap_stat(&self) -> bool {
(self.bits & 0b0000_1000) != 0
}
pub fn s_tap_stat(&self) -> bool {
(self.bits & 0b0000_0100) != 0
}
pub fn step_int_stat(&self) -> StepIntStatus {
match self.bits & 0b0000_0011 {
0x00 => StepIntStatus::None,
0x01 => StepIntStatus::OneStepDetect,
_ => StepIntStatus::ManyStepDetect,
}
}
}
pub struct IntStatus2 {
bits: u8,
}
impl IntStatus2 {
pub(crate) fn new(status_byte: u8) -> Self {
IntStatus2 {
bits: status_byte,
}
}
pub fn ieng_overrun_stat(&self) -> bool {
(self.bits & 0b0001_0000) != 0
}
pub fn actch_z_stat(&self) -> bool {
(self.bits & 0b0000_0100) != 0
}
pub fn actch_y_stat(&self) -> bool {
(self.bits & 0b0000_0010) != 0
}
pub fn actch_x_stat(&self) -> bool {
(self.bits & 0b0000_0001) != 0
}
}
#[derive(Debug)]
pub struct Measurement {
pub x: i16,
pub y: i16,
pub z: i16,
}
impl Measurement {
fn new(x: i16, y: i16, z: i16) -> Self {
Measurement {
x,
y,
z,
}
}
pub(crate) fn from_bytes_unscaled(bytes: &[u8]) -> Self {
Self::new(
Self::to_i16(bytes[0], bytes[1]),
Self::to_i16(bytes[2], bytes[3]),
Self::to_i16(bytes[4], bytes[5]),
)
}
pub(crate) fn from_bytes_scaled(scale: Scale, bytes: &[u8]) -> Self {
let shift = match scale {
Scale::Range2G => 0,
Scale::Range4G => 1,
Scale::Range8G => 2,
Scale::Range16G => 3,
};
Self::new(
Self::to_i16(bytes[0], bytes[1]) << shift,
Self::to_i16(bytes[2], bytes[3]) << shift,
Self::to_i16(bytes[4], bytes[5]) << shift,
)
}
fn to_i16(lsb: u8, msb: u8) -> i16 {
let clear_rsvd_bits = msb & 0x0F;
i16::from_le_bytes([
lsb,
if (clear_rsvd_bits >> 3) == 0u8 {
clear_rsvd_bits
} else {
clear_rsvd_bits | 0xF0
},
])
}
}
pub enum InterruptPins {
None,
Int1,
Int2,
Both,
}
pub enum PinOutputLevel {
ActiveLow,
ActiveHigh,
}
pub enum PinOutputConfig {
PushPull(PinOutputLevel),
OpenDrain(PinOutputLevel),
}
pub enum Scale {
Range2G = 0x00,
Range4G = 0x01,
Range8G = 0x02,
Range16G = 0x03,
}
#[derive(Debug)]
pub enum DataSource {
AccFilt1,
AccFilt2,
AccFilt2Lp,
}
pub enum Filter1Bandwidth {
High,
Low,
}
pub enum OutputDataRate {
Hz12_5,
Hz25,
Hz50,
Hz100,
Hz200,
Hz400,
Hz800,
}
#[derive(Debug)]
pub enum OversampleRate {
OSR0,
OSR1,
OSR2,
OSR3,
}
pub enum PowerMode {
Sleep,
LowPower,
Normal,
}
pub enum Axis {
X,
Y,
Z,
}
#[derive(Debug)]
pub enum Activity {
Still,
Walk,
Run,
}
#[derive(Debug, PartialEq)]
pub struct Frame<'a> {
slice: &'a [u8],
}
impl<'a> Frame<'a> {
pub fn frame_type(&self) -> FrameType {
Header::from_bits_truncate(self.slice[0]).frame_type()
}
pub fn x(&self) -> Option<i16> {
let header = Header::from_bits_truncate(self.slice[0]);
if !matches!(header.frame_type(), FrameType::Data) || !header.has_x_data() {
return None;
}
Some(self.data_at_offset(0, header.resolution_is_12bit()))
}
pub fn y(&self) -> Option<i16> {
let header = Header::from_bits_truncate(self.slice[0]);
if !matches!(header.frame_type(), FrameType::Data) || !header.has_y_data() {
return None;
}
let offset = if header.has_x_data() {
1
} else {
0
};
Some(self.data_at_offset(offset, header.resolution_is_12bit()))
}
pub fn z(&self) -> Option<i16> {
let header = Header::from_bits_truncate(self.slice[0]);
if !matches!(header.frame_type(), FrameType::Data) || !header.has_z_data() {
return None;
}
let offset = if header.has_x_data() {
1
} else {
0
} + if header.has_y_data() {
1
} else {
0
};
Some(self.data_at_offset(offset, header.resolution_is_12bit()))
}
pub fn time(&self) -> Option<u32> {
if !matches!(self.frame_type(), FrameType::Time) {
return None;
}
Some(u32::from_le_bytes([self.slice[1], self.slice[2], self.slice[3], 0]))
}
pub fn fifo_src_chg(&self) -> Option<bool> {
if let FrameType::Control = self.frame_type() {
Some(self.slice[1] & 0b0010 != 0)
} else {
None
}
}
pub fn filt1_bw_chg(&self) -> Option<bool> {
if let FrameType::Control = self.frame_type() {
Some(self.slice[1] & 0b0100 != 0)
} else {
None
}
}
pub fn acc1_chg(&self) -> Option<bool> {
if let FrameType::Control = self.frame_type() {
Some(self.slice[1] & 0b1000 != 0)
} else {
None
}
}
fn data_at_offset(&self, offset: usize, resolution_is_12bit: bool) -> i16 {
let (lsb, msb);
if resolution_is_12bit {
lsb = (self.slice[offset * 2 + 1] & 0xF) | (self.slice[offset * 2 + 2] << 4);
msb = self.slice[offset * 2 + 2] >> 4;
} else {
lsb = self.slice[offset + 1] << 4;
msb = self.slice[offset + 1] >> 4;
}
i16::from_le_bytes([
lsb,
if (msb >> 3) == 0u8 {
msb
} else {
msb | 0xF0
},
])
}
}
pub enum FrameType {
Data,
Time,
Control,
}
#[derive(Debug)]
pub struct FifoFrames<'a> {
index: usize,
bytes: &'a [u8],
}
impl<'a> FifoFrames<'a> {
pub(crate) fn new(bytes: &[u8]) -> FifoFrames {
FifoFrames {
index: 0,
bytes,
}
}
}
impl<'a> Iterator for FifoFrames<'a> {
type Item = Frame<'a>;
fn next(&mut self) -> Option<Self::Item> {
if self.index >= self.bytes.len() {
return None;
}
let header_idx = self.index;
let header = Header::from_bits_truncate(self.bytes[header_idx]);
if matches!(header.frame_type(), FrameType::Data) && !header.has_data() {
self.index += 2;
return None;
}
self.index += header.num_payload_bytes() + 1;
if self.index > self.bytes.len() {
return None;
}
Some(Frame {
slice: &self.bytes[header_idx..self.index],
})
}
}
bitflags! {
struct Header: u8 {
const FH_MODE1 = 0b1000_0000;
const FH_MODE0 = 0b0100_0000;
const FH_PARAM4 = 0b0010_0000;
const FH_PARAM3 = 0b0001_0000;
const FH_PARAM2 = 0b0000_1000;
const FH_PARAM1 = 0b0000_0100;
const FH_PARAM0 = 0b0000_0010;
const TIME = Self::FH_MODE1.bits | Self::FH_PARAM4.bits;
const RESOLUTION = Self::FH_PARAM3.bits;
const AXES = Self::FH_PARAM2.bits | Self::FH_PARAM1.bits | Self::FH_PARAM0.bits;
}
}
impl Header {
pub const fn frame_type(&self) -> FrameType {
if self.contains(Self::TIME) {
FrameType::Time
} else if self.intersects(Self::FH_MODE0) {
FrameType::Control
} else {
FrameType::Data
}
}
pub const fn resolution_is_12bit(&self) -> bool {
match self.frame_type() {
FrameType::Data => self.intersects(Self::RESOLUTION),
_ => false,
}
}
pub const fn has_data(&self) -> bool {
match self.frame_type() {
FrameType::Data => self.intersects(Self::AXES),
_ => false,
}
}
pub const fn num_payload_bytes(&self) -> usize {
match self.frame_type() {
FrameType::Time => 3,
FrameType::Data => {
if !self.has_data() {
return 1;
}
let mut n = self.intersection(Self::AXES).bits();
let mut num_axes = 0;
while n != 0 {
n &= n - 1;
num_axes += 1;
}
if self.resolution_is_12bit() {
num_axes * 2
} else {
num_axes
}
}
FrameType::Control => 1,
}
}
pub const fn has_x_data(&self) -> bool {
match self.frame_type() {
FrameType::Data => self.intersects(Self::FH_PARAM0),
_ => false,
}
}
pub const fn has_y_data(&self) -> bool {
match self.frame_type() {
FrameType::Data => self.intersects(Self::FH_PARAM1),
_ => false,
}
}
pub const fn has_z_data(&self) -> bool {
match self.frame_type() {
FrameType::Data => self.intersects(Self::FH_PARAM2),
_ => false,
}
}
}
pub enum AutoLPTimeoutTrigger {
TimeoutDisabled,
TimeoutEnabledNoReset,
TimeoutEnabledGen2IntReset,
}
pub enum WakeupIntRefMode {
Manual,
OneTime,
EveryTime,
}
pub enum OrientIntRefMode {
Manual,
AccFilt2,
AccFilt2Lp,
}
pub enum ActChgObsPeriod {
Samples32,
Samples64,
Samples128,
Samples256,
Samples512,
}
pub enum TapSensitivity {
SENS0,
SENS1,
SENS2,
SENS3,
SENS4,
SENS5,
SENS6,
SENS7,
}
pub enum MinTapDuration {
Samples4,
Samples8,
Samples12,
Samples16,
}
pub enum DoubleTapDuration {
Samples60,
Samples80,
Samples100,
Samples120,
}
pub enum MaxTapDuration {
Samples6,
Samples9,
Samples12,
Samples18,
}
pub enum GenIntRefMode {
Manual,
OneTime,
EveryTimeFromSrc,
EveryTimeFromLp,
}
pub enum Hysteresis {
None,
Hyst24mg,
Hyst48mg,
Hyst96mg,
}
pub enum GenIntCriterionMode {
Inactivity,
Activity,
}
pub enum GenIntLogicMode {
Or,
And,
}