lis2mdl-rs 2.0.0

Platform-agnostic driver for the LIS2MDL ultra-low-power, high-performance 3-axis digital magnetic sensor with I2C and SPI interfaces.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
use super::super::{
    BusOperation, DelayNs, Error, Lis2mdl, RegisterOperation, SensorOperation, bisync,
    register::OnState,
};

use bitfield_struct::bitfield;
use derive_more::TryFrom;

use st_mem_bank_macro::{named_register, register};

#[repr(u8)]
#[derive(Clone, Copy, PartialEq)]
pub enum Reg {
    OffsetXRegL = 0x45,
    OffsetXRegH = 0x46,
    OffsetYRegL = 0x47,
    OffsetYRegH = 0x48,
    OffsetZRegL = 0x49,
    OffsetZRegH = 0x4A,
    WhoAmI = 0x4F,
    CfgRegA = 0x60,
    CfgRegB = 0x61,
    CfgRegC = 0x62,
    IntCrtlReg = 0x63,
    IntSourceReg = 0x64,
    IntThsLReg = 0x65,
    IntThsHReg = 0x66,
    StatusReg = 0x67,
    OutxLReg = 0x68,
    OutxHReg = 0x69,
    OutyLReg = 0x6A,
    OutyHReg = 0x6B,
    OutzLReg = 0x6C,
    OutzHReg = 0x6D,
    TempOutLReg = 0x6E,
    TempOutHReg = 0x6F,
}

/// Offset XYZ (0x45 - 0x4A)
///
/// Magnetic user offset for hard-iron to compensate environmental
/// effects.
/// 1LSB = 1.5mg
#[named_register(address = Reg::OffsetXRegL, access_type = "Lis2mdl<B, T, OnState>")]
#[derive(Default, Debug)]
pub struct OffsetXYZ {
    pub x: i16,
    pub y: i16,
    pub z: i16,
}

/// WHO_AM_I (0x4F)
///
/// The identification register is used to identify the device
#[register(address = Reg::WhoAmI, access_type = "Lis2mdl<B, T, OnState>")]
#[cfg_attr(feature = "bit_order_msb", bitfield(u8, order = Msb))]
#[cfg_attr(not(feature = "bit_order_msb"), bitfield(u8, order = Lsb))]
pub struct WhoAmI {
    #[bits(8, default = 0b01000000)]
    pub who_am_i: u8,
}

/// CFG_REG_A (0x60)
///
/// Configuration register A (R/W)
/// Controls output data rate, power mode, reboot, soft reset, and temperature compensation.
#[register(address = Reg::CfgRegA, access_type = "Lis2mdl<B, T, OnState>")]
#[cfg_attr(feature = "bit_order_msb", bitfield(u8, order = Msb))]
#[cfg_attr(not(feature = "bit_order_msb"), bitfield(u8, order = Lsb))]
pub struct CfgRegA {
    /// Mode of operation selection (2 bits)
    /// 00: Continuous mode (continuous measurement, data-ready signal generated)
    /// 01: Single measurement mode (performs one measurement, sets DRDY high, then idle)
    /// 10: Idle mode (I2C and SPI active)
    /// 11: Idle mode (default)
    /// Connected enum: Md
    #[bits(2, default = 0b11)]
    pub md: u8,
    /// Output data rate configuration (2 bits)
    /// 00: 10 Hz (default)
    /// 01: 20 Hz
    /// 10: 50 Hz
    /// 11: 100 Hz
    /// Connected enum: Odr
    #[bits(2, default = 0b00)]
    pub odr: u8,
    /// Low-power mode enable (1 bit)
    /// 0: High-resolution mode
    /// 1: Low-power mode enabled
    /// Connected enum: Lp
    #[bits(1, default = 0)]
    pub lp: u8,
    /// Soft reset configuration and user registers (1 bit)
    /// When set, resets configuration and user registers; flash registers keep their values.
    #[bits(1, default = 0)]
    pub soft_rst: u8,
    /// Reboot magnetometer memory content (1 bit)
    /// When set, reboots magnetometer memory content.
    #[bits(1, default = 0)]
    pub reboot: u8,
    /// Temperature compensation enable (1 bit)
    /// Must be set to 1 for correct operation.
    #[bits(1, default = 0)]
    pub comp_temp_en: u8,
}

