1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
//! ADXL343 register addresses
#![allow(non_camel_case_types, clippy::unreadable_literal)]

use bitflags::bitflags;

/// Register addresses
/// Taken from the ADXL343 data sheet (Register Map, p.21)
/// <https://www.analog.com/media/en/technical-documentation/data-sheets/adxl343.pdf>
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[repr(u8)]
pub enum Register {
    /// Device ID (Read Only)
    ///
    /// "The DEVID register holds a fixed device ID code of 0xE5 (345 octal)."
    DEVID = 0x00,

    /// Tap threshold (Read/Write)
    ///
    /// "The THRESH_TAP register is eight bits and holds the threshold
    /// value for tap interrupts. The data format is unsigned, therefore,
    /// the magnitude of the tap event is compared with the value
    /// in THRESH_TAP for normal tap detection. The scale factor is
    /// 62.5 mg/LSB (that is, 0xFF = 16 g). A value of 0 may result in
    /// undesirable behavior if single tap/double tap interrupts are
    /// enabled."
    THRESH_TAP = 0x1D,

    /// X-axis offset (Read/Write)
    ///
    /// "The OFSX, OFSY, and OFSZ registers are each eight bits and
    /// offer user-set offset adjustments in twos complement format
    /// with a scale factor of 15.6 mg/LSB (that is, 0x7F = 2 g). The
    /// value stored in the offset registers is automatically added to the
    /// acceleration data, and the resulting value is stored in the output
    /// data registers."
    OFSX = 0x1E,

    /// Y-axis offset (Read/Write)
    ///
    /// See OFSX notes.
    OFSY = 0x1F,

    /// Z-axis offset (Read/Write)
    ///
    /// See OFSX notes.
    OFSZ = 0x20,

    /// Tap duration (Read/Write)
    ///
    /// "The DUR register is eight bits and contains an unsigned time
    /// value representing the maximum time that an event must be
    /// above the THRESH_TAP threshold to qualify as a tap event. The
    /// scale factor is 625 µs/LSB. A value of 0 disables the single tap/
    /// double tap functions."
    DUR = 0x21,

    /// Tap latency (Read/Write)
    ///
    /// "The latent register is eight bits and contains an unsigned time
    /// value representing the wait time from the detection of a tap
    /// event to the start of the time window (defined by the window
    /// register) during which a possible second tap event can be detected.
    /// The scale factor is 1.25 ms/LSB. A value of 0 disables the double tap
    /// function"
    LATENT = 0x22,

    /// Tap window (Read/Write)
    ///
    /// "The window register is eight bits and contains an unsigned time
    /// value representing the amount of time after the expiration of the
    /// latency time (determined by the latent register) during which a
    /// second valid tap can begin. The scale factor is 1.25 ms/LSB. A
    /// value of 0 disables the double tap function."
    WINDOW = 0x23,

    /// Activity threshold (Read/Write)
    ///
    /// "The THRESH_ACT register is eight bits and holds the threshold
    /// value for detecting activity. The data format is unsigned,
    /// therefore, the magnitude of the activity event is compared
    /// with the value in the THRESH_ACT register. The scale factor
    /// is 62.5 mg/LSB. A value of 0 may result in undesirable behavior
    /// if the activity interrupt is enabled."
    THRESH_ACT = 0x24,

    /// Inactivity threshold (Read/Write)
    ///
    /// "The THRESH_INACT register is eight bits and holds the threshold
    /// value for detecting inactivity. The data format is unsigned,
    /// therefore, the magnitude of the inactivity event is compared
    /// with the value in the THRESH_INACT register. The scale factor
    /// is 62.5 mg/LSB. A value of 0 may result in undesirable behavior
    /// if the inactivity interrupt is enabled."
    THRESH_INACT = 0x25,

    /// Inactivity time (Read/Write)
    ///
    /// "The TIME_INACT register is eight bits and contains an unsigned
    /// time value representing the amount of time that acceleration
    /// must be less than the value in the THRESH_INACT register for
    /// inactivity to be declared. The scale factor is 1 sec/LSB. Unlike
    /// the other interrupt functions, which use unfiltered data (see the
    /// Threshold commands), the inactivity function uses filtered output
    /// data. At least one output sample must be generated for the
    /// inactivity interrupt to be triggered. This results in the function
    /// appearing unresponsive if the TIME_INACT register is set to a
    /// value less than the time constant of the output data rate. A value
    /// of 0 results in an interrupt when the output data is less than the
    /// value in the THRESH_INACT register."
    TIME_INACT = 0x26,

