edrv_hmc5883l/
lib.rs

1//! Driver for HMC5883L.
2
3#![no_std]
4
5pub const ADDRESS: u8 = 0x1E;
6
7pub mod regs {
8
9    pub const CONFIG_A: u8 = 0x00;
10    pub const CONFIG_B: u8 = 0x01;
11    pub const MODE: u8 = 0x02;
12    pub const DATA_X_MSB: u8 = 0x03;
13    pub const DATA_X_LSB: u8 = 0x04;
14    pub const DATA_Z_MSB: u8 = 0x05;
15    pub const DATA_Z_LSB: u8 = 0x06;
16    pub const DATA_Y_MSB: u8 = 0x07;
17    pub const DATA_Y_LSB: u8 = 0x08;
18    pub const STATUS: u8 = 0x09;
19    pub const IDENT_A: u8 = 0x0A;
20    pub const IDENT_B: u8 = 0x0B;
21    pub const IDENT_C: u8 = 0x0C;
22}
23
24/// Select number of samples averaged (1 to 8) per measurement output
25#[derive(Debug, Clone, Copy, PartialEq, Eq)]
26#[repr(u8)]
27pub enum Samples {
28    Samples1 = 0b00, // 1 sample (Default)
29    Samples2 = 0b01, // 2 samples
30    Samples4 = 0b10, // 4 samples
31    Samples8 = 0b11, // 8 samples
32}
33
34#[derive(Debug, Clone, Copy, PartialEq, Eq)]
35#[repr(u8)]
36pub enum Rate {
37    Hz0_75 = 0b000,
38    Hz1_5 = 0b001,
39    Hz3 = 0b010,
40    Hz7_5 = 0b011,
41    Hz15 = 0b100,
42    Hz30 = 0b101,
43    Hz75 = 0b110,
44}
45
46/// Gain settings for the HMC5883L magnetometer.
47///
48/// This enum represents the gain settings for the HMC5883L magnetometer.
49/// Each variant corresponds to a specific gain value, which affects the
50/// sensor's field range, gain in LSB/Gauss, and digital resolution in mG/LSB.
51#[derive(Debug, Clone, Copy, PartialEq, Eq)]
52#[repr(u8)]
53pub enum Gain {
54    /// ±0.88 Ga, 1370 LSB/Gauss, 0.73 mG/LSB
55    Gain1370 = 0b000,
56    /// ±1.3 Ga, 1090 LSB/Gauss, 0.92 mG/LSB (default)
57    Gain1090 = 0b001,
58    /// ±1.9 Ga, 820 LSB/Gauss, 1.22 mG/LSB
59    Gain820 = 0b010,
60    /// ±2.5 Ga, 660 LSB/Gauss, 1.52 mG/LSB
61    Gain660 = 0b011,
62    /// ±4.0 Ga, 440 LSB/Gauss, 2.27 mG/LSB
63    Gain440 = 0b100,
64    /// ±4.7 Ga, 390 LSB/Gauss, 2.56 mG/LSB
65    Gain390 = 0b101,
66    /// ±5.6 Ga, 330 LSB/Gauss, 3.03 mG/LSB
67    Gain330 = 0b110,
68    /// ±8.1 Ga, 230 LSB/Gauss, 4.35 mG/LSB
69    Gain230 = 0b111,
70}
71
72impl Gain {
73    fn resolution(&self) -> f32 {
74        match self {
75            Gain::Gain1370 => 0.73,
76            Gain::Gain1090 => 0.92,
77            Gain::Gain820 => 1.22,
78            Gain::Gain660 => 1.52,
79            Gain::Gain440 => 2.27,
80            Gain::Gain390 => 2.56,
81            Gain::Gain330 => 3.03,
82            Gain::Gain230 => 4.35,
83        }
84    }
85}
86
87/// Configuration settings for the HMC5883L magnetometer.
88///
89/// This struct holds the data rate and gain settings for the magnetometer.
90/// The `data_rate` field determines the data output rate of the magnetometer,
91/// and the `gain` field determines the gain setting of the magnetometer.
92#[derive(Clone, Copy)]
93pub struct Config {
94    pub data_rate: Rate,
95    pub gain: Gain,
96    pub samples: Samples,
97}
98
99impl Default for Config {
100    fn default() -> Self {
101        Config {
102            data_rate: Rate::Hz15,
103            gain: Gain::Gain1090,
104            samples: Samples::Samples8,
105        }
106    }
107}
108
109#[derive(Debug)]
110pub enum Error<IE> {
111    Bus(IE),
112    InvalidDevice,
113}
114
115impl<E> From<E> for Error<E> {
116    fn from(e: E) -> Self {
117        Error::Bus(e)
118    }
119}
120
121pub struct HMC5883L<I2C> {
122    i2c: I2C,
123    addr: u8,
124    gain: Gain,
125}
126
127impl<I2C> HMC5883L<I2C>
128where
129    I2C: embedded_hal::i2c::I2c,
130{
131    pub fn new(i2c: I2C, addr: u8) -> Self {
132        Self {
133            i2c,
134            addr,
135            gain: Gain::Gain1090,
136        }
137    }
138
139    pub fn new_primary(i2c: I2C) -> Self {
140        Self::new(i2c, ADDRESS)
141    }
142
143    pub fn init(&mut self, config: Config) -> Result<(), Error<I2C::Error>> {
144        let id_a = self.read_reg(regs::IDENT_A)?;
145        let id_b = self.read_reg(regs::IDENT_B)?;
146        let id_c = self.read_reg(regs::IDENT_C)?;
147
148        if id_a != 0x48 || id_b != 0x34 || id_c != 0x33 {
149            return Err(Error::InvalidDevice);
150        }
151
152        let mut cra = 0; // Initialize CRA with all bits set to 0
153        cra |= (config.samples as u8) << 5; // Set MA (CRA6 to CRA5) based on config.samples
154        cra |= (config.data_rate as u8) << 2; // Set DO (CRA4 to CRA2) based on config.data_rate
155        self.write_reg(regs::CONFIG_A, cra)?;
156
157        let crb = (config.gain as u8) << 5; // Set GN (CRB7 to CRB5) based on config.gain
158        self.write_reg(regs::CONFIG_B, crb)?;
159
160        self.write_reg(regs::MODE, 0x00)?; // Continuous-measurement mode
161
162        self.gain = config.gain;
163
164        Ok(())
165    }
166
167    pub fn read_raw(&mut self) -> Result<(i16, i16, i16), Error<I2C::Error>> {
168        let mut buf = [0u8; 6];
169
170        // Read 6 bytes starting from DATA_X_MSB
171        self.i2c
172            .write_read(self.addr, &[regs::DATA_X_MSB], &mut buf)?;
173        self.i2c.write(self.addr, &[regs::DATA_X_MSB])?;
174
175        let x = ((buf[0] as i16) << 8) | buf[1] as i16;
176        let y = ((buf[4] as i16) << 8) | buf[5] as i16;
177        let z = ((buf[2] as i16) << 8) | buf[3] as i16;
178
179        Ok((x, y, z))
180    }
181
182    pub fn read_normalized(&mut self) -> Result<(f32, f32, f32), Error<I2C::Error>> {
183        let (x, y, z) = self.read_raw()?;
184
185        let resolution = self.gain.resolution();
186
187        let x_norm = x as f32 * resolution;
188        let y_norm = y as f32 * resolution;
189        let z_norm = z as f32 * resolution;
190
191        Ok((x_norm, y_norm, z_norm))
192    }
193
194    pub fn read_reg(&mut self, reg: u8) -> Result<u8, I2C::Error> {
195        let mut buf = [0];
196        self.i2c.write_read(self.addr, &[reg], &mut buf)?;
197        Ok(buf[0])
198    }
199
200    // Add this new method to write to registers
201    fn write_reg(&mut self, reg: u8, value: u8) -> Result<(), I2C::Error> {
202        self.i2c.write(self.addr, &[reg, value])
203    }
204}