From 9dfda46e0c43559a6ca2ed87c0ceb32d19456c62 Mon Sep 17 00:00:00 2001 From: pennae Date: Sun, 30 Jul 2023 02:10:48 +0200 Subject: [PATCH] rp: add dormant-wake functionality for Input this temporarily takes ownership of pins because we need to clear edge interrupts before waiting for them (otherwise we may wait indefinitely), we want to clean up the dormant-wake bits after a wakeup, and doing anything *else* with the input while we're waiting for a wakeup isn't possible at all. doing it like this lets us not impose any cost on those who don't use dormant wakes without entangling dormant waits too badly with regular interrupt waits. --- embassy-rp/src/gpio.rs | 61 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs index 73e89352..ad9d4262 100644 --- a/embassy-rp/src/gpio.rs +++ b/embassy-rp/src/gpio.rs @@ -75,6 +75,15 @@ pub enum Bank { Qspi = 1, } +#[derive(Debug, Eq, PartialEq, Copy, Clone, Default)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct DormantWakeConfig { + pub edge_high: bool, + pub edge_low: bool, + pub level_high: bool, + pub level_low: bool, +} + pub struct Input<'d, T: Pin> { pin: Flex<'d, T>, } @@ -128,6 +137,11 @@ impl<'d, T: Pin> Input<'d, T> { pub async fn wait_for_any_edge(&mut self) { self.pin.wait_for_any_edge().await; } + + #[inline] + pub fn dormant_wake(&mut self, cfg: DormantWakeConfig) -> DormantWake { + self.pin.dormant_wake(cfg) + } } /// Interrupt trigger levels. @@ -639,15 +653,62 @@ impl<'d, T: Pin> Flex<'d, T> { pub async fn wait_for_any_edge(&mut self) { InputFuture::new(&mut self.pin, InterruptTrigger::AnyEdge).await; } + + #[inline] + pub fn dormant_wake(&mut self, cfg: DormantWakeConfig) -> DormantWake { + let idx = self.pin._pin() as usize; + self.pin.io().intr(idx / 8).write(|w| { + w.set_edge_high(idx % 8, cfg.edge_high); + w.set_edge_low(idx % 8, cfg.edge_low); + }); + self.pin.io().int_dormant_wake().inte(idx / 8).write_set(|w| { + w.set_edge_high(idx % 8, cfg.edge_high); + w.set_edge_low(idx % 8, cfg.edge_low); + w.set_level_high(idx % 8, cfg.level_high); + w.set_level_low(idx % 8, cfg.level_low); + }); + DormantWake { + pin: self.pin.reborrow(), + cfg, + } + } } impl<'d, T: Pin> Drop for Flex<'d, T> { #[inline] fn drop(&mut self) { + let idx = self.pin._pin() as usize; self.pin.pad_ctrl().write(|_| {}); self.pin.gpio().ctrl().write(|w| { w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::NULL as _); }); + self.pin.io().int_dormant_wake().inte(idx / 8).write_clear(|w| { + w.set_edge_high(idx % 8, true); + w.set_edge_low(idx % 8, true); + w.set_level_high(idx % 8, true); + w.set_level_low(idx % 8, true); + }); + } +} + +pub struct DormantWake<'w, T: Pin> { + pin: PeripheralRef<'w, T>, + cfg: DormantWakeConfig, +} + +impl<'w, T: Pin> Drop for DormantWake<'w, T> { + fn drop(&mut self) { + let idx = self.pin._pin() as usize; + self.pin.io().intr(idx / 8).write(|w| { + w.set_edge_high(idx % 8, self.cfg.edge_high); + w.set_edge_low(idx % 8, self.cfg.edge_low); + }); + self.pin.io().int_dormant_wake().inte(idx / 8).write_clear(|w| { + w.set_edge_high(idx % 8, true); + w.set_edge_low(idx % 8, true); + w.set_level_high(idx % 8, true); + w.set_level_low(idx % 8, true); + }); } }