nrf/gpio: add infallible inherent methods, remove some duplication.

This implements Input and Output using FlexPin, to avoid some code duplication.
This commit is contained in:
Dario Nieuwenhuis 2021-12-19 23:25:02 +01:00
parent fcb43caa36
commit 22bc1e4ae1
6 changed files with 221 additions and 262 deletions

View File

@ -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;
}
}

View File

@ -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<Target = T> + '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<bool, Self::Error> {
self.is_low().map(|v| !v)
Ok(self.is_high())
}
fn is_low(&self) -> Result<bool, Self::Error> {
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<Output = ()> + 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<Output = ()> + 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<Output = ()> + 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<bool, Self::Error> {
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<bool, Self::Error> {
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<bool, Self::Error> {
self.is_low().map(|v| !v)
Ok(self.is_high())
}
fn is_low(&self) -> Result<bool, Self::Error> {
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<bool, Self::Error> {
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<bool, Self::Error> {
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<Output = ()> + 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<Output = ()> + 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<Output = ()> + 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<T: Pin>(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<T: Pin>(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<Target = Self> + 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() }
}
// ====================

View File

@ -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<Output = ()> + 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<Output = ()> + 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<Output = ()> + 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<Output = ()> + 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<Output = ()> + 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<Output = ()> + 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 {}
}

View File

@ -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;
}
}

View File

@ -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(),
_ => (),
}
}

View File

@ -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);
}