use super::pin::{Pin, PinId, PinMode, ValidPinMode};
use super::reg::RegisterInterface;
use super::{Interrupt, InterruptOverride};
use core::convert::TryFrom;
#[cfg(feature = "eh1_0_alpha")]
use eh1_0_alpha::digital as eh1;
use hal::digital::v2::{InputPin, OutputPin, StatefulOutputPin, ToggleableOutputPin};
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[allow(missing_docs)]
pub enum DynDisabled {
Floating,
PullDown,
PullUp,
BusKeep,
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[allow(missing_docs)]
pub enum DynInput {
Floating,
PullDown,
PullUp,
BusKeep,
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[allow(missing_docs)]
pub enum DynOutput {
PushPull,
Readable,
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[allow(missing_docs)]
pub enum DynFunction {
Spi,
Xip,
Uart,
I2C,
Pwm,
Pio0,
Pio1,
Clock,
UsbAux,
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[allow(missing_docs)]
pub enum DynPinMode {
Disabled(DynDisabled),
Input(DynInput),
Output(DynOutput),
Function(DynFunction),
}
impl DynPinMode {
#[inline]
fn valid_for(&self, id: DynPinId) -> bool {
use DynFunction::*;
use DynGroup::*;
use DynPinMode::*;
match self {
Disabled(_) => true,
Input(_) => true,
Output(_) => true,
Function(alt) => match id.group {
Bank0 => match alt {
Spi | Uart | I2C | Pwm | Pio0 | Pio1 | UsbAux => true,
Clock if id.num >= 20 && id.num <= 25 => true,
_ => false,
},
#[allow(clippy::match_like_matches_macro)]
Qspi => match alt {
Xip => true,
_ => false,
},
},
}
}
}
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_PUSH_PULL_OUTPUT: DynPinMode = DynPinMode::Output(DynOutput::PushPull);
pub const DYN_READABLE_OUTPUT: DynPinMode = DynPinMode::Output(DynOutput::Readable);
macro_rules! dyn_function {
( $($Func:ident),+ ) => {
crate::paste::paste! {
$(
#[
doc = "Value-level variant of [`DynPinMode`] for alternate "
"peripheral function " $Func
]
pub const [<DYN_FUNCTION_ $Func:upper>]: DynPinMode =
DynPinMode::Function(DynFunction::$Func);
)+
}
};
}
dyn_function!(Spi, Xip, Uart, I2C, Pwm, Pio0, Pio1, Clock, UsbAux);
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[allow(missing_docs)]
pub enum DynGroup {
Bank0,
Qspi,
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[allow(missing_docs)]
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 }
}
}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Error {
InvalidPinType,
InvalidPinMode,
}
#[cfg(feature = "eh1_0_alpha")]
impl eh1::Error for Error {
fn kind(&self) -> eh1::ErrorKind {
eh1::ErrorKind::Other
}
}
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 try_into_mode(&mut self, mode: DynPinMode) -> Result<(), Error> {
if mode.valid_for(self.regs.id) {
if mode != self.mode {
self.regs.do_change_mode(mode);
self.mode = mode;
}
Ok(())
} else {
Err(Error::InvalidPinMode)
}
}
#[inline]
pub fn clear_interrupt(&self, interrupt: Interrupt) {
self.regs.clear_interrupt(interrupt)
}
#[inline]
pub fn interrupt_status(&self, interrupt: Interrupt) -> bool {
self.regs.interrupt_status(interrupt)
}
#[inline]
pub fn is_interrupt_enabled(&self, interrupt: Interrupt) -> bool {
self.regs.is_interrupt_enabled(interrupt)
}
#[inline]
pub fn set_interrupt_enabled(&self, interrupt: Interrupt, enabled: bool) {
self.regs.set_interrupt_enabled(interrupt, enabled)
}
#[inline]
pub fn is_interrupt_forced(&self, interrupt: Interrupt) -> bool {
self.regs.is_interrupt_forced(interrupt)
}
#[inline]
pub fn set_interrupt_forced(&self, interrupt: Interrupt, forced: bool) {
self.regs.set_interrupt_forced(interrupt, forced);
}
#[inline]
pub fn set_interrupt_override(&mut self, override_value: InterruptOverride) {
self.regs.set_interrupt_override(override_value);
}
#[inline]
#[allow(clippy::wrong_self_convention)] pub fn into_floating_disabled(&mut self) {
self.try_into_mode(DYN_FLOATING_DISABLED).unwrap(); }
#[inline]
#[allow(clippy::wrong_self_convention)] pub fn into_pull_down_disabled(&mut self) {
self.try_into_mode(DYN_PULL_DOWN_DISABLED).unwrap(); }
#[inline]
#[allow(clippy::wrong_self_convention)] pub fn into_pull_up_disabled(&mut self) {
self.try_into_mode(DYN_PULL_UP_DISABLED).unwrap(); }
#[inline]
#[allow(clippy::wrong_self_convention)] pub fn into_floating_input(&mut self) {
self.try_into_mode(DYN_FLOATING_INPUT).unwrap(); }
#[inline]
#[allow(clippy::wrong_self_convention)] pub fn into_pull_down_input(&mut self) {
self.try_into_mode(DYN_PULL_DOWN_INPUT).unwrap(); }
#[inline]
#[allow(clippy::wrong_self_convention)] pub fn into_pull_up_input(&mut self) {
self.try_into_mode(DYN_PULL_UP_INPUT).unwrap(); }
#[inline]
#[allow(clippy::wrong_self_convention)] pub fn into_push_pull_output(&mut self) {
self.try_into_mode(DYN_PUSH_PULL_OUTPUT).unwrap(); }
#[inline]
#[allow(clippy::wrong_self_convention)] pub fn into_readable_output(&mut self) {
self.try_into_mode(DYN_READABLE_OUTPUT).unwrap(); }
#[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 {
DynPinMode::Output(_) => Ok(self.regs.read_out_pin()),
_ => Err(Error::InvalidPinType),
}
}
#[inline]
#[allow(clippy::bool_comparison)] fn _is_low(&self) -> Result<bool, Error> {
Ok(self._read()? == false)
}
#[inline]
#[allow(clippy::bool_comparison)] 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]
#[allow(clippy::bool_comparison)] fn _is_set_low(&self) -> Result<bool, Error> {
Ok(self._read_out()? == false)
}
#[inline]
#[allow(clippy::bool_comparison)] 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 + ValidPinMode<I>,
{
#[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 + ValidPinMode<I>,
{
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()
}
}
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()
}
}
impl ToggleableOutputPin for DynPin {
type Error = Error;
#[inline]
fn toggle(&mut self) -> Result<(), Self::Error> {
self._toggle()
}
}
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()
}
}
#[cfg(feature = "eh1_0_alpha")]
impl eh1::ErrorType for DynPin {
type Error = Error;
}
#[cfg(feature = "eh1_0_alpha")]
impl eh1::OutputPin for DynPin {
#[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 = "eh1_0_alpha")]
impl eh1::InputPin for DynPin {
#[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 = "eh1_0_alpha")]
impl eh1::ToggleableOutputPin for DynPin {
#[inline]
fn toggle(&mut self) -> Result<(), Self::Error> {
self._toggle()
}
}
#[cfg(feature = "eh1_0_alpha")]
impl eh1::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()
}
}