Skip to main content

tmag5273/
lib.rs

1//! Platform-agnostic `no_std` driver for the TI TMAG5273 3-axis Hall-effect sensor.
2//!
3//! This crate provides a type-safe, zero-allocation driver for the TMAG5273 family
4//! of linear 3D Hall-effect sensors over I2C, using [`embedded-hal`] 1.0 traits.
5//!
6//! # Supported Variants
7//!
8//! All eight TMAG5273 variants are supported (A1, A2, B1, B2, C1, C2, D1, D2),
9//! differing in default I2C address and magnetic field sensitivity range.
10//!
11//! | Variant | Address | XY range (low / high) | Z range (low / high) |
12//! |---------|---------|----------------------|---------------------|
13//! | A1, B1, C1, D1 | 0x35, 0x22, 0x78, 0x44 | ±40 / ±80 mT | ±40 / ±80 mT |
14//! | A2, B2, C2, D2 | 0x35, 0x22, 0x78, 0x44 | ±133 / ±266 mT | ±133 / ±266 mT |
15//!
16//! # Quick Start
17//!
18//! Three ways to construct the driver (simplest first):
19//!
20//! ```no_run
21//! # use embedded_hal_mock::eh1::i2c::{Mock as I2cMock, Transaction};
22//! # let i2c = I2cMock::new(&[]);
23//! use tmag5273::{Tmag5273, DeviceVariant, ConfigBuilder};
24//!
25//! // Option 1: Plug-and-play — scans all known addresses, auto-detects variant
26//! // let sensor = Tmag5273::scan(i2c).expect("no TMAG5273 found");
27//!
28//! // Option 2: Known address, auto-detect variant
29//! // let sensor = Tmag5273::detect(i2c, 0x22).expect("no sensor at 0x22");
30//!
31//! // Option 3: Fully explicit — no I2C activity until init()
32//! let sensor = Tmag5273::new(i2c, DeviceVariant::B1);
33//!
34//! // Configure and initialize
35//! let config = ConfigBuilder::new().build().unwrap();
36//! // let mut sensor = sensor.init(&config).unwrap();
37//!
38//! // Read measurements
39//! // let mag = sensor.read_magnetic().unwrap();
40//! // let temp = sensor.read_temperature().unwrap();
41//! // let all = sensor.read_all().unwrap(); // coherent temp + mag in one burst
42//! ```
43//!
44//! # Features
45//!
46//! - `crc` — Enables CRC-8 validation on I2C reads (disables burst reads)
47//! - `defmt` — Enables `defmt::Format` derives on all public types
48//! - `libm` — Enables the L3 analysis layer: `magnitude_3d()`, `plane_angles()` with
49//!   per-plane angle/magnitude, `AxisCalibrator` for auto-detecting the optimal hardware
50//!   angle axis pair, and related types (`PlaneAxis`, `PlaneAngle`, `PlaneAngles`,
51//!   `AxisRecommendation`)
52//!
53//! [`embedded-hal`]: https://docs.rs/embedded-hal/1.0
54
55#![no_std]
56#![forbid(unsafe_code)]
57#![deny(missing_docs)]
58#![cfg_attr(docsrs, feature(doc_cfg))]
59#![cfg_attr(docsrs, doc(auto_cfg))]
60
61/// Gated `defmt` logging macros.
62///
63/// These wrap `defmt::trace!` / `debug!` / `warn!` / `error!` behind
64/// `#[cfg(all(feature = "defmt", not(test)))]`. The `not(test)` gate is
65/// needed because `defmt` requires a global logger (e.g., `defmt-rtt`)
66/// at link time, which only exists on real hardware — not in host-side
67/// `cargo test`.
68macro_rules! defmt_trace {
69    ($($arg:tt)*) => {
70        #[cfg(all(feature = "defmt", not(test)))]
71        defmt::trace!($($arg)*);
72    };
73}
74macro_rules! defmt_debug {
75    ($($arg:tt)*) => {
76        #[cfg(all(feature = "defmt", not(test)))]
77        defmt::debug!($($arg)*);
78    };
79}
80macro_rules! defmt_warn {
81    ($($arg:tt)*) => {
82        #[cfg(all(feature = "defmt", not(test)))]
83        defmt::warn!($($arg)*);
84    };
85}
86macro_rules! defmt_error {
87    ($($arg:tt)*) => {
88        #[cfg(all(feature = "defmt", not(test)))]
89        defmt::error!($($arg)*);
90    };
91}
92
93/// Generates a `TryFrom<u8>` impl. Two forms:
94///
95/// **Enum form** — maps discriminant values to named variants:
96/// ```ignore
97/// impl_try_from_u8!(MyEnum { 0 => A, 1 => B });
98/// ```
99///
100/// **Struct form** — delegates to `from_code(u8) -> Option<Self>`:
101/// ```ignore
102/// impl_try_from_u8!(MyStruct);
103/// ```
104///
105/// Unknown values return `Err(value)` and emit a `defmt::warn!` when the
106/// `defmt` feature is active.
107macro_rules! impl_try_from_u8 {
108    // Enum arm: discrete variant mapping.
109    ($enum:ty { $($val:expr => $variant:ident),+ $(,)? }) => {
110        impl TryFrom<u8> for $enum {
111            type Error = u8;
112
113            fn try_from(val: u8) -> Result<Self, u8> {
114                match val {
115                    $($val => Ok(Self::$variant),)+
116                    other => {
117                        defmt_warn!("invalid register value: 0x{:02X}", other);
118                        Err(other)
119                    }
120                }
121            }
122        }
123    };
124    // Struct arm: delegates to `from_code()` for range-validated newtypes.
125    ($struct:ty) => {
126        impl TryFrom<u8> for $struct {
127            type Error = u8;
128
129            fn try_from(val: u8) -> Result<Self, u8> {
130                match Self::from_code(val) {
131                    Some(val) => Ok(val),
132                    None => {
133                        defmt_warn!("invalid register value: 0x{:02X}", val);
134                        Err(val)
135                    }
136                }
137            }
138        }
139    };
140}
141
142mod config;
143mod device;
144mod error;
145mod register;
146mod rotation;
147mod types;
148
149#[cfg(feature = "libm")]
150mod angle;
151
152#[cfg(feature = "libm")]
153pub use angle::{AxisCalibrator, AxisRecommendation, PlaneAngle, PlaneAngles, PlaneAxis, Welford};
154pub use config::{
155    CalibrationConfig, Config, ConfigBuilder, InterruptConfig, ThresholdConfig, ThresholdHysteresis,
156};
157pub use device::{Configured, Tmag5273, Unconfigured};
158pub use error::{ConfigError, Error, InitError};
159pub use rotation::{Cordic, CordicTracker, RotationTracker, TrackingMode, ZeroCrossing};
160pub use types::{
161    AngleEnable, AngleReading, Axis, Celsius, ConversionAverage, ConversionStatus, CordicMagnitude,
162    Crossings, Degrees, DeviceStatus, DeviceVariant, Diagnostics, I2cReadMode, InterruptMode,
163    InterruptState, Lsb, MagneticChannel, MagneticGainChannel, MagneticReading,
164    MagneticTempCoefficient, MagneticThresholdDirection, MicrosIsr, MicrosRange, MilliTesla,
165    NoDelay, OperatingMode, PoleCount, PowerNoiseMode, Range, Rpm, SensorReading, SignedDegrees,
166    SleepTime, TempThresholdConfig, ThresholdCrossingCount, TriggerMode, WAKE_DELAY,
167    WAKE_RETRY_DELAY,
168};