l3gd20/
lib.rs

1//! A platform agnostic driver to interface with the L3GD20 (gyroscope)
2//!
3//! This driver was built using [`embedded-hal`] traits.
4//!
5//! [`embedded-hal`]: https://docs.rs/embedded-hal/1.0
6//!
7//! # Examples
8//!
9//! You should find at least one example in the [f3] crate.
10//!
11//! [f3]: https://docs.rs/f3/0.6
12
13#![deny(warnings)]
14#![no_std]
15
16#[path = "."]
17#[allow(clippy::duplicate_mod)]
18/// Asynchronous support.
19pub mod asynchronous {
20    use bisync::asynchronous::*;
21    /// I2C module.
22    pub mod i2c;
23    /// SPI module.
24    pub mod spi;
25}
26
27// here you could also add `#[cfg]` attributes to enable or disable this module
28#[path = "."]
29/// Blocking support.
30pub mod blocking {
31    use bisync::synchronous::*;
32    /// I2C module.
33    pub mod i2c;
34    /// SPI module.
35    pub mod spi;
36}
37
38/// Re-export the blocking module as the default.
39pub use blocking::*;
40
41/// Minimal time in nanoseconds between chip select assertion and clock edge.
42pub const MINIMUM_CS_SETUP_TIME_NS: u32 = 5;
43
44/// Expected WHO_AM_I register value for the L3GD20 sensor.
45pub const WHO_AM_I_L3GD20: u8 = 0xD4;
46/// Expected WHO_AM_I register value for the L3GD20H sensor.
47pub const WHO_AM_I_L3GD20H: u8 = 0xD7;
48
49/// Trait to represent a value that can be sent to sensor
50pub trait BitValue {
51    /// The width of the bitfield in bits
52    fn width() -> u8;
53    /// The bit 'mask' of the value
54    fn mask() -> u8 {
55        (1 << Self::width()) - 1
56    }
57    /// The number of bits to shift the mask by
58    fn shift() -> u8;
59    /// Convert the type to a byte value to be sent to sensor
60    ///
61    /// # Note
62    /// This value should not be bit shifted.
63    fn value(&self) -> u8;
64}
65
66#[allow(dead_code)]
67#[allow(non_camel_case_types)]
68#[allow(clippy::upper_case_acronyms)]
69#[derive(Clone, Copy)]
70#[cfg_attr(feature = "defmt", derive(defmt::Format))]
71pub enum Register {
72    WHO_AM_I = 0x0F,
73    CTRL_REG1 = 0x20,
74    CTRL_REG2 = 0x21,
75    CTRL_REG3 = 0x22,
76    CTRL_REG4 = 0x23,
77    CTRL_REG5 = 0x24,
78    REFERENCE = 0x25,
79    OUT_TEMP = 0x26,
80    STATUS_REG = 0x27,
81    OUT_X_L = 0x28,
82    OUT_X_H = 0x29,
83    OUT_Y_L = 0x2A,
84    OUT_Y_H = 0x2B,
85    OUT_Z_L = 0x2C,
86    OUT_Z_H = 0x2D,
87    FIFO_CTRL_REG = 0x2E,
88    FIFO_SRC_REG = 0x2F,
89    INT1_CFG = 0x30,
90    INT1_SRC = 0x31,
91    INT1_TSH_XH = 0x32,
92    INT1_TSH_XL = 0x33,
93    INT1_TSH_YH = 0x34,
94    INT1_TSH_YL = 0x35,
95    INT1_TSH_ZH = 0x36,
96    INT1_TSH_ZL = 0x37,
97    INT1_DURATION = 0x38,
98}
99
100/// Output Data Rate
101#[derive(Debug, Clone, Copy)]
102#[cfg_attr(feature = "defmt", derive(defmt::Format))]
103pub enum Odr {
104    /// 95 Hz data rate
105    Hz95 = 0x00,
106    /// 190 Hz data rate
107    Hz190 = 0x01,
108    /// 380 Hz data rate
109    Hz380 = 0x02,
110    /// 760 Hz data rate
111    Hz760 = 0x03,
112}
113
114impl BitValue for Odr {
115    fn width() -> u8 {
116        2
117    }
118    fn shift() -> u8 {
119        6
120    }
121    fn value(&self) -> u8 {
122        *self as u8
123    }
124}
125
126impl Odr {
127    fn from_u8(from: u8) -> Self {
128        // Extract ODR value, converting to enum (ROI: 0b1100_0000)
129        match (from >> Odr::shift()) & Odr::mask() {
130            x if x == Odr::Hz95 as u8 => Odr::Hz95,
131            x if x == Odr::Hz190 as u8 => Odr::Hz190,
132            x if x == Odr::Hz380 as u8 => Odr::Hz380,
133            x if x == Odr::Hz760 as u8 => Odr::Hz760,
134            _ => unreachable!(),
135        }
136    }
137}
138
139/// Full scale selection
140#[derive(Debug, Clone, Copy)]
141#[cfg_attr(feature = "defmt", derive(defmt::Format))]
142pub enum Scale {
143    /// 250 Degrees Per Second
144    Dps250 = 0x00,
145    /// 500 Degrees Per Second
146    Dps500 = 0x01,
147    /// 2000 Degrees Per Second
148    Dps2000 = 0x03,
149}
150
151impl BitValue for Scale {
152    fn width() -> u8 {
153        2
154    }
155    fn shift() -> u8 {
156        4
157    }
158    fn value(&self) -> u8 {
159        *self as u8
160    }
161}
162
163impl Scale {
164    fn from_u8(from: u8) -> Self {
165        // Extract scale value from register, ensure that we mask with
166        // `0b0000_0011` to extract `FS1-FS2` part of register
167        match (from >> Scale::shift()) & Scale::mask() {
168            x if x == Scale::Dps250 as u8 => Scale::Dps250,
169            x if x == Scale::Dps500 as u8 => Scale::Dps500,
170            x if x == Scale::Dps2000 as u8 => Scale::Dps2000,
171            // Special case for Dps2000
172            0x02 => Scale::Dps2000,
173            _ => unreachable!(),
174        }
175    }
176}
177
178/// Bandwidth of sensor
179///
180/// The bandwidth of the sensor is equal to the cut-off for the low-pass
181/// filter. The cut-off depends on the `Odr` of the sensor, for specific
182/// information consult the data sheet.
183#[derive(Debug, Clone, Copy)]
184#[cfg_attr(feature = "defmt", derive(defmt::Format))]
185pub enum Bandwidth {
186    /// Lowest possible cut-off for any `Odr` configuration
187    Low = 0x00,
188    /// Medium cut-off, can be the same as `High` for some `Odr` configurations
189    Medium = 0x01,
190    /// High cut-off
191    High = 0x02,
192    /// Maximum cut-off for any `Odr` configuration
193    Maximum = 0x03,
194}
195
196impl BitValue for Bandwidth {
197    fn width() -> u8 {
198        2
199    }
200    fn shift() -> u8 {
201        4
202    }
203    fn value(&self) -> u8 {
204        *self as u8
205    }
206}
207
208impl Bandwidth {
209    fn from_u8(from: u8) -> Self {
210        // Shift and mask bandwidth of register, (ROI: 0b0011_0000)
211        match (from >> Bandwidth::shift()) & Bandwidth::mask() {
212            x if x == Bandwidth::Low as u8 => Bandwidth::Low,
213            x if x == Bandwidth::Medium as u8 => Bandwidth::Medium,
214            x if x == Bandwidth::High as u8 => Bandwidth::High,
215            x if x == Bandwidth::Maximum as u8 => Bandwidth::Maximum,
216            _ => unreachable!(),
217        }
218    }
219}
220
221impl Register {
222    fn addr(self) -> u8 {
223        self as u8
224    }
225}
226
227impl Scale {
228    /// Convert a measurement to degrees
229    pub fn degrees(&self, val: i16) -> f32 {
230        match *self {
231            Scale::Dps250 => val as f32 * 0.00875,
232            Scale::Dps500 => val as f32 * 0.0175,
233            Scale::Dps2000 => val as f32 * 0.07,
234        }
235    }
236
237    /// Convert a measurement to radians
238    pub fn radians(&self, val: i16) -> f32 {
239        // TODO: Use `to_radians` or other built in method
240        // NOTE: `to_radians` is only exported in `std` (07.02.18)
241        self.degrees(val) * (core::f32::consts::PI / 180.0)
242    }
243}
244
245/// XYZ triple
246#[derive(Debug, Clone, Copy)]
247#[cfg_attr(feature = "defmt", derive(defmt::Format))]
248pub struct I16x3 {
249    /// X component
250    pub x: i16,
251    /// Y component
252    pub y: i16,
253    /// Z component
254    pub z: i16,
255}
256
257/// Several measurements
258#[derive(Debug, Clone, Copy)]
259#[cfg_attr(feature = "defmt", derive(defmt::Format))]
260pub struct Measurements {
261    /// Gyroscope measurements
262    pub gyro: I16x3,
263    /// Raw temperature sensor measurement
264    pub temp_raw: i8,
265}
266
267impl Measurements {
268    /// Convert the raw temperature value to degrees celcius
269    pub fn temp_celcius(&self) -> i16 {
270        25 - self.temp_raw as i16
271    }
272}
273
274/// Sensor status
275#[derive(Debug, Clone, Copy)]
276#[cfg_attr(feature = "defmt", derive(defmt::Format))]
277pub struct Status {
278    /// Overrun (data has overwritten previously unread data)
279    /// has occurred on at least one axis
280    pub overrun: bool,
281    /// Overrun occurred on Z-axis
282    pub z_overrun: bool,
283    /// Overrun occurred on Y-axis
284    pub y_overrun: bool,
285    /// Overrun occurred on X-axis
286    pub x_overrun: bool,
287    /// New data is available for either X, Y, Z - axis
288    pub new_data: bool,
289    /// New data is available on Z-axis
290    pub z_new: bool,
291    /// New data is available on Y-axis
292    pub y_new: bool,
293    /// New data is available on X-axis
294    pub x_new: bool,
295}
296
297impl Status {
298    fn from_u8(from: u8) -> Self {
299        Status {
300            overrun: (from & (1 << 7)) != 0,
301            z_overrun: (from & (1 << 6)) != 0,
302            y_overrun: (from & (1 << 5)) != 0,
303            x_overrun: (from & (1 << 4)) != 0,
304            new_data: (from & (1 << 3)) != 0,
305            z_new: (from & (1 << 2)) != 0,
306            y_new: (from & (1 << 1)) != 0,
307            x_new: (from & 1) != 0,
308        }
309    }
310}