Skip to main content

status_led/
polarity.rs

1use embedded_hal::digital::PinState;
2
3mod sealed {
4    pub trait Sealed {}
5}
6
7/// Compile-time logic-level polarity: maps logical ON/OFF to physical High/Low.
8///
9/// This trait is sealed; only the built-in [`ActiveHigh`] / [`ActiveLow`] markers
10/// implement it.  For runtime polarity use [`PolarityMode`] with [`FlexLed`] or
11/// [`FlexPwmLed`].
12///
13/// [`FlexLed`]: crate::FlexLed
14/// [`FlexPwmLed`]: crate::pwm::FlexPwmLed
15pub trait Polarity: sealed::Sealed {
16    /// Physical pin level corresponding to logical ON (GPIO).
17    fn physical_on() -> PinState;
18    /// Physical pin level corresponding to logical OFF (GPIO).
19    fn physical_off() -> PinState;
20    /// Given a physical high level, return whether the LED is logically ON (GPIO).
21    fn is_logical_on(physical_high: bool) -> bool;
22    /// Map a gamma-corrected on-time fraction (0=off, 255=fully on) to a PWM duty
23    /// fraction (0=0% duty, 255=100% duty).
24    fn map_duty(brightness: u8) -> u8;
25    /// The corresponding runtime [`PolarityMode`].
26    const MODE: PolarityMode;
27}
28
29/// Active-high: LED is on when the pin is High (most common).
30pub struct ActiveHigh;
31impl sealed::Sealed for ActiveHigh {}
32impl Polarity for ActiveHigh {
33    const MODE: PolarityMode = PolarityMode::ActiveHigh;
34    #[inline]
35    fn physical_on() -> PinState {
36        PinState::High
37    }
38    #[inline]
39    fn physical_off() -> PinState {
40        PinState::Low
41    }
42    #[inline]
43    fn is_logical_on(physical_high: bool) -> bool {
44        physical_high
45    }
46    #[inline]
47    fn map_duty(brightness: u8) -> u8 {
48        brightness
49    }
50}
51
52/// Active-low: LED is on when the pin is Low (common on Nucleo, ESP32 boards).
53pub struct ActiveLow;
54impl sealed::Sealed for ActiveLow {}
55impl Polarity for ActiveLow {
56    const MODE: PolarityMode = PolarityMode::ActiveLow;
57    #[inline]
58    fn physical_on() -> PinState {
59        PinState::Low
60    }
61    #[inline]
62    fn physical_off() -> PinState {
63        PinState::High
64    }
65    #[inline]
66    fn is_logical_on(physical_high: bool) -> bool {
67        !physical_high
68    }
69    #[inline]
70    fn map_duty(brightness: u8) -> u8 {
71        255 - brightness
72    }
73}
74
75// ─── Runtime polarity ─────────────────────────────────
76
77/// Runtime polarity mode, for use with [`FlexLed`] / [`FlexPwmLed`].
78///
79/// Unlike the compile-time [`ActiveHigh`] / [`ActiveLow`] markers, this enum is
80/// suitable when the polarity is determined at runtime (e.g. from configuration).
81///
82/// [`FlexLed`]: crate::FlexLed
83/// [`FlexPwmLed`]: crate::pwm::FlexPwmLed
84#[derive(Clone, Copy, Debug, PartialEq, Eq)]
85#[cfg_attr(feature = "defmt", derive(defmt::Format))]
86pub enum PolarityMode {
87    /// Active-high: pin High = LED on.
88    ActiveHigh,
89    /// Active-low: pin Low = LED on.
90    ActiveLow,
91}
92
93impl PolarityMode {
94    #[inline]
95    pub(crate) fn physical_on(self) -> PinState {
96        match self {
97            Self::ActiveHigh => PinState::High,
98            Self::ActiveLow => PinState::Low,
99        }
100    }
101
102    #[inline]
103    pub(crate) fn physical_off(self) -> PinState {
104        match self {
105            Self::ActiveHigh => PinState::Low,
106            Self::ActiveLow => PinState::High,
107        }
108    }
109
110    #[inline]
111    pub(crate) fn is_logical_on(self, physical_high: bool) -> bool {
112        match self {
113            Self::ActiveHigh => physical_high,
114            Self::ActiveLow => !physical_high,
115        }
116    }
117
118    #[inline]
119    #[allow(dead_code)] // only used when feature = "pwm"
120    pub(crate) fn map_duty(self, brightness: u8) -> u8 {
121        match self {
122            Self::ActiveHigh => brightness,
123            Self::ActiveLow => 255 - brightness,
124        }
125    }
126}