nrf: add owned Peripherals struct, migrate gpio and spim

This commit is contained in:
Dario Nieuwenhuis 2021-03-19 04:08:44 +01:00
parent d9aaa0edf8
commit fcf6a63b5c
5 changed files with 516 additions and 43 deletions

View File

@ -6,6 +6,8 @@
#[path = "../example_common.rs"] #[path = "../example_common.rs"]
mod example_common; mod example_common;
use embassy_nrf::gpio::{Level, Output};
use embassy_nrf::peripherals::Peripherals;
use embassy_traits::spi::FullDuplex; use embassy_traits::spi::FullDuplex;
use example_common::*; use example_common::*;
@ -16,7 +18,6 @@ use embassy::util::Forever;
use embedded_hal::digital::v2::*; use embedded_hal::digital::v2::*;
use futures::pin_mut; use futures::pin_mut;
use nrf52840_hal::clocks; use nrf52840_hal::clocks;
use nrf52840_hal::gpio;
use embassy_nrf::{interrupt, pac, rtc, spim}; use embassy_nrf::{interrupt, pac, rtc, spim};
@ -24,25 +25,20 @@ use embassy_nrf::{interrupt, pac, rtc, spim};
async fn run() { async fn run() {
info!("running!"); info!("running!");
let p = unsafe { embassy_nrf::pac::Peripherals::steal() }; let mut p = unsafe { Peripherals::steal() };
let p0 = gpio::p0::Parts::new(p.P0);
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 { let config = spim::Config {
pins,
frequency: spim::Frequency::M16, frequency: spim::Frequency::M16,
mode: spim::MODE_0, mode: spim::MODE_0,
orc: 0x00, orc: 0x00,
}; };
let mut ncs = p0.p0_31.into_push_pull_output(gpio::Level::High); let mut irq = interrupt::take!(SPIM3);
let spim = spim::Spim::new(p.SPIM3, interrupt::take!(SPIM3), config); let spim = spim::Spim::new(p.spim3, irq, p.p0_29, p.p0_28, p.p0_30, config);
pin_mut!(spim); pin_mut!(spim);
let mut ncs = Output::new(p.p0_31, Level::High);
// Example on how to talk to an ENC28J60 chip // Example on how to talk to an ENC28J60 chip
// softreset // softreset

307
embassy-nrf/src/gpio.rs Normal file
View File

@ -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<T: Pin> {
pin: T,
}
impl<T: Pin> Input<T> {
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<T: Pin> Drop for Input<T> {
fn drop(&mut self) {
self.pin.conf().reset();
}
}
impl<T: Pin> InputPin for Input<T> {
type Error = Infallible;
fn is_high(&self) -> Result<bool, Self::Error> {
self.is_low().map(|v| !v)
}
fn is_low(&self) -> Result<bool, Self::Error> {
Ok(self.pin.block().in_.read().bits() & (1 << self.pin.pin()) == 0)
}
}
pub struct Output<T: Pin> {
pin: T,
}
impl<T: Pin> Output<T> {
// 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<T: Pin> Drop for Output<T> {
fn drop(&mut self) {
self.pin.conf().reset();
}
}
impl<T: Pin> OutputPin for Output<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(())
}
/// 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<T: Pin> StatefulOutputPin for Output<T> {
/// Is the output pin set as high?
fn is_set_high(&self) -> Result<bool, Self::Error> {
self.is_set_low().map(|v| !v)
}
/// 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)
}
}
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);
}

View File

