use core::convert::Infallible;
use core::marker::PhantomData;
use embedded_hal::digital::{ErrorType, InputPin, OutputPin, StatefulOutputPin};
pub struct Input<PULL = Floating> {
_pull: PhantomData<PULL>,
}
pub struct Output;
pub struct Analog;
pub struct TimerB;
pub struct TimerA;
pub struct Floating;
pub struct PullUp;
pub struct PullDown;
pub struct P1;
pub struct P2;
pub struct P3;
pub struct P4;
pub trait PortRegs {
const IN: usize;
const OUT: usize;
const DIR: usize;
const REN: usize;
const SEL0: usize;
const SEL1: usize;
const IES: usize;
const IE: usize;
const IFG: usize;
const IV: usize;
}
impl PortRegs for P1 {
const IN: usize = 0x0200;
const OUT: usize = 0x0202;
const DIR: usize = 0x0204;
const REN: usize = 0x0206;
const SEL0: usize = 0x020A;
const SEL1: usize = 0x020C;
const IES: usize = 0x0218;
const IE: usize = 0x021A;
const IFG: usize = 0x021C;
const IV: usize = 0x020E;
}
impl PortRegs for P2 {
const IN: usize = 0x0201;
const OUT: usize = 0x0203;
const DIR: usize = 0x0205;
const REN: usize = 0x0207;
const SEL0: usize = 0x020B;
const SEL1: usize = 0x020D;
const IES: usize = 0x0219;
const IE: usize = 0x021B;
const IFG: usize = 0x021D;
const IV: usize = 0x021E;
}
impl PortRegs for P3 {
const IN: usize = 0x0220;
const OUT: usize = 0x0222;
const DIR: usize = 0x0224;
const REN: usize = 0x0226;
const SEL0: usize = 0x022A;
const SEL1: usize = 0x022C;
const IES: usize = 0x0238;
const IE: usize = 0x023A;
const IFG: usize = 0x023C;
const IV: usize = 0x022E;
}
impl PortRegs for P4 {
const IN: usize = 0x0221;
const OUT: usize = 0x0223;
const DIR: usize = 0x0225;
const REN: usize = 0x0227;
const SEL0: usize = 0x022B;
const SEL1: usize = 0x022D;
const IES: usize = 0x0239;
const IE: usize = 0x023B;
const IFG: usize = 0x023D;
const IV: usize = 0x023E;
}
pub struct Pin<PORT, const N: u8, MODE> {
_port: PhantomData<PORT>,
_mode: PhantomData<MODE>,
}
impl<PORT, const N: u8, MODE> Pin<PORT, N, MODE> {
const fn new() -> Self {
Pin {
_port: PhantomData,
_mode: PhantomData,
}
}
}
#[inline(always)]
unsafe fn set_bit(addr: usize, bit: u8) {
let p = addr as *mut u8;
p.write_volatile(p.read_volatile() | (1 << bit));
}
#[inline(always)]
unsafe fn clear_bit(addr: usize, bit: u8) {
let p = addr as *mut u8;
p.write_volatile(p.read_volatile() & !(1 << bit));
}
#[inline(always)]
unsafe fn read_bit(addr: usize, bit: u8) -> bool {
let p = addr as *const u8;
p.read_volatile() & (1 << bit) != 0
}
#[inline(always)]
unsafe fn select_gpio<PORT: PortRegs>(bit: u8) {
clear_bit(PORT::SEL0, bit);
clear_bit(PORT::SEL1, bit);
}
#[inline(always)]
unsafe fn select_analog<PORT: PortRegs>(bit: u8) {
set_bit(PORT::SEL0, bit);
set_bit(PORT::SEL1, bit);
}
#[inline(always)]
unsafe fn select_secondary<PORT: PortRegs>(bit: u8) {
set_bit(PORT::SEL0, bit);
clear_bit(PORT::SEL1, bit);
}
impl<PORT: PortRegs, const N: u8, MODE> Pin<PORT, N, MODE> {
pub fn into_floating_input(self) -> Pin<PORT, N, Input<Floating>> {
unsafe {
clear_bit(PORT::DIR, N);
clear_bit(PORT::REN, N);
select_gpio::<PORT>(N);
}
Pin::new()
}
pub fn into_pull_up_input(self) -> Pin<PORT, N, Input<PullUp>> {
unsafe {
clear_bit(PORT::DIR, N);
set_bit(PORT::OUT, N); set_bit(PORT::REN, N);
select_gpio::<PORT>(N);
}
Pin::new()
}
pub fn into_pull_down_input(self) -> Pin<PORT, N, Input<PullDown>> {
unsafe {
clear_bit(PORT::DIR, N);
clear_bit(PORT::OUT, N); set_bit(PORT::REN, N);
select_gpio::<PORT>(N);
}
Pin::new()
}
pub fn into_output(self) -> Pin<PORT, N, Output> {
unsafe {
clear_bit(PORT::OUT, N); set_bit(PORT::DIR, N);
clear_bit(PORT::REN, N);
select_gpio::<PORT>(N);
}
Pin::new()
}
pub fn into_analog(self) -> Pin<PORT, N, Analog> {
unsafe {
clear_bit(PORT::DIR, N);
clear_bit(PORT::REN, N);
select_analog::<PORT>(N);
}
Pin::new()
}
pub fn into_timer_b_output(self) -> Pin<PORT, N, TimerB> {
unsafe {
set_bit(PORT::DIR, N); clear_bit(PORT::REN, N);
select_secondary::<PORT>(N);
}
Pin::new()
}
pub fn into_timer_a_capture(self) -> Pin<PORT, N, TimerA> {
unsafe {
clear_bit(PORT::DIR, N); clear_bit(PORT::REN, N);
select_secondary::<PORT>(N);
}
Pin::new()
}
pub fn into_timer_a_output(self) -> Pin<PORT, N, TimerA> {
unsafe {
set_bit(PORT::DIR, N); clear_bit(PORT::REN, N);
select_secondary::<PORT>(N);
}
Pin::new()
}
}
impl<PORT, const N: u8, MODE> ErrorType for Pin<PORT, N, MODE> {
type Error = Infallible;
}
impl<PORT: PortRegs, const N: u8, PULL> InputPin for Pin<PORT, N, Input<PULL>> {
fn is_high(&mut self) -> Result<bool, Self::Error> {
Ok(unsafe { read_bit(PORT::IN, N) })
}
fn is_low(&mut self) -> Result<bool, Self::Error> {
Ok(!unsafe { read_bit(PORT::IN, N) })
}
}
pub enum Edge {
Rising,
Falling,
}
#[cfg(feature = "critical-section")]
impl<PORT: PortRegs, const N: u8, PULL> Pin<PORT, N, Input<PULL>> {
pub fn enable_interrupt(&mut self, edge: Edge) {
critical_section::with(|_| unsafe {
match edge {
Edge::Rising => clear_bit(PORT::IES, N),
Edge::Falling => set_bit(PORT::IES, N),
}
clear_bit(PORT::IFG, N);
set_bit(PORT::IE, N);
});
}
pub fn disable_interrupt(&mut self) {
critical_section::with(|_| unsafe { clear_bit(PORT::IE, N) });
}
pub fn interrupt_pending(&self) -> bool {
unsafe { read_bit(PORT::IFG, N) }
}
pub fn clear_interrupt_pending(&mut self) {
critical_section::with(|_| unsafe { clear_bit(PORT::IFG, N) });
}
pub fn set_interrupt_pending(&mut self) {
critical_section::with(|_| unsafe { set_bit(PORT::IFG, N) });
}
}
pub fn read_iv<PORT: PortRegs>() -> u16 {
unsafe { (PORT::IV as *const u16).read_volatile() }
}
#[cfg(feature = "critical-section")]
pub fn clear_irq<PORT: PortRegs>(pin: u8) {
critical_section::with(|_| unsafe { clear_bit(PORT::IFG, pin) });
}
pub fn unlock_pins(pmm: &pac::Pmm) {
pmm.pm5ctl0().modify(|_, w| w.locklpm5().clear_bit());
}
impl<PORT: PortRegs, const N: u8> OutputPin for Pin<PORT, N, Output> {
fn set_high(&mut self) -> Result<(), Self::Error> {
unsafe { set_bit(PORT::OUT, N) };
Ok(())
}
fn set_low(&mut self) -> Result<(), Self::Error> {
unsafe { clear_bit(PORT::OUT, N) };
Ok(())
}
}
impl<PORT: PortRegs, const N: u8> StatefulOutputPin for Pin<PORT, N, Output> {
fn is_set_high(&mut self) -> Result<bool, Self::Error> {
Ok(unsafe { read_bit(PORT::OUT, N) })
}
fn is_set_low(&mut self) -> Result<bool, Self::Error> {
Ok(!unsafe { read_bit(PORT::OUT, N) })
}
}
macro_rules! port_parts {
($PortParts:ident, $Port:ident) => {
pub struct $PortParts {
pub pin0: Pin<$Port, 0, Input<Floating>>,
pub pin1: Pin<$Port, 1, Input<Floating>>,
pub pin2: Pin<$Port, 2, Input<Floating>>,
pub pin3: Pin<$Port, 3, Input<Floating>>,
pub pin4: Pin<$Port, 4, Input<Floating>>,
pub pin5: Pin<$Port, 5, Input<Floating>>,
pub pin6: Pin<$Port, 6, Input<Floating>>,
pub pin7: Pin<$Port, 7, Input<Floating>>,
}
impl $PortParts {
fn new() -> Self {
Self {
pin0: Pin::new(),
pin1: Pin::new(),
pin2: Pin::new(),
pin3: Pin::new(),
pin4: Pin::new(),
pin5: Pin::new(),
pin6: Pin::new(),
pin7: Pin::new(),
}
}
}
};
}
port_parts!(Port1Parts, P1);
port_parts!(Port2Parts, P2);
port_parts!(Port3Parts, P3);
port_parts!(Port4Parts, P4);
pub trait GpioExt {
type Parts;
fn split(self) -> Self::Parts;
}
impl GpioExt for pac::Port1_2 {
type Parts = (Port1Parts, Port2Parts);
fn split(self) -> Self::Parts {
(Port1Parts::new(), Port2Parts::new())
}
}
impl GpioExt for pac::Port3_4 {
type Parts = (Port3Parts, Port4Parts);
fn split(self) -> Self::Parts {
(Port3Parts::new(), Port4Parts::new())
}
}