use core::convert::TryFrom;
use paste::paste;
use crate::ehal::digital::v2::OutputPin;
#[cfg(feature = "unproven")]
use crate::ehal::digital::v2::{InputPin, StatefulOutputPin, ToggleableOutputPin};
use super::pin::*;
use super::reg::RegisterInterface;
#[derive(PartialEq, Eq, Clone, Copy)]
pub enum DynDisabled {
Floating,
PullDown,
PullUp,
}
#[derive(PartialEq, Eq, Clone, Copy)]
pub enum DynInput {
Floating,
PullDown,
PullUp,
}
#[derive(PartialEq, Eq, Clone, Copy)]
pub enum DynInterrupt {
Floating,
PullDown,
PullUp,
}
#[derive(PartialEq, Eq, Clone, Copy)]
pub enum DynOutput {
PushPull,
Readable,
}
#[derive(PartialEq, Eq, Clone, Copy)]
pub enum DynAlternate {
B,
C,
D,
E,
F,
G,
#[cfg(any(feature = "samd21", feature = "min-samd51g"))]
H,
#[cfg(feature = "min-samd51g")]
I,
#[cfg(feature = "min-samd51g")]
J,
#[cfg(feature = "min-samd51g")]
K,
#[cfg(feature = "min-samd51g")]
L,
#[cfg(feature = "min-samd51g")]
M,
#[cfg(feature = "min-samd51g")]
N,
}
#[derive(PartialEq, Eq, Clone, Copy)]
pub enum DynPinMode {
Disabled(DynDisabled),
Input(DynInput),
Interrupt(DynInterrupt),
Output(DynOutput),
Alternate(DynAlternate),
}
pub const DYN_FLOATING_DISABLED: DynPinMode = DynPinMode::Disabled(DynDisabled::Floating);
pub const DYN_PULL_DOWN_DISABLED: DynPinMode = DynPinMode::Disabled(DynDisabled::PullDown);
pub const DYN_PULL_UP_DISABLED: DynPinMode = DynPinMode::Disabled(DynDisabled::PullUp);
pub const DYN_FLOATING_INPUT: DynPinMode = DynPinMode::Input(DynInput::Floating);
pub const DYN_PULL_DOWN_INPUT: DynPinMode = DynPinMode::Input(DynInput::PullDown);
pub const DYN_PULL_UP_INPUT: DynPinMode = DynPinMode::Input(DynInput::PullUp);
pub const DYN_FLOATING_INTERRUPT: DynPinMode = DynPinMode::Interrupt(DynInterrupt::Floating);
pub const DYN_PULL_DOWN_INTERRUPT: DynPinMode = DynPinMode::Interrupt(DynInterrupt::PullDown);
pub const DYN_PULL_UP_INTERRUPT: DynPinMode = DynPinMode::Interrupt(DynInterrupt::PullUp);
pub const DYN_PUSH_PULL_OUTPUT: DynPinMode = DynPinMode::Output(DynOutput::PushPull);
pub const DYN_READABLE_OUTPUT: DynPinMode = DynPinMode::Output(DynOutput::Readable);
macro_rules! dyn_alternate {
( $($Letter:ident),+ ) => {
paste! {
$(
#[
doc = "Value-level variant of [`DynPinMode`] for alternate "
"peripheral function " $Letter
]
pub const [<DYN_ALTERNATE_ $Letter>]: DynPinMode =
DynPinMode::Alternate(DynAlternate::$Letter);
)+
}
};
}
dyn_alternate!(B, C, D, E, F, G);
#[cfg(any(feature = "samd21", feature = "min-samd51g"))]
dyn_alternate!(H);
#[cfg(feature = "min-samd51g")]
dyn_alternate!(I, J, K, L, M, N);
#[derive(PartialEq, Clone, Copy)]
pub enum DynGroup {
A,
#[cfg(any(feature = "samd21", feature = "min-samd51g"))]
B,
#[cfg(feature = "min-samd51n")]
C,
#[cfg(feature = "min-samd51p")]
D,
}
#[derive(PartialEq, Clone, Copy)]
pub struct DynPinId {
pub group: DynGroup,
pub num: u8,
}
struct DynRegisters {
id: DynPinId,
}
unsafe impl RegisterInterface for DynRegisters {
#[inline]
fn id(&self) -> DynPinId {
self.id
}
}
impl DynRegisters {
#[inline]
unsafe fn new(id: DynPinId) -> Self {
DynRegisters { id }
}
}
pub enum Error {
InvalidPinType,
}
pub struct DynPin {
regs: DynRegisters,
mode: DynPinMode,
}
impl DynPin {
#[inline]
unsafe fn new(id: DynPinId, mode: DynPinMode) -> Self {
DynPin {
regs: DynRegisters::new(id),
mode,
}
}
#[inline]
pub fn id(&self) -> DynPinId {
self.regs.id
}
#[inline]
pub fn mode(&self) -> DynPinMode {
self.mode
}
#[inline]
pub fn into_mode(&mut self, mode: DynPinMode) {
if mode != self.mode {
self.regs.change_mode(mode);
self.mode = mode;
}
}
#[inline]
pub fn into_floating_disabled(&mut self) {
self.into_mode(DYN_FLOATING_DISABLED);
}
#[inline]
pub fn into_pull_down_disabled(&mut self) {
self.into_mode(DYN_PULL_DOWN_DISABLED);
}
#[inline]
pub fn into_pull_up_disabled(&mut self) {
self.into_mode(DYN_PULL_UP_DISABLED);
}
#[inline]
pub fn into_floating_input(&mut self) {
self.into_mode(DYN_FLOATING_INPUT);
}
#[inline]
pub fn into_pull_down_input(&mut self) {
self.into_mode(DYN_PULL_DOWN_INPUT);
}
#[inline]
pub fn into_pull_up_input(&mut self) {
self.into_mode(DYN_PULL_UP_INPUT);
}
#[inline]
pub fn into_floating_interrupt(&mut self) {
self.into_mode(DYN_FLOATING_INTERRUPT);
}
#[inline]
pub fn into_pull_down_interrupt(&mut self) {
self.into_mode(DYN_PULL_DOWN_INTERRUPT);
}
#[inline]
pub fn into_pull_up_interrupt(&mut self) {
self.into_mode(DYN_PULL_UP_INTERRUPT);
}
#[inline]
pub fn into_push_pull_output(&mut self) {
self.into_mode(DYN_PUSH_PULL_OUTPUT);
}
#[inline]
pub fn into_readable_output(&mut self) {
self.into_mode(DYN_READABLE_OUTPUT);
}
#[inline]
pub fn into_alternate(&mut self, config: DynAlternate) {
self.into_mode(DynPinMode::Alternate(config));
}
#[inline]
pub fn get_drive_strength(&self) -> bool {
self.regs.read_drive_strength()
}
#[inline]
pub fn set_drive_strength(&mut self, stronger: bool) {
self.regs.write_drive_strength(stronger);
}
#[inline]
fn _read(&self) -> Result<bool, Error> {
match self.mode {
DynPinMode::Input(_) | DYN_READABLE_OUTPUT => Ok(self.regs.read_pin()),
_ => Err(Error::InvalidPinType),
}
}
#[inline]
fn _write(&mut self, bit: bool) -> Result<(), Error> {
match self.mode {
DynPinMode::Output(_) => {
self.regs.write_pin(bit);
Ok(())
}
_ => Err(Error::InvalidPinType),
}
}
#[inline]
fn _toggle(&mut self) -> Result<(), Error> {
match self.mode {
DynPinMode::Output(_) => {
self.regs.toggle_pin();
Ok(())
}
_ => Err(Error::InvalidPinType),
}
}
#[inline]
fn _read_out(&self) -> Result<bool, Error> {
match self.mode {
DYN_READABLE_OUTPUT => Ok(self.regs.read_out_pin()),
_ => Err(Error::InvalidPinType),
}
}
#[inline]
fn _is_low(&self) -> Result<bool, Error> {
Ok(self._read()? == false)
}
#[inline]
fn _is_high(&self) -> Result<bool, Error> {
Ok(self._read()? == true)
}
#[inline]
fn _set_low(&mut self) -> Result<(), Error> {
self._write(false)
}
#[inline]
fn _set_high(&mut self) -> Result<(), Error> {
self._write(true)
}
#[inline]
fn _is_set_low(&self) -> Result<bool, Error> {
Ok(self._read_out()? == false)
}
#[inline]
fn _is_set_high(&self) -> Result<bool, Error> {
Ok(self._read_out()? == true)
}
}
impl<I, M> From<Pin<I, M>> for DynPin
where
I: PinId,
M: PinMode,
{
#[inline]
fn from(_pin: Pin<I, M>) -> Self {
unsafe { DynPin::new(I::DYN, M::DYN) }
}
}
impl<I, M> TryFrom<DynPin> for Pin<I, M>
where
I: PinId,
M: PinMode,
{
type Error = Error;
#[inline]
fn try_from(pin: DynPin) -> Result<Self, Error> {
if pin.regs.id == I::DYN && pin.mode == M::DYN {
Ok(unsafe { Self::new() })
} else {
Err(Error::InvalidPinType)
}
}
}
impl OutputPin for DynPin {
type Error = Error;
#[inline]
fn set_high(&mut self) -> Result<(), Self::Error> {
self._set_high()
}
#[inline]
fn set_low(&mut self) -> Result<(), Self::Error> {
self._set_low()
}
}
#[cfg(feature = "unproven")]
impl InputPin for DynPin {
type Error = Error;
#[inline]
fn is_high(&self) -> Result<bool, Self::Error> {
self._is_high()
}
#[inline]
fn is_low(&self) -> Result<bool, Self::Error> {
self._is_low()
}
}
#[cfg(feature = "unproven")]
impl ToggleableOutputPin for DynPin {
type Error = Error;
#[inline]
fn toggle(&mut self) -> Result<(), Self::Error> {
self._toggle()
}
}
#[cfg(feature = "unproven")]
impl StatefulOutputPin for DynPin {
#[inline]
fn is_set_high(&self) -> Result<bool, Self::Error> {
self._is_set_high()
}
#[inline]
fn is_set_low(&self) -> Result<bool, Self::Error> {
self._is_set_low()
}
}