use crate::peripherals::{Gpio0, Gpio1, Gpio2, IoConfig};
use core::marker::PhantomData;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Pull {
None,
Up,
Down,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum InterruptTrigger {
RisingEdge,
FallingEdge,
HighLevel,
LowLevel,
}
#[derive(Debug, Clone, Copy)]
pub struct InputConfig {
pub pull: Pull,
}
impl Default for InputConfig {
fn default() -> Self {
Self { pull: Pull::None }
}
}
impl InputConfig {
pub const fn new() -> Self {
Self { pull: Pull::None }
}
pub const fn with_pull(mut self, pull: Pull) -> Self {
self.pull = pull;
self
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct OutputConfig {
pub open_drain: bool,
pub initial_high: bool,
}
impl OutputConfig {
pub const fn new() -> Self {
Self { open_drain: false, initial_high: false }
}
pub const fn with_open_drain(mut self, od: bool) -> Self {
self.open_drain = od;
self
}
pub const fn with_initial(mut self, high: bool) -> Self {
self.initial_high = high;
self
}
}
pub struct InputMode;
pub struct OutputMode;
pub struct AnyPin<'d> {
block: u8,
bit: u8,
_lifetime: PhantomData<&'d mut ()>,
}
impl<'d> AnyPin<'d> {
pub unsafe fn steal(pin: u8) -> Self {
Self { block: pin / 8, bit: pin % 8, _lifetime: PhantomData }
}
pub fn number(&self) -> u8 {
self.block * 8 + self.bit
}
fn set_oen(&self, input: bool) {
let r = regs(self.block);
let mask = 1 << self.bit;
if input {
r.gpio_sw_oen().modify(|r, w| unsafe { w.bits(r.bits() | mask) });
} else {
r.gpio_sw_oen().modify(|r, w| unsafe { w.bits(r.bits() & !mask) });
}
}
pub fn init_input(self, config: InputConfig) -> Input<'d> {
self.set_oen(true);
apply_pull(self.number(), config.pull);
Input { pin: self, config }
}
pub fn init_output(self, config: OutputConfig) -> Output<'d> {
let mut out = Output { pin: self, config };
if config.initial_high {
out.set_high();
} else {
out.set_low();
}
out.pin.set_oen(false);
out
}
pub fn init_flex(self, config: OutputConfig) -> Flex<'d> {
if config.initial_high {
unsafe { regs(self.block).gpio_data_set().write(|w| w.bits(1 << self.bit)) };
} else {
unsafe { regs(self.block).gpio_data_clr().write(|w| w.bits(1 << self.bit)) };
}
self.set_oen(false);
Flex { pin: self, config }
}
}
pub struct Input<'d> {
pin: AnyPin<'d>,
#[allow(dead_code)]
config: InputConfig,
}
impl<'d> Input<'d> {
pub fn is_high(&self) -> bool {
(regs(self.pin.block).gpio_sw_out().read().bits() >> self.pin.bit) & 1 != 0
}
pub fn is_low(&self) -> bool {
!self.is_high()
}
pub fn number(&self) -> u8 {
self.pin.number()
}
pub fn enable_interrupt(&self) {
let r = regs(self.pin.block);
r.gpio_int_en().modify(|r, w| unsafe { w.bits(r.bits() | (1 << self.pin.bit)) });
}
pub fn disable_interrupt(&self) {
let r = regs(self.pin.block);
r.gpio_int_en().modify(|r, w| unsafe { w.bits(r.bits() & !(1 << self.pin.bit)) });
}
pub fn clear_interrupt(&self) {
unsafe { regs(self.pin.block).gpio_int_eoi().write(|w| w.bits(1 << self.pin.bit)) };
}
pub fn set_interrupt_trigger(&self, trigger: InterruptTrigger) {
let r = regs(self.pin.block);
let mask = 1u32 << self.pin.bit;
let (edge, high) = match trigger {
InterruptTrigger::RisingEdge => (true, true),
InterruptTrigger::FallingEdge => (true, false),
InterruptTrigger::HighLevel => (false, true),
InterruptTrigger::LowLevel => (false, false),
};
r.gpio_int_type().modify(|r, w| unsafe { w.bits(if edge { r.bits() | mask } else { r.bits() & !mask }) });
r.gpio_int_polarity().modify(|r, w| unsafe { w.bits(if high { r.bits() | mask } else { r.bits() & !mask }) });
}
pub fn interrupt_pending(&self) -> bool {
(regs(self.pin.block).gpio_int_raw().read().bits() >> self.pin.bit) & 1 != 0
}
}
impl embedded_hal::digital::ErrorType for Input<'_> {
type Error = core::convert::Infallible;
}
impl embedded_hal::digital::InputPin for Input<'_> {
fn is_high(&mut self) -> Result<bool, Self::Error> {
Ok(Input::is_high(self))
}
fn is_low(&mut self) -> Result<bool, Self::Error> {
Ok(Input::is_low(self))
}
}
pub struct Output<'d> {
pin: AnyPin<'d>,
config: OutputConfig,
}
impl<'d> Output<'d> {
pub fn set_high(&mut self) {
unsafe { regs(self.pin.block).gpio_data_set().write(|w| w.bits(1 << self.pin.bit)) };
}
pub fn set_low(&mut self) {
unsafe { regs(self.pin.block).gpio_data_clr().write(|w| w.bits(1 << self.pin.bit)) };
}
pub fn toggle(&mut self) {
let r = regs(self.pin.block);
let val = r.gpio_sw_out().read().bits();
if val & (1 << self.pin.bit) != 0 {
unsafe { r.gpio_data_clr().write(|w| w.bits(1 << self.pin.bit)) };
} else {
unsafe { r.gpio_data_set().write(|w| w.bits(1 << self.pin.bit)) };
}
}
pub fn is_set_high(&self) -> bool {
(regs(self.pin.block).gpio_sw_out().read().bits() >> self.pin.bit) & 1 != 0
}
pub fn number(&self) -> u8 {
self.pin.number()
}
pub fn into_flex(self) -> Flex<'d> {
Flex { pin: self.pin, config: self.config }
}
}
impl embedded_hal::digital::ErrorType for Output<'_> {
type Error = core::convert::Infallible;
}
impl embedded_hal::digital::OutputPin for Output<'_> {
fn set_low(&mut self) -> Result<(), Self::Error> {
Output::set_low(self);
Ok(())
}
fn set_high(&mut self) -> Result<(), Self::Error> {
Output::set_high(self);
Ok(())
}
}
impl embedded_hal::digital::StatefulOutputPin for Output<'_> {
fn is_set_high(&mut self) -> Result<bool, Self::Error> {
Ok(Output::is_set_high(self))
}
fn is_set_low(&mut self) -> Result<bool, Self::Error> {
Ok(!Output::is_set_high(self))
}
}
pub struct Flex<'d> {
pin: AnyPin<'d>,
#[allow(dead_code)]
config: OutputConfig,
}
impl<'d> Flex<'d> {
pub fn set_high(&mut self) {
self.pin.set_oen(false);
unsafe { regs(self.pin.block).gpio_data_set().write(|w| w.bits(1 << self.pin.bit)) };
}
pub fn set_low(&mut self) {
self.pin.set_oen(false);
unsafe { regs(self.pin.block).gpio_data_clr().write(|w| w.bits(1 << self.pin.bit)) };
}
pub fn toggle(&mut self) {
let r = regs(self.pin.block);
self.pin.set_oen(false);
let val = r.gpio_sw_out().read().bits();
if val & (1 << self.pin.bit) != 0 {
unsafe { r.gpio_data_clr().write(|w| w.bits(1 << self.pin.bit)) };
} else {
unsafe { r.gpio_data_set().write(|w| w.bits(1 << self.pin.bit)) };
}
}
pub fn is_set_high(&self) -> bool {
(regs(self.pin.block).gpio_sw_out().read().bits() >> self.pin.bit) & 1 != 0
}
pub fn is_high(&self) -> bool {
let r = regs(self.pin.block);
let oen = r.gpio_sw_oen().read().bits();
self.pin.set_oen(true);
let val = (r.gpio_sw_out().read().bits() >> self.pin.bit) & 1 != 0;
let mask = 1 << self.pin.bit;
if oen & mask != 0 {
r.gpio_sw_oen().modify(|reg, w| unsafe { w.bits(reg.bits() | mask) });
} else {
r.gpio_sw_oen().modify(|reg, w| unsafe { w.bits(reg.bits() & !mask) });
}
val
}
pub fn is_low(&self) -> bool {
!self.is_high()
}
pub fn number(&self) -> u8 {
self.pin.number()
}
}
impl embedded_hal::digital::ErrorType for Flex<'_> {
type Error = core::convert::Infallible;
}
impl embedded_hal::digital::OutputPin for Flex<'_> {
fn set_low(&mut self) -> Result<(), Self::Error> {
Flex::set_low(self);
Ok(())
}
fn set_high(&mut self) -> Result<(), Self::Error> {
Flex::set_high(self);
Ok(())
}
}
impl embedded_hal::digital::StatefulOutputPin for Flex<'_> {
fn is_set_high(&mut self) -> Result<bool, Self::Error> {
Ok(Flex::is_set_high(self))
}
fn is_set_low(&mut self) -> Result<bool, Self::Error> {
Ok(!Flex::is_set_high(self))
}
}
impl embedded_hal::digital::InputPin for Flex<'_> {
fn is_high(&mut self) -> Result<bool, Self::Error> {
Ok(Flex::is_high(self))
}
fn is_low(&mut self) -> Result<bool, Self::Error> {
Ok(Flex::is_low(self))
}
}
fn regs(block: u8) -> &'static ws63_pac::gpio0::RegisterBlock {
unsafe {
match block {
0 => &*Gpio0::ptr(),
1 => &*Gpio1::ptr(),
2 => &*Gpio2::ptr(),
_ => unreachable!(),
}
}
}
const IO_CONFIG_BASE: usize = 0x4400_D000;
const PAD_GPIO_CTRL_OFF: usize = 0x800;
const PAD_PE_BIT: u32 = 1 << 9;
const PAD_PS_BIT: u32 = 1 << 10;
fn apply_pull(pin: u8, pull: Pull) {
if pin > 14 {
return;
}
let reg = (IO_CONFIG_BASE + PAD_GPIO_CTRL_OFF + (pin as usize) * 4) as *mut u32;
let (pe, ps) = match pull {
Pull::None => (false, false),
Pull::Up => (true, true),
Pull::Down => (true, false),
};
unsafe {
let mut v = core::ptr::read_volatile(reg);
v &= !(PAD_PE_BIT | PAD_PS_BIT);
if pe {
v |= PAD_PE_BIT;
}
if ps {
v |= PAD_PS_BIT;
}
core::ptr::write_volatile(reg, v);
}
}
pub struct GpioPin<'d, MODE> {
block: u8,
bit: u8,
_mode: PhantomData<&'d MODE>,
}
impl<MODE> GpioPin<'_, MODE> {
pub fn number(&self) -> u8 {
self.block * 8 + self.bit
}
}
impl GpioPin<'_, OutputMode> {
pub fn set_high(&mut self) {
unsafe { regs(self.block).gpio_data_set().write(|w| w.bits(1 << self.bit)) };
}
pub fn set_low(&mut self) {
unsafe { regs(self.block).gpio_data_clr().write(|w| w.bits(1 << self.bit)) };
}
pub fn toggle(&mut self) {
let r = regs(self.block);
let val = r.gpio_sw_out().read().bits();
if val & (1 << self.bit) != 0 {
unsafe { r.gpio_data_clr().write(|w| w.bits(1 << self.bit)) };
} else {
unsafe { r.gpio_data_set().write(|w| w.bits(1 << self.bit)) };
}
}
pub fn is_set_high(&self) -> bool {
(regs(self.block).gpio_sw_out().read().bits() >> self.bit) & 1 != 0
}
pub fn into_input(self) -> GpioPin<'static, InputMode> {
regs(self.block).gpio_sw_oen().modify(|r, w| unsafe { w.bits(r.bits() | (1 << self.bit)) });
GpioPin { block: self.block, bit: self.bit, _mode: PhantomData }
}
}
impl GpioPin<'_, InputMode> {
pub fn is_high(&self) -> bool {
(regs(self.block).gpio_sw_out().read().bits() >> self.bit) & 1 != 0
}
pub fn is_low(&self) -> bool {
!self.is_high()
}
pub fn enable_interrupt(&self) {
regs(self.block).gpio_int_en().modify(|r, w| unsafe { w.bits(r.bits() | (1 << self.bit)) });
}
pub fn disable_interrupt(&self) {
regs(self.block).gpio_int_en().modify(|r, w| unsafe { w.bits(r.bits() & !(1 << self.bit)) });
}
pub fn clear_interrupt(&self) {
unsafe { regs(self.block).gpio_int_eoi().write(|w| w.bits(1 << self.bit)) };
}
pub fn interrupt_pending(&self) -> bool {
(regs(self.block).gpio_int_raw().read().bits() >> self.bit) & 1 != 0
}
pub fn into_output(self) -> GpioPin<'static, OutputMode> {
regs(self.block).gpio_sw_oen().modify(|r, w| unsafe { w.bits(r.bits() & !(1 << self.bit)) });
GpioPin { block: self.block, bit: self.bit, _mode: PhantomData }
}
}
impl embedded_hal::digital::ErrorType for GpioPin<'_, OutputMode> {
type Error = core::convert::Infallible;
}
impl embedded_hal::digital::OutputPin for GpioPin<'_, OutputMode> {
fn set_low(&mut self) -> Result<(), Self::Error> {
GpioPin::set_low(self);
Ok(())
}
fn set_high(&mut self) -> Result<(), Self::Error> {
GpioPin::set_high(self);
Ok(())
}
}
impl embedded_hal::digital::StatefulOutputPin for GpioPin<'_, OutputMode> {
fn is_set_high(&mut self) -> Result<bool, Self::Error> {
Ok(GpioPin::is_set_high(self))
}
fn is_set_low(&mut self) -> Result<bool, Self::Error> {
Ok(!GpioPin::is_set_high(self))
}
}
impl embedded_hal::digital::ErrorType for GpioPin<'_, InputMode> {
type Error = core::convert::Infallible;
}
impl embedded_hal::digital::InputPin for GpioPin<'_, InputMode> {
fn is_high(&mut self) -> Result<bool, Self::Error> {
Ok(GpioPin::is_high(self))
}
fn is_low(&mut self) -> Result<bool, Self::Error> {
Ok(GpioPin::is_low(self))
}
}
pub fn create_input_pin(pin: u8) -> GpioPin<'static, InputMode> {
let block = pin / 8;
let bit = pin % 8;
regs(block).gpio_sw_oen().modify(|r, w| unsafe { w.bits(r.bits() | (1 << bit)) });
GpioPin { block, bit, _mode: PhantomData }
}
pub fn create_output_pin(pin: u8) -> GpioPin<'static, OutputMode> {
let block = pin / 8;
let bit = pin % 8;
regs(block).gpio_sw_oen().modify(|r, w| unsafe { w.bits(r.bits() & !(1 << bit)) });
GpioPin { block, bit, _mode: PhantomData }
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct OutputSignal(pub(crate) u8);
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct InputSignal(pub(crate) u8);
pub trait PeripheralOutput: crate::private::Sealed {
fn output_signal(&self) -> OutputSignal;
}
pub trait PeripheralInput: crate::private::Sealed {
fn input_signal(&self) -> InputSignal;
}
impl crate::private::Sealed for Output<'_> {}
impl crate::private::Sealed for Input<'_> {}
impl crate::private::Sealed for Flex<'_> {}
impl crate::private::Sealed for GpioPin<'_, OutputMode> {}
impl crate::private::Sealed for GpioPin<'_, InputMode> {}
pub struct Io<'d> {
pub io_config: IoConfig<'d>,
}
impl<'d> Io<'d> {
pub fn new(io_config: IoConfig<'d>) -> Self {
Self { io_config }
}
pub fn register_block(&self) -> &ws63_pac::io_config::RegisterBlock {
self.io_config.register_block()
}
}
#[cfg(feature = "async")]
mod asynch_impl {
use super::{Input, InterruptTrigger, regs};
use crate::asynch::IrqSignal;
use crate::interrupt::{self, Interrupt};
use core::future::Future;
use core::pin::Pin;
use core::task::{Context, Poll};
use embedded_hal_async::digital::Wait;
static GPIO_SIGNAL: [IrqSignal; 3] = [IrqSignal::new(), IrqSignal::new(), IrqSignal::new()];
fn bank_irq(bank: usize) -> Interrupt {
match bank {
0 => Interrupt::GPIO_INT0,
1 => Interrupt::GPIO_INT1,
_ => Interrupt::GPIO_INT2,
}
}
pub fn on_interrupt(bank: u8) {
let r = regs(bank);
let fired = r.gpio_int_raw().read().bits();
r.gpio_int_en().modify(|v, w| unsafe { w.bits(v.bits() & !fired) });
unsafe { r.gpio_int_eoi().write(|w| w.bits(fired)) };
GPIO_SIGNAL[bank as usize].signal();
interrupt::clear_pending(bank_irq(bank as usize));
}
async fn arm_and_wait(input: &mut Input<'_>, trig: InterruptTrigger) {
let bank = input.pin.block as usize;
input.set_interrupt_trigger(trig);
input.clear_interrupt();
GPIO_SIGNAL[bank].reset();
input.enable_interrupt();
unsafe { interrupt::enable(bank_irq(bank)) };
GpioWaitFuture { bank }.await;
}
struct GpioWaitFuture {
bank: usize,
}
impl Future for GpioWaitFuture {
type Output = ();
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
if GPIO_SIGNAL[self.bank].take_fired() {
Poll::Ready(())
} else {
GPIO_SIGNAL[self.bank].register(cx.waker());
Poll::Pending
}
}
}
impl Wait for Input<'_> {
async fn wait_for_high(&mut self) -> Result<(), Self::Error> {
if self.is_high() {
return Ok(());
}
arm_and_wait(self, InterruptTrigger::HighLevel).await;
Ok(())
}
async fn wait_for_low(&mut self) -> Result<(), Self::Error> {
if self.is_low() {
return Ok(());
}
arm_and_wait(self, InterruptTrigger::LowLevel).await;
Ok(())
}
async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> {
arm_and_wait(self, InterruptTrigger::RisingEdge).await;
Ok(())
}
async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> {
arm_and_wait(self, InterruptTrigger::FallingEdge).await;
Ok(())
}
async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> {
let trig = if self.is_high() { InterruptTrigger::FallingEdge } else { InterruptTrigger::RisingEdge };
arm_and_wait(self, trig).await;
Ok(())
}
}
}
#[cfg(feature = "async")]
pub use asynch_impl::on_interrupt;