From 22bc1e4ae17fbc76442d4ee1375cf3a86e0b4757 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 19 Dec 2021 23:25:02 +0100 Subject: [PATCH] nrf/gpio: add infallible inherent methods, remove some duplication. This implements Input and Output using FlexPin, to avoid some code duplication. --- docs/modules/ROOT/examples/basic/src/main.rs | 5 +- embassy-nrf/src/gpio.rs | 344 ++++++------------- embassy-nrf/src/gpiote.rs | 106 +++++- examples/nrf/src/bin/blinky.rs | 6 +- examples/nrf/src/bin/mpsc.rs | 5 +- examples/nrf/src/bin/spim.rs | 17 +- 6 files changed, 221 insertions(+), 262 deletions(-) diff --git a/docs/modules/ROOT/examples/basic/src/main.rs b/docs/modules/ROOT/examples/basic/src/main.rs index 2a9b1fac..8394f73b 100644 --- a/docs/modules/ROOT/examples/basic/src/main.rs +++ b/docs/modules/ROOT/examples/basic/src/main.rs @@ -14,14 +14,13 @@ use embassy_nrf::{ peripherals::P0_13, Peripherals, }; -use embedded_hal::digital::v2::OutputPin; #[embassy::task] async fn blinker(mut led: Output<'static, P0_13>, interval: Duration) { loop { - unwrap!(led.set_high()); + led.set_high(); Timer::after(interval).await; - unwrap!(led.set_low()); + led.set_low(); Timer::after(interval).await; } } diff --git a/embassy-nrf/src/gpio.rs b/embassy-nrf/src/gpio.rs index cb06f097..ddc84cf5 100644 --- a/embassy-nrf/src/gpio.rs +++ b/embassy-nrf/src/gpio.rs @@ -1,7 +1,6 @@ #![macro_use] use core::convert::Infallible; -use core::future::Future; use core::hint::unreachable_unchecked; use core::marker::PhantomData; @@ -38,26 +37,23 @@ pub enum Pull { /// GPIO input driver. pub struct Input<'d, T: Pin> { - pub(crate) pin: T, - phantom: PhantomData<&'d mut T>, + pub(crate) pin: FlexPin<'d, T>, } impl<'d, T: Pin> Input<'d, T> { pub fn new(pin: impl Unborrow + 'd, pull: Pull) -> Self { - unborrow!(pin); + let mut pin = FlexPin::new(pin); + pin.set_as_input(pull); - init_input(&pin, pull); - - Self { - pin, - phantom: PhantomData, - } + Self { pin } } -} -impl<'d, T: Pin> Drop for Input<'d, T> { - fn drop(&mut self) { - self.pin.conf().reset(); + fn is_high(&self) -> bool { + self.pin.is_high() + } + + fn is_low(&self) -> bool { + self.pin.is_low() } } @@ -65,65 +61,11 @@ impl<'d, T: Pin> InputPin for Input<'d, T> { type Error = Infallible; fn is_high(&self) -> Result { - self.is_low().map(|v| !v) + Ok(self.is_high()) } fn is_low(&self) -> Result { - Ok(self.pin.block().in_.read().bits() & (1 << self.pin.pin()) == 0) - } -} - -#[cfg(feature = "gpiote")] -impl<'d, T: Pin> embassy::traits::gpio::WaitForHigh for Input<'d, T> { - type Future<'a> - where - Self: 'a, - = impl Future + Unpin + 'a; - - fn wait_for_high<'a>(&'a mut self) -> Self::Future<'a> { - self.pin.conf().modify(|_, w| w.sense().high()); - - crate::gpiote::PortInputFuture { - pin_port: self.pin.pin_port(), - phantom: PhantomData, - } - } -} - -#[cfg(feature = "gpiote")] -impl<'d, T: Pin> embassy::traits::gpio::WaitForLow for Input<'d, T> { - type Future<'a> - where - Self: 'a, - = impl Future + Unpin + 'a; - - fn wait_for_low<'a>(&'a mut self) -> Self::Future<'a> { - self.pin.conf().modify(|_, w| w.sense().low()); - - crate::gpiote::PortInputFuture { - pin_port: self.pin.pin_port(), - phantom: PhantomData, - } - } -} - -#[cfg(feature = "gpiote")] -impl<'d, T: Pin> embassy::traits::gpio::WaitForAnyEdge for Input<'d, T> { - type Future<'a> - where - Self: 'a, - = impl Future + Unpin + 'a; - - fn wait_for_any_edge<'a>(&'a mut self) -> Self::Future<'a> { - if self.is_high().ok().unwrap() { - self.pin.conf().modify(|_, w| w.sense().low()); - } else { - self.pin.conf().modify(|_, w| w.sense().high()); - } - crate::gpiote::PortInputFuture { - pin_port: self.pin.pin_port(), - phantom: PhantomData, - } + Ok(self.is_low()) } } @@ -160,8 +102,7 @@ pub enum OutputDrive { /// GPIO output driver. pub struct Output<'d, T: Pin> { - pub(crate) pin: T, - phantom: PhantomData<&'d mut T>, + pub(crate) pin: FlexPin<'d, T>, } impl<'d, T: Pin> Output<'d, T> { @@ -170,63 +111,56 @@ impl<'d, T: Pin> Output<'d, T> { initial_output: Level, drive: OutputDrive, ) -> Self { - unborrow!(pin); - + let mut pin = FlexPin::new(pin); match initial_output { Level::High => pin.set_high(), Level::Low => pin.set_low(), } + pin.set_as_output(drive); - init_output(&pin, drive); - - Self { - pin, - phantom: PhantomData, - } + Self { pin } } -} -impl<'d, T: Pin> Drop for Output<'d, T> { - fn drop(&mut self) { - self.pin.conf().reset(); + /// Set the output as high. + pub fn set_high(&mut self) { + self.pin.set_high() + } + + /// Set the output as low. + pub fn set_low(&mut self) { + self.pin.set_low() + } + + /// Is the output pin set as high? + pub fn is_set_high(&self) -> bool { + self.pin.is_set_high() + } + + /// Is the output pin set as low? + pub fn is_set_low(&self) -> bool { + self.pin.is_set_low() } } impl<'d, T: Pin> OutputPin for Output<'d, T> { type Error = Infallible; - /// Set the output as high. fn set_high(&mut self) -> Result<(), Self::Error> { - unsafe { - self.pin - .block() - .outset - .write(|w| w.bits(1u32 << self.pin.pin())); - } - Ok(()) + Ok(self.set_high()) } - /// Set the output as low. fn set_low(&mut self) -> Result<(), Self::Error> { - unsafe { - self.pin - .block() - .outclr - .write(|w| w.bits(1u32 << self.pin.pin())); - } - Ok(()) + Ok(self.set_low()) } } impl<'d, T: Pin> StatefulOutputPin for Output<'d, T> { - /// Is the output pin set as high? fn is_set_high(&self) -> Result { - self.is_set_low().map(|v| !v) + Ok(self.is_set_high()) } - /// Is the output pin set as low? fn is_set_low(&self) -> Result { - Ok(self.pin.block().out.read().bits() & (1 << self.pin.pin()) == 0) + Ok(self.is_set_low()) } } @@ -256,7 +190,24 @@ impl<'d, T: Pin> FlexPin<'d, T> { /// Put the pin into input mode. pub fn set_as_input(&mut self, pull: Pull) { - init_input(&self.pin, pull); + self.pin.conf().write(|w| { + w.dir().input(); + w.input().connect(); + match pull { + Pull::None => { + w.pull().disabled(); + } + Pull::Up => { + w.pull().pullup(); + } + Pull::Down => { + w.pull().pulldown(); + } + } + w.drive().s0s1(); + w.sense().disabled(); + w + }); } /// Put the pin into output mode. @@ -264,13 +215,59 @@ impl<'d, T: Pin> FlexPin<'d, T> { /// The pin level will be whatever was set before (or low by default). If you want it to begin /// at a specific level, call `set_high`/`set_low` on the pin first. pub fn set_as_output(&mut self, drive: OutputDrive) { - init_output(&self.pin, drive); + let drive = match drive { + OutputDrive::Standard => DRIVE_A::S0S1, + OutputDrive::HighDrive0Standard1 => DRIVE_A::H0S1, + OutputDrive::Standard0HighDrive1 => DRIVE_A::S0H1, + OutputDrive::HighDrive => DRIVE_A::H0H1, + OutputDrive::Disconnect0Standard1 => DRIVE_A::D0S1, + OutputDrive::Disconnect0HighDrive1 => DRIVE_A::D0H1, + OutputDrive::Standard0Disconnect1 => DRIVE_A::S0D1, + OutputDrive::HighDrive0Disconnect1 => DRIVE_A::H0D1, + }; + + self.pin.conf().write(|w| { + w.dir().output(); + w.input().disconnect(); + w.pull().disabled(); + w.drive().variant(drive); + w.sense().disabled(); + w + }); } /// Put the pin into disconnected mode. pub fn set_as_disconnected(&mut self) { self.pin.conf().reset(); } + + pub fn is_high(&self) -> bool { + !self.is_low() + } + + pub fn is_low(&self) -> bool { + self.pin.block().in_.read().bits() & (1 << self.pin.pin()) == 0 + } + + /// Set the output as high. + pub fn set_high(&mut self) { + self.pin.set_high() + } + + /// Set the output as low. + pub fn set_low(&mut self) { + self.pin.set_low() + } + + /// Is the output pin set as high? + pub fn is_set_high(&self) -> bool { + !self.is_set_low() + } + + /// Is the output pin set as low? + pub fn is_set_low(&self) -> bool { + self.pin.block().out.read().bits() & (1 << self.pin.pin()) == 0 + } } impl<'d, T: Pin> Drop for FlexPin<'d, T> { @@ -286,103 +283,33 @@ impl<'d, T: Pin> InputPin for FlexPin<'d, T> { type Error = Infallible; fn is_high(&self) -> Result { - self.is_low().map(|v| !v) + Ok(self.is_high()) } fn is_low(&self) -> Result { - Ok(self.pin.block().in_.read().bits() & (1 << self.pin.pin()) == 0) + Ok(self.is_low()) } } impl<'d, T: Pin> OutputPin for FlexPin<'d, T> { type Error = Infallible; - /// Set the output as high. fn set_high(&mut self) -> Result<(), Self::Error> { - unsafe { - self.pin - .block() - .outset - .write(|w| w.bits(1u32 << self.pin.pin())); - } - Ok(()) + Ok(self.set_high()) } - /// Set the output as low. fn set_low(&mut self) -> Result<(), Self::Error> { - unsafe { - self.pin - .block() - .outclr - .write(|w| w.bits(1u32 << self.pin.pin())); - } - Ok(()) + Ok(self.set_low()) } } impl<'d, T: Pin> StatefulOutputPin for FlexPin<'d, T> { - /// Is the output pin set as high? fn is_set_high(&self) -> Result { - self.is_set_low().map(|v| !v) + Ok(self.is_set_high()) } - /// Is the output pin set as low? fn is_set_low(&self) -> Result { - Ok(self.pin.block().out.read().bits() & (1 << self.pin.pin()) == 0) - } -} - -#[cfg(feature = "gpiote")] -impl<'d, T: Pin> embassy::traits::gpio::WaitForHigh for FlexPin<'d, T> { - type Future<'a> - where - Self: 'a, - = impl Future + Unpin + 'a; - - fn wait_for_high<'a>(&'a mut self) -> Self::Future<'a> { - self.pin.conf().modify(|_, w| w.sense().high()); - - crate::gpiote::PortInputFuture { - pin_port: self.pin.pin_port(), - phantom: PhantomData, - } - } -} - -#[cfg(feature = "gpiote")] -impl<'d, T: Pin> embassy::traits::gpio::WaitForLow for FlexPin<'d, T> { - type Future<'a> - where - Self: 'a, - = impl Future + Unpin + 'a; - - fn wait_for_low<'a>(&'a mut self) -> Self::Future<'a> { - self.pin.conf().modify(|_, w| w.sense().low()); - - crate::gpiote::PortInputFuture { - pin_port: self.pin.pin_port(), - phantom: PhantomData, - } - } -} - -#[cfg(feature = "gpiote")] -impl<'d, T: Pin> embassy::traits::gpio::WaitForAnyEdge for FlexPin<'d, T> { - type Future<'a> - where - Self: 'a, - = impl Future + Unpin + 'a; - - fn wait_for_any_edge<'a>(&'a mut self) -> Self::Future<'a> { - if self.is_high().ok().unwrap() { - self.pin.conf().modify(|_, w| w.sense().low()); - } else { - self.pin.conf().modify(|_, w| w.sense().high()); - } - crate::gpiote::PortInputFuture { - pin_port: self.pin.pin_port(), - phantom: PhantomData, - } + Ok(self.is_set_low()) } } @@ -493,55 +420,6 @@ impl sealed::Pin for AnyPin { } } -// ===================== - -/// Set up a pin for input -#[inline] -fn init_input(pin: &T, pull: Pull) { - pin.conf().write(|w| { - w.dir().input(); - w.input().connect(); - match pull { - Pull::None => { - w.pull().disabled(); - } - Pull::Up => { - w.pull().pullup(); - } - Pull::Down => { - w.pull().pulldown(); - } - } - w.drive().s0s1(); - w.sense().disabled(); - w - }); -} - -/// Set up a pin for output -#[inline] -fn init_output(pin: &T, drive: OutputDrive) { - let drive = match drive { - OutputDrive::Standard => DRIVE_A::S0S1, - OutputDrive::HighDrive0Standard1 => DRIVE_A::H0S1, - OutputDrive::Standard0HighDrive1 => DRIVE_A::S0H1, - OutputDrive::HighDrive => DRIVE_A::H0H1, - OutputDrive::Disconnect0Standard1 => DRIVE_A::D0S1, - OutputDrive::Disconnect0HighDrive1 => DRIVE_A::D0H1, - OutputDrive::Standard0Disconnect1 => DRIVE_A::S0D1, - OutputDrive::HighDrive0Disconnect1 => DRIVE_A::H0D1, - }; - - pin.conf().write(|w| { - w.dir().output(); - w.input().disconnect(); - w.pull().disabled(); - w.drive().variant(drive); - w.sense().disabled(); - w - }); -} - // ==================== pub trait OptionalPin: Unborrow + sealed::OptionalPin + Sized { @@ -601,9 +479,7 @@ pub(crate) fn deconfigure_pin(psel_bits: u32) { if psel_bits & 0x8000_0000 != 0 { return; } - unsafe { - AnyPin::steal(psel_bits as _).conf().reset(); - } + unsafe { AnyPin::steal(psel_bits as _).conf().reset() } } // ==================== diff --git a/embassy-nrf/src/gpiote.rs b/embassy-nrf/src/gpiote.rs index 4b595c58..99dfd1eb 100644 --- a/embassy-nrf/src/gpiote.rs +++ b/embassy-nrf/src/gpiote.rs @@ -5,11 +5,11 @@ use core::task::{Context, Poll}; use embassy::interrupt::{Interrupt, InterruptExt}; use embassy::waitqueue::AtomicWaker; use embassy_hal_common::unsafe_impl_unborrow; -use embedded_hal::digital::v2::{InputPin, StatefulOutputPin}; +use embedded_hal::digital::v2::InputPin; use futures::future::poll_fn; use crate::gpio::sealed::Pin as _; -use crate::gpio::{AnyPin, Input, Output, Pin as GpioPin}; +use crate::gpio::{AnyPin, FlexPin, Input, Output, Pin as GpioPin}; use crate::pac; use crate::ppi::{Event, Task}; use crate::{interrupt, peripherals}; @@ -177,11 +177,11 @@ impl<'d, C: Channel, T: GpioPin> InputChannel<'d, C, T> { InputChannelPolarity::Toggle => w.mode().event().polarity().toggle(), }; #[cfg(any(feature = "nrf52833", feature = "nrf52840"))] - w.port().bit(match pin.pin.port() { + w.port().bit(match pin.pin.pin.port() { crate::gpio::Port::Port0 => false, crate::gpio::Port::Port1 => true, }); - unsafe { w.psel().bits(pin.pin.pin()) } + unsafe { w.psel().bits(pin.pin.pin.pin()) } }); g.events_in[num].reset(); @@ -250,7 +250,7 @@ impl<'d, C: Channel, T: GpioPin> OutputChannel<'d, C, T> { g.config[num].write(|w| { w.mode().task(); - match pin.is_set_high().unwrap() { + match pin.is_set_high() { true => w.outinit().high(), false => w.outinit().low(), }; @@ -260,11 +260,11 @@ impl<'d, C: Channel, T: GpioPin> OutputChannel<'d, C, T> { OutputChannelPolarity::Toggle => w.polarity().toggle(), }; #[cfg(any(feature = "nrf52833", feature = "nrf52840"))] - w.port().bit(match pin.pin.port() { + w.port().bit(match pin.pin.pin.port() { crate::gpio::Port::Port0 => false, crate::gpio::Port::Port1 => true, }); - unsafe { w.psel().bits(pin.pin.pin()) } + unsafe { w.psel().bits(pin.pin.pin.pin()) } }); OutputChannel { ch, _pin: pin } @@ -311,9 +311,11 @@ impl<'d, C: Channel, T: GpioPin> OutputChannel<'d, C, T> { } } +// ======================= + pub(crate) struct PortInputFuture<'a> { - pub(crate) pin_port: u8, - pub(crate) phantom: PhantomData<&'a mut AnyPin>, + pin_port: u8, + phantom: PhantomData<&'a mut AnyPin>, } impl<'a> Unpin for PortInputFuture<'a> {} @@ -340,6 +342,92 @@ impl<'a> Future for PortInputFuture<'a> { } } +impl<'d, T: GpioPin> embassy::traits::gpio::WaitForHigh for Input<'d, T> { + type Future<'a> + where + Self: 'a, + = impl Future + Unpin + 'a; + + fn wait_for_high<'a>(&'a mut self) -> Self::Future<'a> { + self.pin.wait_for_high() + } +} + +impl<'d, T: GpioPin> embassy::traits::gpio::WaitForLow for Input<'d, T> { + type Future<'a> + where + Self: 'a, + = impl Future + Unpin + 'a; + + fn wait_for_low<'a>(&'a mut self) -> Self::Future<'a> { + self.pin.wait_for_low() + } +} + +impl<'d, T: GpioPin> embassy::traits::gpio::WaitForAnyEdge for Input<'d, T> { + type Future<'a> + where + Self: 'a, + = impl Future + Unpin + 'a; + + fn wait_for_any_edge<'a>(&'a mut self) -> Self::Future<'a> { + self.pin.wait_for_any_edge() + } +} + +impl<'d, T: GpioPin> embassy::traits::gpio::WaitForHigh for FlexPin<'d, T> { + type Future<'a> + where + Self: 'a, + = impl Future + Unpin + 'a; + + fn wait_for_high<'a>(&'a mut self) -> Self::Future<'a> { + self.pin.conf().modify(|_, w| w.sense().high()); + + PortInputFuture { + pin_port: self.pin.pin_port(), + phantom: PhantomData, + } + } +} + +impl<'d, T: GpioPin> embassy::traits::gpio::WaitForLow for FlexPin<'d, T> { + type Future<'a> + where + Self: 'a, + = impl Future + Unpin + 'a; + + fn wait_for_low<'a>(&'a mut self) -> Self::Future<'a> { + self.pin.conf().modify(|_, w| w.sense().low()); + + PortInputFuture { + pin_port: self.pin.pin_port(), + phantom: PhantomData, + } + } +} + +impl<'d, T: GpioPin> embassy::traits::gpio::WaitForAnyEdge for FlexPin<'d, T> { + type Future<'a> + where + Self: 'a, + = impl Future + Unpin + 'a; + + fn wait_for_any_edge<'a>(&'a mut self) -> Self::Future<'a> { + if self.is_high() { + self.pin.conf().modify(|_, w| w.sense().low()); + } else { + self.pin.conf().modify(|_, w| w.sense().high()); + } + PortInputFuture { + pin_port: self.pin.pin_port(), + phantom: PhantomData, + } + } +} + +// ======================= + mod sealed { pub trait Channel {} } diff --git a/examples/nrf/src/bin/blinky.rs b/examples/nrf/src/bin/blinky.rs index a12fe58f..0fc004ed 100644 --- a/examples/nrf/src/bin/blinky.rs +++ b/examples/nrf/src/bin/blinky.rs @@ -5,21 +5,19 @@ #[path = "../example_common.rs"] mod example_common; -use defmt::unwrap; use embassy::executor::Spawner; use embassy::time::{Duration, Timer}; use embassy_nrf::gpio::{Level, Output, OutputDrive}; use embassy_nrf::Peripherals; -use embedded_hal::digital::v2::OutputPin; #[embassy::main] async fn main(_spawner: Spawner, p: Peripherals) { let mut led = Output::new(p.P0_13, Level::Low, OutputDrive::Standard); loop { - unwrap!(led.set_high()); + led.set_high(); Timer::after(Duration::from_millis(300)).await; - unwrap!(led.set_low()); + led.set_low(); Timer::after(Duration::from_millis(300)).await; } } diff --git a/examples/nrf/src/bin/mpsc.rs b/examples/nrf/src/bin/mpsc.rs index c85b7c28..454fb954 100644 --- a/examples/nrf/src/bin/mpsc.rs +++ b/examples/nrf/src/bin/mpsc.rs @@ -13,7 +13,6 @@ use embassy::time::{Duration, Timer}; use embassy::util::Forever; use embassy_nrf::gpio::{Level, Output, OutputDrive}; use embassy_nrf::Peripherals; -use embedded_hal::digital::v2::OutputPin; enum LedState { On, @@ -53,8 +52,8 @@ async fn main(spawner: Spawner, p: Peripherals) { Err(TryRecvError::Closed) => break, }; match maybe_message { - Some(LedState::On) => unwrap!(led.set_high()), - Some(LedState::Off) => unwrap!(led.set_low()), + Some(LedState::On) => led.set_high(), + Some(LedState::Off) => led.set_low(), _ => (), } } diff --git a/examples/nrf/src/bin/spim.rs b/examples/nrf/src/bin/spim.rs index d34ca0b8..fc31d140 100644 --- a/examples/nrf/src/bin/spim.rs +++ b/examples/nrf/src/bin/spim.rs @@ -10,7 +10,6 @@ use embassy_nrf::gpio::{Level, Output, OutputDrive}; use embassy_nrf::Peripherals; use embassy_nrf::{interrupt, spim}; use embassy_traits::spi::FullDuplex; -use embedded_hal::digital::v2::*; use example_common::*; #[embassy::main] @@ -29,12 +28,12 @@ async fn main(_spawner: Spawner, p: Peripherals) { // softreset cortex_m::asm::delay(10); - unwrap!(ncs.set_low()); + ncs.set_low(); cortex_m::asm::delay(5); let tx = [0xFF]; unwrap!(spim.read_write(&mut [], &tx).await); cortex_m::asm::delay(10); - unwrap!(ncs.set_high()); + ncs.set_high(); cortex_m::asm::delay(100000); @@ -42,31 +41,31 @@ async fn main(_spawner: Spawner, p: Peripherals) { // read ESTAT cortex_m::asm::delay(5000); - unwrap!(ncs.set_low()); + ncs.set_low(); cortex_m::asm::delay(5000); let tx = [0b000_11101, 0]; unwrap!(spim.read_write(&mut rx, &tx).await); cortex_m::asm::delay(5000); - unwrap!(ncs.set_high()); + ncs.set_high(); info!("estat: {=[?]}", rx); // Switch to bank 3 cortex_m::asm::delay(10); - unwrap!(ncs.set_low()); + ncs.set_low(); cortex_m::asm::delay(5); let tx = [0b100_11111, 0b11]; unwrap!(spim.read_write(&mut rx, &tx).await); cortex_m::asm::delay(10); - unwrap!(ncs.set_high()); + ncs.set_high(); // read EREVID cortex_m::asm::delay(10); - unwrap!(ncs.set_low()); + ncs.set_low(); cortex_m::asm::delay(5); let tx = [0b000_10010, 0]; unwrap!(spim.read_write(&mut rx, &tx).await); cortex_m::asm::delay(10); - unwrap!(ncs.set_high()); + ncs.set_high(); info!("erevid: {=[?]}", rx); }