Skip to main content

i3g4250d/
lib.rs

1//! A platform agnostic driver to interface with the I3G4250D (gyroscope)
2//!
3//! This driver was built using [`embedded-hal`] traits.
4//!
5//! [`embedded-hal`]: https://docs.rs/embedded-hal/0.2
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(missing_docs)]
14#![deny(warnings)]
15#![no_std]
16
17
18
19use embedded_hal::blocking::spi::{Transfer, Write};
20use embedded_hal::digital::v2::OutputPin;
21use embedded_hal::spi::{Mode};
22
23/// SPI mode
24pub const MODE: Mode = embedded_hal::spi::MODE_3;
25
26
27/// I3G4250D driver
28pub struct I3G4250D<SPI, CS> {
29    spi: SPI,
30    cs: CS,
31}
32
33impl<SPI, CS, E> I3G4250D<SPI, CS>
34where
35    SPI: Transfer<u8, Error = E> + Write<u8, Error = E>,
36    CS: OutputPin,
37{
38    /// Creates a new driver from a SPI peripheral and a NCS pin
39    pub fn new(spi: SPI, cs: CS) -> Result<Self, E> {
40        let mut i3g4259d = I3G4250D { spi, cs };
41
42        // power up and enable all the axes
43        i3g4259d.write_register(Register::CTRL_REG1, 0b00_00_1_111)?;
44
45        Ok(i3g4259d)
46    }
47
48    /// Temperature measurement + gyroscope measurements
49    pub fn all(&mut self) -> Result<Measurements, E> {
50        let mut bytes = [0u8; 9];
51        self.read_many(Register::OUT_TEMP, &mut bytes)?;
52
53        Ok(Measurements {
54            gyro: I16x3 {
55                x: (bytes[3] as u16 + ((bytes[4] as u16) << 8)) as i16,
56                y: (bytes[5] as u16 + ((bytes[6] as u16) << 8)) as i16,
57                z: (bytes[7] as u16 + ((bytes[8] as u16) << 8)) as i16,
58            },
59            temp: bytes[1] as i8,
60        })
61    }
62
63    /// Gyroscope measurements
64    pub fn gyro(&mut self) -> Result<I16x3, E> {
65        let mut bytes = [0u8; 7];
66        self.read_many(Register::OUT_X_L, &mut bytes)?;
67
68        Ok(I16x3 {
69            x: (bytes[1] as u16 + ((bytes[2] as u16) << 8)) as i16,
70            y: (bytes[3] as u16 + ((bytes[4] as u16) << 8)) as i16,
71            z: (bytes[5] as u16 + ((bytes[6] as u16) << 8)) as i16,
72        })
73    }
74
75    /// Temperature sensor measurement
76    pub fn temp(&mut self) -> Result<i8, E> {
77        Ok(self.read_register(Register::OUT_TEMP)? as i8)
78    }
79
80    /// Reads the WHO_AM_I register; should return `0xD4`
81    pub fn who_am_i(&mut self) -> Result<u8, E> {
82        self.read_register(Register::WHO_AM_I)
83    }
84
85    /// Read `STATUS_REG` of sensor
86    pub fn status(&mut self) -> Result<Status, E> {
87        let sts = self.read_register(Register::STATUS_REG)?;
88        Ok(Status::from_u8(sts))
89    }
90
91    /// Get the current Output Data Rate
92    pub fn odr(&mut self) -> Result<Odr, E> {
93        // Read control register
94        let reg1 = self.read_register(Register::CTRL_REG1)?;
95        Ok(Odr::from_u8(reg1))
96    }
97
98    /// Set the Output Data Rate
99    pub fn set_odr(&mut self, odr: Odr) -> Result<&mut Self, E> {
100        self.change_config(Register::CTRL_REG1, odr)
101    }
102
103    /// Get current Bandwidth
104    pub fn bandwidth(&mut self) -> Result<Bandwidth, E> {
105        let reg1 = self.read_register(Register::CTRL_REG1)?;
106        Ok(Bandwidth::from_u8(reg1))
107    }
108
109    /// Set low-pass cut-off frequency (i.e. bandwidth)
110    ///
111    /// See `Bandwidth` for further explanation
112    pub fn set_bandwidth(&mut self, bw: Bandwidth) -> Result<&mut Self, E> {
113        self.change_config(Register::CTRL_REG1, bw)
114    }
115
116    /// Get the current Full Scale Selection
117    ///
118    /// This is the sensitivity of the sensor, see `Scale` for more information
119    pub fn scale(&mut self) -> Result<Scale, E> {
120        let scl = self.read_register(Register::CTRL_REG4)?;
121        Ok(Scale::from_u8(scl))
122    }
123
124    /// Set the Full Scale Selection
125    ///
126    /// This sets the sensitivity of the sensor, see `Scale` for more
127    /// information
128    pub fn set_scale(&mut self, scale: Scale) -> Result<&mut Self, E> {
129        self.change_config(Register::CTRL_REG4, scale)
130    }
131
132    fn read_register(&mut self, reg: Register) -> Result<u8, E> {
133        let _ = self.cs.set_low();
134
135        let mut buffer = [reg.addr() | SINGLE | READ, 0];
136        self.spi.transfer(&mut buffer)?;
137
138        let _ = self.cs.set_high();
139
140        Ok(buffer[1])
141    }
142
143    /// Read multiple bytes starting from the `start_reg` register.
144    /// This function will attempt to fill the provided buffer.
145    fn read_many(&mut self,
146                 start_reg: Register,
147                 buffer: &mut [u8])
148                 -> Result<(), E> {
149        let _ = self.cs.set_low();
150        buffer[0] = start_reg.addr() | MULTI | READ ;
151        self.spi.transfer(buffer)?;
152        let _ = self.cs.set_high();
153
154        Ok(())
155    }
156
157
158    fn write_register(&mut self, reg: Register, byte: u8) -> Result<(), E> {
159        let _ = self.cs.set_low();
160
161        let buffer = [reg.addr() | SINGLE | WRITE, byte];
162        self.spi.write(&buffer)?;
163
164        let _ = self.cs.set_high();
165
166        Ok(())
167    }
168
169    /// Change configuration in register
170    ///
171    /// Helper function to update a particular part of a register without
172    /// affecting other parts of the register that might contain desired
173    /// configuration. This allows the `I3G4250D` struct to be used like
174    /// a builder interface when configuring specific parameters.
175    fn change_config<B: BitValue>(&mut self, reg: Register, bits: B) -> Result<&mut Self, E> {
176        // Create bit mask from width and shift of value
177        let mask = B::mask() << B::shift();
178        // Extract the value as u8
179        let bits = (bits.value() << B::shift()) & mask;
180        // Read current value of register
181        let current = self.read_register(reg)?;
182        // Use supplied mask so we don't affect more than necessary
183        let masked = current & !mask;
184        // Use `or` to apply the new value without affecting other parts
185        let new_reg = masked | bits;
186        self.write_register(reg, new_reg)?;
187        Ok(self)
188    }
189}
190
191/// Trait to represent a value that can be sent to sensor
192trait BitValue {
193    /// The width of the bitfield in bits
194    fn width() -> u8;
195    /// The bit 'mask' of the value
196    fn mask() -> u8 {
197        (1 << Self::width()) - 1
198    }
199    /// The number of bits to shift the mask by
200    fn shift() -> u8;
201    /// Convert the type to a byte value to be sent to sensor
202    ///
203    /// # Note
204    /// This value should not be bit shifted.
205    fn value(&self) -> u8;
206}
207
208#[allow(dead_code)]
209#[allow(non_camel_case_types)]
210#[derive(Clone, Copy)]
211enum Register {
212    WHO_AM_I = 0x0F,
213    CTRL_REG1 = 0x20,
214    CTRL_REG2 = 0x21,
215    CTRL_REG3 = 0x22,
216    CTRL_REG4 = 0x23,
217    CTRL_REG5 = 0x24,
218    REFERENCE = 0x25,
219    OUT_TEMP = 0x26,
220    STATUS_REG = 0x27,
221    OUT_X_L = 0x28,
222    OUT_X_H = 0x29,
223    OUT_Y_L = 0x2A,
224    OUT_Y_H = 0x2B,
225    OUT_Z_L = 0x2C,
226    OUT_Z_H = 0x2D,
227    FIFO_CTRL_REG = 0x2E,
228    FIFO_SRC_REG = 0x2F,
229    INT1_CFG = 0x30,
230    INT1_SRC = 0x31,
231    INT1_TSH_XH = 0x32,
232    INT1_TSH_XL = 0x33,
233    INT1_TSH_YH = 0x34,
234    INT1_TSH_YL = 0x35,
235    INT1_TSH_ZH = 0x36,
236    INT1_TSH_ZL = 0x37,
237    INT1_DURATION = 0x38,
238}
239
240
241
242/// Output Data Rate
243#[derive(Debug, Clone, Copy)]
244pub enum Odr {
245    /// 100 Hz data rate
246    Hz100 = 0x00,
247    /// 200 Hz data rate
248    Hz200 = 0x01,
249    /// 400 Hz data rate
250    Hz400 = 0x02,
251    /// 800 Hz data rate
252    Hz800 = 0x03,
253}
254
255impl BitValue for Odr {
256    fn width() -> u8 {
257        2
258    }
259    fn shift() -> u8 {
260        6
261    }
262    fn value(&self) -> u8 {
263        *self as u8
264    }
265}
266
267impl Odr {
268    fn from_u8(from: u8) -> Self {
269        // Extract ODR value, converting to enum (ROI: 0b1100_0000)
270        match (from >> Odr::shift()) & Odr::mask() {
271            x if x == Odr::Hz100 as u8 => Odr::Hz100,
272            x if x == Odr::Hz200 as u8 => Odr::Hz200,
273            x if x == Odr::Hz400 as u8 => Odr::Hz400,
274            x if x == Odr::Hz800 as u8 => Odr::Hz800,
275            _ => unreachable!(),
276        }
277    }
278}
279
280/// Full scale selection
281#[derive(Debug, Clone, Copy)]
282pub enum Scale {
283    /// 245 Degrees Per Second
284    Dps245 = 0x00,
285    /// 500 Degrees Per Second
286    Dps500 = 0x01,
287    /// 2000 Degrees Per Second
288    Dps2000 = 0x03,
289}
290
291impl BitValue for Scale {
292    fn width() -> u8 {
293        2
294    }
295    fn shift() -> u8 {
296        4
297    }
298    fn value(&self) -> u8 {
299        *self as u8
300    }
301}
302
303impl Scale {
304    fn from_u8(from: u8) -> Self {
305        // Extract scale value from register, ensure that we mask with
306        // `0b0000_0011` to extract `FS1-FS2` part of register
307        match (from >> Scale::shift()) & Scale::mask() {
308            x if x == Scale::Dps245 as u8 => Scale::Dps245,
309            x if x == Scale::Dps500 as u8 => Scale::Dps500,
310            x if x == Scale::Dps2000 as u8 => Scale::Dps2000,
311            // Special case for Dps2000
312            0x02 => Scale::Dps2000,
313            _ => unreachable!(),
314        }
315    }
316}
317
318/// Bandwidth of sensor
319///
320/// The bandwidth of the sensor is equal to the cut-off for the low-pass
321/// filter. The cut-off depends on the `Odr` of the sensor, for specific
322/// information consult the data sheet.
323#[derive(Debug, Clone, Copy)]
324pub enum Bandwidth {
325    /// Lowest possible cut-off for any `Odr` configuration
326    Low = 0x00,
327    /// Medium cut-off, can be the same as `High` for some `Odr` configurations
328    Medium = 0x01,
329    /// High cut-off
330    High = 0x02,
331    /// Maximum cut-off for any `Odr` configuration
332    Maximum = 0x03,
333}
334
335impl BitValue for Bandwidth {
336    fn width() -> u8 {
337        2
338    }
339    fn shift() -> u8 {
340        4
341    }
342    fn value(&self) -> u8 {
343        *self as u8
344    }
345}
346
347impl Bandwidth {
348    fn from_u8(from: u8) -> Self {
349        // Shift and mask bandwidth of register, (ROI: 0b0011_0000)
350        match (from >> Bandwidth::shift()) & Bandwidth::mask() {
351            x if x == Bandwidth::Low as u8 => Bandwidth::Low,
352            x if x == Bandwidth::Medium as u8 => Bandwidth::Medium,
353            x if x == Bandwidth::High as u8 => Bandwidth::High,
354            x if x == Bandwidth::Maximum as u8 => Bandwidth::Maximum,
355            _ => unreachable!(),
356        }
357    }
358}
359
360const READ: u8 = 1 << 7;
361const WRITE: u8 = 0 << 7;
362const MULTI: u8 = 1 << 6;
363const SINGLE: u8 = 0 << 6;
364
365impl Register {
366    fn addr(self) -> u8 {
367        self as u8
368    }
369}
370
371impl Scale {
372    /// Convert a measurement to degrees
373    pub fn degrees(&self, val: i16) -> f32 {
374        match *self {
375            Scale::Dps245 => val as f32 * 0.00875,
376            Scale::Dps500 => val as f32 * 0.0175,
377            Scale::Dps2000 => val as f32 * 0.07,
378        }
379    }
380
381    /// Convert a measurement to radians
382    pub fn radians(&self, val: i16) -> f32 {
383        // TODO: Use `to_radians` or other built in method
384        // NOTE: `to_radians` is only exported in `std` (07.02.18)
385        self.degrees(val) * (core::f32::consts::PI / 180.0)
386    }
387}
388
389/// XYZ triple
390#[derive(Debug)]
391pub struct I16x3 {
392    /// X component
393    pub x: i16,
394    /// Y component
395    pub y: i16,
396    /// Z component
397    pub z: i16,
398}
399
400/// Several measurements
401#[derive(Debug)]
402pub struct Measurements {
403    /// Gyroscope measurements
404    pub gyro: I16x3,
405    /// Temperature sensor measurement
406    pub temp: i8,
407}
408
409/// Sensor status
410#[derive(Debug, Clone, Copy)]
411pub struct Status {
412    /// Overrun (data has overwritten previously unread data)
413    /// has occurred on at least one axis
414    pub overrun: bool,
415    /// Overrun occurred on Z-axis
416    pub z_overrun: bool,
417    /// Overrun occurred on Y-axis
418    pub y_overrun: bool,
419    /// Overrun occurred on X-axis
420    pub x_overrun: bool,
421    /// New data is available for either X, Y, Z - axis
422    pub new_data: bool,
423    /// New data is available on Z-axis
424    pub z_new: bool,
425    /// New data is available on Y-axis
426    pub y_new: bool,
427    /// New data is available on X-axis
428    pub x_new: bool,
429}
430
431impl Status {
432    fn from_u8(from: u8) -> Self {
433        Status {
434            overrun: (from & 1 << 7) != 0,
435            z_overrun: (from & 1 << 6) != 0,
436            y_overrun: (from & 1 << 5) != 0,
437            x_overrun: (from & 1 << 4) != 0,
438            new_data: (from & 1 << 3) != 0,
439            z_new: (from & 1 << 2) != 0,
440            y_new: (from & 1 << 1) != 0,
441            x_new: (from & 1 << 0) != 0,
442        }
443    }
444}