Skip to main content

qmc5883l/
lib.rs

1//! A platform agnostic driver to interface with the QMC5883L magnetometer.
2//!
3//! This driver was built using [`embedded-hal`] traits.
4//!
5//! [`embedded-hal`]: https://docs.rs/embedded-hal/~0.2
6
7#![deny(missing_docs)]
8#![no_std]
9
10extern crate embedded_hal as hal;
11
12use hal::blocking::i2c::{Write, WriteRead};
13
14const I2C_ADDRESS: u8 = 0x0d;
15
16#[allow(dead_code)]
17#[allow(non_camel_case_types)]
18#[derive(Copy, Clone)]
19#[repr(u8)]
20enum Register {
21    DATA_OUT_X_L = 0,
22    DATA_OUT_X_H = 1,
23    DATA_OUT_Y_L = 2,
24    DATA_OUT_Y_H = 3,
25    DATA_OUT_Z_L = 4,
26    DATA_OUT_Z_H = 5,
27    STATUS = 6,
28    TOUT_L = 7,
29    TOUT_H = 8,
30    CONTROL1 = 9,
31    CONTROL2 = 10,
32    PERIOD = 11,
33    CHIP_ID = 13,
34}
35
36const STATUS_OVL: u8 = 0b010;
37const STATUS_DRDY: u8 = 0b001;
38
39const MODE_CONTINUOUS: u8 = 0b01;
40
41const CTRL2_SOFT_RST: u8 = 1 << 7;
42const CTRL2_ROL_PNT: u8 = 1 << 6;
43const CTRL2_INT_ENB: u8 = 1 << 0;
44
45/// Update frequency; 10Hz recommended for low power consumption.
46#[repr(u8)]
47pub enum OutputDataRate {
48    /// 10Hz update rate.
49    Rate10Hz = 0,
50    /// 50Hz update rate.
51    Rate50Hz = 0b0100,
52    /// 100Hz update rate.
53    Rate100Hz = 0b1000,
54    /// 200Hz update rate.
55    Rate200Hz = 0b1100,
56}
57
58/// Oversampling rate; controls bandwidth of internal digital filter.
59/// Larger oversampling gets less in-band noise but higher power consumption.
60#[repr(u8)]
61pub enum OversampleRate {
62    /// Oversample by 64.
63    Rate64 = 3 << 6,
64    /// Oversample by 128.
65    Rate128 = 1 << 7,
66    /// Oversample by 256.
67    Rate256 = 1 << 6,
68    /// Oversample by 512.
69    Rate512 = 0,
70}
71
72/// Field range of magnetic sensor.
73#[repr(u8)]
74pub enum FieldRange {
75    /// ± 2 gauss
76    Range2Gauss = 0,
77    /// ± 8 gauss
78    Range8Gauss = 1 << 3,
79}
80
81/// QMC5883L Error
82#[derive(Debug, Copy, Clone)]
83pub enum Error<E> {
84    /// CHIP_ID returned invalid value (returned value is argument).
85    InvalidDevice(u8),
86    /// Read taken from magnetometer before ready.
87    NotReady,
88    /// Reading overflowed.
89    Overflow,
90    /// Underlying I2C bus error.
91    BusError(E),
92}
93
94impl<E> core::convert::From<E> for Error<E> {
95    fn from(e: E) -> Self {
96        Error::BusError(e)
97    }
98}
99
100/// QMC5883L driver
101pub struct QMC5883L<I2C> {
102    i2c: I2C,
103}
104
105impl<I2C, E> QMC5883L<I2C>
106where
107    I2C: WriteRead<Error = E> + Write<Error = E>,
108{
109    /// Creates a new QMC5883L device from an I2C peripheral; begins with a soft reset.
110    pub fn new(i2c: I2C) -> Result<Self, Error<E>> {
111        let mut dev = QMC5883L { i2c: i2c };
112        let id = dev.read_u8(Register::CHIP_ID)?;
113        if id != 0xff {
114            return Err(Error::InvalidDevice(id));
115        }
116        dev.reset()?;
117        Ok(dev)
118    }
119
120    /// Soft reset the device.
121    pub fn reset(&mut self) -> Result<(), E> {
122        self.write_u8(Register::CONTROL2, CTRL2_SOFT_RST)?;
123        self.write_u8(Register::CONTROL2, CTRL2_ROL_PNT | CTRL2_INT_ENB)?;
124        self.write_u8(Register::PERIOD, 1)
125    }
126
127    /// Set the device field range.
128    pub fn set_field_range(&mut self, rng: FieldRange) -> Result<(), E> {
129        let ctrl1 = self.read_u8(Register::CONTROL1)?;
130        let v = (ctrl1 & !(FieldRange::Range8Gauss as u8)) | (rng as u8);
131        self.write_u8(Register::CONTROL1, v)
132    }
133
134    /// Set the device oversampling rate.
135    pub fn set_oversample(&mut self, osr: OversampleRate) -> Result<(), E> {
136        let ctrl1 = self.read_u8(Register::CONTROL1)?;
137        let v = (ctrl1 & !(OversampleRate::Rate64 as u8)) | (osr as u8);
138        self.write_u8(Register::CONTROL1, v)
139    }
140
141    /// Set the device output data rate.
142    pub fn set_output_data_rate(&mut self, odr: OutputDataRate) -> Result<(), E> {
143        let ctrl1 = self.read_u8(Register::CONTROL1)?;
144        let v = (ctrl1 & !(OutputDataRate::Rate200Hz as u8)) | (odr as u8);
145        self.write_u8(Register::CONTROL1, v)
146    }
147
148    /// Put device in continous mode.
149    pub fn continuous(&mut self) -> Result<(), E> {
150        let ctrl1 = self.read_u8(Register::CONTROL1)?;
151        self.write_u8(Register::CONTROL1, ctrl1 | MODE_CONTINUOUS)
152    }
153
154    /// Put device in standby mode.
155    pub fn standby(&mut self) -> Result<(), E> {
156        let ctrl1 = self.read_u8(Register::CONTROL1)?;
157        self.write_u8(Register::CONTROL1, ctrl1 & !MODE_CONTINUOUS)
158    }
159
160    /// Enable interrupt pin.
161    pub fn enable_interrupt(&mut self) -> Result<(), E> {
162        self.write_u8(Register::CONTROL2, CTRL2_ROL_PNT)
163    }
164
165    /// Disable interrupt pin.
166    pub fn disable_interrupt(&mut self) -> Result<(), E> {
167        self.write_u8(Register::CONTROL2, CTRL2_ROL_PNT | CTRL2_INT_ENB)
168    }
169
170    /// Read temperature sensor; temperature coefficient is about 100 LSB/°C.
171    pub fn temp(&mut self) -> Result<i16, E> {
172        let temp_l = self.read_u8(Register::TOUT_L)? as i16;
173        let temp_h = self.read_u8(Register::TOUT_H)? as i16;
174        Ok((temp_h << 8) | temp_l)
175    }
176
177    /// Read raw (x,y,z) from magnetometer.
178    pub fn mag(&mut self) -> Result<(i16, i16, i16), Error<E>> {
179        let buf: &mut [u8; 7] = &mut [0; 7];
180        self.i2c
181            .write_read(I2C_ADDRESS, &[Register::STATUS as u8], buf)?;
182        let status = buf[0];
183        if (status & STATUS_DRDY) == 0 {
184            return Err(Error::NotReady);
185        } else if (status & STATUS_OVL) != 0 {
186            return Err(Error::Overflow);
187        }
188        let x = ((buf[2] as i16) << 8) | (buf[1] as i16);
189        let y = ((buf[4] as i16) << 8) | (buf[3] as i16);
190        let z = ((buf[6] as i16) << 8) | (buf[5] as i16);
191        Ok((x, y, z))
192    }
193
194    fn read_u8(&mut self, reg: Register) -> Result<u8, E> {
195        let buf: &mut [u8; 1] = &mut [0];
196        self.i2c.write_read(I2C_ADDRESS, &[reg as u8], buf)?;
197        Ok(buf[0])
198    }
199
200    fn write_u8(&mut self, reg: Register, v: u8) -> Result<(), E> {
201        self.i2c.write(I2C_ADDRESS, &[reg as u8, v])
202    }
203}