lis3dh_async/
register.rs

1use core::convert::TryInto;
2use num_enum::TryFromPrimitive;
3
4/// Possible I²C slave addresses.
5#[derive(Copy, Clone, Debug, Eq, PartialEq)]
6#[cfg_attr(feature = "defmt", derive(defmt::Format))]
7#[repr(u8)]
8pub enum SlaveAddr {
9    /// Default slave address (`0x18`)
10    Default = 0x18,
11
12    /// Alternate slave address (`0x19`)
13    Alternate = 0x19,
14}
15
16impl SlaveAddr {
17    pub fn addr(self) -> u8 {
18        self as u8
19    }
20}
21
22/// Enumerate all device registers.
23#[allow(dead_code, non_camel_case_types, clippy::upper_case_acronyms)]
24#[derive(Copy, Clone, Debug, Eq, PartialEq)]
25#[cfg_attr(feature = "defmt", derive(defmt::Format))]
26#[repr(u8)]
27pub enum Register {
28    STATUS_AUX = 0x07,
29    OUT_ADC1_L = 0x08,
30    OUT_ADC1_H = 0x09,
31    OUT_ADC2_L = 0x0A,
32    OUT_ADC2_H = 0x0B,
33    OUT_ADC3_L = 0x0C,
34    OUT_ADC3_H = 0x0D,
35    WHOAMI = 0x0F,
36    CTRL0 = 0x1E,
37    TEMP_CFG = 0x1F,
38    CTRL1 = 0x20,
39    CTRL2 = 0x21,
40    CTRL3 = 0x22,
41    CTRL4 = 0x23,
42    CTRL5 = 0x24,
43    CTRL6 = 0x25,
44    REFERENCE = 0x26,
45    STATUS = 0x27,
46    OUT_X_L = 0x28,
47    OUT_X_H = 0x29,
48    OUT_Y_L = 0x2A,
49    OUT_Y_H = 0x2B,
50    OUT_Z_L = 0x2C,
51    OUT_Z_H = 0x2D,
52    FIFO_CTRL = 0x2E,
53    FIFO_SRC = 0x2F,
54    INT1_CFG = 0x30,
55    INT1_SRC = 0x31,
56    INT1_THS = 0x32,
57    INT1_DURATION = 0x33,
58    INT2_CFG = 0x34,
59    INT2_SRC = 0x35,
60    INT2_THS = 0x36,
61    INT2_DURATION = 0x37,
62    CLICK_CFG = 0x38,
63    CLICK_SRC = 0x39,
64    CLICK_THS = 0x3A,
65    TIME_LIMIT = 0x3B,
66    TIME_LATENCY = 0x3C,
67    TIME_WINDOW = 0x3D,
68    ACT_THS = 0x3E,
69    ACT_DUR = 0x3F,
70}
71
72impl Register {
73    /// Get register address
74    pub fn addr(self) -> u8 {
75        self as u8
76    }
77
78    /// Is the register read-only?
79    pub fn read_only(self) -> bool {
80        matches!(
81            self,
82            Register::STATUS_AUX
83                | Register::OUT_ADC1_L
84                | Register::OUT_ADC1_H
85                | Register::OUT_ADC2_L
86                | Register::OUT_ADC2_H
87                | Register::OUT_ADC3_L
88                | Register::OUT_ADC3_H
89                | Register::WHOAMI
90                | Register::STATUS
91                | Register::OUT_X_L
92                | Register::OUT_X_H
93                | Register::OUT_Y_L
94                | Register::OUT_Y_H
95                | Register::OUT_Z_L
96                | Register::OUT_Z_H
97                | Register::FIFO_SRC
98                | Register::INT1_SRC
99                | Register::INT2_SRC
100                | Register::CLICK_SRC
101        )
102    }
103}
104
105/// Full-scale selection.
106#[allow(non_camel_case_types)]
107#[derive(Copy, Clone, Debug, Eq, PartialEq, TryFromPrimitive)]
108#[cfg_attr(feature = "defmt", derive(defmt::Format))]
109#[repr(u8)]
110#[derive(Default)]
111pub enum Range {
112    /// ±16g
113    G16 = 0b11,
114
115    /// ±8g
116    G8 = 0b10,
117
118    /// ±4g
119    G4 = 0b01,
120
121    /// ±2g (Default)
122    #[default]
123    G2 = 0b00,
124}
125
126impl Range {
127    pub const fn bits(self) -> u8 {
128        self as u8
129    }
130
131    /// Convert the range into an value in mili-g
132    pub const fn as_mg(self) -> u8 {
133        match self {
134            Range::G16 => 186,
135            Range::G8 => 62,
136            Range::G4 => 32,
137            Range::G2 => 16,
138        }
139    }
140}
141
142#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
143#[cfg_attr(feature = "defmt", derive(defmt::Format))]
144pub struct Threshold(pub(crate) u8);
145
146impl Threshold {
147    /// Convert a value in multiples of the `g` constant (roughly 9.81) to a threshold.
148    #[inline(always)]
149    pub fn g(range: Range, gs: f32) -> Self {
150        Self::mg(range, gs * 1000.0)
151    }
152
153    #[inline(always)]
154    pub fn mg(range: Range, mgs: f32) -> Self {
155        let value = mgs / (range.as_mg() as f32);
156
157        let result = crude_ceil(value);
158
159        Threshold(result.try_into().unwrap())
160    }
161
162    pub const ZERO: Self = Threshold(0);
163}
164
165/// a crude `.ceil()`, the std one is not currently available when using no_std
166fn crude_ceil(value: f32) -> u64 {
167    let truncated = value as u64;
168
169    let round_up = value - (truncated as f32) > 0.0;
170
171    if round_up {
172        truncated + 1
173    } else {
174        truncated
175    }
176}
177
178/// Output data rate.
179#[allow(non_camel_case_types)]
180#[derive(Copy, Clone, Debug, Eq, PartialEq, TryFromPrimitive)]
181#[cfg_attr(feature = "defmt", derive(defmt::Format))]
182#[repr(u8)]
183pub enum DataRate {
184    /// 400Hz (Default)
185    Hz_400 = 0b0111,
186
187    /// 200Hz
188    Hz_200 = 0b0110,
189
190    /// 100Hz
191    Hz_100 = 0b0101,
192
193    /// 50Hz
194    Hz_50 = 0b0100,
195
196    /// 25Hz
197    Hz_25 = 0b0011,
198
199    /// 10Hz
200    Hz_10 = 0b0010,
201
202    /// 1Hz
203    Hz_1 = 0b0001,
204
205    /// Power down
206    PowerDown = 0b0000,
207}
208
209impl DataRate {
210    pub const fn bits(self) -> u8 {
211        self as u8
212    }
213
214    pub const fn sample_rate(self) -> f32 {
215        match self {
216            DataRate::Hz_400 => 400.0,
217            DataRate::Hz_200 => 200.0,
218            DataRate::Hz_100 => 100.0,
219            DataRate::Hz_50 => 50.0,
220            DataRate::Hz_25 => 25.0,
221            DataRate::Hz_10 => 10.0,
222            DataRate::Hz_1 => 1.0,
223            DataRate::PowerDown => 0.0,
224        }
225    }
226}
227
228#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
229#[cfg_attr(feature = "defmt", derive(defmt::Format))]
230pub struct Duration(pub(crate) u8);
231
232impl Duration {
233    /// Convert a number of seconds into a duration. Internally a duration is represented
234    /// as a multiple of `1 / ODR` where ODR (the output data rate) is of type [`DataRate`].
235    #[inline(always)]
236    pub fn seconds(output_data_rate: DataRate, seconds: f32) -> Self {
237        let duration = output_data_rate.sample_rate() * seconds;
238
239        Self(duration as u8)
240    }
241
242    /// Convert a number of miliseconds into a duration. Internally a duration is represented
243    /// as a multiple of `1 / ODR` where ODR (the output data rate) is of type [`DataRate`].
244    #[inline(always)]
245    pub fn miliseconds(output_data_rate: DataRate, miliseconds: f32) -> Self {
246        Self::seconds(output_data_rate, miliseconds * 1000.0)
247    }
248
249    pub const ZERO: Self = Duration(0);
250}
251
252/// Data status structure. Decoded from the `STATUS_REG` register.
253///
254/// `STATUS_REG` has the following bit fields:
255///   * `ZYXOR` - X, Y and Z-axis data overrun
256///   * `ZOR` - Z-axis data overrun
257///   * `YOR` - Y-axis data overrun
258///   * `XOR` - X-axis data overrun
259///   * `ZYXDA` - X, Y and Z-axis new data available
260///   * `ZDA` - Z-axis new data available
261///   * `YDA` Y-axis new data available
262///   * `XDA` X-axis new data available
263///
264/// This struct splits the fields into more convenient groups:
265///  * `zyxor` -> `ZYXOR`
266///  * `xyzor` -> (`XOR`, `YOR`, `ZOR`)
267///  * `zyxda` -> `ZYXDA`
268///  * `xyzda` -> (`XDA`, `YDA`, `ZDA`)
269#[derive(Debug)]
270#[cfg_attr(feature = "defmt", derive(defmt::Format))]
271pub struct DataStatus {
272    /// ZYXOR bit
273    pub zyxor: bool,
274
275    /// (XOR, YOR, ZOR) bits
276    pub xyzor: (bool, bool, bool),
277
278    /// ZYXDA bit
279    pub zyxda: bool,
280
281    /// (XDA, YDA, ZDA) bits
282    pub xyzda: (bool, bool, bool),
283}
284
285/// Information about what is stored in the FIFO
286#[derive(Copy, Clone, Debug, Eq, PartialEq)]
287#[cfg_attr(feature = "defmt", derive(defmt::Format))]
288pub struct FifoStatus {
289    /// The watermark bit is set high when FIFO content exceeds watermark level
290    pub watermark: bool,
291    /// The overrun bit is set high when FIFO buffer is full; this means that the FIFO buffer
292    /// contains 32 unread samples. At the following ODR a new sample set replaces the
293    /// oldest FIFO value. The OVRN bit is set to 0 when the first sample set has been
294    /// read
295    pub overrun: bool,
296    /// The empty bit is set high when all FIFO samples have been read and FIFO is empty
297    pub empty: bool,
298    /// The current number of unread samples stored in the
299    /// FIFO buffer. When FIFO is enabled, this value increases
300    /// at ODR frequency until the buffer is full, whereas,
301    /// it decreases every time one sample set is retrieved from FIFO.
302    pub stack_size: u8,
303}
304
305impl FifoStatus {
306    /// Interpret the content of the `FIFO_SRC_REG` register
307    pub const fn from_bits(status: u8) -> Self {
308        Self {
309            watermark: (status >> 7) & 1 == 1,
310            overrun: (status >> 6) & 1 == 1,
311            empty: (status >> 5) & 1 == 1,
312            stack_size: status & 0b0001_1111,
313        }
314    }
315}
316
317/// FIFO behavior. See [the spec](https://www.st.com/resource/en/datasheet/lis3dh.pdf#page=22) for
318/// full details.
319#[derive(Copy, Clone, Debug, Eq, PartialEq)]
320#[cfg_attr(feature = "defmt", derive(defmt::Format))]
321pub enum FifoMode {
322    /// The FIFO is not operational
323    ByPass,
324    /// In FIFO mode, the buffer continues filling data from the X, Y and Z accelerometer channels
325    /// until it is full (a set of 32 samples stored). When the FIFO is full, it stops collecting data from
326    /// the input channels and the FIFO content remains unchanged.
327    Fifo,
328    /// In Stream mode the FIFO continues filling data from the X, Y, and Z accelerometer channels
329    /// until the buffer is full (a set of 32 samples stored) at which point the FIFO buffer index
330    /// restarts from the beginning and older data is replaced by the current data. The oldest values
331    /// continue to be overwritten until a read operation frees the FIFO slots
332    Stream,
333    /// In Stream-to-FIFO mode, data from the X, Y and Z accelerometer channels are collected in
334    /// a combination of Stream mode and FIFO mode. The FIFO buffer starts operating in Stream
335    /// mode and switches to FIFO mode when interrupt 1 occurs.
336    StreamToFifoInt1,
337    /// In Stream-to-FIFO mode, data from the X, Y and Z accelerometer channels are collected in
338    /// a combination of Stream mode and FIFO mode. The FIFO buffer starts operating in Stream
339    /// mode and switches to FIFO mode when interrupt 2 occurs.
340    StreamToFifoInt2,
341}
342
343impl FifoMode {
344    /// Convert the mode to bits that can be written to the `FIFO_CTRL_REG` register.
345    pub const fn to_bits(self) -> u8 {
346        let mut trigger = false;
347
348        let mode = match self {
349            FifoMode::ByPass => 0b00,
350            FifoMode::Fifo => 0b01,
351            FifoMode::Stream => 0b10,
352            FifoMode::StreamToFifoInt1 => 0b11,
353            FifoMode::StreamToFifoInt2 => {
354                trigger = true;
355
356                0b11
357            }
358        };
359
360        mode << 6 | (trigger as u8) << 5
361    }
362}
363
364/// Operating mode.
365#[derive(Copy, Clone, Debug, Eq, PartialEq)]
366#[cfg_attr(feature = "defmt", derive(defmt::Format))]
367#[repr(u8)]
368pub enum Mode {
369    /// High-resolution mode (12-bit data output)
370    HighResolution,
371
372    /// Normal mode (10-bit data output)
373    Normal,
374
375    /// Low-power mode (8-bit data output)
376    LowPower,
377}
378
379// === WHO_AMI_I (0Fh) ===
380
381/// `WHO_AM_I` device identification register
382pub const DEVICE_ID: u8 = 0x33;
383
384// === TEMP_CFG_REG (1Fh) ===
385
386pub const ADC_EN: u8 = 0b1000_0000;
387pub const TEMP_EN: u8 = 0b0100_0000;
388
389// === CTRL_REG1 (20h) ===
390
391pub const ODR_MASK: u8 = 0b1111_0000;
392pub const LP_EN: u8 = 0b0000_1000;
393pub const Z_EN: u8 = 0b0000_0100;
394pub const Y_EN: u8 = 0b0000_0010;
395pub const X_EN: u8 = 0b0000_0001;
396
397// === CTRL_REG4 (23h) ===
398
399pub const BDU: u8 = 0b1000_0000;
400pub const FS_MASK: u8 = 0b0011_0000;
401pub const HR: u8 = 0b0000_1000;
402
403// === STATUS_REG (27h) ===
404
405pub const ZYXOR: u8 = 0b1000_0000;
406pub const ZOR: u8 = 0b0100_0000;
407pub const YOR: u8 = 0b0010_0000;
408pub const XOR: u8 = 0b0001_0000;
409pub const ZYXDA: u8 = 0b0000_1000;
410pub const ZDA: u8 = 0b0000_0100;
411pub const YDA: u8 = 0b0000_0010;
412pub const XDA: u8 = 0b0000_0001;
413
414#[cfg(test)]
415mod test {
416    use super::*;
417
418    #[test]
419    fn threshold_g_vs_mg() {
420        assert_eq!(
421            Threshold::g(Range::G2, 1.5),
422            Threshold::mg(Range::G2, 1500.0)
423        );
424    }
425
426    #[test]
427    fn duration_seconds_vs_miliseconds() {
428        assert_eq!(
429            Duration::seconds(DataRate::Hz_400, 1.5),
430            Duration::miliseconds(DataRate::Hz_400, 1500.0)
431        );
432    }
433}