/// CFG_REG_B (0x61)
///
/// Configuration register B (R/W)
/// Controls offset cancellation, interrupt on data off, set pulse frequency, and low-pass filter.
#[register(address = Reg::CfgRegB, access_type = "Lis2mdl<B, T, OnState>")]
#[cfg_attr(feature = "bit_order_msb", bitfield(u8, order = Msb))]
#[cfg_attr(not(feature = "bit_order_msb"), bitfield(u8, order = Lsb))]
pub struct CfgRegB {
    /// Digital low-pass filter enable (1 bit)
    /// 0: Filter disabled (bandwidth = ODR/2)
    /// 1: Filter enabled (bandwidth = ODR/4)
    /// Connected enum: Lpf
    #[bits(1, default = 0)]
    pub lpf: u8,
    /// Set pulse frequency selection (2 bits)
    /// 00: Set pulse released every 64 ODR cycles (default)
    /// 01: Enables offset cancellation
    /// 10: Set pulse is released only at power-on after PD condition
    /// Connected enum: SetRst
    #[bits(2, default = 0)]
    pub set_rst: u8,
    /// Interrupt block checks data after hard-iron correction (1 bit)
    /// If 1, interrupt recognition checks data after hard-iron correction.
    /// Connected enum: IntOnDataoff
    #[bits(1, default = 0)]
    pub int_on_dataoff: u8,
    /// Offset cancellation in single measurement mode enable (1 bit)
    /// 0: Disabled (default)
    /// 1: Enabled (OFF_CANC bit must be set to 1 when enabling)
    #[bits(1, default = 0)]
    pub off_canc_one_shot: u8,
    /// Reserved bits (3 bits, read-only)
    #[bits(3, access = RO, default = 0)]
    pub not_used_01: u8,
}

/// CFG_REG_C (0x62)
///
/// Configuration register C (R/W)
/// Controls interrupt pin behavior, interface selection, byte order, SPI mode, self-test, and data-ready pin.
#[register(address = Reg::CfgRegC, access_type = "Lis2mdl<B, T, OnState>")]
#[cfg_attr(feature = "bit_order_msb", bitfield(u8, order = Msb))]
#[cfg_attr(not(feature = "bit_order_msb"), bitfield(u8, order = Lsb))]
pub struct CfgRegC {
    /// Data-ready signal driven on INT/DRDY pin (push-pull output) (1 bit)
    #[bits(1, default = 0)]
    pub drdy_on_pin: u8,
    /// Self-test enable (1 bit)
    #[bits(1, default = 0)]
    pub self_test: u8,
    /// Enable 4-wire SPI interface (disables interrupt and data-ready signaling) (1 bit)
    /// Connected enum: Sim
    #[bits(1, default = 0)]
    pub four_wire_spi: u8,
    /// Byte order inversion (low and high bytes of data are swapped) (1 bit)
    #[bits(1, default = 0)]
    pub ble: u8,
    /// Block data update enable (prevents reading incoherent data during update) (1 bit)
    /// Connected enum: Ble
    #[bits(1, default = 0)]
    pub bdu: u8,
    /// I2C interface disable (1 bit)
    /// If 1, I2C interface is inhibited; only SPI interface can be used.
    /// Connected enum: I2cDis
    #[bits(1, default = 0)]
    pub i2c_dis: u8,
    /// Interrupt signal driven on INT/DRDY pin (push-pull output) (1 bit)
    #[bits(1, default = 0)]
    pub int_on_pin: u8,
    /// Reserved bit (1 bit, read-only)
    #[bits(1, access = RO, default = 0)]
    pub not_used_02: u8,
}

/// INT_CRTL_REG (0x63)
///
/// Interrupt control register (R/W)
/// Enables and configures interrupt recognition.
#[register(address = Reg::IntCrtlReg, access_type = "Lis2mdl<B, T, OnState>")]
#[cfg_attr(feature = "bit_order_msb", bitfield(u8, order = Msb))]
#[cfg_attr(not(feature = "bit_order_msb"), bitfield(u8, order = Lsb))]
pub struct IntCrtlReg {
    /// Interrupt enable (1 bit)
    /// Enables generation of interrupt (INT bit in INT_SOURCE_REG).
    #[bits(1, default = 0b0)]
    pub ien: u8,
    /// Interrupt latch enable (1 bit)
    /// 0: INT bit is pulsed
    /// 1: INT bit is latched until INT_SOURCE_REG is read
    #[bits(1, default = 0b0)]
    pub iel: u8,
    /// Interrupt polarity (1 bit)
    /// 0: INT = 0 signals interrupt
    /// 1: INT = 1 signals interrupt
    #[bits(1, default = 0b0)]
    pub iea: u8,
    /// Reserved bits (2 bits, read-only)
    #[bits(2, access = RO, default = 0)]
    pub not_used_01: u8,
    /// Z-axis interrupt enable (1 bit)
    #[bits(1, default = 0b1)]
    pub zien: u8,
    /// Y-axis interrupt enable (1 bit)
    #[bits(1, default = 0b1)]
    pub yien: u8,
    /// X-axis interrupt enable (1 bit)
    #[bits(1, default = 0b1)]
    pub xien: u8,
}

