use core::convert::Infallible;
use embassy_hal_internal::{Peri, PeripheralType};
use embassy_sync::waitqueue::AtomicWaker;
use pac::peripherals::rgpio::{RGPIO, RGPIO_PCOR, RGPIO_PSOR, RGPIO_PTOR};
use crate::pac;
const PIN_COUNT: u8 = 32;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Level {
Low,
High,
}
pub struct State {
wakers: [AtomicWaker; PIN_COUNT as usize],
}
impl State {
pub(crate) const fn new() -> Self {
Self {
wakers: [const { AtomicWaker::new() }; PIN_COUNT as usize],
}
}
fn wake(&self, pending_mask: u32) {
let mut mask = pending_mask;
while mask != 0 {
let pin = mask.trailing_zeros() as usize;
self.wakers[pin].wake();
mask &= mask - 1;
}
}
}
pub(crate) trait SealedInstance {
fn regs() -> RGPIO;
fn state() -> &'static State;
const LPCG: Option<u32>;
}
#[allow(private_bounds)]
pub trait Instance: SealedInstance + PeripheralType {
const IRQ: Option<u16>;
}
macro_rules! impl_gpio {
($instance:ident, $base:expr, $irq:expr) => {
impl crate::gpio::SealedInstance for crate::peripherals::$instance {
const LPCG: ::core::option::Option<u32> = crate::clock::lpcg(stringify!($instance));
#[inline]
fn regs() -> crate::pac::peripherals::rgpio::RGPIO {
unsafe { crate::pac::peripherals::rgpio::RGPIO::from_ptr($base as *mut ()) }
}
#[inline]
fn state() -> &'static crate::gpio::State {
static STATE: crate::gpio::State = crate::gpio::State::new();
&STATE
}
}
impl crate::gpio::Instance for crate::peripherals::$instance {
const IRQ: ::core::option::Option<u16> = $irq;
}
};
}
pub(crate) use impl_gpio;
pub fn on_interrupt<T: Instance>(pending_mask: u32) {
T::state().wake(pending_mask);
}
pub struct Gpio {
port: RGPIO,
taken: u32,
}
impl Gpio {
pub fn new<T: Instance>(peri: Peri<'static, T>) -> Self {
let _ = peri;
crate::clock::ungate(T::LPCG);
Self {
port: T::regs(),
taken: 0,
}
}
pub fn pin(&mut self, pin: u8) -> Pin {
self.try_pin(pin)
.expect("GPIO pin index out of range or already taken")
}
pub fn try_pin(&mut self, pin: u8) -> Option<Pin> {
claim(&mut self.taken, pin).then_some(Pin {
port: self.port,
pin,
})
}
}
pub struct Pin {
port: RGPIO,
pin: u8,
}
fn claim(taken: &mut u32, pin: u8) -> bool {
if pin >= PIN_COUNT {
return false;
}
let mask = 1u32 << pin;
if *taken & mask != 0 {
return false;
}
*taken |= mask;
true
}
impl Pin {
#[inline]
fn mask(&self) -> u32 {
1u32 << self.pin
}
pub fn into_output(self, initial: Level) -> Self {
match initial {
Level::Low => self.write_low(),
Level::High => self.write_high(),
}
self.port.PDDR().modify(|w| w.0 |= self.mask());
self
}
pub fn into_input(self) -> Self {
self.port.PDDR().modify(|w| w.0 &= !self.mask());
self
}
#[inline]
fn write_high(&self) {
self.port.PSOR().write_value(RGPIO_PSOR(self.mask()));
}
#[inline]
fn write_low(&self) {
self.port.PCOR().write_value(RGPIO_PCOR(self.mask()));
}
pub fn set_high(&mut self) {
self.write_high();
}
pub fn set_low(&mut self) {
self.write_low();
}
pub fn toggle(&mut self) {
self.port.PTOR().write_value(RGPIO_PTOR(self.mask()));
}
pub fn is_set_high(&self) -> bool {
self.port.PDOR().read().0 & self.mask() != 0
}
pub fn is_high(&self) -> bool {
self.port.PDIR().read().0 & self.mask() != 0
}
}
impl embedded_hal::digital::ErrorType for Pin {
type Error = Infallible;
}
impl embedded_hal::digital::OutputPin for Pin {
fn set_high(&mut self) -> Result<(), Infallible> {
Pin::set_high(self);
Ok(())
}
fn set_low(&mut self) -> Result<(), Infallible> {
Pin::set_low(self);
Ok(())
}
}
impl embedded_hal::digital::StatefulOutputPin for Pin {
fn is_set_high(&mut self) -> Result<bool, Infallible> {
Ok(Pin::is_set_high(self))
}
fn is_set_low(&mut self) -> Result<bool, Infallible> {
Ok(!Pin::is_set_high(self))
}
fn toggle(&mut self) -> Result<(), Infallible> {
Pin::toggle(self);
Ok(())
}
}
impl embedded_hal::digital::InputPin for Pin {
fn is_high(&mut self) -> Result<bool, Infallible> {
Ok(Pin::is_high(self))
}
fn is_low(&mut self) -> Result<bool, Infallible> {
Ok(!Pin::is_high(self))
}
}
#[cfg(test)]
mod tests {
use super::{Gpio, PIN_COUNT, claim};
use crate::pac::peripherals::rgpio::RGPIO;
fn port() -> RGPIO {
unsafe { RGPIO::from_ptr(core::ptr::NonNull::<u32>::dangling().as_ptr().cast()) }
}
#[test]
fn claim_hands_each_pin_out_once() {
let mut taken = 0;
assert!(claim(&mut taken, 3));
assert!(!claim(&mut taken, 3));
assert!(claim(&mut taken, 0));
assert!(claim(&mut taken, PIN_COUNT - 1));
assert!(!claim(&mut taken, PIN_COUNT));
}
#[test]
fn port_refuses_a_second_handle_to_a_pin() {
let mut gpio = Gpio {
port: port(),
taken: 0,
};
assert!(gpio.try_pin(5).is_some());
assert!(gpio.try_pin(5).is_none());
assert!(gpio.try_pin(6).is_some());
}
#[test]
#[should_panic(expected = "already taken")]
fn pin_panics_on_reuse() {
let mut gpio = Gpio {
port: port(),
taken: 0,
};
let _a = gpio.pin(1);
let _b = gpio.pin(1);
}
}