Skip to main content

lh_adxl345/
lib.rs

1//! Adxl 345 Accelerometer Driver
2//!
3//! Using embedded-hal
4//! Supports I2C / SPI communications though a RegisterBus Trait
5//!
6//! Reference: <https://www.analog.com/media/en/technical-documentation/data-sheets/adxl345.pdf>
7
8//! ## Example I2C:
9//!
10//! ```rust
11//! 
12//! use lh_adxl345 as adxl;
13//! ...
14//!
15//! // RP2040 Hal Boilerplate
16//! ...
17//!
18//! // Setting I2C Pins
19//! let sda_pin: gpio::Pin<_, gpio::FunctionI2c, _> = pins.gpio4.reconfigure();
20//! let scl_pin: gpio::Pin<_, gpio::FunctionI2c, _> = pins.gpio5.reconfigure();
21//!
22//! // HAL I2C0 Init
23//! let i2c = hal::I2C::i2c0(
24//!     pac.I2C0,
25//!     sda_pin,
26//!     scl_pin,
27//!     400.kHz(),
28//!     &mut pac.RESETS,
29//!     &clocks.system_clock,
30//! );
31//!
32//! // Adxl Bus
33//! let adxlbus = adxl::AdxlBusI2c {
34//!     i2c:  i2c,
35//!     addr: adxl::reg::ADXL_ADDR,
36//! };
37//!
38//! // Adxl Device
39//! let mut adxl = adxl::Adxl345::new(adxlbus);
40//!
41//! // Adxl Init
42//! if let Err(e) = adxl.init_defaults() {
43//!     println!("Init Err: {}", e);
44//!     return;
45//! }
46//!
47//! // Adxl Calibrate
48//! println!("\n Running Calibration ...");
49//! match adxl.calibrate_axis_offsets() {
50//!     Ok((x,y,z)) => {
51//!         println!("Calibration values | X: {x} | Y: {y} | Z: {z} |\n");
52//!     }
53//!     Err(e) => {
54//!         println!("Err: {}", e);
55//!     }
56//! }
57//!
58//! // Read Axis in m/s^2
59//! match adxl.read_axis() {
60//!     Ok((x, y, z)) => println!("Axis | X: {x:6.3} | Y: {y:6.3} | Z: {z:6.3} |"),
61//!     Err(e) => println!("Err: {}", e),
62//! }
63//!
64//! // Read Axis in raw LSB units
65//! match adxl.read_axis_lsb_units() {
66//!     Ok((x, y, z)) => println!("Axis | X: {x:6} | Y: {y:6} | Z: {z:6} |"),
67//!     Err(e) => println!("Err: {}", e),
68//! }
69//! ```
70
71//! Example SPI Setup:
72//!
73//! ``` rust
74//! 
75//! use lh_adxl345 as adxl;
76//! ...
77//!
78//! // RP2040 Hal Boilerplate
79//! ...
80//!
81//! // ADXL Four wire SPI wiring:
82//! // SCL >> CLK
83//! // SDA >> MOSI
84//! // SDO >> MISO
85//! // CS  >> GPIO | Note: For power-up in SPI Mode, the ADXL CS Pin needs to be pulled to GND with a resistor.
86//!
87//! // SPI Pins
88//! let spi_mosi = pins.gpio19.into_function::<hal::gpio::FunctionSpi>();
89//! let spi_miso = pins.gpio16.into_function::<hal::gpio::FunctionSpi>();
90//! let spi_sclk = pins.gpio18.into_function::<hal::gpio::FunctionSpi>();
91//! let spi_cs   = pins.gpio17.into_push_pull_output_in_state(gpio::PinState::High);
92//!
93//! // HAL SPI Bus Init
94//! let spi = hal::spi::Spi::<_, _, _, 8>::new(pac.SPI0, (spi_mosi, spi_miso, spi_sclk));
95//! let spi = spi.init(
96//!     &mut pac.RESETS,
97//!     clocks.peripheral_clock.freq(),
98//!     2.MHz(),
99//!     embedded_hal::spi::MODE_3,
100//! );
101//!
102//! // SPI Adxl Bus
103//! let adxlbus = adxl::AdxlBusSpi {
104//!     spi:    spi,
105//!     spi_cs: spi_cs,
106//! };
107//!
108//! // Adxl Device
109//! let mut adxl = adxl::Adxl345::new(adxlbus);
110//!
111//! ...
112//! ```
113
114#![no_std]
115#![allow(clippy::too_many_arguments)]
116
117pub mod reg;
118mod reg_helper;
119
120use core::fmt::{Debug, Display};
121use core::result::Result;
122
123pub use reg_helper::{ErrorReg, RegisterBus};
124
125use embedded_hal::digital::StatefulOutputPin;
126use embedded_hal::{i2c, spi};
127
128// —————————————————————————————————————————————————————————————————————————————————————————————————
129//                                             Globals
130// —————————————————————————————————————————————————————————————————————————————————————————————————
131
132/// Gravity m/s^2
133pub const G: f32 = 9.80665; // Gravity m/s^2
134
135/// Temp write buffer for appending reg addr.
136pub const MAX_BUF_LEN: usize = 16;
137
138/// Resolution
139#[derive(Debug, Clone, Copy, PartialEq)]
140#[repr(u8)]
141pub enum Res {
142    Bit10 = 0,
143    Full  = 1,
144}
145
146/// Justify
147#[derive(Debug, Clone, Copy, PartialEq)]
148#[repr(u8)]
149pub enum Justify {
150    Right = 0,
151    Left  = 1,
152}
153
154/// Range Bits
155#[derive(Debug, Clone, Copy, PartialEq)]
156#[repr(u8)]
157pub enum Range {
158    R2G  = 0b_00,
159    R4G  = 0b_01,
160    R8G  = 0b_10,
161    R16G = 0b_11,
162}
163
164/// Unit Scale Factor
165#[derive(Debug, Clone, Copy, PartialEq)]
166#[repr(u16)]
167pub enum Unit {
168    U2G  = 256,
169    U4G  = 128,
170    U8G  = 64,
171    U16G = 32,
172}
173
174/// Wakeup Bits
175#[derive(Debug, Clone, Copy, PartialEq)]
176#[repr(u8)]
177pub enum Wakeup {
178    Hz8 = 0b_00,
179    Hz4 = 0b_01,
180    Hz2 = 0b_10,
181    Hz1 = 0b_11,
182}
183
184/// Fifo Mode
185#[derive(Debug, Clone, Copy, PartialEq)]
186#[repr(u8)]
187pub enum FifoMode {
188    Bypass  = 0b_00,
189    Fifo    = 0b_01,
190    Stream  = 0b_10,
191    Trigger = 0b_11,
192}
193
194/// Data Rate
195#[derive(Debug, Clone, Copy, PartialEq)]
196#[repr(u8)]
197pub enum Rate {
198    R3200 = 0b_1111,
199    R1600 = 0b_1110,
200    R800  = 0b_1101,
201    R400  = 0b_1100,
202    R200  = 0b_1011,
203    R100  = 0b_1010,
204    R50   = 0b_1001,
205    R25   = 0b_1000,
206    R12_5 = 0b_0111,
207    R6_25 = 0b_0110,
208    R3_13 = 0b_0101,
209    R1_56 = 0b_0100,
210    R0_78 = 0b_0011,
211    R0_39 = 0b_0010,
212    R0_20 = 0b_0001,
213    R0_10 = 0b_0000,
214}
215
216/// Interrupt Source
217#[derive(Debug, Clone, Copy, PartialEq)]
218pub struct IntSource {
219    pub data_ready: bool,
220    pub single_tap: bool,
221    pub double_tap: bool,
222    pub activity:   bool,
223    pub inactivity: bool,
224    pub free_fall:  bool,
225    pub watermark:  bool,
226    pub overrun:    bool,
227}
228
229pub type AdxlResult<T, B> = Result<T, Error<ErrorReg<<B as RegisterBus>::Error>>>;
230
231// —————————————————————————————————————————————————————————————————————————————————————————————————
232//                                              Adxl
233// —————————————————————————————————————————————————————————————————————————————————————————————————
234
235pub struct AdxlBusI2c<I2C> {
236    pub i2c:  I2C,
237    pub addr: u8,
238}
239
240pub struct AdxlBusSpi<SPI, CS> {
241    pub spi:    SPI,
242    pub spi_cs: CS,
243}
244
245pub struct Adxl345<B> {
246    bus:     B,
247    res:     Res,
248    justify: Justify,
249    range:   Range,
250    unit:    Unit,
251}
252
253impl<B: RegisterBus> Adxl345<B> {
254    pub fn new(bus: B) -> Self {
255        Self {
256            bus,
257            res: Res::Full,
258            justify: Justify::Right,
259            range: Range::R16G,
260            unit: Unit::U2G,
261        }
262    }
263
264    // —————————————————————————————————————————— Init —————————————————————————————————————————————
265
266    /// Resets the registers to the datasheet values, excluding axes offsets.
267    pub fn reset(&mut self) -> AdxlResult<(), B> {
268        // Reset POWER CTL
269        self.write_reg_addr_raw(0x2D, &[0x00])?;
270        // Reset THRESH TAP
271        self.write_reg_addr_raw(0x1D, &[0x00])?;
272
273        // Reset range registers to 0x00
274        for reg in 0x21..=0x2B {
275            self.write_reg_addr_raw(reg, &[0x00])?;
276        }
277
278        // BW_RATE set to 0x0A (100Hz)
279        self.write_reg_addr_raw(0x2C, &[0x0A])?;
280
281        // Remaining registers
282        for reg in 0x2E..=0x31 {
283            self.write_reg_addr_raw(reg, &[0x00])?;
284        }
285
286        // FIFO_CTL
287        self.write_reg_addr_raw(0x38, &[0x00])?;
288        Ok(())
289    }
290
291    /// Initalize some sane defaults and validates the device id.
292    pub fn init_defaults(&mut self) -> AdxlResult<(), B> {
293        // Validate Device ID
294        let id = reg::DEVID.read(&mut self.bus)?;
295        if reg::ADXL_DEVICE_ID != id as u8 {
296            return Err(Error::Init);
297        }
298
299        self.reset()?;
300
301        // Fifo:  Stream | Trigger 0 | Watermark Samples
302        self.set_fifo_ctl(FifoMode::Stream, false, 24)?;
303
304        // Set Rate
305        self.set_rate(Rate::R800)?;
306
307        // Data Format
308        self.set_data_format(Res::Full, Justify::Right, Range::R16G)?;
309
310        // Start measuring
311        self.set_measure(true)?;
312
313        Ok(())
314    }
315
316    // ——————————————————————————————————————————— Raw —————————————————————————————————————————————
317
318    /// Raw register read
319    pub fn read_reg_addr_raw(&mut self, reg_addr: u8, buf: &mut [u8]) -> AdxlResult<(), B> {
320        self.bus
321            .read_register(reg_addr, buf)
322            .map_err(ErrorReg::Bus)?;
323        Ok(())
324    }
325
326    /// Raw register write    
327    pub fn write_reg_addr_raw(&mut self, reg_addr: u8, buf: &[u8]) -> AdxlResult<(), B> {
328        self.bus
329            .write_register(reg_addr, buf)
330            .map_err(ErrorReg::Bus)?;
331        Ok(())
332    }
333
334    // —————————————————————————————————————————— Read —————————————————————————————————————————————
335
336    /// Device ID
337    pub fn read_device_id(&mut self) -> AdxlResult<u8, B> {
338        Ok(reg::DEVID.read(&mut self.bus)? as u8)
339    }
340
341    /// Fifo Status: Trigger
342    pub fn read_fifo_status_trig(&mut self) -> AdxlResult<u8, B> {
343        Ok(reg::FIFO_STATUS.read_field(&mut self.bus, reg::FL_FS::FIFO_TRIG)? as u8)
344    }
345
346    /// Fifo Status: Num of samples available in the FIFO buffer
347    pub fn read_fifo_status_entries(&mut self) -> AdxlResult<u8, B> {
348        Ok(reg::FIFO_STATUS.read_field(&mut self.bus, reg::FL_FS::ENTRIES)? as u8)
349    }
350
351    /// Read XYZ Axis data as raw byte buffer
352    #[inline]
353    pub fn read_axis_raw_buf(&mut self) -> AdxlResult<[u8; 6], B> {
354        let mut buf = [0_u8; 6];
355        reg::DATAX0.read_raw_buffer(&mut self.bus, &mut buf)?;
356
357        Ok(buf)
358    }
359
360    /// Read XYZ Axis as a tuple of i16 values in LSB units
361    pub fn read_axis_lsb_units(&mut self) -> AdxlResult<(i16, i16, i16), B> {
362        // Burst read 6 bytes starting from DATAX0
363        let buf = self.read_axis_raw_buf()?;
364
365        // Extracting data
366        let mut x = i16::from_le_bytes([buf[0], buf[1]]);
367        let mut y = i16::from_le_bytes([buf[2], buf[3]]);
368        let mut z = i16::from_le_bytes([buf[4], buf[5]]);
369
370        // Handle left justified data format
371        if self.justify == Justify::Left {
372            let shift: u8 = {
373                match (self.res, self.range) {
374                    (_, Range::R2G) => 6,
375                    (Res::Bit10, _) => 6,
376                    (Res::Full, Range::R4G) => 5,
377                    (Res::Full, Range::R8G) => 4,
378                    (Res::Full, Range::R16G) => 3,
379                }
380            };
381
382            x >>= shift;
383            y >>= shift;
384            z >>= shift;
385        }
386
387        Ok((x, y, z))
388    }
389
390    /// Read XYZ Axis as a tuple of f32 values as Acceleration units in m/s^2
391    pub fn read_axis(&mut self) -> AdxlResult<(f32, f32, f32), B> {
392        let data = self.read_axis_lsb_units()?;
393
394        // Convert to m/s²
395        Ok(self.convert_lsb_to_accel(data))
396    }
397
398    /// Convert from LSB to Acceleration units in m/s^s
399    #[inline]
400    pub fn convert_lsb_to_accel(&self, data: (i16, i16, i16)) -> (f32, f32, f32) {
401        // Convert to m/s²
402        (
403            data.0 as f32 / (self.unit as u16 as f32) * G,
404            data.1 as f32 / (self.unit as u16 as f32) * G,
405            data.2 as f32 / (self.unit as u16 as f32) * G,
406        )
407    }
408
409    /// Read ACT_TAP_STATUS: Asleep
410    pub fn is_asleep(&mut self) -> AdxlResult<bool, B> {
411        Ok(reg::ACT_TAP_STATUS.read_field(&mut self.bus, reg::FL_ATS::ASLEEP)? as u8 != 0)
412    }
413
414    /// Read ACT_TAP_STATUS: ACT (X Y Z)
415    pub fn read_act_source_status(&mut self) -> AdxlResult<(bool, bool, bool), B> {
416        let mut buf = [0_u32; 3];
417
418        reg::ACT_TAP_STATUS.read_multiple_fields(
419            &mut self.bus,
420            &[
421                reg::FL_ATS::ACT_X_SRC,
422                reg::FL_ATS::ACT_Y_SRC,
423                reg::FL_ATS::ACT_Z_SRC,
424            ],
425            &mut buf,
426        )?;
427
428        let (x, y, z) = (buf[0] != 0, buf[1] != 0, buf[2] != 0);
429
430        Ok((x, y, z))
431    }
432
433    /// Read ACT_TAP_STATUS: TAP (X Y Z)
434    pub fn read_tap_source_status(&mut self) -> AdxlResult<(bool, bool, bool), B> {
435        let mut buf = [0_u32; 3];
436
437        reg::ACT_TAP_STATUS.read_multiple_fields(
438            &mut self.bus,
439            &[
440                reg::FL_ATS::TAP_X_SRC,
441                reg::FL_ATS::TAP_Y_SRC,
442                reg::FL_ATS::TAP_Z_SRC,
443            ],
444            &mut buf,
445        )?;
446
447        let (x, y, z) = (buf[0] != 0, buf[1] != 0, buf[2] != 0);
448
449        Ok((x, y, z))
450    }
451
452    #[rustfmt::skip]
453    /// Read INT_SOURCE:
454    /// (data_ready, single_tap, double_tap, activity, inactivity, free_fall, watermark, overrun)
455    /// Interrupts are cleared once reading this register, hence why they are provided all at once!
456    /// Output is a IntSource struct holding the values of the respective fields
457    pub fn read_int_source(&mut self) -> AdxlResult<IntSource, B> {
458        let value = reg::INT_SOURCE.read(&mut self.bus)? as u8;
459
460        let data_ready = (value & 0b_1000_0000) != 0;
461        let single_tap = (value & 0b_0100_0000) != 0;
462        let double_tap = (value & 0b_0010_0000) != 0;
463        let activity   = (value & 0b_0001_0000) != 0;
464        let inactivity = (value & 0b_0000_1000) != 0;
465        let free_fall  = (value & 0b_0000_0100) != 0;
466        let watermark  = (value & 0b_0000_0010) != 0;
467        let overrun    = (value & 0b_0000_0001) != 0;
468
469        Ok(IntSource {
470            data_ready,
471            single_tap,
472            double_tap,
473            activity,
474            inactivity,
475            free_fall,
476            watermark,
477            overrun,
478        })
479    }
480
481    // ——————————————————————————————————————————— Set —————————————————————————————————————————————
482
483    /// MEASURE flag has to be set to 1 to start collecting data
484    pub fn set_measure(&mut self, on: bool) -> AdxlResult<(), B> {
485        reg::POWER_CTL.write_field(&mut self.bus, reg::FL_PC::MEASURE, on as u32)?;
486        Ok(())
487    }
488
489    // #[rustfmt::skip]
490    /// Set POWER CTL register
491    pub fn set_power_ctl(
492        &mut self,
493        link: bool,
494        auto_sleep: bool,
495        measure: bool,
496        sleep: bool,
497        wakeup: Wakeup,
498    ) -> AdxlResult<(), B> {
499        let value = (link as u8) << 5
500            | (auto_sleep as u8) << 4
501            | (measure as u8) << 3
502            | (sleep as u8) << 2
503            | (wakeup as u8);
504
505        reg::POWER_CTL.write(&mut self.bus, value as u32)?;
506        Ok(())
507    }
508
509    /// Set BW_RATE - Low Power mode
510    pub fn set_low_power(&mut self, on: bool) -> AdxlResult<(), B> {
511        reg::BW_RATE.write_field(&mut self.bus, reg::FL_BR::LOW_POWER, on as u32)?;
512        Ok(())
513    }
514
515    /// Set BW_RATE - RATE Code
516    pub fn set_rate(&mut self, rate: Rate) -> AdxlResult<(), B> {
517        reg::BW_RATE.write_field(&mut self.bus, reg::FL_BR::RATE, rate as u32)?;
518        Ok(())
519    }
520
521    /// Start or stop DATA_FORMAT - SELF TEST
522    pub fn set_self_test(&mut self, on: bool) -> AdxlResult<(), B> {
523        reg::DATA_FORMAT.write_field(&mut self.bus, reg::FL_DF::SELF_TEST, on as u32)?;
524        Ok(())
525    }
526
527    /// Set DATA FORMAT register
528    pub fn set_data_format(
529        &mut self,
530        res: Res,
531        justify: Justify,
532        range: Range,
533    ) -> AdxlResult<(), B> {
534        //
535        reg::DATA_FORMAT.write_multiple_fields(&mut self.bus, &[
536            (reg::FL_DF::FULL_RES, res as u32),
537            (reg::FL_DF::JUSTIFY, justify as u32),
538            (reg::FL_DF::RANGE, range as u32),
539        ])?;
540
541        self.res = res;
542        self.justify = justify;
543        self.range = range;
544
545        self.unit = {
546            match (res, range) {
547                (Res::Full, _) => Unit::U2G,
548                (Res::Bit10, Range::R2G) => Unit::U2G,
549                (Res::Bit10, Range::R4G) => Unit::U4G,
550                (Res::Bit10, Range::R8G) => Unit::U8G,
551                (Res::Bit10, Range::R16G) => Unit::U16G,
552            }
553        };
554
555        Ok(())
556    }
557
558    /// Set SPI wire mode: three wires (true) / four wires(false - default)
559    pub fn set_spi_wire_mode(&mut self, three_wire_mode: bool) -> AdxlResult<(), B> {
560        reg::DATA_FORMAT.write_field(&mut self.bus, reg::FL_DF::SPI, three_wire_mode as u32)?;
561        Ok(())
562    }
563
564    /// Set FIFO CTL register
565    pub fn set_fifo_ctl(
566        &mut self,
567        fifo_mode: FifoMode,
568        trigger: bool,
569        watermark_samples_num: u8,
570    ) -> AdxlResult<(), B> {
571        let watermark_samples_num = watermark_samples_num.min(31);
572
573        reg::FIFO_CTL.write_multiple_fields(&mut self.bus, &[
574            (reg::FL_FC::FIFO_MODE, fifo_mode as u32),
575            (reg::FL_FC::TRIGGER, trigger as u32),
576            (reg::FL_FC::SAMPLES, watermark_samples_num as u32),
577        ])?;
578
579        Ok(())
580    }
581
582    /// Calibrates the built-in axis offsets.
583    /// Returns the calibration values for reference.
584    /// Runs `init_defaults` after completion.
585    ///
586    /// Procedure: Place the ADXL level with the Z axis oriented downwards and run this function to calibrate.
587    pub fn calibrate_axis_offsets(&mut self) -> AdxlResult<(i8, i8, i8), B> {
588        // Init
589        self.set_measure(false)?;
590        self.clear_axis_offsets()?;
591        self.set_fifo_ctl(FifoMode::Stream, false, 24)?;
592        self.set_rate(Rate::R400)?;
593        self.set_data_format(Res::Full, Justify::Right, Range::R16G)?;
594
595        // Start measuring
596        self.set_measure(true)?;
597
598        // Buffer
599        let mut buf = [(0_i16, 0_i16, 0_i16); 32];
600
601        // Flushing buffer
602        for i in 0..32 {
603            buf[i] = self.read_axis_lsb_units()?;
604        }
605
606        // Waiting for buffer to fill to read the contents
607        loop {
608            match self.read_fifo_status_entries() {
609                Ok(s) if s >= 31 => {
610                    // Reading values
611                    for el in buf.iter_mut() {
612                        *el = self.read_axis_lsb_units()?;
613                    }
614                    break;
615                }
616                Err(e) => {
617                    return Err(e);
618                }
619                _ => {
620                    continue;
621                }
622            }
623        }
624
625        // Finding means
626        let sum_x: i32 = buf.iter().map(|&(x, ..)| x as i32).sum();
627        let sum_y: i32 = buf.iter().map(|&(_, y, _)| y as i32).sum();
628        let sum_z: i32 = buf.iter().map(|&(_, _, z)| z as i32).sum();
629
630        let n = buf.len() as i32;
631        let m_x = (sum_x + n / 2) / n; // Integer rounding
632        let m_y = (sum_y + n / 2) / n;
633        let m_z = (sum_z + n / 2) / n - self.unit as i32; // Removing 1g
634
635        let x_off = -(m_x / 4) as i8;
636        let y_off = -(m_y / 4) as i8;
637        let z_off = -(m_z / 4) as i8;
638
639        // Writing offsets
640        self.set_axis_offsets(x_off, y_off, z_off)?;
641
642        // Restoring Defaults
643        self.init_defaults()?;
644
645        Ok((x_off, y_off, z_off))
646    }
647
648    /// Set the X Y Z axis offsets
649    /// Can run `calibrate_axis_offsets()` for auto offset calibration
650    ///
651    /// User-set offset adjustments. Signed with a scale
652    /// factor of 15.6 mg/LSB (that is, 0x7F = 2 g). The value stored in
653    /// the offset registers is automatically added to the acceleration data
654    #[inline]
655    pub fn set_axis_offsets(&mut self, x: i8, y: i8, z: i8) -> AdxlResult<(), B> {
656        reg::OFSX.write(&mut self.bus, x as u32)?;
657        reg::OFSY.write(&mut self.bus, y as u32)?;
658        reg::OFSZ.write(&mut self.bus, z as u32)?;
659        Ok(())
660    }
661
662    /// Clear the X Y Z axis offsets
663    #[inline]
664    pub fn clear_axis_offsets(&mut self) -> AdxlResult<(), B> {
665        self.set_axis_offsets(0, 0, 0)?;
666        Ok(())
667    }
668
669    /// Set THRESH_TAP. Unsigned with a scale factor of 62.5 mg/LSB (that is, 0xFF = 16 g)
670    /// A value of 0 may result in undesirable behavior if
671    /// single tap/double tap interrupts are enabled.
672    pub fn set_thresh_tap(&mut self, val: u8) -> AdxlResult<(), B> {
673        reg::THRESH_TAP.write(&mut self.bus, val as u32)?;
674        Ok(())
675    }
676
677    /// Set DUR.
678    /// Unsigned time value representing the maximum time that an event must be
679    /// above the THRESH_TAP threshold to qualify as a tap event. The scale factor
680    /// is 625 µs/LSB. A value of 0 disables the single tap/ double tap
681    /// functions.
682    pub fn set_duration(&mut self, val: u8) -> AdxlResult<(), B> {
683        reg::DUR.write(&mut self.bus, val as u32)?;
684        Ok(())
685    }
686
687    /// Set LATENT.
688    /// Unsigned time value representing the wait time from the detection of a tap event to the
689    /// start of the time window (defined by the window register) during
690    /// which a possible second tap event can be detected. The scale
691    /// factor is 1.25 ms/LSB. A value of 0 disables the double tap function.
692    pub fn set_latent(&mut self, val: u8) -> AdxlResult<(), B> {
693        reg::LATENT.write(&mut self.bus, val as u32)?;
694        Ok(())
695    }
696
697    /// Set WINDOW.
698    /// Unsigned time value representing the amount of time after the expiration of the
699    /// latency time (determined by the latent register) during which a
700    /// second valid tap can begin. The scale factor is 1.25 ms/LSB. A
701    /// value of 0 disables the double tap function.
702    /// A value of 0 disables the double tap function.
703    pub fn set_window(&mut self, val: u8) -> AdxlResult<(), B> {
704        reg::WINDOW.write(&mut self.bus, val as u32)?;
705        Ok(())
706    }
707
708    /// Set THRESH_ACT.
709    /// Threshold value for detecting activity.
710    /// Unsigned, so the magnitude of the activity event is compared with the value in the
711    /// THRESH_ACT register. The scale factor is 62.5 mg/LSB. A value
712    /// of 0 may result in undesirable behavior if the activity interrupt is enabled.
713    pub fn set_thresh_act(&mut self, val: u8) -> AdxlResult<(), B> {
714        reg::THRESH_ACT.write(&mut self.bus, val as u32)?;
715        Ok(())
716    }
717
718    /// Set THRESH_INACT.
719    /// Threshold value for detecting inactivity.
720    /// Unsigned, so the magnitude of the activity event is compared with the value in the
721    /// THRESH_ACT register. The scale factor is 62.5 mg/LSB. A value
722    /// of 0 may result in undesirable behavior if the activity interrupt is enabled.
723    pub fn set_thresh_inact(&mut self, val: u8) -> AdxlResult<(), B> {
724        reg::THRESH_INACT.write(&mut self.bus, val as u32)?;
725        Ok(())
726    }
727
728    /// Set TIME_INACT.
729    /// Unsigned time value representing the amount of time that acceleration must
730    /// be less than the value in the THRESH_INACT register for inactivity
731    /// to be declared. The scale factor is 1 sec/LSB.
732    /// The function appears unresponsive if the TIME_INACT register is set to
733    /// a value less than the time constant of the output data rate.
734    /// A value of 0 results in an interrupt when the output data is less than the value
735    /// in the THRESH_INACT register.
736    pub fn set_time_inact(&mut self, val: u8) -> AdxlResult<(), B> {
737        reg::TIME_INACT.write(&mut self.bus, val as u32)?;
738        Ok(())
739    }
740
741    /// Set ACT_INACT_CTL register
742    /// DC - The current acceleration magnitude is compared directly with THRESH_ACT
743    /// and THRESH_INACT to determine whether activity or inactivity is detected.
744    /// AC - The acceleration value at the start of activity detection is taken as a reference value.
745    /// New samples of acceleration are then compared to this reference value.
746    ///
747    /// ACT/INACT - A setting of 1 enables x-, y-, or z-axis participation in detecting activity or inactivity.
748    pub fn set_act_inact_ctl(
749        &mut self,
750        act_acdc: bool,
751        act_x_en: bool,
752        act_y_en: bool,
753        act_z_en: bool,
754        inact_acdc: bool,
755        inact_x_en: bool,
756        inact_y_en: bool,
757        inact_z_en: bool,
758    ) -> AdxlResult<(), B> {
759        let value = (act_acdc as u8) << 7
760            | (act_x_en as u8) << 6
761            | (act_y_en as u8) << 5
762            | (act_z_en as u8) << 4
763            | (inact_acdc as u8) << 3
764            | (inact_x_en as u8) << 2
765            | (inact_y_en as u8) << 1
766            | (inact_z_en as u8);
767
768        reg::ACT_INACT_CTL.write(&mut self.bus, value as u32)?;
769
770        Ok(())
771    }
772
773    /// Set THRESH_FF
774    /// Unsigned format, for free-fall detection. The acceleration
775    /// on all axes is compared with the value in THRESH_FF to determine
776    /// if a free-fall event occurred. The scale factor is 62.5 mg/LSB.
777    /// Values between 300 mg and 600 mg (0x05 to 0x09) are recommended.
778    pub fn set_thresh_ff(&mut self, val: u8) -> AdxlResult<(), B> {
779        reg::THRESH_FF.write(&mut self.bus, val as u32)?;
780        Ok(())
781    }
782
783    /// Set TIME_FF
784    /// Unsigned time value representing the minimum time that the value of all axes
785    /// must be less than THRESH_FF to generate a free-fall interrupt.
786    /// The scale factor is 5 ms/LSB. Values between 100 / 350 ms (0x14 to 0x46) are recommended.
787    pub fn set_time_ff(&mut self, val: u8) -> AdxlResult<(), B> {
788        reg::TIME_FF.write(&mut self.bus, val as u32)?;
789        Ok(())
790    }
791
792    /// Set TAP_AXES: Supress
793    /// Setting the suppress bit suppresses double tap detection if acceleration greater
794    ///  than the value in THRESH_TAP is present between taps.
795    pub fn set_tap_supress(&mut self, on: bool) -> AdxlResult<(), B> {
796        reg::TAP_AXES.write_field(&mut self.bus, reg::FL_TA::SUPPRESS, on as u32)?;
797        Ok(())
798    }
799
800    /// Set TAP_AXES: TAP X Y Z
801    /// A setting of 1 in the TAP_X enable, TAP_Y enable, or TAP_Z
802    /// enable bit enables x-, y-, or z-axis participation in tap detection.
803    /// A setting of 0 excludes the selected axis from participation in tap detection.
804    pub fn set_tap_axes(&mut self, tap_x: bool, tap_y: bool, tap_z: bool) -> AdxlResult<(), B> {
805        reg::TAP_AXES.write_multiple_fields(&mut self.bus, &[
806            (reg::FL_TA::TAP_X_EN, tap_x as u32),
807            (reg::FL_TA::TAP_Y_EN, tap_y as u32),
808            (reg::FL_TA::TAP_Z_EN, tap_z as u32),
809        ])?;
810
811        Ok(())
812    }
813
814    #[inline]
815    /// Set INT_ENABLE register
816    /// Enables their respective functions to generate interrupts
817    /// It is recommended that interrupts be configured before enabling their outputs.
818    pub fn set_int_enable_direct(&mut self, value: u8) -> AdxlResult<(), B> {
819        reg::INT_ENABLE.write(&mut self.bus, value as u32)?;
820
821        Ok(())
822    }
823
824    /// Set INT_ENABLE register
825    /// Enables their respective functions to generate interrupts
826    /// It is recommended that interrupts be configured before enabling their outputs.
827    pub fn set_int_enable(
828        &mut self,
829        data_ready: bool,
830        single_tap: bool,
831        double_tap: bool,
832        activity: bool,
833        inactivity: bool,
834        free_fall: bool,
835        watermark: bool,
836        overrun: bool,
837    ) -> AdxlResult<(), B> {
838        let value = (data_ready as u8) << 7
839            | (single_tap as u8) << 6
840            | (double_tap as u8) << 5
841            | (activity as u8) << 4
842            | (inactivity as u8) << 3
843            | (free_fall as u8) << 2
844            | (watermark as u8) << 1
845            | (overrun as u8);
846
847        reg::INT_ENABLE.write(&mut self.bus, value as u32)?;
848
849        Ok(())
850    }
851
852    #[inline]
853    /// Set INT_MAP register
854    /// Any bits set to 0 in this register send their respective interrupts to
855    /// the INT1 pin, whereas bits set to 1 send their respective interrupts
856    /// to the INT2 pin. All selected interrupts for a given pin are OR’ed
857    pub fn set_int_map_direct(&mut self, value: u8) -> AdxlResult<(), B> {
858        reg::INT_MAP.write(&mut self.bus, value as u32)?;
859
860        Ok(())
861    }
862
863    /// Set INT_MAP register
864    /// Any bits set to 0 in this register send their respective interrupts to
865    /// the INT1 pin, whereas bits set to 1 send their respective interrupts
866    /// to the INT2 pin. All selected interrupts for a given pin are OR’ed
867    pub fn set_int_map(
868        &mut self,
869        data_ready: bool,
870        single_tap: bool,
871        double_tap: bool,
872        activity: bool,
873        inactivity: bool,
874        free_fall: bool,
875        watermark: bool,
876        overrun: bool,
877    ) -> AdxlResult<(), B> {
878        let value = (data_ready as u8) << 7
879            | (single_tap as u8) << 6
880            | (double_tap as u8) << 5
881            | (activity as u8) << 4
882            | (inactivity as u8) << 3
883            | (free_fall as u8) << 2
884            | (watermark as u8) << 1
885            | (overrun as u8);
886
887        reg::INT_MAP.write(&mut self.bus, value as u32)?;
888
889        Ok(())
890    }
891}
892
893// —————————————————————————————————————————————————————————————————————————————————————————————————
894//                                              Error
895// —————————————————————————————————————————————————————————————————————————————————————————————————
896
897#[derive(Debug, Clone, Copy, PartialEq, Eq)]
898pub enum Error<E: Debug> {
899    Init,
900    Config,
901    BufferLen,
902    Inner(E),
903}
904
905impl<E: Debug + Display> Display for Error<E> {
906    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
907        match &self {
908            Error::Init => write!(f, "device id mismatch"),
909            Error::Config => write!(f, "configuration error"),
910            Error::BufferLen => write!(f, "buffer len error"),
911            Error::Inner(e) => Display::fmt(e, f),
912        }
913    }
914}
915
916impl<E: Debug> From<E> for Error<E> {
917    fn from(error: E) -> Self {
918        Error::Inner(error)
919    }
920}
921
922// —————————————————————————————————————————————————————————————————————————————————————————————————
923//                                             Traits
924// —————————————————————————————————————————————————————————————————————————————————————————————————
925
926// ————————————————————————————————————————————— I2C ———————————————————————————————————————————————
927
928/// I2C RegisterBus implementation
929impl<I2C: i2c::I2c> RegisterBus for AdxlBusI2c<I2C> {
930    type Error = I2C::Error;
931
932    fn read_register(&mut self, reg_addr: u8, buffer: &mut [u8]) -> Result<(), Self::Error> {
933        self.i2c.write_read(self.addr, &[reg_addr], buffer)
934    }
935
936    fn write_register(&mut self, reg_addr: u8, data: &[u8]) -> Result<(), Self::Error> {
937        assert!(data.len() <= MAX_BUF_LEN, "buffer too large");
938
939        let mut buf = [0u8; MAX_BUF_LEN + 1];
940        buf[0] = reg_addr;
941        buf[1..data.len() + 1].copy_from_slice(data);
942
943        self.i2c.write(self.addr, &buf[..data.len() + 1])
944    }
945}
946
947// ————————————————————————————————————————————— SPI ———————————————————————————————————————————————
948
949/// SPI RegisterBus implementation
950impl<SPI: spi::SpiBus, CS: StatefulOutputPin> RegisterBus for AdxlBusSpi<SPI, CS> {
951    type Error = SPI::Error;
952
953    fn read_register(&mut self, reg_addr: u8, buffer: &mut [u8]) -> Result<(), Self::Error> {
954        assert!(buffer.len() <= MAX_BUF_LEN, "buffer too large");
955
956        let addr_byte = if buffer.len() > 1 {
957            reg_addr | 0xC0 // multi-byte read
958        }
959        else {
960            reg_addr | 0x80 // single-byte read
961        };
962
963        // Temp buffer to store read data
964        let mut temp_buf = [0u8; MAX_BUF_LEN + 1];
965
966        self.spi_cs.set_low().expect("SPI CS pin");
967        let result = self
968            .spi
969            .transfer(&mut temp_buf[..buffer.len() + 1], &[addr_byte]);
970        self.spi_cs.set_high().expect("SPI CS pin");
971
972        // Copy received data into the user buffer (skip the first byte)
973        buffer.copy_from_slice(&temp_buf[1..buffer.len() + 1]);
974
975        result
976    }
977
978    fn write_register(&mut self, reg_addr: u8, data: &[u8]) -> Result<(), Self::Error> {
979        assert!(data.len() <= MAX_BUF_LEN, "buffer too large");
980
981        let addr_byte = if data.len() > 1 {
982            reg_addr | 0x40 // multi-byte write
983        }
984        else {
985            reg_addr // single-byte write
986        };
987
988        // Prepare buffer: first byte = address, rest = data
989        let mut buf = [0u8; MAX_BUF_LEN + 1];
990        buf[0] = addr_byte;
991        buf[1..data.len() + 1].copy_from_slice(data);
992
993        self.spi_cs.set_low().expect("SPI CS pin");
994        let result = self.spi.write(&buf[..data.len() + 1]);
995        self.spi_cs.set_high().expect("SPI CS pin");
996
997        result
998    }
999}