/// INT_SOURCE_REG (0x64)
///
/// Interrupt source register (R)
/// Indicates interrupt event status and axis threshold crossing.
#[register(address = Reg::IntSourceReg, access_type = "Lis2mdl<B, T, OnState>")]
#[cfg_attr(feature = "bit_order_msb", bitfield(u8, order = Msb))]
#[cfg_attr(not(feature = "bit_order_msb"), bitfield(u8, order = Lsb))]
pub struct IntSourceReg {
    /// Interrupt event occurred (1 bit)
    #[bits(1, access = RO)]
    pub int: u8,
    /// Magnetic ROI flag (1 bit)
    /// Always enabled; reset by reading this register.
    #[bits(1, access = RO)]
    pub mroi: u8,
    /// Negative threshold exceeded on Z-axis (1 bit)
    #[bits(1, access = RO)]
    pub n_th_s_z: u8,
    /// Negative threshold exceeded on Y-axis (1 bit)
    #[bits(1, access = RO)]
    pub n_th_s_y: u8,
    /// Negative threshold exceeded on X-axis (1 bit)
    #[bits(1, access = RO)]
    pub n_th_s_x: u8,
    /// Positive threshold exceeded on Z-axis (1 bit)
    #[bits(1, access = RO)]
    pub p_th_s_z: u8,
    /// Positive threshold exceeded on Y-axis (1 bit)
    #[bits(1, access = RO)]
    pub p_th_s_y: u8,
    /// Positive threshold exceeded on X-axis (1 bit)
    #[bits(1, access = RO)]
    pub p_th_s_x: u8,
}

/// INT_THS_L_REG - INT_THS_H_REG (0x65 - 0x66)
///
/// These registers set the threshold value for the output to generate the interrupt (INT bit in INT_SOURCE_REG(64h)).
/// This threshold is common to all three (axes) output values and is unsigned unipolar. The threshold value is correlated
/// to the current gain and it is unsigned because the threshold is considered as an absolute value, but crossing the
/// threshold is detected for both the positive and negative sides.
#[register(address = Reg::IntThsLReg, access_type = "Lis2mdl<B, T, OnState>")]
#[cfg_attr(feature = "bit_order_msb", bitfield(u16, order = Msb))]
#[cfg_attr(not(feature = "bit_order_msb"), bitfield(u16, order = Lsb))]
pub struct IntThs {
    #[bits(16)]
    pub threshold: u16,
}

/// STATUS_REG (0x67)
///
/// Status register (R)
/// Indicates data availability and overrun status for each axis.
#[register(address = Reg::StatusReg, access_type = "Lis2mdl<B, T, OnState>")]
#[cfg_attr(feature = "bit_order_msb", bitfield(u8, order = Msb))]
#[cfg_attr(not(feature = "bit_order_msb"), bitfield(u8, order = Lsb))]
pub struct StatusReg {
    /// X-axis new data available (1 bit)
    #[bits(1)]
    pub xda: u8,
    /// Y-axis new data available (1 bit)
    #[bits(1)]
    pub yda: u8,
    /// Z-axis new data available (1 bit)
    #[bits(1)]
    pub zda: u8,
    /// X-, Y-, and Z-axis new data available (1 bit)
    #[bits(1)]
    pub zyxda: u8,
    /// X-axis data overrun (1 bit)
    #[bits(1)]
    pub _xor: u8,
    /// Y-axis data overrun (1 bit)
    #[bits(1)]
    pub yor: u8,
    /// Z-axis data overrun (1 bit)
    #[bits(1)]
    pub zor: u8,
    /// X-, Y-, and Z-axis data overrun (1 bit)
    #[bits(1)]
    pub zyxor: u8,
}

