use atmega32u4;
use hal::digital;
use core::marker;
pub trait PortExt {
type Parts;
fn split(self) -> Self::Parts;
}
pub mod mode {
use core::marker;
pub trait Io {}
pub mod io {
use core::marker;
pub struct Input<MODE> {
_mode: marker::PhantomData<MODE>,
}
pub struct Output;
pub struct PullUp;
pub struct Floating;
impl<MODE> super::Io for Input<MODE> {}
impl super::Io for Output {}
}
pub struct Pwm<TIMER> {
pub(crate) _tim: marker::PhantomData<TIMER>,
}
}
macro_rules! port_impl {
($PortEnum:ident, $PORTX:ident, $portx:ident, $PXx:ident, [
$($PXi:ident: ($pxi:ident, $i:expr, $MODE:ty),)+
]) => {
pub mod $portx {
use core::marker;
use atmega32u4;
use hal::digital;
use super::{PortExt, mode};
pub struct Parts {
pub ddr: DDR,
$(
pub $pxi: $PXi<$MODE>,
)+
}
impl PortExt for atmega32u4::$PORTX {
type Parts = Parts;
fn split(self) -> Parts {
Parts {
ddr: DDR { _0: () },
$(
$pxi: $PXi { _mode: marker::PhantomData },
)+
}
}
}
pub trait PortDDR {
#[doc(hidden)]
fn ddr(&mut self) -> &atmega32u4::$portx::DDR;
}
pub struct DDR {
_0: (),
}
impl PortDDR for DDR {
fn ddr(&mut self) -> &atmega32u4::$portx::DDR {
unsafe { &(*atmega32u4::$PORTX::ptr()).ddr }
}
}
pub struct $PXx<MODE> {
i: u8,
_mode: marker::PhantomData<MODE>,
}
impl digital::OutputPin for $PXx<mode::io::Output> {
fn set_high(&mut self) {
unsafe {
(*atmega32u4::$PORTX::ptr())
.port.modify(|r, w| w.bits(r.bits() | (1 << self.i)))
}
}
fn set_low(&mut self) {
unsafe {
(*atmega32u4::$PORTX::ptr())
.port.modify(|r, w| w.bits(r.bits() & !(1 << self.i)))
}
}
}
impl digital::StatefulOutputPin for $PXx<mode::io::Output> {
fn is_set_high(&self) -> bool {
(unsafe {
(*atmega32u4::$PORTX::ptr()).port.read().bits()
} & (1 << self.i)) != 0
}
fn is_set_low(&self) -> bool {
(unsafe {
(*atmega32u4::$PORTX::ptr()).port.read().bits()
} & (1 << self.i)) == 0
}
}
impl digital::toggleable::Default for $PXx<mode::io::Output> { }
impl<MODE> digital::InputPin for $PXx<mode::io::Input<MODE>> {
fn is_high(&self) -> bool {
(unsafe {
(*atmega32u4::$PORTX::ptr()).pin.read().bits()
} & (1 << self.i)) != 0
}
fn is_low(&self) -> bool {
(unsafe {
(*atmega32u4::$PORTX::ptr()).pin.read().bits()
} & (1 << self.i)) == 0
}
}
$(
pub struct $PXi<MODE> {
pub(crate) _mode: marker::PhantomData<MODE>,
}
impl<MODE> $PXi<MODE> {
pub fn downgrade(self) -> super::Pin<MODE> {
super::Pin {
i: $i,
port: super::Port::$PortEnum,
_mode: marker::PhantomData,
}
}
pub fn downgrade_port(self) -> $PXx<MODE> {
$PXx {
i: $i,
_mode: marker::PhantomData,
}
}
}
impl<MODE: mode::Io> $PXi<MODE> {
pub fn into_floating_input<D: PortDDR>(
self,
ddr: &mut D,
) -> $PXi<mode::io::Input<mode::io::Floating>> {
ddr.ddr().modify(|r, w| unsafe { w.bits(r.bits() & !(1 << $i)) });
unsafe {
(*atmega32u4::$PORTX::ptr())
.port.modify(|r, w| w.bits(r.bits() & !(1 << $i)))
}
$PXi { _mode: marker::PhantomData }
}
pub fn into_pull_up_input<D: PortDDR>(
self,
ddr: &mut D,
) -> $PXi<mode::io::Input<mode::io::PullUp>> {
ddr.ddr().modify(|r, w| unsafe { w.bits(r.bits() & !(1 << $i)) });
unsafe {
(*atmega32u4::$PORTX::ptr())
.port.modify(|r, w| w.bits(r.bits() | (1 << $i)))
}
$PXi { _mode: marker::PhantomData }
}
pub fn into_output<D: PortDDR>(self, ddr: &mut D) -> $PXi<mode::io::Output> {
ddr.ddr().modify(|r, w| unsafe { w.bits(r.bits() | (1 << $i)) });
$PXi { _mode: marker::PhantomData }
}
}
impl digital::OutputPin for $PXi<mode::io::Output> {
fn set_high(&mut self) {
unsafe {
(*atmega32u4::$PORTX::ptr())
.port.modify(|r, w| w.bits(r.bits() | (1 << $i)))
}
}
fn set_low(&mut self) {
unsafe {
(*atmega32u4::$PORTX::ptr())
.port.modify(|r, w| w.bits(r.bits() & !(1 << $i)))
}
}
}
impl digital::StatefulOutputPin for $PXi<mode::io::Output> {
fn is_set_high(&self) -> bool {
(unsafe {
(*atmega32u4::$PORTX::ptr()).port.read().bits()
} & (1 << $i)) != 0
}
fn is_set_low(&self) -> bool {
(unsafe {
(*atmega32u4::$PORTX::ptr()).port.read().bits()
} & (1 << $i)) == 0
}
}
impl digital::toggleable::Default for $PXi<mode::io::Output> { }
impl<MODE> digital::InputPin for $PXi<mode::io::Input<MODE>> {
fn is_high(&self) -> bool {
(unsafe {
(*atmega32u4::$PORTX::ptr()).pin.read().bits()
} & (1 << $i)) != 0
}
fn is_low(&self) -> bool {
(unsafe {
(*atmega32u4::$PORTX::ptr()).pin.read().bits()
} & (1 << $i)) == 0
}
}
)+
}
}
}
macro_rules! generic_pin_impl {
($($PortEnum:ident: $Port:ident,)+) => {
#[derive(Clone, Copy, Debug)]
enum Port {
$($PortEnum,)+
}
#[derive(Debug)]
pub struct Pin<MODE> {
i: u8,
port: Port,
_mode: marker::PhantomData<MODE>,
}
impl digital::OutputPin for Pin<mode::io::Output> {
fn set_high(&mut self) {
match self.port {
$(
Port::$PortEnum => unsafe {
(*atmega32u4::$Port::ptr())
.port.modify(|r, w| w.bits(r.bits() | (1 << self.i)))
},
)+
}
}
fn set_low(&mut self) {
match self.port {
$(
Port::$PortEnum => unsafe {
(*atmega32u4::$Port::ptr())
.port.modify(|r, w| w.bits(r.bits() & !(1 << self.i)))
},
)+
}
}
}
impl digital::StatefulOutputPin for Pin<mode::io::Output> {
fn is_set_high(&self) -> bool {
match self.port {
$(
Port::$PortEnum => unsafe {
((*atmega32u4::$Port::ptr()).port.read().bits() & (1 << self.i)) != 0
},
)+
}
}
fn is_set_low(&self) -> bool {
match self.port {
$(
Port::$PortEnum => unsafe {
((*atmega32u4::$Port::ptr()).port.read().bits() & (1 << self.i)) == 0
},
)+
}
}
}
impl digital::toggleable::Default for Pin<mode::io::Output> { }
impl<MODE> digital::InputPin for Pin<mode::io::Input<MODE>> {
fn is_high(&self) -> bool {
match self.port {
$(
Port::$PortEnum => unsafe {
((*atmega32u4::$Port::ptr()).pin.read().bits() & (1 << self.i)) != 0
},
)+
}
}
fn is_low(&self) -> bool {
match self.port {
$(
Port::$PortEnum => unsafe {
((*atmega32u4::$Port::ptr()).pin.read().bits() & (1 << self.i)) == 0
},
)+
}
}
}
}
}
generic_pin_impl!(
B: PORTB,
C: PORTC,
D: PORTD,
E: PORTE,
F: PORTF,
);
port_impl! (B, PORTB, portb, PBx, [
PB0: (pb0, 0, mode::io::Input<mode::io::Floating>),
PB1: (pb1, 1, mode::io::Input<mode::io::Floating>),
PB2: (pb2, 2, mode::io::Input<mode::io::Floating>),
PB3: (pb3, 3, mode::io::Input<mode::io::Floating>),
PB4: (pb4, 4, mode::io::Input<mode::io::Floating>),
PB5: (pb5, 5, mode::io::Input<mode::io::Floating>),
PB6: (pb6, 6, mode::io::Input<mode::io::Floating>),
PB7: (pb7, 7, mode::io::Input<mode::io::Floating>),
]);
port_impl! (C, PORTC, portc, PCx, [
PC6: (pc6, 6, mode::io::Input<mode::io::Floating>),
PC7: (pc7, 7, mode::io::Input<mode::io::Floating>),
]);
port_impl! (D, PORTD, portd, PDx, [
PD0: (pd0, 0, mode::io::Input<mode::io::Floating>),
PD1: (pd1, 1, mode::io::Input<mode::io::Floating>),
PD2: (pd2, 2, mode::io::Input<mode::io::Floating>),
PD3: (pd3, 3, mode::io::Input<mode::io::Floating>),
PD4: (pd4, 4, mode::io::Input<mode::io::Floating>),
PD5: (pd5, 5, mode::io::Input<mode::io::Floating>),
PD6: (pd6, 6, mode::io::Input<mode::io::Floating>),
PD7: (pd7, 7, mode::io::Input<mode::io::Floating>),
]);
port_impl! (E, PORTE, porte, PEx, [
PE2: (pe2, 2, mode::io::Input<mode::io::Floating>),
PE6: (pe6, 6, mode::io::Input<mode::io::Floating>),
]);
port_impl! (F, PORTF, portf, PFx, [
PF0: (pf0, 0, mode::io::Input<mode::io::Floating>),
PF1: (pf1, 1, mode::io::Input<mode::io::Floating>),
PF4: (pf4, 4, mode::io::Input<mode::io::Floating>),
PF5: (pf5, 5, mode::io::Input<mode::io::Floating>),
PF6: (pf6, 6, mode::io::Input<mode::io::Floating>),
PF7: (pf7, 7, mode::io::Input<mode::io::Floating>),
]);
#[doc(hidden)]
#[macro_export]
macro_rules! define_pins {
(
$(#[$pins_attr:meta])*
name: $Pins:ident,
ddr: $DDR:ident {
$($portx:ident: $PORTX:ty,)+
},
pins: {
$(
$(#[$attr:meta])*
$name:ident: ($port:ident, $pin:ident, $PIN:ident),
)+
}
) => {
pub struct $DDR {
$(
$portx: $crate::port::$portx::DDR,
)+
}
$(
impl $crate::port::$portx::PortDDR for $DDR {
fn ddr(&mut self) -> &atmega32u4::$portx::DDR {
self.$portx.ddr()
}
}
)+
$(#[$pins_attr])*
pub struct $Pins {
$(
$(#[$attr])*
pub $name: $crate::port::$port::$PIN<
$crate::port::mode::io::Input<$crate::port::mode::io::Floating>
>,
)+
pub ddr: $DDR,
}
impl $Pins {
pub fn new(
$( $portx: $PORTX, )+
) -> Pins {
use $crate::port::PortExt;
$( let $portx = $portx.split(); )+
Pins {
$(
$name: $port.$pin,
)+
ddr: $DDR {
$(
$portx: $portx.ddr,
)+
}
}
}
}
};
}