    /// Axis enable control for activity and inactivity detection (Read/Write)
    ///
    /// See data sheet for documentation (p.22)
    ACT_INACT_CTL = 0x27,

    /// Free-fall threshold (Read/Write)
    ///
    /// "The THRESH_FF register is eight bits and holds the threshold
    /// value, in unsigned format, for free-fall detection. The acceleration on
    /// all axes is compared with the value in THRESH_FF to determine if
    /// a free-fall event occurred. The scale factor is 62.5 mg/LSB. Note
    /// that a value of 0 mg may result in undesirable behavior if the
    /// free-fall interrupt is enabled. Values between 300 mg and 600 mg
    /// (0x05 to 0x09) are recommended."
    THRESH_FF = 0x28,

    /// Free-fall time (Read/Write)
    ///
    /// "The TIME_FF register is eight bits and stores an unsigned time
    /// value representing the minimum time that the value of all axes
    /// must be less than THRESH_FF to generate a free-fall interrupt.
    /// The scale factor is 5 ms/LSB. A value of 0 may result in undesirable
    /// behavior if the free-fall interrupt is enabled. Values between 100 ms
    /// and 350 ms (0x14 to 0x46) are recommended."
    TIME_FF = 0x29,

    /// Axis control for single/double tap (Read/Write)
    ///
    /// See data sheet for documentation (p.23)
    TAP_AXES = 0x2A,

    /// Source for single/double tap (Read Only)
    ///
    /// See data sheet for documentation (p.23)
    ACT_TAP_STATUS = 0x2B,

    /// Data rate and power mode control (Read/Write)
    ///
    /// See data sheet for documentation (p.23)
    BW_RATE = 0x2C,

    /// Power-saving features control (Read/Write)
    ///
    /// See data sheet for documentation (p.23)
    POWER_CTL = 0x2D,

    /// Interrupt enable control (Read/Write)
    ///
    /// See data sheet for table (p.24)
    ///
    /// "Setting bits in this register to a value of 1 enables their respective
    /// functions to generate interrupts, whereas a value of 0 prevents
    /// the functions from generating interrupts. The DATA_READY,
    /// watermark, and overrun bits enable only the interrupt output;
    /// the functions are always enabled. It is recommended that interrupts
    /// be configured before enabling their outputs."
    INT_ENABLE = 0x2E,

    /// Interrupt mapping control (Read/Write)
    ///
    /// See data sheet for table (p.24)
    ///
    /// "Any bits set to 0 in this register send their respective interrupts to
    /// the INT1 pin, whereas bits set to 1 send their respective interrupts
    /// to the INT2 pin. All selected interrupts for a given pin are OR’ed"
    INT_MAP = 0x2F,

    /// Source of interrupts (Read Only)
    ///
    /// See data sheet for table (p.24)
    ///
    /// "Bits set to 1 in this register indicate that their respective functions
    /// have triggered an event, whereas a value of 0 indicates that the
    /// corresponding event has not occurred. The DATA_READY,
    /// watermark, and overrun bits are always set if the corresponding
    /// events occur, regardless of the INT_ENABLE register settings,
    /// and are cleared by reading data from the DATAX, DATAY, and
    /// DATAZ registers. The DATA_READY and watermark bits may
    /// require multiple reads, as indicated in the FIFO mode descriptions
    /// in the FIFO section. Other bits, and the corresponding interrupts,
    /// are cleared by reading the INT_SOURCE register."
    INT_SOURCE = 0x30,

    /// Data format control (Read/Write)
    ///
    /// See `DataFormatFlags` below and data sheet for full documentation (p.24)
    ///
    /// "The DATA_FORMAT register controls the presentation of data
    /// to Register 0x32 through Register 0x37."
    DATA_FORMAT = 0x31,

    /// X-axis data 0 (Read Only)
    ///
    /// "These six bytes (Register 0x32 to Register 0x37) are eight bits
    /// each and hold the output data for each axis. Register 0x32 and
    /// Register 0x33 hold the output data for the x-axis, Register 0x34 and
    /// Register 0x35 hold the output data for the y-axis, and Register 0x36
    /// and Register 0x37 hold the output data for the z-axis. The output
    /// data is twos complement, with DATAx0 as the least significant
    /// byte and DATAx1 as the most significant byte, where x represent X,
    /// Y, or Z. The DATA_FORMAT register (Address 0x31) controls
    /// the format of the data. It is recommended that a multiple-byte
    /// read of all registers be performed to prevent a change in data
    /// between reads of sequential registers."
    DATAX0 = 0x32,

    /// X-axis data 1 (Read Only)
    ///
    /// See DATAX0 notes.
    DATAX1 = 0x33,