/// OutXYZ (0x68 - 0x6D)
///
/// The output data represents the raw magnetic data only if
/// relative OFFSET_axis_REG is equal to zero, otherwise hard-iron
/// calibration is included.
#[named_register(address = Reg::OutxLReg, access_type = "Lis2mdl<B, T, OnState>")]
pub struct OutXYZ {
    pub x: i16,
    pub y: i16,
    pub z: i16,
}

/// TempOut (0x6E - 0x6F)
///
/// Temperature register of the sensor
#[register(address = Reg::TempOutLReg, access_type = "Lis2mdl<B, T, OnState>")]
#[derive(Default, Debug)]
pub struct TempOut(pub i16);

/// Mode of operation
#[repr(u8)]
#[derive(Clone, Copy, PartialEq, Default, Debug)]
pub enum Md {
    /// Continuous mode (continuous measurement, data-ready signal generated)
    ContinuousMode = 0,
    /// Single measurement mode (performs one measurement, sets DRDY high, then idle)
    SingleTrigger = 1,
    /// Idle mode (I2C and SPI active): bits 10 or 11
    #[default]
    PowerDown = 3,
}

impl TryFrom<u8> for Md {
    type Error = ();

    fn try_from(value: u8) -> Result<Self, Self::Error> {
        let md = match value {
            0 => Md::ContinuousMode,
            1 => Md::SingleTrigger,
            _ => Md::PowerDown,
        };

        Ok(md)
    }
}

/// Output data rate configuration for the sensor.
///
/// Determines the frequency at which data is updated and made available.
#[repr(u8)]
#[derive(Clone, Copy, PartialEq, Default, Debug, TryFrom)]
#[try_from(repr)]
pub enum Odr {
    /// Output data rate at 10 Hz (default)
    #[default]
    _10hz = 0,
    /// Output data rate at 20 Hz
    _20hz = 1,
    /// Output data rate at 50 Hz
    _50hz = 2,
    /// Output data rate at 100 Hz
    _100hz = 3,
}

/// Power mode
#[repr(u8)]
#[derive(Clone, Copy, PartialEq, Default, Debug, TryFrom)]
#[try_from(repr)]
pub enum PowerMode {
    /// High-resolution mode
    #[default]
    HighResolution = 0,
    /// Low-power mode enabled
    LowPower = 1,
}

/// Digital low pass filter
#[repr(u8)]
#[derive(Clone, Copy, PartialEq, Default, Debug, TryFrom)]
#[try_from(repr)]
pub enum LowPassFilter {
    /// Filter disabled (bandwidth = ODR/2)
    #[default]
    OdrDiv2 = 0,
    /// Filter enabled (bandwidth = ODR/4)
    OdrDiv4 = 1,
}

#[repr(u8)]
#[derive(Clone, Copy, PartialEq, Default, Debug, TryFrom)]
#[try_from(repr)]
pub enum SetRst {
    /// Set pulse released every 64 ODR cycles (default)
    #[default]
    SetSensOdrDiv63 = 0,
    /// Enables offset cancellation
    SensOffCancEveryOdr = 1,
    /// Set pulse released only at power-on after power-down condition
    SetSensOnlyAtPowerOn = 2,
}

#[repr(u8)]
#[derive(Clone, Copy, PartialEq, Default, Debug, TryFrom)]
#[try_from(repr)]
pub enum Ble {
    /// LSB at low address (default byte order)
    #[default]
    LsbAtLowAdd = 0,
    /// MSB at low address (byte order inverted)
    MsbAtLowAdd = 1,
}

#[repr(u8)]
#[derive(Clone, Copy, PartialEq, Default, TryFrom)]
#[try_from(repr)]
pub enum IntOnDataoff {
    /// Interrupt recognition checks data before hard-iron correction
    #[default]
    CheckBefore = 0,
    /// Interrupt recognition checks data after hard-iron correction
    CheckAfter = 1,
}

#[repr(u8)]
#[derive(Clone, Copy, PartialEq, Default, TryFrom)]
#[try_from(repr)]
pub enum Sim {
    /// SPI 4-wire interface enabled (disables interrupt and data-ready signaling)
    Spi4Wire = 1,
    /// SPI 3-wire interface enabled
    #[default]
    Spi3Wire = 0,
}

#[repr(u8)]
#[derive(Clone, Copy, PartialEq, Default, TryFrom)]
#[try_from(repr)]
pub enum I2cSwitch {
    /// I2C interface enabled (default)
    #[default]
    I2cEnable = 0,
    /// I2C interface disabled; only SPI interface can be used
    I2cDisable = 1,
}