Skip to main content

adxl355/
lib.rs

1//! ADXL355 embedded-hal SPI driver crate
2//!
3//! A platform agnostic driver to interface with the ADXL355 Accelerometer.
4//! This driver uses SPI via [embedded-hal] and implements the [`Accelerometer` trait][trait]
5//! from the `accelerometer` crate.
6//!
7//! [embedded-hal]: https://docs.rs/embedded-hal
8//! [trait]: https://docs.rs/accelerometer/latest/accelerometer/trait.Accelerometer.html
9//!
10//!
11//! # Usage
12//!
13//! Use embedded-hal implementation to get SPI and a GPIO OutputPin for the chip select,
14//! then create the accelerometer handle
15//!
16//! ```
17//!
18//! use adxl355::{Adxl355, Config as ADXLConfig, ODR_LPF, Range, Accelerometer};
19//!
20//! // to create sensor with default configuration:
21//! let mut accelerometer = Adxl355::default(spi, cs)?;
22//!
23//! // start measurements
24//! accelerometer.start();
25//!
26//! // to get 3d accerlation data:
27//! let accel = accelerometer.accel_norm()?;
28//! println!("{:?}", accel);
29//!
30//! // One can also use conf module to supply configuration:
31//!
32//! let mut accelerometer =
33//!     Adxl355::new(spi, cs,
34//!                     ADXLConfig::new()
35//!                     .odr(ODR_LPF::ODR_31_25_Hz)
36//!                     .range(Range::_2G))?;
37//! ```
38//!
39//! # References
40//!
41//! - [Register Map][1]
42//!
43//! [1]: https://www.analog.com/media/en/technical-documentation/data-sheets/adxl354_355.pdf
44//!
45//! - [`embedded-hal`][2]
46//!
47//! [2]: https://github.com/rust-embedded/embedded-hal
48//!
49//!
50
51
52
53#![no_std]
54
55mod conf;
56mod register;
57
58use core::fmt::Debug;
59
60use embedded_hal as hal;
61
62use hal::blocking::spi;
63use hal::digital::v2::OutputPin;
64
65pub use accelerometer::{Accelerometer, RawAccelerometer, error, Error, vector::{I32x3, F32x3}};
66
67pub use conf::*;
68use register::Register;
69
70const SPI_READ: u8 = 0x01;
71const SPI_WRITE: u8 = 0x00;
72
73const EXPECTED_DEVICE_ID: u8 = 0xED;
74
75const ACCEL_MAX_I20: u32 = 524_287; // = 2^(20-1)-1
76
77
78/// ADXL355 driver
79pub struct Adxl355<SPI, CS> {
80    spi: SPI,
81    cs: CS,
82
83    // configuration
84    odr: ODR_LPF,
85    hpf: HPF_CORNER,
86    range: Range,
87}
88
89
90impl<SPI, CS, E, PinError> Adxl355<SPI, CS>
91where
92    SPI: spi::Transfer<u8, Error=E> + spi::Write<u8, Error=E>,
93    CS: OutputPin<Error = PinError>
94{
95
96
97    /// Creates a new `adxl355` driver from a SPI peripheral with
98    /// default configuration.
99    pub fn default(spi:SPI, cs:CS) -> Result<Self, E> {
100        Adxl355::new(spi, cs, &Config::new())
101    }
102
103    /// Takes a config object to initialize the adxl355 driver
104    pub fn new(spi:SPI, cs:CS, config: &Config) -> Result<Self, E> {
105        let mut adxl355 = Adxl355 {
106            spi,
107            cs,
108            odr: config.odr.unwrap_or_default(),
109            hpf: config.hpf.unwrap_or_default(),
110            range: config.range.unwrap_or_default()
111        };
112
113
114        let id = adxl355.get_device_id();
115
116        if id != EXPECTED_DEVICE_ID {
117            // error
118
119        }
120
121        adxl355.write_reg(Register::FILTER.addr(), (adxl355.hpf.val() << 4) | adxl355.odr.val());
122        adxl355.write_reg(Register::RANGE.addr(), adxl355.range.val());
123
124        Ok(adxl355)
125    }
126
127    /// Puts the device in `Measurement mode`. The defaut after power up is `Standby mode`.
128    pub fn start(&mut self) {
129        self.write_reg(Register::POWER_CTL.addr(), 0);
130    }
131
132
133    /// Returns the raw contents of the temperature registers
134    pub fn read_temp_raw(&mut self) -> u16 {
135
136        let mut bytes = [(Register::TEMP2.addr() << 1)  | SPI_READ, 0, 0];
137        self.read(&mut bytes);
138
139        let temp_h = ((bytes[1] & 0x0F) as u16) << 8;
140        let temp_l = (bytes[2] as u16) & 0x00FF;
141
142        temp_h | temp_l
143    }
144
145    /// Get the device ID
146    pub fn get_device_id(&mut self) -> u8 {
147        let reg = Register::DEVID.addr();
148        let mut output = [1u8];
149        self.read_reg(reg, &mut output);
150        output[0]
151    }
152
153    fn write_reg(&mut self, reg: u8, value: u8) {
154        let mut bytes = [(reg << 1)  | SPI_WRITE, value];
155        self.cs.set_low().ok();
156        self.spi.write(&mut bytes).ok();
157        self.cs.set_high().ok();
158    }
159
160    fn read_reg(&mut self, reg: u8, buffer: &mut [u8]) {
161        let mut bytes = [(reg << 1)  | SPI_READ, 0];
162        self.cs.set_low().ok();
163        self.spi.transfer(&mut bytes).ok();
164        self.cs.set_high().ok();
165        buffer[0] = bytes[1];
166    }
167
168    fn read(&mut self, bytes: &mut [u8]) {
169        self.cs.set_low().ok();
170        self.spi.transfer(bytes).ok();
171        self.cs.set_high().ok();
172    }
173}
174
175impl<SPI, CS, E, EO> RawAccelerometer<I32x3> for Adxl355<SPI, CS>
176where
177    SPI: spi::Transfer<u8, Error=E> + spi::Write<u8, Error=E>,
178    CS: OutputPin<Error = EO>,
179    E: Debug
180{
181    type Error = E;
182
183    /// Gets acceleration vector reading from the accelerometer
184    /// Returns a 3D vector with x,y,z, fields in a Result
185    fn accel_raw(&mut self) -> Result<I32x3, Error<E>> {
186        let mut bytes = [0u8; 9+1];
187        bytes[0] = (Register::XDATA3.addr() << 1)  | SPI_READ;
188        self.read(&mut bytes);
189
190        // combine 3 bytes into one i32 value
191        // right-shift with sign-extend to 20-bit
192        let x = ((((bytes[1] as i32) << 24) | ((bytes[2] as i32) << 16) | ((bytes[3] & 0xF0) as i32) << 8)) >> 12;
193        let y = ((((bytes[4] as i32) << 24) | ((bytes[5] as i32) << 16) | ((bytes[6] & 0xF0) as i32) << 8)) >> 12;
194        let z = ((((bytes[7] as i32) << 24) | ((bytes[8] as i32) << 16) | ((bytes[9] & 0xF0) as i32) << 8)) >> 12;
195
196        Ok(I32x3::new(x, y, z))
197    }
198
199}
200
201impl<SPI, CS, E, PinError> Accelerometer for Adxl355<SPI, CS>
202where
203    SPI: spi::Transfer<u8, Error=E> + spi::Write<u8, Error=E>,
204    CS: OutputPin<Error = PinError>,
205    E: Debug
206{
207    type Error = E;
208
209    fn sample_rate(&mut self) -> Result<f32, Error<Self::Error>> {
210        Ok(self.odr.into())
211    }
212
213    fn accel_norm(&mut self) -> Result<F32x3, Error<Self::Error>> {
214        let raw_data: I32x3 = self.accel_raw()?;
215        let range: f32 = self.range.into(); // range in [g], so 2, 4 or 8
216
217        let x = (raw_data.x as f32 / ACCEL_MAX_I20 as f32) * range;
218        let y = (raw_data.y as f32 / ACCEL_MAX_I20 as f32) * range;
219        let z = (raw_data.z as f32 / ACCEL_MAX_I20 as f32) * range;
220
221        Ok(F32x3::new(x, y, z))
222    }
223}