    /// Y-axis data 0 (Read Only)
    ///
    /// See DATAX0 notes.
    DATAY0 = 0x34,

    /// Y-axis data 1 (Read Only)
    ///
    /// See DATAX0 notes.
    DATAY1 = 0x35,

    /// Z-axis data 0 (Read Only)
    ///
    /// See DATAX0 notes.
    DATAZ0 = 0x36,

    /// Z-axis data 1 (Read Only)
    ///
    /// See DATAX0 notes.
    DATAZ1 = 0x37,

    /// FIFO control (Read/Write)
    ///
    /// See data sheet for documentation (p.25)
    FIFO_CTL = 0x38,

    /// FIFO status (Read Only)
    ///
    /// See data sheet for documentation (p.25)
    FIFO_STATUS = 0x39,
}

impl Register {
    /// Get register address
    pub fn addr(self) -> u8 {
        self as u8
    }

    /// Is the register read-only?
    pub fn read_only(self) -> bool {
        match self {
            Register::DEVID
            | Register::ACT_TAP_STATUS
            | Register::INT_SOURCE
            | Register::DATAX0
            | Register::DATAX1
            | Register::DATAY0
            | Register::DATAY1
            | Register::DATAZ0
            | Register::DATAZ1
            | Register::FIFO_STATUS => true,
            _ => false,
        }
    }
}

bitflags! {
    /// Flags passed as operands to `Register::DATA_FORMAT`
    ///
    /// "The DATA_FORMAT register controls the presentation of data
    /// to Register 0x32 through Register 0x37. All data, except that for
    /// the ±16 g range, must be clipped to avoid rollover."
    pub struct DataFormatFlags: u8 {
        /// "A setting of 1 in the SELF_TEST bit applies a self-test force to
        /// the sensor, causing a shift in the output data. A value of 0 disables
        /// the self-test force."
        const SELF_TEST = 0b10000000;

        /// "A value of 1 in the SPI bit sets the device to 3-wire SPI mode,
        /// and a value of 0 sets the device to 4-wire SPI mode"
        const SPI = 0b01000000;

        /// "A value of 0 in the INT_INVERT bit sets the interrupts to active
        /// high, and a value of 1 sets the interrupts to active low."
        const INT_INVERT = 0b00100000;

        /// "When this bit is set to a value of 1, the device is in full resolution
        /// mode, where the output resolution increases with the g range
        /// set by the range bits to maintain a 4 mg/LSB scale factor. When
        /// the FULL_RES bit is set to 0, the device is in 10-bit mode, and
        /// the range bits determine the maximum g range and scale factor"
        const FULL_RES = 0b00001000;

        /// A setting of 1 in the justify bit selects left-justified (MSB) mode,
        /// and a setting of 0 selects right-justified mode with sign extension.
        const JUSTIFY = 0b00000100;

        /// Range high bit (see `DataFormatRange`)
        const RANGE_HI = 0b00000010;

        /// Range low bit (see `DataFormatRange`)
        const RANGE_LO = 0b00000001;
    }
}

/// Default `DATA_FORMAT` settings:
///
/// - `SELF_TEST`: false
/// - `SPI`: false
/// - `INT_INVERT`: false
/// - `FULL_RES`: false
/// - `JUSTIFY`: false
/// - Range: ±2g (i.e. 0)
impl Default for DataFormatFlags {
    fn default() -> Self {
        DataFormatFlags::empty()
    }
}

impl From<DataFormatRange> for DataFormatFlags {
    fn from(range: DataFormatRange) -> DataFormatFlags {
        range.bits()
    }
}

/// g-Range setting flags which can be OR'd with `DataFormatFlags` and passed as
/// operands to `Register::DATA_FORMAT`
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[repr(u8)]
pub enum DataFormatRange {
    /// ±2g
    PLUSMINUS_2G = 0b00,

    /// ±4g
    PLUSMINUS_4G = 0b01,

    /// ±8g
    PLUSMINUS_8G = 0b10,

    /// ±16g
    PLUSMINUS_16G = 0b11,
}

impl DataFormatRange {
    /// Get `DataFormatFlags` representation
    pub fn bits(self) -> DataFormatFlags {
        match self {
            DataFormatRange::PLUSMINUS_2G => DataFormatFlags::empty(),
            DataFormatRange::PLUSMINUS_4G => DataFormatFlags::RANGE_LO,
            DataFormatRange::PLUSMINUS_8G => DataFormatFlags::RANGE_HI,
            DataFormatRange::PLUSMINUS_16G => DataFormatFlags::RANGE_HI | DataFormatFlags::RANGE_LO,
        }
    }
}