use crate::brightness::{self, Brightness};
use embedded_hal::digital::{OutputPin, PinState, StatefulOutputPin};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum PolarityMode {
ActiveHigh,
ActiveLow,
}
impl PolarityMode {
#[inline]
pub(crate) fn physical_on(self) -> PinState {
match self {
Self::ActiveHigh => PinState::High,
Self::ActiveLow => PinState::Low,
}
}
#[inline]
pub(crate) fn physical_off(self) -> PinState {
match self {
Self::ActiveHigh => PinState::Low,
Self::ActiveLow => PinState::High,
}
}
#[inline]
pub(crate) fn is_logical_on(self, physical_high: bool) -> bool {
match self {
Self::ActiveHigh => physical_high,
Self::ActiveLow => !physical_high,
}
}
#[inline]
#[allow(dead_code)] pub(crate) fn map_duty(self, brightness: Brightness) -> Brightness {
match self {
Self::ActiveHigh => brightness,
Self::ActiveLow => brightness::max_value() - brightness,
}
}
}
pub struct Led<P> {
pin: P,
polarity: PolarityMode,
}
impl<P> Led<P> {
#[inline]
pub fn from_pin(pin: P, polarity: PolarityMode) -> Self {
Self { pin, polarity }
}
}
impl<P: OutputPin> Led<P> {
pub fn new(mut pin: P, polarity: PolarityMode) -> Result<Self, P::Error> {
pin.set_state(polarity.physical_off())?;
Ok(Self { pin, polarity })
}
#[inline]
pub fn on(&mut self) -> Result<(), P::Error> {
self.pin.set_state(self.polarity.physical_on())
}
#[inline]
pub fn off(&mut self) -> Result<(), P::Error> {
self.pin.set_state(self.polarity.physical_off())
}
#[inline]
pub fn set(&mut self, state: bool) -> Result<(), P::Error> {
if state { self.on() } else { self.off() }
}
}
impl<P: StatefulOutputPin> Led<P> {
#[inline]
pub fn is_on(&mut self) -> Result<bool, P::Error> {
self.pin
.is_set_high()
.map(|h| self.polarity.is_logical_on(h))
}
#[inline]
pub fn is_off(&mut self) -> Result<bool, P::Error> {
self.is_on().map(|on| !on)
}
#[inline]
pub fn toggle(&mut self) -> Result<(), P::Error> {
self.pin.toggle()
}
}
impl<P> Led<P> {
#[inline]
pub fn polarity(&self) -> PolarityMode {
self.polarity
}
#[inline]
pub fn release(self) -> P {
self.pin
}
}
impl<P> core::fmt::Debug for Led<P> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Led")
.field("polarity", &self.polarity)
.finish_non_exhaustive()
}
}
#[cfg(feature = "defmt")]
impl<P> defmt::Format for Led<P> {
fn format(&self, fmt: defmt::Formatter) {
defmt::write!(fmt, "Led {{ polarity: {} }}", self.polarity)
}
}
#[cfg(test)]
mod tests {
use super::*;
use embedded_hal_mock::eh1::digital::{
Mock as PinMock, State as EState, Transaction as PinTrans,
};
#[test]
fn new_active_high_sets_off() {
let e = [PinTrans::set(EState::Low)];
Led::new(PinMock::new(&e), PolarityMode::ActiveHigh)
.unwrap()
.release()
.done();
}
#[test]
fn new_active_low_sets_off() {
let e = [PinTrans::set(EState::High)];
Led::new(PinMock::new(&e), PolarityMode::ActiveLow)
.unwrap()
.release()
.done();
}
#[test]
fn from_pin_no_touch() {
Led::from_pin(PinMock::new(&[]), PolarityMode::ActiveHigh)
.release()
.done();
}
#[test]
fn active_high_on_off() {
let e = [
PinTrans::set(EState::Low),
PinTrans::set(EState::High),
PinTrans::set(EState::Low),
];
let mut led = Led::new(PinMock::new(&e), PolarityMode::ActiveHigh).unwrap();
led.on().unwrap();
led.off().unwrap();
led.release().done();
}
#[test]
fn active_low_on_off() {
let e = [
PinTrans::set(EState::High),
PinTrans::set(EState::Low),
PinTrans::set(EState::High),
];
let mut led = Led::new(PinMock::new(&e), PolarityMode::ActiveLow).unwrap();
led.on().unwrap();
led.off().unwrap();
led.release().done();
}
#[test]
fn set_state() {
let e = [
PinTrans::set(EState::Low),
PinTrans::set(EState::High),
PinTrans::set(EState::Low),
];
let mut led = Led::new(PinMock::new(&e), PolarityMode::ActiveHigh).unwrap();
led.set(true).unwrap();
led.set(false).unwrap();
led.release().done();
}
#[test]
fn toggle() {
let e = [PinTrans::set(EState::Low), PinTrans::toggle()];
let mut led = Led::new(PinMock::new(&e), PolarityMode::ActiveHigh).unwrap();
led.toggle().unwrap();
led.release().done();
}
#[test]
fn is_on_active_high() {
let e = [PinTrans::set(EState::Low), PinTrans::get_state(EState::Low)];
let mut led = Led::new(PinMock::new(&e), PolarityMode::ActiveHigh).unwrap();
assert!(!led.is_on().unwrap());
led.release().done();
}
#[test]
fn is_on_active_low() {
let e = [
PinTrans::set(EState::High),
PinTrans::get_state(EState::High),
];
let mut led = Led::new(PinMock::new(&e), PolarityMode::ActiveLow).unwrap();
assert!(!led.is_on().unwrap());
led.release().done();
}
#[test]
fn polarity_accessor() {
let led = Led::from_pin(PinMock::new(&[]), PolarityMode::ActiveLow);
assert_eq!(led.polarity(), PolarityMode::ActiveLow);
led.release().done();
}
}