use core::convert::Infallible;
use embedded_hal::digital::blocking as hal;
use num_derive::{FromPrimitive, ToPrimitive};
use num_traits::{FromPrimitive, ToPrimitive};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
#[derive(Clone, Debug, PartialEq, FromPrimitive, ToPrimitive)]
pub enum PinState {
High = 1,
Low,
Floating,
}
#[derive(Debug)]
pub struct AtomicPinState {
state: AtomicUsize,
}
impl AtomicPinState {
pub fn new() -> Self {
Self::new_with_state(PinState::Floating)
}
pub fn new_with_state(state: PinState) -> Self {
AtomicPinState {
state: AtomicUsize::new(state.to_usize().unwrap()),
}
}
pub fn load(&self, order: Ordering) -> PinState {
PinState::from_usize(self.state.load(order)).unwrap()
}
pub fn store(&self, state: PinState, order: Ordering) {
self.state.store(state.to_usize().unwrap(), order);
}
pub fn fetch_update<F>(&self, set_order: Ordering, fetch_order: Ordering, mut f: F)
where
F: FnMut(PinState) -> Option<PinState>,
{
let _ = self.state.fetch_update(set_order, fetch_order, |pin| {
let pin = PinState::from_usize(pin).unwrap();
f(pin).map(|x| x.to_usize().unwrap())
});
}
}
impl Default for AtomicPinState {
fn default() -> Self {
Self::new()
}
}
#[derive(Clone, Debug)]
pub struct InputPin {
state: Arc<AtomicPinState>,
}
impl InputPin {
pub fn new(state: Arc<AtomicPinState>) -> Self {
InputPin { state }
}
}
impl hal::InputPin for InputPin {
type Error = Infallible;
fn is_high(&self) -> Result<bool, Self::Error> {
Ok(self.state.load(Ordering::SeqCst) == PinState::High)
}
fn is_low(&self) -> Result<bool, Self::Error> {
Ok(self.state.load(Ordering::SeqCst) == PinState::Low)
}
}
#[derive(Clone, Debug)]
pub struct PushPullPin {
state: Arc<AtomicPinState>,
}
impl PushPullPin {
pub fn new(state: Arc<AtomicPinState>) -> Self {
PushPullPin { state }
}
}
impl hal::OutputPin for PushPullPin {
type Error = Infallible;
fn set_high(&mut self) -> Result<(), Self::Error> {
self.state.store(PinState::High, Ordering::SeqCst);
Ok(())
}
fn set_low(&mut self) -> Result<(), Self::Error> {
self.state.store(PinState::Low, Ordering::SeqCst);
Ok(())
}
}
impl hal::StatefulOutputPin for PushPullPin {
fn is_set_high(&self) -> Result<bool, Self::Error> {
Ok(self.state.load(Ordering::SeqCst) == PinState::High)
}
fn is_set_low(&self) -> Result<bool, Self::Error> {
Ok(self.state.load(Ordering::SeqCst) == PinState::Low)
}
}
impl hal::ToggleableOutputPin for PushPullPin {
type Error = Infallible;
fn toggle(&mut self) -> Result<(), Self::Error> {
self.state
.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |x| match x {
PinState::Low => Some(PinState::High),
PinState::High => Some(PinState::Low),
_ => None,
});
Ok(())
}
}
impl hal::InputPin for PushPullPin {
type Error = Infallible;
fn is_high(&self) -> Result<bool, Self::Error> {
Ok(self.state.load(Ordering::SeqCst) == PinState::High)
}
fn is_low(&self) -> Result<bool, Self::Error> {
Ok(self.state.load(Ordering::SeqCst) == PinState::Low)
}
}
#[derive(Clone, Debug)]
pub struct OpenDrainPin {
state: Arc<AtomicPinState>,
}
impl OpenDrainPin {
pub fn new(state: Arc<AtomicPinState>) -> Self {
OpenDrainPin { state }
}
}
impl hal::OutputPin for OpenDrainPin {
type Error = Infallible;
fn set_high(&mut self) -> Result<(), Self::Error> {
self.state.store(PinState::Low, Ordering::SeqCst);
Ok(())
}
fn set_low(&mut self) -> Result<(), Self::Error> {
self.state.store(PinState::Floating, Ordering::SeqCst);
Ok(())
}
}
impl hal::StatefulOutputPin for OpenDrainPin {
fn is_set_high(&self) -> Result<bool, Self::Error> {
Ok(self.state.load(Ordering::SeqCst) == PinState::Low)
}
fn is_set_low(&self) -> Result<bool, Self::Error> {
Ok(self.state.load(Ordering::SeqCst) == PinState::Floating)
}
}
impl hal::ToggleableOutputPin for OpenDrainPin {
type Error = Infallible;
fn toggle(&mut self) -> Result<(), Self::Error> {
self.state
.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |x| match x {
PinState::Low => Some(PinState::Floating),
PinState::Floating => Some(PinState::Low),
_ => None,
});
Ok(())
}
}
impl hal::InputPin for OpenDrainPin {
type Error = Infallible;
fn is_high(&self) -> Result<bool, Self::Error> {
Ok(self.state.load(Ordering::SeqCst) == PinState::High)
}
fn is_low(&self) -> Result<bool, Self::Error> {
Ok(self.state.load(Ordering::SeqCst) == PinState::Low)
}
}
#[cfg(feature = "vcd-value")]
impl From<vcd::Value> for PinState {
fn from(val: vcd::Value) -> PinState {
use vcd::Value::*;
match val {
V0 => PinState::Low,
V1 => PinState::High,
Z => PinState::Floating,
X => PinState::Floating,
}
}
}
#[cfg(feature = "vcd-value")]
impl From<PinState> for vcd::Value {
fn from(state: PinState) -> vcd::Value {
use vcd::Value;
use PinState::*;
match state {
High => Value::V1,
Low => Value::V0,
Floating => Value::Z,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[cfg(feature = "vcd-value")]
fn vcd_value() {
use vcd::Value::*;
use PinState::*;
assert_eq!(V0, Low.into());
assert_eq!(V1, High.into());
assert_eq!(Z, Floating.into());
assert_eq!(Low, V0.into());
assert_eq!(High, V1.into());
assert_eq!(Floating, Z.into());
assert_eq!(Floating, X.into());
}
#[test]
fn atomic_pin_state() {
use PinState::*;
let state = AtomicPinState::new();
let state_def = AtomicPinState::default();
assert_eq!(
state_def.load(Ordering::SeqCst),
state.load(Ordering::SeqCst)
);
assert_eq!(Floating, state.load(Ordering::SeqCst));
assert_eq!(Floating, state.load(Ordering::SeqCst));
state.store(High, Ordering::SeqCst);
assert_eq!(High, state.load(Ordering::SeqCst));
let state = AtomicPinState::new_with_state(Low);
assert_eq!(Low, state.load(Ordering::SeqCst));
}
#[test]
fn hal_input_pin() {
use hal::InputPin as HalInputPin;
use PinState::*;
let state = Arc::new(AtomicPinState::new());
let pin = InputPin::new(state.clone());
assert_eq!(Ok(false), pin.is_high());
assert_eq!(Ok(false), pin.is_low());
state.store(High, Ordering::SeqCst);
assert_eq!(Ok(true), pin.is_high());
assert_eq!(Ok(false), pin.is_low());
state.store(Low, Ordering::SeqCst);
assert_eq!(Ok(false), pin.is_high());
assert_eq!(Ok(true), pin.is_low());
}
#[test]
fn hal_push_pull_pin() {
use hal::InputPin as HalInputPin;
use hal::OutputPin as HalOutputPin;
use hal::StatefulOutputPin as HalStatefulOutputPin;
use hal::ToggleableOutputPin;
use PinState::*;
let state = Arc::new(AtomicPinState::new());
let mut pin = PushPullPin::new(state.clone());
assert_eq!(Floating, state.load(Ordering::SeqCst));
assert_eq!(Ok(()), pin.set_high());
assert_eq!(High, state.load(Ordering::SeqCst));
assert_eq!(Ok(true), pin.is_high());
assert_eq!(Ok(false), pin.is_low());
assert_eq!(Ok(false), pin.is_set_low());
assert_eq!(Ok(true), pin.is_set_high());
assert_eq!(Ok(()), pin.set_low());
assert_eq!(Ok(true), pin.is_set_low());
assert_eq!(Ok(false), pin.is_set_high());
assert_eq!(Low, state.load(Ordering::SeqCst));
assert_eq!(Ok(false), pin.is_high());
assert_eq!(Ok(true), pin.is_low());
assert_eq!(Ok(()), pin.toggle());
assert_eq!(High, state.load(Ordering::SeqCst));
assert_eq!(Ok(()), pin.toggle());
assert_eq!(Low, state.load(Ordering::SeqCst));
}
#[test]
fn hal_open_drain_pin() {
use hal::InputPin as HalInputPin;
use hal::OutputPin as HalOutputPin;
use hal::StatefulOutputPin as HalStatefulOutputPin;
use hal::ToggleableOutputPin;
use PinState::*;
let state = Arc::new(AtomicPinState::new());
let mut pin = OpenDrainPin::new(state.clone());
assert_eq!(Floating, state.load(Ordering::SeqCst));
assert_eq!(Ok(()), pin.set_high());
assert_eq!(Low, state.load(Ordering::SeqCst));
assert_eq!(Ok(false), pin.is_high());
assert_eq!(Ok(true), pin.is_low());
assert_eq!(Ok(false), pin.is_set_low());
assert_eq!(Ok(true), pin.is_set_high());
assert_eq!(Ok(()), pin.set_low());
assert_eq!(Floating, state.load(Ordering::SeqCst));
assert_eq!(Ok(false), pin.is_high());
assert_eq!(Ok(false), pin.is_low());
assert_eq!(Ok(true), pin.is_set_low());
assert_eq!(Ok(false), pin.is_set_high());
assert_eq!(Ok(()), pin.toggle());
assert_eq!(Low, state.load(Ordering::SeqCst));
assert_eq!(Ok(()), pin.toggle());
assert_eq!(Floating, state.load(Ordering::SeqCst));
}
}