lis3mdl_driver/
lib.rs

1//! A platform agnostic driver to interface with the
2//! [LIS3MDL](https://www.st.com/en/mems-and-sensors/lis3mdl.html) (3-axis magnetic sensor).
3//!
4//! This driver was built using [`embedded-hal`] traits.
5//!
6//! [`embedded-hal`]: https://docs.rs/embedded-hal/0.2\
7
8#![no_std]
9
10use core::mem;
11use embedded_hal as hal;
12use crate::hal::blocking::i2c::{Write, WriteRead};
13
14use bitflags::bitflags;
15
16/// The full scale for measurement from 4 Gauss to 16 Gauss; Sensitivity of the sensor
17#[derive(Debug)]
18pub enum FullScale {
19    Fs4g,
20    Fs8g,
21    Fs12g,
22    Fs16g
23}
24
25/// The mode to operate in, impacts noise, power consumption, and speed
26#[derive(Debug)]
27pub enum OperatingMode {
28    LowPower,
29    MediumPerformance,
30    HighPerformance,
31    UltraHighPerformance
32}
33
34/// State of the LIS3MDL
35#[derive(Debug)]
36pub enum MeasurementMode {
37    Idle,
38    SingleMeasurement,
39    Continuous
40}
41
42/// Possible data rates at which the xyz data can be provided
43#[allow(non_camel_case_types)]
44#[derive(Debug)]
45pub enum DataRate {
46    ODR_0_625Hz,
47    ODR_1_25Hz,
48    ODR_2_5Hz,
49    ODR_5Hz,
50    ODR_10Hz,
51    ODR_20Hz,
52    ODR_40Hz,
53    ODR_80Hz,
54    /// Fastest obtainable data rate for the given operating mode
55    ODR_Fast
56}
57
58/// Driver Errors
59#[derive(Debug)]
60pub enum Error {
61    /// Any issue with the I<sup>2</sup>C connection
62    CommunicationError,
63    /// An invalid value was found, should not happen
64    InvalidValue,
65    /// The wrong device was present when queried, checked once on driver setup
66    IncorrectDeviceIdFound,
67}
68
69fn i2c_error<E>(_: E) -> Error {
70    Error::CommunicationError
71}
72
73/// XYZ triple for raw values (int16)
74#[derive(Debug)]
75pub struct I16xyz {
76    /// X component
77    pub x: i16,
78    /// Y component
79    pub y: i16,
80    /// Z component
81    pub z: i16,
82}
83
84/// XYZ triple 32-bit float
85#[derive(Debug)]
86pub struct I32xyz {
87    pub x: i32,
88    pub y: i32,
89    pub z: i32,
90}
91
92pub enum Address {
93    Addr1E = 0x1E,
94    Addr1C = 0x1C,
95}
96
97const LIS3MDL_DEVICE_ID: u8 = 0x3D;
98
99/// LIS3MDL driver
100pub struct Lis3mdl<I2C> {
101    i2c: I2C,
102    address: u8,
103}
104
105impl<I2C, E> Lis3mdl<I2C> where I2C: WriteRead<Error=E> + Write<Error=E>,
106{
107    /// Create a new driver from an I<sup>2</sup>C peripheral and configures default settings:
108    ///
109    /// * Full Scale Range: 12G
110    /// * Measurement Mode: Continuous Measurement
111    /// * Operating Mode: Ultra High Performance
112    /// * Enable the temperature sensor
113    /// * Set data rate to fast
114    /// * Enables Block Data Update
115    ///
116    /// These defaults may be changed after initialization with `set_full_scale`,
117    /// `set_measurement_mode`, `set_operating_mode` `set_temperature_sensor`,
118    /// `set_data_rate`, and `set_block_data_update`, respectively.
119    pub fn new (i2c: I2C, addr: Address) -> Result<Self,Error> {
120        let mut lis3mdl = Lis3mdl {
121            i2c,
122            address: addr as u8,
123        };
124
125        if lis3mdl.who_am_i()? != LIS3MDL_DEVICE_ID {
126            return Err(Error::IncorrectDeviceIdFound)
127        }
128
129        lis3mdl.set_full_scale(FullScale::Fs12g)?;
130        lis3mdl.set_operating_mode(OperatingMode::UltraHighPerformance)?;
131        lis3mdl.set_measurement_mode(MeasurementMode::Continuous)?;
132        lis3mdl.set_temperature_sensor_enable(true)?;
133        lis3mdl.set_data_rate(DataRate::ODR_Fast)?;
134        lis3mdl.set_block_data_update(true)?;
135
136        Ok(lis3mdl)
137    }
138
139    /// Reads the WHO_AM_I register; should return `0x3D`
140    pub fn who_am_i(&mut self) -> Result<u8, Error> {
141        self.read_register(Register::WHO_AM_I).map_err(i2c_error)
142    }
143
144    /// Reads the XYZ components values of the magnetic field and returns the raw signed 16-bit
145    /// integer value of each axis. This will return whatever is present in the data registers for
146    /// each axis, so it is recommend to ensure that you are in the proper measurement mode and
147    /// either synchronizing the read with interrupt/`xyz_data_available` or using
148    /// `set_block_data_update`.
149    ///
150    /// To obtain the value in Gauss or milliGauss either use `get_mag_axes_mgauss` or divide by
151    /// column 2 in the table below (obtained from Table 2 in AN4602 Rev 1):
152    ///
153    /// | Full-scale (G) | Gain@16-bit (LSB/Gauss) |
154    /// |----------------|-------------------------|
155    /// |        4       |           6842          |
156    /// |        8       |           3421          |
157    /// |        12      |           2281          |
158    /// |        16      |           1711          |
159    pub fn get_raw_mag_axes(&mut self) -> Result<I16xyz, Error> {
160
161        let x = self.read_x_raw()?;
162        let y = self.read_y_raw()?;
163        let z = self.read_z_raw()?;
164
165        Ok(I16xyz {
166            x,
167            y,
168            z
169        })
170    }
171
172    /// True if the XYZ data is available to be read
173    pub fn xyz_data_available(&mut self) -> Result<bool, Error> {
174        Ok(self.read_device_status()?.contains(StatusRegisterBits::ZYXDA))
175    }
176
177    /// Provide the magnetic field strength in each axis in milliGauss. Uses `get_raw_mag_axes` to
178    /// obtain the value.
179    pub fn get_mag_axes_mgauss(&mut self) -> Result<I32xyz, Error> {
180        let mag_data = self.get_raw_mag_axes()?;
181
182        let fullscale = FullScaleBits::from_bits(self.read_register(Register::CTRL_REG2)?).unwrap();
183
184        // Gain values from Table 2 in AN4602 Rev 1
185        let sensitivity: f64 = match fullscale {
186            FullScaleBits::FS4G => Ok(1000_f64/6842_f64),
187            FullScaleBits::FS8G => Ok(1000_f64/3421_f64),
188            FullScaleBits::FS12G => Ok(1000_f64/2281_f64),
189            FullScaleBits::FS16G => Ok(1000_f64/1711_f64),
190            _ => Err(Error::InvalidValue)
191        }?;
192
193        Ok(I32xyz {
194            x: (mag_data.x as f64 * sensitivity) as i32,
195            y: (mag_data.y as f64 * sensitivity) as i32,
196            z: (mag_data.z as f64 * sensitivity) as i32
197        })
198    }
199
200    /// Set the Full Scale from between 4 Gauss and 16 Gauss to adjust the input dynamic range,
201    /// based on the magnetic field to be measured. This will affect the output of
202    /// `get_raw_mag_axes` so use `get_mag_axes_mgauss` unless you intend to adjust the values
203    /// yourself.
204    pub fn set_full_scale(&mut self, scale: FullScale) -> Result<(),Error> {
205        // Mask for just the full scale bits.
206        let fs_mask = !(ControlRegister2Bits::FS1 | ControlRegister2Bits::FS0);
207
208        let fs = match scale {
209            FullScale::Fs4g => FullScaleBits::FS4G,
210            FullScale::Fs8g => FullScaleBits::FS8G,
211            FullScale::Fs12g => FullScaleBits::FS12G,
212            FullScale::Fs16g => FullScaleBits::FS16G,
213        };
214
215        // Zero out the full scale bits, we will replace them with the bitwise OR for the new setting
216        let existing_settings = self.read_control_register_2()? & fs_mask;
217
218        let reg2bits = ControlRegister2Bits::from_bits_truncate(fs.bits());
219
220        // Update the full scale setting, preserving the other values
221        self.set_control_register_2(reg2bits | existing_settings)
222    }
223
224    /// Adjust the operating mode. This will have an impact on current consumption, the max data
225    /// rate, and the output noise with Ultra High Performance (UHP) being the slowest and highest
226    /// current consumption with the lowest noise, and Low Power (LP) being the highest level of
227    /// noise, but offering up to 1000 Hz data rate and lowest current consumption. See AN4602 for
228    /// more details.
229    pub fn set_operating_mode(&mut self, mode: OperatingMode) -> Result<(), Error> {
230        // Masks for just the operating mode bits for X, Y, and Z axes
231        let reg1_mask = !(ControlRegister1Bits::OM1 | ControlRegister1Bits::OM0);
232        let reg4_mask = !(ControlRegister4Bits::OMZ1 | ControlRegister4Bits::OMZ0);
233
234        let om = match mode {
235            OperatingMode::LowPower => (ControlRegister1Bits::empty(), ControlRegister4Bits::empty()),
236            OperatingMode::MediumPerformance => (ControlRegister1Bits::OM0, ControlRegister4Bits::OMZ0),
237            OperatingMode::HighPerformance => (ControlRegister1Bits::OM1, ControlRegister4Bits::OMZ1),
238            OperatingMode::UltraHighPerformance => (ControlRegister1Bits::OM1
239                                                        | ControlRegister1Bits::OM0,
240                                                    ControlRegister4Bits::OMZ1
241                                                        | ControlRegister4Bits::OMZ0),
242        };
243
244        // zero out the entries for OM1/OM0 and OMZ1/OMZ0
245        let existing_reg_1_settings = self.read_control_register_1()? & reg1_mask;
246        let existing_reg_4_settings = self.read_control_register_4()? & reg4_mask;
247
248        // Update the operating mode settings, preserving the other values
249        self.set_control_register_1(existing_reg_1_settings | om.0)?;
250        self.set_control_register_4(existing_reg_4_settings | om.1)?;
251        Ok(())
252    }
253
254    /// Select between 3 measurement modes: Idle, Single Measurement, and Continuous. Configure to
255    /// Idle if not being used, Single Measurement if only one measurement is desired, and Continuous
256    /// if a constant stream of data is needed.
257    pub fn set_measurement_mode(&mut self, mode: MeasurementMode) -> Result<(), Error> {
258        // Mask for the measurement mode setting
259        let mm_mask = !(ControlRegister3Bits::MD1 | ControlRegister3Bits::MD0);
260
261        let mm = match mode {
262            MeasurementMode::Idle => ControlRegister3Bits::MD1,
263            MeasurementMode::SingleMeasurement => ControlRegister3Bits::MD0,
264            MeasurementMode::Continuous => ControlRegister3Bits::empty(),
265        };
266
267        // zero out the entries for MD1 and MD0
268        let existing_reg_3_settings = self.read_control_register_3()? & mm_mask;
269
270        // Update the measurement mode settings, preserving the other values
271        self.set_control_register_3(existing_reg_3_settings | mm)
272    }
273
274    /// Set the output data rate. Specific data rates from 0.625 Hz to 80 Hz can be configured for
275    /// any given operating mode, and Fast will be the highest achievable data rate for the given
276    /// operating mode at 1000 Hz for Low Power and 155 Hz for Ultra High Performance. See AN4602
277    /// for more details.
278    pub fn set_data_rate(&mut self, rate: DataRate) -> Result<(), Error> {
279        // Mask for the data rate setting
280        let odr_mask = !(ControlRegister1Bits::DO2 | ControlRegister1Bits::DO1
281            | ControlRegister1Bits::DO0 | ControlRegister1Bits::FAST_ODR);
282
283        let odr = match rate {
284            DataRate::ODR_0_625Hz => ControlRegister1Bits::empty(),
285            DataRate::ODR_1_25Hz => ControlRegister1Bits::DO0,
286            DataRate::ODR_2_5Hz => ControlRegister1Bits::DO1,
287            DataRate::ODR_5Hz => ControlRegister1Bits::DO1 | ControlRegister1Bits::DO0,
288            DataRate::ODR_10Hz => ControlRegister1Bits::DO2,
289            DataRate::ODR_20Hz => ControlRegister1Bits::DO2 | ControlRegister1Bits::DO0,
290            DataRate::ODR_40Hz => ControlRegister1Bits::DO2 | ControlRegister1Bits::DO1,
291            DataRate::ODR_80Hz => ControlRegister1Bits::DO2 | ControlRegister1Bits::DO1
292                                  | ControlRegister1Bits::DO0,
293            DataRate::ODR_Fast => ControlRegister1Bits::FAST_ODR,
294        };
295
296        // zero out the entries for DO2, DO1, DO0, and FAST_ODR
297        let existing_reg_1_settings = self.read_control_register_1()? & odr_mask;
298
299        // Update the measurement mode settings, preserving other values
300        self.set_control_register_1(existing_reg_1_settings | odr)
301    }
302
303    /// Blocks the refresh of data for a given axis until the initiated read for that axis
304    /// completes. Strongly recommended if the reading of the magnetic data cannot be synchronized
305    /// with the XYZDA in the status register. Ensures that data registers for each channel always
306    /// contain the most recent magnetic data.
307    pub fn set_block_data_update(&mut self, block: bool) -> Result<(), Error> {
308        let bdu_mask = !ControlRegister5Bits::BDU;
309
310        let existing_reg_5_settings = self.read_control_register_5()? & bdu_mask;
311
312        if block {
313            self.set_control_register_5(existing_reg_5_settings | ControlRegister5Bits::BDU)
314        }
315        else {
316            self.set_control_register_5(existing_reg_5_settings)
317        }
318    }
319
320    /// Enables the temperature sensor
321    pub fn set_temperature_sensor_enable(&mut self, enabled: bool) -> Result<(), Error> {
322        let temp_mask = !ControlRegister1Bits::TEMP_EN;
323
324        let existing_reg_1_settings = self.read_control_register_1()? & temp_mask;
325
326        if enabled {
327            self.set_control_register_1(existing_reg_1_settings | ControlRegister1Bits::TEMP_EN)
328        }
329        else {
330            self.set_control_register_1(existing_reg_1_settings)
331        }
332    }
333
334    fn read_x_raw(&mut self) -> Result<i16,Error> {
335        self.read_register_i16(Register::OUT_X_H,Register::OUT_X_L)
336    }
337
338    fn read_y_raw(&mut self) -> Result<i16,Error> {
339        self.read_register_i16(Register::OUT_Y_H,Register::OUT_Y_L)
340    }
341
342    fn read_z_raw(&mut self) -> Result<i16,Error> {
343        self.read_register_i16(Register::OUT_Z_H,Register::OUT_Z_L)
344    }
345
346    fn read_device_status(&mut self) -> Result<StatusRegisterBits, Error> {
347        Ok(StatusRegisterBits::from_bits_truncate(self.read_register(Register::STATUS_REG)?))
348    }
349
350    fn set_control_register_1(&mut self, bits: ControlRegister1Bits) -> Result<(),Error> {
351        Ok(self.write_register(Register::CTRL_REG1, bits.bits())?)
352    }
353
354    fn set_control_register_2(&mut self, bits: ControlRegister2Bits) -> Result<(),Error> {
355        Ok(self.write_register(Register::CTRL_REG2, bits.bits())?)
356    }
357
358    fn set_control_register_3(&mut self, bits: ControlRegister3Bits) -> Result<(),Error> {
359        Ok(self.write_register(Register::CTRL_REG3, bits.bits())?)
360    }
361
362    fn set_control_register_4(&mut self, bits: ControlRegister4Bits) -> Result<(),Error> {
363        Ok(self.write_register(Register::CTRL_REG4, bits.bits())?)
364    }
365
366    fn set_control_register_5(&mut self, bits: ControlRegister5Bits) -> Result<(),Error> {
367        Ok(self.write_register(Register::CTRL_REG5, bits.bits())?)
368    }
369
370    fn read_control_register_1(&mut self) -> Result<ControlRegister1Bits, Error> {
371        Ok(ControlRegister1Bits::from_bits_truncate(self.read_register(Register::CTRL_REG1)?))
372    }
373
374    fn read_control_register_2(&mut self) -> Result<ControlRegister2Bits, Error> {
375        Ok(ControlRegister2Bits::from_bits_truncate(self.read_register(Register::CTRL_REG2)?))
376    }
377
378    fn read_control_register_3(&mut self) -> Result<ControlRegister3Bits, Error> {
379        Ok(ControlRegister3Bits::from_bits_truncate(self.read_register(Register::CTRL_REG3)?))
380    }
381
382    fn read_control_register_4(&mut self) -> Result<ControlRegister4Bits, Error> {
383        Ok(ControlRegister4Bits::from_bits_truncate(self.read_register(Register::CTRL_REG4)?))
384    }
385
386    fn read_control_register_5(&mut self) -> Result<ControlRegister5Bits, Error> {
387        Ok(ControlRegister5Bits::from_bits_truncate(self.read_register(Register::CTRL_REG5)?))
388    }
389
390    fn read_register_i16(&mut self, reg_low: Register, reg_high: Register) -> Result<i16, Error> {
391        let low = self.read_register(reg_low)?;
392        let high = self.read_register(reg_high)?;
393
394        // Convert the low and high bytes to signed 16-bit integer
395        let signed = i16::from_le_bytes([high, low]);
396        Ok(signed)
397    }
398
399    fn read_register(&mut self, reg: Register) -> Result<u8, Error> {
400        let mut buffer: [u8; 1] = unsafe { mem::uninitialized() };
401        self.i2c.write_read(self.address, &[reg.addr()], &mut buffer).map_err(i2c_error)?;
402        Ok(buffer[0])
403    }
404
405    fn write_register(&mut self, reg: Register, byte: u8) -> Result<(), Error> {
406        self.i2c.write(self.address, &[reg.addr(), byte]).map_err(i2c_error)
407    }
408}
409
410#[allow(non_camel_case_types, dead_code)]
411#[derive(Debug, Copy, Clone)]
412enum Register {
413    OFFSET_X_REG_L_M = 0x05,
414    OFFSET_X_REG_H_M = 0x06,
415    OFFSET_Y_REG_L_M = 0x07,
416    OFFSET_Y_REG_H_M = 0x08,
417    OFFSET_Z_REG_L_M = 0x09,
418    OFFSET_Z_REG_H_M = 0x0A,
419
420    WHO_AM_I = 0x0F,
421
422    CTRL_REG1 = 0x20,
423    CTRL_REG2 = 0x21,
424    CTRL_REG3 = 0x22,
425    CTRL_REG4 = 0x23,
426    CTRL_REG5 = 0x24,
427
428    STATUS_REG = 0x27,
429    OUT_X_L = 0x28,
430    OUT_X_H = 0x29,
431    OUT_Y_L = 0x2A,
432    OUT_Y_H = 0x2B,
433    OUT_Z_L = 0x2C,
434    OUT_Z_H = 0x2D,
435    TEMP_OUT_L = 0x2E,
436    TEMP_OUT_H = 0x2F,
437    INT_CFG = 0x30,
438    INT_SRC = 0x31,
439    INT_THS_L = 0x32,
440    INT_THS_H = 0x33
441}
442
443impl Register {
444    fn addr(self) -> u8 {
445        self as u8
446    }
447}
448
449bitflags! {
450#[allow(non_camel_case_types, dead_code)]
451struct ControlRegister1Bits: u8 {
452    const TEMP_EN = 0b1000_0000;
453    const OM1 = 0b0100_0000;
454    const OM0 = 0b0010_0000;
455    const DO2 = 0b0001_0000;
456    const DO1 = 0b0000_1000;
457    const DO0 = 0b0000_0100;
458    const FAST_ODR = 0b0000_0010;
459    const ST = 0b0000_0001;
460}
461}
462
463bitflags! {
464#[allow(non_camel_case_types, dead_code)]
465struct ControlRegister2Bits: u8 {
466    const FS1 = 0b0100_0000;
467    const FS0 = 0b0010_0000;
468    const REBOOT = 0b0000_1000;
469    const SOFT_RST = 0b0000_0100;
470}
471}
472
473bitflags! {
474#[allow(non_camel_case_types, dead_code)]
475struct ControlRegister3Bits: u8 {
476    const LP = 0b0010_0000;
477    const SIM = 0b0000_0100;
478    const MD1 = 0b0000_0010;
479    const MD0 = 0b0000_0001;
480}
481}
482
483bitflags! {
484struct ControlRegister4Bits: u8 {
485    const OMZ1 = 0b0000_1000;
486    const OMZ0 = 0b0000_0100;
487    const BLE = 0b0000_0010;
488    const UHP = 0b0000_1100;
489}
490}
491
492bitflags! {
493struct ControlRegister5Bits: u8 {
494    const FAST_READ = 0b1000_0000;
495    const BDU = 0b0100_0000;
496}
497}
498
499bitflags! {
500struct StatusRegisterBits: u8 {
501    const ZYXOR = 0b1000_0000;
502    const ZOR = 0b0100_0000;
503    const YOR = 0b0010_0000;
504    const XOR = 0b0001_0000;
505    const ZYXDA = 0b0000_1000;
506    const ZDA = 0b0000_0100;
507    const YDA = 0b0000_0010;
508    const XDA = 0b0000_0001;
509}
510}
511
512bitflags! {
513#[allow(non_camel_case_types, dead_code)]
514struct FullScaleBits: u8 {
515    const FS4G = 0b0000_0000;
516    const FS8G = 0b0010_0000;
517    const FS12G = 0b0100_0000;
518    const FS16G = 0b0110_0000;
519}
520}