adxl345_eh_driver/
lib.rs

1// Credits to https://github.com/adafruit/Adafruit_ADXL345
2#![no_std]
3use bitfield::bitfield;
4use embedded_hal::i2c::{ErrorType, I2c};
5
6pub mod address {
7    pub const PRIMARY: u8 = 0x1D;
8    pub const SECONDARY: u8 = 0x53;
9}
10
11const ID_VAL: u8 = 0b11100101;
12const EARTH_GRAVITY: f32 = 9.80665;
13const LSB_SCALE_FACTOR_FULL_RES: f32 = 0.0039;
14
15#[derive(Debug)]
16pub enum Error<E> {
17    IdMismatch,
18    BusError(E),
19}
20
21impl<E> From<E> for Error<E> {
22    fn from(error: E) -> Self {
23        Error::BusError(error)
24    }
25}
26
27pub struct Driver<Bus> {
28    bus: Bus,
29    addr: u8,
30}
31
32impl<Bus> Driver<Bus>
33where
34    Bus: I2c + ErrorType,
35{
36    /// Create a new sensor using an I2C interface and a given address. By default (if `addr` is `None`), the
37    /// primary address is used, but the secondary address (or a different one) can be used by passing
38    /// `Some(address::SECONDARY)`.
39    pub fn new(bus: Bus, addr: Option<u8>) -> Result<Driver<Bus>, Error<Bus::Error>> {
40        let addr = addr.unwrap_or(address::PRIMARY);
41        let mut driver = Driver { bus, addr };
42        driver.get_id()?;
43        driver.power_on()?;
44
45        Ok(driver)
46    }
47
48    fn get_id(&mut self) -> Result<u8, Error<Bus::Error>> {
49        let tx_buf = [registers::DEVID; 1];
50        let mut rx_buf = [0u8; 1];
51        self.bus.write_read(self.addr, &tx_buf, &mut rx_buf)?;
52        if rx_buf[0] != ID_VAL {
53            return Err(Error::IdMismatch);
54        }
55
56        Ok(rx_buf[0])
57    }
58
59    fn power_on(&mut self) -> Result<(), Error<Bus::Error>> {
60        let tx_buf = [registers::POWER_CTL, 0x08];
61        self.bus.write(self.addr, &tx_buf)?;
62
63        Ok(())
64    }
65
66    pub fn set_tap_detection_ints(
67        &mut self,
68        single_tap: Option<InterruptMapPin>,
69        double_tap: Option<InterruptMapPin>,
70        axes: u8,
71        duration: u8,
72        latent: u8,
73        window: u8,
74        threshold: u8,
75    ) -> Result<(), Error<Bus::Error>> {
76        // Disable all interrupt(s)
77        let tx_buf = [registers::INT_ENABLE, 0u8];
78        self.bus.write(self.addr, &tx_buf)?;
79
80        // Configure tap detection parameters
81        let tx_buf = [registers::TAP_AXES, axes];
82        self.bus.write(self.addr, &tx_buf)?;
83
84        let tx_buf = [registers::DUR, duration];
85        self.bus.write(self.addr, &tx_buf)?;
86
87        let tx_buf = [registers::LATENT, latent];
88        self.bus.write(self.addr, &tx_buf)?;
89
90        let tx_buf = [registers::WINDOW, window];
91        self.bus.write(self.addr, &tx_buf)?;
92
93        let tx_buf = [registers::THRESH_TAP, threshold];
94        self.bus.write(self.addr, &tx_buf)?;
95
96        // Map chosen interrupt(s) to pin
97        let mut int_map = InterruptsFlags(0);
98        int_map.set_single_tap(single_tap.unwrap_or(InterruptMapPin::INT1).as_bool());
99        int_map.set_double_tap(double_tap.unwrap_or(InterruptMapPin::INT1).as_bool());
100        let tx_buf = [registers::INT_MAP, int_map.0];
101        self.bus.write(self.addr, &tx_buf)?;
102
103        // Enable interrupt(s)
104        let mut int_config = InterruptsFlags(0);
105        int_config.set_single_tap(single_tap.is_some());
106        int_config.set_double_tap(double_tap.is_some());
107        let tx_buf = [registers::INT_ENABLE, int_config.0];
108        self.bus.write(self.addr, &tx_buf)?;
109
110        Ok(())
111    }
112
113    /// Pass `InterruptMapPin` to enable the given interrupt. If `None` is passed, the interrupt is disabled.
114    pub fn set_tap_detection_ints_defaults(
115        &mut self,
116        single_tap: Option<InterruptMapPin>,
117        double_tap: Option<InterruptMapPin>,
118    ) -> Result<(), Error<Bus::Error>> {
119        let axes = 0x03; // All axes
120        let duration = 0x10; // 0x10 * 625 us = 10ms
121        let latent = 0x50; // 0x50 * 1.25 ms = 100ms
122        let window = 0xFF; // 0xFF * 1.25 ms = 318.75ms
123        let threshold = 0x20; // 0x40 * 62.5 mg = 2g
124        self.set_tap_detection_ints(
125            single_tap, double_tap, axes, duration, latent, window, threshold,
126        )
127    }
128
129    pub fn clear_ints(&mut self) -> Result<InterruptsFlags, Error<Bus::Error>> {
130        let tx_buf = [registers::INT_SOURCE; 1];
131        let mut rx_buf = [0u8; 1];
132        self.bus.write_read(self.addr, &tx_buf, &mut rx_buf)?;
133
134        Ok(InterruptsFlags(rx_buf[0]))
135    }
136
137    pub fn get_accel_raw(&mut self) -> Result<(i16, i16, i16), Error<Bus::Error>> {
138        let tx_buf = [registers::DATAX0; 1];
139        let mut rx_buf = [0u8; 6]; // Read all axes
140        self.bus.write_read(self.addr, &tx_buf, &mut rx_buf)?;
141        let datax: i16 = ((rx_buf[1] as i16) << 8) | rx_buf[0] as i16;
142        let datay: i16 = ((rx_buf[3] as i16) << 8) | rx_buf[2] as i16;
143        let dataz: i16 = ((rx_buf[5] as i16) << 8) | rx_buf[4] as i16;
144
145        Ok((datax, datay, dataz))
146    }
147
148    pub fn set_range(&mut self, range: GRange) -> Result<(), Error<Bus::Error>> {
149        let mut rx_buf = [0u8; 1];
150        self.bus.read(self.addr, &mut rx_buf)?;
151
152        // Clear least significant nibble
153        let mut format = rx_buf[0] & 0xF0;
154        format |= range as u8;
155
156        // Make sure that the FULL-RES bit is enabled for range scaling
157        format |= 0x08;
158
159        self.bus
160            .write(self.addr, &[registers::DATA_FORMAT, format])?;
161
162        Ok(())
163    }
164
165    pub fn set_datarate(&mut self, rate: OutputDataRate) -> Result<(), Error<Bus::Error>> {
166        let tx_buf = [registers::BW_RATE, rate as u8];
167        self.bus.write(self.addr, &tx_buf)?;
168
169        Ok(())
170    }
171
172    pub fn get_accel(&mut self) -> Result<(f32, f32, f32), Error<Bus::Error>> {
173        let accel = self.get_accel_raw()?;
174        let accel_g: (f32, f32, f32) = (
175            (accel.0 as f32) * EARTH_GRAVITY * LSB_SCALE_FACTOR_FULL_RES,
176            (accel.1 as f32) * EARTH_GRAVITY * LSB_SCALE_FACTOR_FULL_RES,
177            (accel.2 as f32) * EARTH_GRAVITY * LSB_SCALE_FACTOR_FULL_RES,
178        );
179
180        Ok(accel_g)
181    }
182}
183
184#[allow(dead_code)]
185mod registers {
186    pub const DEVID: u8 = 0x00; // Device ID
187    pub const THRESH_TAP: u8 = 0x1D; // Tap threshold
188    pub const OFSX: u8 = 0x1E; // X-axis offset
189    pub const OFSY: u8 = 0x1F; // Y-axis offset
190    pub const OFSZ: u8 = 0x20; // Z-axis offset
191    pub const DUR: u8 = 0x21; // Tap duration
192    pub const LATENT: u8 = 0x22; // Tap latency
193    pub const WINDOW: u8 = 0x23; // Tap window
194    pub const THRESH_ACT: u8 = 0x24; // Activity threshold
195    pub const THRESH_INACT: u8 = 0x25; // Inactivity threshold
196    pub const TIME_INACT: u8 = 0x26; // Inactivity time
197    pub const ACT_INACT_CTL: u8 = 0x27; // Axis enable control for activity and inactivity detection
198    pub const THRESH_FF: u8 = 0x28; // Free-fall threshold
199    pub const TIME_FF: u8 = 0x29; // Free-fall time
200    pub const TAP_AXES: u8 = 0x2A; // Axis control for single/double tap
201    pub const ACT_TAP_STATUS: u8 = 0x2B; // Source for single/double tap
202    pub const BW_RATE: u8 = 0x2C; // Data rate and power mode control
203    pub const POWER_CTL: u8 = 0x2D; // Power-saving features control
204    pub const INT_ENABLE: u8 = 0x2E; // Interrupt enable control
205    pub const INT_MAP: u8 = 0x2F; // Interrupt mapping control
206    pub const INT_SOURCE: u8 = 0x30; // Source of interrupts
207    pub const DATA_FORMAT: u8 = 0x31; // Data format control
208    pub const DATAX0: u8 = 0x32; // X-axis data 0
209    pub const DATAX1: u8 = 0x33; // X-axis data 1
210    pub const DATAY0: u8 = 0x34; // Y-axis data 0
211    pub const DATAY1: u8 = 0x35; // Y-axis data 1
212    pub const DATAZ0: u8 = 0x36; // Z-axis data 0
213    pub const DATAZ1: u8 = 0x37; // Z-axis data 1
214    pub const FIFO_CTL: u8 = 0x38; // FIFO control
215    pub const FIFO_STATUS: u8 = 0x39; // FIFO status
216}
217
218#[repr(u8)]
219#[derive(Clone, Copy)]
220pub enum GRange {
221    Two = 0,     // +/- 2g (default value)
222    Four = 1,    // +/- 4g
223    Eight = 2,   // +/- 8g
224    Sixteen = 3, // +/- 16g
225}
226
227#[repr(u8)]
228#[derive(Clone, Copy)]
229pub enum OutputDataRate {
230    Hz0_10 = 0,
231    Hz0_20 = 1,
232    Hz0_39 = 2,
233    Hz0_78 = 3,
234    Hz1_56 = 4,
235    Hz3_13 = 5,
236    Hz6_25 = 6,
237    Hz12_5 = 7,
238    Hz25 = 8,
239    Hz50 = 9,
240    Hz100 = 10,
241    Hz200 = 11,
242    Hz400 = 12,
243    Hz800 = 13,
244    Hz1600 = 14,
245    Hz3200 = 15,
246}
247
248bitfield! {
249    pub struct InterruptsFlags(u8);
250    impl Debug;
251    pub data_ready, set_data_ready: 7;
252    pub single_tap, set_single_tap: 6;
253    pub double_tap, set_double_tap: 5;
254    pub activity, set_activity: 4;
255    pub inactivity, set_inactivity: 3;
256    pub free_fall, set_free_fall: 2;
257    pub watermark, set_watermark: 1;
258    pub overrun, set_overrun: 0;
259}
260
261#[repr(u8)]
262#[derive(Clone, Copy)]
263pub enum InterruptMapPin {
264    INT1 = 0,
265    INT2 = 1,
266}
267
268impl InterruptMapPin {
269    pub fn as_bool(self) -> bool {
270        match self {
271            InterruptMapPin::INT1 => false,
272            InterruptMapPin::INT2 => true,
273        }
274    }
275}