@ -94,8 +94,10 @@ pub(crate) fn slice_in_ram_or<T>(slice: &[u8], err: T) -> Result<(), T> {
pub(crate) mod fmt; pub(crate) mod fmt;
pub mod buffered_uarte; pub mod buffered_uarte;
pub mod gpio;
pub mod gpiote; pub mod gpiote;
pub mod interrupt; pub mod interrupt;
pub mod peripherals;
#[cfg(feature = "52840")] #[cfg(feature = "52840")]
pub mod qspi; pub mod qspi;
pub mod rtc; pub mod rtc;

View File

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

View File

@ -1,19 +1,20 @@
use core::future::Future; use core::future::Future;
use core::marker::PhantomData;
use core::pin::Pin; use core::pin::Pin;
use core::sync::atomic::{compiler_fence, Ordering}; use core::sync::atomic::{compiler_fence, Ordering};
use core::task::Poll; use core::task::Poll;
use embassy::traits; use embassy::traits;
use embassy::util::WakerRegistration; use embassy::util::{PeripheralBorrow, WakerRegistration};
use embassy_extras::peripheral::{PeripheralMutex, PeripheralState}; use embassy_extras::peripheral::{PeripheralMutex, PeripheralState};
use futures::future::poll_fn; use futures::future::poll_fn;
use traits::spi::FullDuplex; use traits::spi::FullDuplex;
use crate::gpio::Pin as GpioPin;
use crate::interrupt::{self, Interrupt}; 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::{ pub use embedded_hal::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3};
Frequency, Mode, Phase, Pins, Polarity, MODE_0, MODE_1, MODE_2, MODE_3, pub use pac::spim0::frequency::FREQUENCY_A as Frequency;
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
@ -30,41 +31,63 @@ struct State<T: Instance> {
waker: WakerRegistration, waker: WakerRegistration,
} }
pub struct Spim<T: Instance> { pub struct Spim<'d, T: Instance> {
inner: PeripheralMutex<State<T>>, inner: PeripheralMutex<State<T>>,
phantom: PhantomData<&'d mut T>,
} }
pub struct Config { pub struct Config {
pub pins: Pins,
pub frequency: Frequency, pub frequency: Frequency,
pub mode: Mode, pub mode: Mode,
pub orc: u8, pub orc: u8,
} }
impl<T: Instance> Spim<T> { impl<'d, T: Instance> Spim<'d, T> {
pub fn new(mut spim: T, irq: T::Interrupt, config: Config) -> Self { pub fn new(
spim: impl PeripheralBorrow<Target = T> + 'd,
irq: impl PeripheralBorrow<Target = T::Interrupt> + 'd,
sck: impl PeripheralBorrow<Target = impl GpioPin> + 'd,
miso: impl PeripheralBorrow<Target = impl GpioPin> + 'd,
mosi: impl PeripheralBorrow<Target = impl GpioPin> + '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(); 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. // Select pins.
r.psel.sck.write(|w| { r.psel.sck.write(|w| {
unsafe { w.bits(config.pins.sck.psel_bits()) }; unsafe { w.bits(sck.psel_bits()) };
w.connect().connected() w.connect().connected()
}); });
r.psel.mosi.write(|w| {
match config.pins.mosi {
Some(mosi) => r.psel.mosi.write(|w| {
unsafe { w.bits(mosi.psel_bits()) }; unsafe { w.bits(mosi.psel_bits()) };
w.connect().connected() w.connect().connected()
}), });
None => r.psel.mosi.write(|w| w.connect().disconnected()), r.psel.miso.write(|w| {
}
match config.pins.miso {
Some(miso) => r.psel.miso.write(|w| {
unsafe { w.bits(miso.psel_bits()) }; unsafe { w.bits(miso.psel_bits()) };
w.connect().connected() w.connect().connected()
}), });
None => r.psel.miso.write(|w| w.connect().disconnected()),
}
// Enable SPIM instance. // Enable SPIM instance.
r.enable.write(|w| w.enable().enabled()); r.enable.write(|w| w.enable().enabled());
@ -114,6 +137,7 @@ impl<T: Instance> Spim<T> {
}, },
irq, irq,
), ),
phantom: PhantomData,
} }
} }
@ -122,7 +146,7 @@ impl<T: Instance> Spim<T> {
} }
} }
impl<T: Instance> FullDuplex<u8> for Spim<T> { impl<'d, T: Instance> FullDuplex<u8> for Spim<'d, T> {
type Error = Error; type Error = Error;
#[rustfmt::skip] #[rustfmt::skip]
@ -222,19 +246,19 @@ mod sealed {
} }
} }
pub trait Instance: sealed::Instance { pub trait Instance: sealed::Instance + 'static {
type Interrupt: Interrupt; type Interrupt: Interrupt;
} }
macro_rules! make_impl { macro_rules! make_impl {
($SPIMx:ident, $IRQ:ident) => { ($type:ident, $irq:ident) => {
impl sealed::Instance for pac::$SPIMx { impl sealed::Instance for peripherals::$type {
fn regs(&mut self) -> &pac::spim0::RegisterBlock { fn regs(&mut self) -> &pac::spim0::RegisterBlock {
self unsafe { &*pac::$type::ptr() }
} }
} }
impl Instance for pac::$SPIMx { impl Instance for peripherals::$type {
type Interrupt = interrupt::$IRQ; type Interrupt = interrupt::$irq;
} }
}; };
} }