From fcf6a63b5cf1d1505ec01ea42a1a75f33794b038 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 19 Mar 2021 04:08:44 +0100 Subject: [PATCH] nrf: add owned Peripherals struct, migrate gpio and spim --- embassy-nrf-examples/src/bin/spim.rs | 18 +- embassy-nrf/src/gpio.rs | 307 +++++++++++++++++++++++++++ embassy-nrf/src/lib.rs | 2 + embassy-nrf/src/peripherals.rs | 144 +++++++++++++ embassy-nrf/src/spim.rs | 88 +++++--- 5 files changed, 516 insertions(+), 43 deletions(-) create mode 100644 embassy-nrf/src/gpio.rs create mode 100644 embassy-nrf/src/peripherals.rs diff --git a/embassy-nrf-examples/src/bin/spim.rs b/embassy-nrf-examples/src/bin/spim.rs index d3d942e4..8eaac5e1 100644 --- a/embassy-nrf-examples/src/bin/spim.rs +++ b/embassy-nrf-examples/src/bin/spim.rs @@ -6,6 +6,8 @@ #[path = "../example_common.rs"] mod example_common; +use embassy_nrf::gpio::{Level, Output}; +use embassy_nrf::peripherals::Peripherals; use embassy_traits::spi::FullDuplex; use example_common::*; @@ -16,7 +18,6 @@ use embassy::util::Forever; use embedded_hal::digital::v2::*; use futures::pin_mut; use nrf52840_hal::clocks; -use nrf52840_hal::gpio; use embassy_nrf::{interrupt, pac, rtc, spim}; @@ -24,25 +25,20 @@ use embassy_nrf::{interrupt, pac, rtc, spim}; async fn run() { info!("running!"); - let p = unsafe { embassy_nrf::pac::Peripherals::steal() }; - let p0 = gpio::p0::Parts::new(p.P0); + let mut p = unsafe { Peripherals::steal() }; - let pins = spim::Pins { - sck: p0.p0_29.into_push_pull_output(gpio::Level::Low).degrade(), - miso: Some(p0.p0_28.into_floating_input().degrade()), - mosi: Some(p0.p0_30.into_push_pull_output(gpio::Level::Low).degrade()), - }; let config = spim::Config { - pins, frequency: spim::Frequency::M16, mode: spim::MODE_0, orc: 0x00, }; - let mut ncs = p0.p0_31.into_push_pull_output(gpio::Level::High); - let spim = spim::Spim::new(p.SPIM3, interrupt::take!(SPIM3), config); + let mut irq = interrupt::take!(SPIM3); + let spim = spim::Spim::new(p.spim3, irq, p.p0_29, p.p0_28, p.p0_30, config); pin_mut!(spim); + let mut ncs = Output::new(p.p0_31, Level::High); + // Example on how to talk to an ENC28J60 chip // softreset diff --git a/embassy-nrf/src/gpio.rs b/embassy-nrf/src/gpio.rs new file mode 100644 index 00000000..9f4604b0 --- /dev/null +++ b/embassy-nrf/src/gpio.rs @@ -0,0 +1,307 @@ +use core::convert::Infallible; +use core::hint::unreachable_unchecked; + +use embedded_hal::digital::v2::{InputPin, OutputPin, StatefulOutputPin}; + +use crate::pac; +use crate::pac::p0 as gpio; +use crate::peripherals; + +/// Represents a digital input or output level. +#[derive(Debug, Eq, PartialEq)] +pub enum Level { + Low, + High, +} + +/// Represents a pull setting for an input. +#[derive(Debug, Eq, PartialEq)] +pub enum Pull { + None, + Up, + Down, +} + +/// A GPIO port with up to 32 pins. +#[derive(Debug, Eq, PartialEq)] +pub enum Port { + /// Port 0, available on all nRF52 and nRF51 MCUs. + Port0, + + /// Port 1, only available on some nRF52 MCUs. + #[cfg(any(feature = "52833", feature = "52840"))] + Port1, +} + +pub struct Input { + pin: T, +} + +impl Input { + pub fn new(pin: T, pull: 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 + }); + + Self { pin } + } +} + +impl Drop for Input { + fn drop(&mut self) { + self.pin.conf().reset(); + } +} + +impl InputPin for Input { + type Error = Infallible; + + fn is_high(&self) -> Result { + self.is_low().map(|v| !v) + } + + fn is_low(&self) -> Result { + Ok(self.pin.block().in_.read().bits() & (1 << self.pin.pin()) == 0) + } +} + +pub struct Output { + pin: T, +} + +impl Output { + // TODO opendrain + pub fn new(pin: T, initial_output: Level) -> Self { + pin.conf().write(|w| { + w.dir().output(); + w.input().disconnect(); + w.pull().disabled(); + w.drive().s0s1(); + w.sense().disabled(); + w + }); + + Self { pin } + } +} + +impl Drop for Output { + fn drop(&mut self) { + self.pin.conf().reset(); + } +} + +impl OutputPin for Output { + 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(()) + } + + /// 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(()) + } +} + +impl StatefulOutputPin for Output { + /// Is the output pin set as high? + fn is_set_high(&self) -> Result { + self.is_set_low().map(|v| !v) + } + + /// 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) + } +} + +pub(crate) mod sealed { + use super::*; + + pub trait Pin { + fn pin_port(&self) -> u8; + + #[inline] + fn _pin(&self) -> u8 { + #[cfg(any(feature = "52833", feature = "52840"))] + { + self.pin_port() % 32 + } + + #[cfg(not(any(feature = "52833", feature = "52840")))] + { + self.pin_port() + } + } + + fn block(&self) -> &gpio::RegisterBlock { + unsafe { + match self.pin_port() / 32 { + 0 => &*pac::P0::ptr(), + #[cfg(any(feature = "52833", feature = "52840"))] + 1 => &*pac::P1::ptr(), + _ => unreachable_unchecked(), + } + } + } + + fn conf(&self) -> &gpio::PIN_CNF { + &self.block().pin_cnf[self._pin() as usize] + } + + /// Set the output as high. + fn set_high(&self) { + unsafe { + self.block().outset.write(|w| w.bits(1u32 << self._pin())); + } + } + + /// Set the output as low. + fn set_low(&self) { + unsafe { + self.block().outclr.write(|w| w.bits(1u32 << self._pin())); + } + } + } +} + +pub trait Pin: sealed::Pin + Sized { + #[inline] + fn pin(&self) -> u8 { + self._pin() + } + + #[inline] + fn port(&self) -> Port { + match self.pin_port() / 32 { + 1 => Port::Port0, + #[cfg(any(feature = "52833", feature = "52840"))] + 1 => Port::Port1, + _ => unsafe { unreachable_unchecked() }, + } + } + + #[inline] + fn psel_bits(&self) -> u32 { + self.pin_port() as u32 + } + + fn degrade(self) -> AnyPin { + AnyPin { + pin_port: self.pin_port(), + } + } +} + +pub struct AnyPin { + pin_port: u8, +} + +impl AnyPin { + pub unsafe fn from_psel_bits(psel_bits: u32) -> Self { + Self { + pin_port: psel_bits as u8, + } + } +} + +impl Pin for AnyPin {} +impl sealed::Pin for AnyPin { + fn pin_port(&self) -> u8 { + self.pin_port + } +} + +macro_rules! make_impl { + ($type:ident, $port_num:expr, $pin_num:expr) => { + impl Pin for peripherals::$type {} + impl sealed::Pin for peripherals::$type { + fn pin_port(&self) -> u8 { + $port_num * 32 + $pin_num + } + } + }; +} + +make_impl!(P0_00, 0, 0); +make_impl!(P0_01, 0, 1); +make_impl!(P0_02, 0, 2); +make_impl!(P0_03, 0, 3); +make_impl!(P0_04, 0, 4); +make_impl!(P0_05, 0, 5); +make_impl!(P0_06, 0, 6); +make_impl!(P0_07, 0, 7); +make_impl!(P0_08, 0, 8); +make_impl!(P0_09, 0, 9); +make_impl!(P0_10, 0, 10); +make_impl!(P0_11, 0, 11); +make_impl!(P0_12, 0, 12); +make_impl!(P0_13, 0, 13); +make_impl!(P0_14, 0, 14); +make_impl!(P0_15, 0, 15); +make_impl!(P0_16, 0, 16); +make_impl!(P0_17, 0, 17); +make_impl!(P0_18, 0, 18); +make_impl!(P0_19, 0, 19); +make_impl!(P0_20, 0, 20); +make_impl!(P0_21, 0, 21); +make_impl!(P0_22, 0, 22); +make_impl!(P0_23, 0, 23); +make_impl!(P0_24, 0, 24); +make_impl!(P0_25, 0, 25); +make_impl!(P0_26, 0, 26); +make_impl!(P0_27, 0, 27); +make_impl!(P0_28, 0, 28); +make_impl!(P0_29, 0, 29); +make_impl!(P0_30, 0, 30); +make_impl!(P0_31, 0, 31); + +#[cfg(any(feature = "52833", feature = "52840"))] +mod _p1 { + use super::*; + make_impl!(P1_00, 1, 0); + make_impl!(P1_01, 1, 1); + make_impl!(P1_02, 1, 2); + make_impl!(P1_03, 1, 3); + make_impl!(P1_04, 1, 4); + make_impl!(P1_05, 1, 5); + make_impl!(P1_06, 1, 6); + make_impl!(P1_07, 1, 7); + make_impl!(P1_08, 1, 8); + make_impl!(P1_09, 1, 9); + make_impl!(P1_10, 1, 10); + make_impl!(P1_11, 1, 11); + make_impl!(P1_12, 1, 12); + make_impl!(P1_13, 1, 13); + make_impl!(P1_14, 1, 14); + make_impl!(P1_15, 1, 15); +} diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index 2c72b912..07759996 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -94,8 +94,10 @@ pub(crate) fn slice_in_ram_or(slice: &[u8], err: T) -> Result<(), T> { pub(crate) mod fmt; pub mod buffered_uarte; +pub mod gpio; pub mod gpiote; pub mod interrupt; +pub mod peripherals; #[cfg(feature = "52840")] pub mod qspi; pub mod rtc; diff --git a/embassy-nrf/src/peripherals.rs b/embassy-nrf/src/peripherals.rs new file mode 100644 index 00000000..bc2ed881 --- /dev/null +++ b/embassy-nrf/src/peripherals.rs @@ -0,0 +1,144 @@ +use embassy::util::PeripheralBorrow; + +macro_rules! peripherals { + ($($(#[$cfg:meta])? $name:ident: $type:ident),*$(,)?) => { + $( + $(#[$cfg])? + pub struct $type { _private: () } + + $(#[$cfg])? + impl PeripheralBorrow for $type { + type Target = $type; + unsafe fn unborrow(self) -> $type { + self + } + } + + $(#[$cfg])? + impl PeripheralBorrow for &mut $type { + type Target = $type; + unsafe fn unborrow(self) -> $type { + ::core::ptr::read(self) + } + } + )* + + pub struct Peripherals { + $( + $(#[$cfg])? + pub $name: $type, + )* + } + + impl Peripherals { + pub unsafe fn steal() -> Self { + Self { + $( + $(#[$cfg])? + $name: $type { _private: () }, + )* + } + } + } + + }; +} + +peripherals! { + // RTC + rtc0: RTC0, + rtc1: RTC1, + #[cfg(any(feature = "52832", feature = "52833", feature = "52840"))] + rtc2: RTC2, + + // QSPI + #[cfg(feature = "52840")] + qspi: QSPI, + + // UARTE + uarte0: UARTE0, + #[cfg(any(feature = "52833", feature = "52840", feature = "9160"))] + uarte1: UARTE1, + + // SPIM + // TODO this is actually shared with SPI, SPIM, SPIS, TWI, TWIS, TWIS. + // When they're all implemented, they should be only one peripheral here. + spim0: SPIM0, + #[cfg(any(feature = "52832", feature = "52833", feature = "52840"))] + spim1: SPIM1, + #[cfg(any(feature = "52832", feature = "52833", feature = "52840"))] + spim2: SPIM2, + #[cfg(any(feature = "52833", feature = "52840"))] + spim3: SPIM3, + + // GPIOTE + gpiote: GPIOTE, + + // GPIO port 0 + p0_00: P0_00, + p0_01: P0_01, + p0_02: P0_02, + p0_03: P0_03, + p0_04: P0_04, + p0_05: P0_05, + p0_06: P0_06, + p0_07: P0_07, + p0_08: P0_08, + p0_09: P0_09, + p0_10: P0_10, + p0_11: P0_11, + p0_12: P0_12, + p0_13: P0_13, + p0_14: P0_14, + p0_15: P0_15, + p0_16: P0_16, + p0_17: P0_17, + p0_18: P0_18, + p0_19: P0_19, + p0_20: P0_20, + p0_21: P0_21, + p0_22: P0_22, + p0_23: P0_23, + p0_24: P0_24, + p0_25: P0_25, + p0_26: P0_26, + p0_27: P0_27, + p0_28: P0_28, + p0_29: P0_29, + p0_30: P0_30, + p0_31: P0_31, + + // GPIO port 1 + #[cfg(any(feature = "52833", feature = "52840"))] + p1_00: P1_00, + #[cfg(any(feature = "52833", feature = "52840"))] + p1_01: P1_01, + #[cfg(any(feature = "52833", feature = "52840"))] + p1_02: P1_02, + #[cfg(any(feature = "52833", feature = "52840"))] + p1_03: P1_03, + #[cfg(any(feature = "52833", feature = "52840"))] + p1_04: P1_04, + #[cfg(any(feature = "52833", feature = "52840"))] + p1_05: P1_05, + #[cfg(any(feature = "52833", feature = "52840"))] + p1_06: P1_06, + #[cfg(any(feature = "52833", feature = "52840"))] + p1_07: P1_07, + #[cfg(any(feature = "52833", feature = "52840"))] + p1_08: P1_08, + #[cfg(any(feature = "52833", feature = "52840"))] + p1_09: P1_09, + #[cfg(any(feature = "52833", feature = "52840"))] + p1_10: P1_10, + #[cfg(any(feature = "52833", feature = "52840"))] + p1_11: P1_11, + #[cfg(any(feature = "52833", feature = "52840"))] + p1_12: P1_12, + #[cfg(any(feature = "52833", feature = "52840"))] + p1_13: P1_13, + #[cfg(any(feature = "52833", feature = "52840"))] + p1_14: P1_14, + #[cfg(any(feature = "52833", feature = "52840"))] + p1_15: P1_15, +} diff --git a/embassy-nrf/src/spim.rs b/embassy-nrf/src/spim.rs index b236f538..38cfa005 100644 --- a/embassy-nrf/src/spim.rs +++ b/embassy-nrf/src/spim.rs @@ -1,19 +1,20 @@ use core::future::Future; +use core::marker::PhantomData; use core::pin::Pin; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; use embassy::traits; -use embassy::util::WakerRegistration; +use embassy::util::{PeripheralBorrow, WakerRegistration}; use embassy_extras::peripheral::{PeripheralMutex, PeripheralState}; use futures::future::poll_fn; use traits::spi::FullDuplex; +use crate::gpio::Pin as GpioPin; use crate::interrupt::{self, Interrupt}; -use crate::{pac, slice_in_ram_or}; +use crate::{pac, peripherals, slice_in_ram_or}; -pub use crate::hal::spim::{ - Frequency, Mode, Phase, Pins, Polarity, MODE_0, MODE_1, MODE_2, MODE_3, -}; +pub use embedded_hal::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3}; +pub use pac::spim0::frequency::FREQUENCY_A as Frequency; #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -30,41 +31,63 @@ struct State { waker: WakerRegistration, } -pub struct Spim { +pub struct Spim<'d, T: Instance> { inner: PeripheralMutex>, + phantom: PhantomData<&'d mut T>, } pub struct Config { - pub pins: Pins, pub frequency: Frequency, pub mode: Mode, pub orc: u8, } -impl Spim { - pub fn new(mut spim: T, irq: T::Interrupt, config: Config) -> Self { +impl<'d, T: Instance> Spim<'d, T> { + pub fn new( + spim: impl PeripheralBorrow + 'd, + irq: impl PeripheralBorrow + 'd, + sck: impl PeripheralBorrow + 'd, + miso: impl PeripheralBorrow + 'd, + mosi: impl PeripheralBorrow + 'd, + config: Config, + ) -> Self { + let mut spim = unsafe { spim.unborrow() }; + let irq = unsafe { irq.unborrow() }; + let sck = unsafe { sck.unborrow() }; + let miso = unsafe { miso.unborrow() }; + let mosi = unsafe { mosi.unborrow() }; + let r = spim.regs(); + // Configure pins + sck.conf().write(|w| w.dir().output()); + mosi.conf().write(|w| w.dir().output()); + miso.conf().write(|w| w.input().connect()); + + match config.mode.polarity { + Polarity::IdleHigh => { + sck.set_high(); + mosi.set_high(); + } + Polarity::IdleLow => { + sck.set_low(); + mosi.set_low(); + } + } + // Select pins. r.psel.sck.write(|w| { - unsafe { w.bits(config.pins.sck.psel_bits()) }; + unsafe { w.bits(sck.psel_bits()) }; + w.connect().connected() + }); + r.psel.mosi.write(|w| { + unsafe { w.bits(mosi.psel_bits()) }; + w.connect().connected() + }); + r.psel.miso.write(|w| { + unsafe { w.bits(miso.psel_bits()) }; w.connect().connected() }); - - match config.pins.mosi { - Some(mosi) => r.psel.mosi.write(|w| { - unsafe { w.bits(mosi.psel_bits()) }; - w.connect().connected() - }), - None => r.psel.mosi.write(|w| w.connect().disconnected()), - } - match config.pins.miso { - Some(miso) => r.psel.miso.write(|w| { - unsafe { w.bits(miso.psel_bits()) }; - w.connect().connected() - }), - None => r.psel.miso.write(|w| w.connect().disconnected()), - } // Enable SPIM instance. r.enable.write(|w| w.enable().enabled()); @@ -114,6 +137,7 @@ impl Spim { }, irq, ), + phantom: PhantomData, } } @@ -122,7 +146,7 @@ impl Spim { } } -impl FullDuplex for Spim { +impl<'d, T: Instance> FullDuplex for Spim<'d, T> { type Error = Error; #[rustfmt::skip] @@ -222,19 +246,19 @@ mod sealed { } } -pub trait Instance: sealed::Instance { +pub trait Instance: sealed::Instance + 'static { type Interrupt: Interrupt; } macro_rules! make_impl { - ($SPIMx:ident, $IRQ:ident) => { - impl sealed::Instance for pac::$SPIMx { + ($type:ident, $irq:ident) => { + impl sealed::Instance for peripherals::$type { fn regs(&mut self) -> &pac::spim0::RegisterBlock { - self + unsafe { &*pac::$type::ptr() } } } - impl Instance for pac::$SPIMx { - type Interrupt = interrupt::$IRQ; + impl Instance for peripherals::$type { + type Interrupt = interrupt::$irq; } }; }