diff --git a/embassy-extras/src/lib.rs b/embassy-extras/src/lib.rs index 536e86c6..be08ddf1 100644 --- a/embassy-extras/src/lib.rs +++ b/embassy-extras/src/lib.rs @@ -3,7 +3,9 @@ // This mod MUST go first, so that the others see its macros. pub(crate) mod fmt; +mod macros; pub mod peripheral; +pub mod peripheral_shared; pub mod ring_buffer; pub mod usb; diff --git a/embassy-extras/src/macros.rs b/embassy-extras/src/macros.rs new file mode 100644 index 00000000..9110f225 --- /dev/null +++ b/embassy-extras/src/macros.rs @@ -0,0 +1,107 @@ +#[macro_export] +macro_rules! peripherals { + ($($(#[$cfg:meta])? $name:ident),*$(,)?) => { + pub mod peripherals { + $( + $(#[$cfg])? + #[allow(non_camel_case_types)] + pub struct $name { _private: () } + + $(#[$cfg])? + impl embassy::util::Steal for $name { + #[inline] + unsafe fn steal() -> Self { + Self{ _private: ()} + } + } + + $(#[$cfg])? + impl embassy::util::PeripheralBorrow for $name { + type Target = $name; + #[inline] + unsafe fn unborrow(self) -> $name { + self + } + } + + $(#[$cfg])? + impl embassy::util::PeripheralBorrow for &mut $name { + type Target = $name; + #[inline] + unsafe fn unborrow(self) -> $name { + ::core::ptr::read(self) + } + } + )* + } + + #[allow(non_snake_case)] + pub struct Peripherals { + $( + $(#[$cfg])? + pub $name: peripherals::$name, + )* + } + + impl Peripherals { + ///Returns all the peripherals *once* + #[inline] + pub fn take() -> Option { + + #[no_mangle] + static mut _EMBASSY_DEVICE_PERIPHERALS: bool = false; + + cortex_m::interrupt::free(|_| { + if unsafe { _EMBASSY_DEVICE_PERIPHERALS } { + None + } else { + Some(unsafe { ::steal() }) + } + }) + } + } + + impl embassy::util::Steal for Peripherals { + #[inline] + unsafe fn steal() -> Self { + Self { + $( + $(#[$cfg])? + $name: ::steal(), + )* + } + } + } + + }; +} + +#[macro_export] +macro_rules! unborrow { + ($($name:ident),*) => { + $( + let mut $name = unsafe { $name.unborrow() }; + )* + } +} + +#[macro_export] +macro_rules! impl_unborrow { + ($type:ident) => { + impl ::embassy::util::PeripheralBorrow for $type { + type Target = $type; + #[inline] + unsafe fn unborrow(self) -> Self::Target { + self + } + } + + impl<'a> ::embassy::util::PeripheralBorrow for &'a mut $type { + type Target = $type; + #[inline] + unsafe fn unborrow(self) -> Self::Target { + unsafe { ::core::ptr::read(self) } + } + } + }; +} diff --git a/embassy-extras/src/peripheral.rs b/embassy-extras/src/peripheral.rs index e2435d63..68972c54 100644 --- a/embassy-extras/src/peripheral.rs +++ b/embassy-extras/src/peripheral.rs @@ -1,7 +1,6 @@ use core::cell::UnsafeCell; use core::marker::{PhantomData, PhantomPinned}; use core::pin::Pin; -use core::sync::atomic::{compiler_fence, Ordering}; use embassy::interrupt::{Interrupt, InterruptExt}; @@ -39,8 +38,6 @@ impl PeripheralMutex { } this.irq.disable(); - compiler_fence(Ordering::SeqCst); - this.irq.set_handler(|p| { // Safety: it's OK to get a &mut to the state, since // - We're in the IRQ, no one else can't preempt us @@ -50,8 +47,6 @@ impl PeripheralMutex { }); this.irq .set_handler_context((&mut this.state) as *mut _ as *mut ()); - - compiler_fence(Ordering::SeqCst); this.irq.enable(); this.irq_setup_done = true; @@ -61,14 +56,11 @@ impl PeripheralMutex { let this = unsafe { self.get_unchecked_mut() }; this.irq.disable(); - compiler_fence(Ordering::SeqCst); // Safety: it's OK to get a &mut to the state, since the irq is disabled. let state = unsafe { &mut *this.state.get() }; - let r = f(state, &mut this.irq); - compiler_fence(Ordering::SeqCst); this.irq.enable(); r diff --git a/embassy-extras/src/peripheral_shared.rs b/embassy-extras/src/peripheral_shared.rs new file mode 100644 index 00000000..c6211339 --- /dev/null +++ b/embassy-extras/src/peripheral_shared.rs @@ -0,0 +1,63 @@ +use core::cell::UnsafeCell; +use core::marker::{PhantomData, PhantomPinned}; +use core::pin::Pin; + +use embassy::interrupt::{Interrupt, InterruptExt}; + +pub trait PeripheralState { + type Interrupt: Interrupt; + fn on_interrupt(&self); +} + +pub struct Peripheral { + state: UnsafeCell, + + irq_setup_done: bool, + irq: S::Interrupt, + + _not_send: PhantomData<*mut ()>, + _pinned: PhantomPinned, +} + +impl Peripheral { + pub fn new(irq: S::Interrupt, state: S) -> Self { + Self { + irq, + irq_setup_done: false, + + state: UnsafeCell::new(state), + _not_send: PhantomData, + _pinned: PhantomPinned, + } + } + + pub fn register_interrupt(self: Pin<&mut Self>) { + let this = unsafe { self.get_unchecked_mut() }; + if this.irq_setup_done { + return; + } + + this.irq.disable(); + this.irq.set_handler(|p| { + let state = unsafe { &*(p as *const S) }; + state.on_interrupt(); + }); + this.irq + .set_handler_context((&this.state) as *const _ as *mut ()); + this.irq.enable(); + + this.irq_setup_done = true; + } + + pub fn state(self: Pin<&mut Self>) -> &S { + let this = unsafe { self.get_unchecked_mut() }; + unsafe { &*this.state.get() } + } +} + +impl Drop for Peripheral { + fn drop(&mut self) { + self.irq.disable(); + self.irq.remove_handler(); + } +} diff --git a/embassy-macros/src/lib.rs b/embassy-macros/src/lib.rs index 450edbe4..a14c374f 100644 --- a/embassy-macros/src/lib.rs +++ b/embassy-macros/src/lib.rs @@ -137,6 +137,20 @@ pub fn interrupt_declare(item: TokenStream) -> TokenStream { &HANDLER } } + + impl ::embassy::util::PeripheralBorrow for #name_interrupt { + type Target = #name_interrupt; + unsafe fn unborrow(self) -> #name_interrupt { + self + } + } + + impl ::embassy::util::PeripheralBorrow for &mut #name_interrupt { + type Target = #name_interrupt; + unsafe fn unborrow(self) -> #name_interrupt { + ::core::ptr::read(self) + } + } }; result.into() } @@ -158,12 +172,10 @@ pub fn interrupt_take(item: TokenStream) -> TokenStream { static HANDLER: ::embassy::interrupt::Handler; } - let func = HANDLER.func.load(::embassy::export::atomic::Ordering::Acquire); - let ctx = HANDLER.ctx.load(::embassy::export::atomic::Ordering::Acquire); - if !func.is_null() { - let func: fn(*mut ()) = ::core::mem::transmute(func); - func(ctx) - } + let func = HANDLER.func.load(::embassy::export::atomic::Ordering::Relaxed); + let ctx = HANDLER.ctx.load(::embassy::export::atomic::Ordering::Relaxed); + let func: fn(*mut ()) = ::core::mem::transmute(func); + func(ctx) } static TAKEN: ::embassy::export::atomic::AtomicBool = ::embassy::export::atomic::AtomicBool::new(false); diff --git a/embassy-nrf-examples/Cargo.toml b/embassy-nrf-examples/Cargo.toml index 432e8950..2a5b52dd 100644 --- a/embassy-nrf-examples/Cargo.toml +++ b/embassy-nrf-examples/Cargo.toml @@ -28,5 +28,4 @@ cortex-m = { version = "0.7.1", features = ["inline-asm"] } cortex-m-rt = "0.6.13" embedded-hal = { version = "0.2.4" } panic-probe = "0.1.0" -nrf52840-hal = { version = "0.12.1" } futures = { version = "0.3.8", default-features = false, features = ["async-await"] } \ No newline at end of file diff --git a/embassy-nrf-examples/src/bin/buffered_uart.rs b/embassy-nrf-examples/src/bin/buffered_uart.rs index 71e9b4a7..c7c82e84 100644 --- a/embassy-nrf-examples/src/bin/buffered_uart.rs +++ b/embassy-nrf-examples/src/bin/buffered_uart.rs @@ -3,57 +3,49 @@ #![feature(min_type_alias_impl_trait)] #![feature(impl_trait_in_bindings)] #![feature(type_alias_impl_trait)] +#![allow(incomplete_features)] #[path = "../example_common.rs"] mod example_common; -use example_common::*; use cortex_m_rt::entry; use defmt::panic; -use futures::pin_mut; -use nrf52840_hal as hal; -use nrf52840_hal::gpio; - use embassy::executor::{task, Executor}; use embassy::io::{AsyncBufReadExt, AsyncWriteExt}; -use embassy::util::Forever; -use embassy_nrf::buffered_uarte; -use embassy_nrf::interrupt; - -static mut TX_BUFFER: [u8; 4096] = [0; 4096]; -static mut RX_BUFFER: [u8; 4096] = [0; 4096]; +use embassy::util::{Forever, Steal}; +use embassy_nrf::gpio::NoPin; +use embassy_nrf::{buffered_uarte::BufferedUarte, interrupt, peripherals, rtc, uarte, Peripherals}; +use example_common::*; +use futures::pin_mut; #[task] async fn run() { - let p = unwrap!(embassy_nrf::pac::Peripherals::take()); + let p = unsafe { Peripherals::steal() }; - let port0 = gpio::p0::Parts::new(p.P0); + let mut config = uarte::Config::default(); + config.parity = uarte::Parity::EXCLUDED; + config.baudrate = uarte::Baudrate::BAUD115200; - let pins = buffered_uarte::Pins { - rxd: port0.p0_08.into_floating_input().degrade(), - txd: port0 - .p0_06 - .into_push_pull_output(gpio::Level::Low) - .degrade(), - cts: None, - rts: None, - }; - - let ppi = hal::ppi::Parts::new(p.PPI); + let mut tx_buffer = [0u8; 4096]; + let mut rx_buffer = [0u8; 4096]; let irq = interrupt::take!(UARTE0_UART0); - let u = buffered_uarte::BufferedUarte::new( - p.UARTE0, - p.TIMER0, - ppi.ppi0, - ppi.ppi1, - irq, - unsafe { &mut RX_BUFFER }, - unsafe { &mut TX_BUFFER }, - pins, - buffered_uarte::Parity::EXCLUDED, - buffered_uarte::Baudrate::BAUD115200, - ); + let u = unsafe { + BufferedUarte::new( + p.UARTE0, + p.TIMER0, + p.PPI_CH0, + p.PPI_CH1, + irq, + p.P0_08, + p.P0_06, + NoPin, + NoPin, + config, + &mut rx_buffer, + &mut tx_buffer, + ) + }; pin_mut!(u); info!("uarte initialized!"); @@ -79,13 +71,25 @@ async fn run() { } } +static RTC: Forever> = Forever::new(); +static ALARM: Forever> = Forever::new(); static EXECUTOR: Forever = Forever::new(); #[entry] fn main() -> ! { info!("Hello World!"); + let p = unwrap!(embassy_nrf::Peripherals::take()); + + unsafe { embassy_nrf::system::configure(Default::default()) }; + let rtc = RTC.put(rtc::RTC::new(p.RTC1, interrupt::take!(RTC1))); + rtc.start(); + unsafe { embassy::time::set_clock(rtc) }; + + let alarm = ALARM.put(rtc.alarm0()); let executor = EXECUTOR.put(Executor::new()); + executor.set_alarm(alarm); + executor.run(|spawner| { unwrap!(spawner.spawn(run())); }); diff --git a/embassy-nrf-examples/src/bin/executor_fairness_test.rs b/embassy-nrf-examples/src/bin/executor_fairness_test.rs index 6d0a311d..67610ef0 100644 --- a/embassy-nrf-examples/src/bin/executor_fairness_test.rs +++ b/embassy-nrf-examples/src/bin/executor_fairness_test.rs @@ -3,6 +3,7 @@ #![feature(min_type_alias_impl_trait)] #![feature(impl_trait_in_bindings)] #![feature(type_alias_impl_trait)] +#![allow(incomplete_features)] #[path = "../example_common.rs"] mod example_common; @@ -14,9 +15,8 @@ use defmt::panic; use embassy::executor::{task, Executor}; use embassy::time::{Duration, Instant, Timer}; use embassy::util::Forever; -use embassy_nrf::pac; +use embassy_nrf::peripherals; use embassy_nrf::{interrupt, rtc}; -use nrf52840_hal::clocks; #[task] async fn run1() { @@ -42,24 +42,19 @@ async fn run3() { .await; } -static RTC: Forever> = Forever::new(); -static ALARM: Forever> = Forever::new(); +static RTC: Forever> = Forever::new(); +static ALARM: Forever> = Forever::new(); static EXECUTOR: Forever = Forever::new(); #[entry] fn main() -> ! { info!("Hello World!"); - let p = unwrap!(embassy_nrf::pac::Peripherals::take()); - - clocks::Clocks::new(p.CLOCK) - .enable_ext_hfosc() - .set_lfclk_src_external(clocks::LfOscConfiguration::NoExternalNoBypass) - .start_lfclk(); + let p = unwrap!(embassy_nrf::Peripherals::take()); + unsafe { embassy_nrf::system::configure(Default::default()) }; let rtc = RTC.put(rtc::RTC::new(p.RTC1, interrupt::take!(RTC1))); rtc.start(); - unsafe { embassy::time::set_clock(rtc) }; let alarm = ALARM.put(rtc.alarm0()); diff --git a/embassy-nrf-examples/src/bin/gpiote_channel.rs b/embassy-nrf-examples/src/bin/gpiote_channel.rs index 3764ba1c..d223df7a 100644 --- a/embassy-nrf-examples/src/bin/gpiote_channel.rs +++ b/embassy-nrf-examples/src/bin/gpiote_channel.rs @@ -3,6 +3,7 @@ #![feature(min_type_alias_impl_trait)] #![feature(impl_trait_in_bindings)] #![feature(type_alias_impl_trait)] +#![allow(incomplete_features)] #[path = "../example_common.rs"] mod example_common; @@ -10,31 +11,44 @@ use example_common::*; use cortex_m_rt::entry; use defmt::panic; -use nrf52840_hal::gpio; use embassy::executor::{task, Executor}; use embassy::util::Forever; -use embassy_nrf::gpiote::{Gpiote, InputChannel, InputChannelPolarity}; -use embassy_nrf::interrupt; +use embassy_nrf::gpio::{Input, Pull}; +use embassy_nrf::gpiote::{self, InputChannel, InputChannelPolarity}; +use embassy_nrf::{interrupt, Peripherals}; #[task] async fn run() { - let p = unwrap!(embassy_nrf::pac::Peripherals::take()); - let port0 = gpio::p0::Parts::new(p.P0); - - let (g, chs) = Gpiote::new(p.GPIOTE, interrupt::take!(GPIOTE)); + let p = Peripherals::take().unwrap(); + let g = gpiote::initialize(p.GPIOTE, interrupt::take!(GPIOTE)); info!("Starting!"); - let pin1 = port0.p0_11.into_pullup_input().degrade(); - let pin2 = port0.p0_12.into_pullup_input().degrade(); - let pin3 = port0.p0_24.into_pullup_input().degrade(); - let pin4 = port0.p0_25.into_pullup_input().degrade(); - - let ch1 = InputChannel::new(g, chs.ch0, pin1, InputChannelPolarity::HiToLo); - let ch2 = InputChannel::new(g, chs.ch1, pin2, InputChannelPolarity::LoToHi); - let ch3 = InputChannel::new(g, chs.ch2, pin3, InputChannelPolarity::Toggle); - let ch4 = InputChannel::new(g, chs.ch3, pin4, InputChannelPolarity::Toggle); + let ch1 = InputChannel::new( + g, + p.GPIOTE_CH0, + Input::new(p.P0_11, Pull::Up), + InputChannelPolarity::HiToLo, + ); + let ch2 = InputChannel::new( + g, + p.GPIOTE_CH1, + Input::new(p.P0_12, Pull::Up), + InputChannelPolarity::LoToHi, + ); + let ch3 = InputChannel::new( + g, + p.GPIOTE_CH2, + Input::new(p.P0_24, Pull::Up), + InputChannelPolarity::Toggle, + ); + let ch4 = InputChannel::new( + g, + p.GPIOTE_CH3, + Input::new(p.P0_25, Pull::Up), + InputChannelPolarity::Toggle, + ); let button1 = async { loop { diff --git a/embassy-nrf-examples/src/bin/gpiote_port.rs b/embassy-nrf-examples/src/bin/gpiote_port.rs index 36ee1c8e..199a33da 100644 --- a/embassy-nrf-examples/src/bin/gpiote_port.rs +++ b/embassy-nrf-examples/src/bin/gpiote_port.rs @@ -3,23 +3,24 @@ #![feature(min_type_alias_impl_trait)] #![feature(impl_trait_in_bindings)] #![feature(type_alias_impl_trait)] +#![allow(incomplete_features)] #[path = "../example_common.rs"] mod example_common; -use example_common::*; use core::pin::Pin; use cortex_m_rt::entry; use defmt::panic; -use nrf52840_hal::gpio; - use embassy::executor::{task, Executor}; use embassy::traits::gpio::{WaitForHigh, WaitForLow}; use embassy::util::Forever; -use embassy_nrf::gpiote::{Gpiote, GpiotePin}; +use embassy_nrf::gpio::{AnyPin, Input, Pin as _, Pull}; +use embassy_nrf::gpiote::{self, PortInput}; use embassy_nrf::interrupt; +use embassy_nrf::Peripherals; +use example_common::*; -async fn button(n: usize, mut pin: GpiotePin) { +async fn button(n: usize, mut pin: PortInput<'static, AnyPin>) { loop { Pin::new(&mut pin).wait_for_low().await; info!("Button {:?} pressed!", n); @@ -30,26 +31,25 @@ async fn button(n: usize, mut pin: GpiotePin) { #[task] async fn run() { - let p = unwrap!(embassy_nrf::pac::Peripherals::take()); - let port0 = gpio::p0::Parts::new(p.P0); + let p = Peripherals::take().unwrap(); - let (g, _) = Gpiote::new(p.GPIOTE, interrupt::take!(GPIOTE)); + let g = gpiote::initialize(p.GPIOTE, interrupt::take!(GPIOTE)); let button1 = button( 1, - GpiotePin::new(g, port0.p0_11.into_pullup_input().degrade()), + PortInput::new(g, Input::new(p.P0_11.degrade(), Pull::Up)), ); let button2 = button( 2, - GpiotePin::new(g, port0.p0_12.into_pullup_input().degrade()), + PortInput::new(g, Input::new(p.P0_12.degrade(), Pull::Up)), ); let button3 = button( 3, - GpiotePin::new(g, port0.p0_24.into_pullup_input().degrade()), + PortInput::new(g, Input::new(p.P0_24.degrade(), Pull::Up)), ); let button4 = button( 4, - GpiotePin::new(g, port0.p0_25.into_pullup_input().degrade()), + PortInput::new(g, Input::new(p.P0_25.degrade(), Pull::Up)), ); futures::join!(button1, button2, button3, button4); } diff --git a/embassy-nrf-examples/src/bin/multiprio.rs b/embassy-nrf-examples/src/bin/multiprio.rs index 65c64621..aa2824f6 100644 --- a/embassy-nrf-examples/src/bin/multiprio.rs +++ b/embassy-nrf-examples/src/bin/multiprio.rs @@ -58,6 +58,7 @@ #![feature(min_type_alias_impl_trait)] #![feature(impl_trait_in_bindings)] #![feature(type_alias_impl_trait)] +#![allow(incomplete_features)] #[path = "../example_common.rs"] mod example_common; @@ -65,13 +66,11 @@ use example_common::*; use cortex_m_rt::entry; use defmt::panic; -use nrf52840_hal::clocks; - use embassy::executor::{task, Executor, InterruptExecutor}; use embassy::interrupt::InterruptExt; use embassy::time::{Duration, Instant, Timer}; use embassy::util::Forever; -use embassy_nrf::{interrupt, pac, rtc}; +use embassy_nrf::{interrupt, peripherals, rtc}; #[task] async fn run_high() { @@ -115,25 +114,21 @@ async fn run_low() { } } -static RTC: Forever> = Forever::new(); -static ALARM_HIGH: Forever> = Forever::new(); +static RTC: Forever> = Forever::new(); +static ALARM_HIGH: Forever> = Forever::new(); static EXECUTOR_HIGH: Forever> = Forever::new(); -static ALARM_MED: Forever> = Forever::new(); +static ALARM_MED: Forever> = Forever::new(); static EXECUTOR_MED: Forever> = Forever::new(); -static ALARM_LOW: Forever> = Forever::new(); +static ALARM_LOW: Forever> = Forever::new(); static EXECUTOR_LOW: Forever = Forever::new(); #[entry] fn main() -> ! { info!("Hello World!"); - let p = unwrap!(embassy_nrf::pac::Peripherals::take()); - - clocks::Clocks::new(p.CLOCK) - .enable_ext_hfosc() - .set_lfclk_src_external(clocks::LfOscConfiguration::NoExternalNoBypass) - .start_lfclk(); + let p = unwrap!(embassy_nrf::Peripherals::take()); + unsafe { embassy_nrf::system::configure(Default::default()) }; let rtc = RTC.put(rtc::RTC::new(p.RTC1, interrupt::take!(RTC1))); rtc.start(); unsafe { embassy::time::set_clock(rtc) }; diff --git a/embassy-nrf-examples/src/bin/ppi.rs b/embassy-nrf-examples/src/bin/ppi.rs new file mode 100644 index 00000000..382c18f8 --- /dev/null +++ b/embassy-nrf-examples/src/bin/ppi.rs @@ -0,0 +1,111 @@ +#![no_std] +#![no_main] +#![feature(min_type_alias_impl_trait)] +#![feature(impl_trait_in_bindings)] +#![feature(type_alias_impl_trait)] +#![allow(incomplete_features)] + +#[path = "../example_common.rs"] +mod example_common; +use core::future::pending; + +use example_common::*; + +use cortex_m_rt::entry; +use defmt::panic; + +use embassy::executor::{task, Executor}; +use embassy::util::Forever; +use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pull}; +use embassy_nrf::gpiote::{self, InputChannel, InputChannelPolarity}; +use embassy_nrf::ppi::Ppi; +use embassy_nrf::{interrupt, Peripherals}; +use gpiote::{OutputChannel, OutputChannelPolarity}; + +#[task] +async fn run() { + let p = Peripherals::take().unwrap(); + let g = gpiote::initialize(p.GPIOTE, interrupt::take!(GPIOTE)); + + info!("Starting!"); + + let button1 = InputChannel::new( + g, + p.GPIOTE_CH0, + Input::new(p.P0_11, Pull::Up), + InputChannelPolarity::HiToLo, + ); + let button2 = InputChannel::new( + g, + p.GPIOTE_CH1, + Input::new(p.P0_12, Pull::Up), + InputChannelPolarity::HiToLo, + ); + let button3 = InputChannel::new( + g, + p.GPIOTE_CH2, + Input::new(p.P0_24, Pull::Up), + InputChannelPolarity::HiToLo, + ); + let button4 = InputChannel::new( + g, + p.GPIOTE_CH3, + Input::new(p.P0_25, Pull::Up), + InputChannelPolarity::HiToLo, + ); + + let led1 = OutputChannel::new( + g, + p.GPIOTE_CH4, + Output::new(p.P0_13, Level::Low, OutputDrive::Standard), + OutputChannelPolarity::Toggle, + ); + + let led2 = OutputChannel::new( + g, + p.GPIOTE_CH5, + Output::new(p.P0_14, Level::Low, OutputDrive::Standard), + OutputChannelPolarity::Toggle, + ); + + let mut ppi = Ppi::new(p.PPI_CH0); + ppi.set_event(button1.event_in()); + ppi.set_task(led1.task_out()); + ppi.enable(); + + let mut ppi = Ppi::new(p.PPI_CH1); + ppi.set_event(button2.event_in()); + ppi.set_task(led1.task_clr()); + ppi.enable(); + + let mut ppi = Ppi::new(p.PPI_CH2); + ppi.set_event(button3.event_in()); + ppi.set_task(led1.task_set()); + ppi.enable(); + + let mut ppi = Ppi::new(p.PPI_CH3); + ppi.set_event(button4.event_in()); + ppi.set_task(led1.task_out()); + ppi.set_fork_task(led2.task_out()); + ppi.enable(); + + info!("PPI setup!"); + info!("Press button 1 to toggle LED 1"); + info!("Press button 2 to turn on LED 1"); + info!("Press button 3 to turn off LED 1"); + info!("Press button 4 to toggle LEDs 1 and 2"); + // Block forever so the above drivers don't get dropped + pending::<()>().await; +} + +static EXECUTOR: Forever = Forever::new(); + +#[entry] +fn main() -> ! { + info!("Hello World!"); + + let executor = EXECUTOR.put(Executor::new()); + executor.run(|spawner| { + unwrap!(spawner.spawn(run())); + }); +} diff --git a/embassy-nrf-examples/src/bin/qspi.rs b/embassy-nrf-examples/src/bin/qspi.rs index 7d8a45f7..2005b997 100644 --- a/embassy-nrf-examples/src/bin/qspi.rs +++ b/embassy-nrf-examples/src/bin/qspi.rs @@ -3,20 +3,20 @@ #![feature(min_type_alias_impl_trait)] #![feature(impl_trait_in_bindings)] #![feature(type_alias_impl_trait)] +#![allow(incomplete_features)] #[path = "../example_common.rs"] mod example_common; -use example_common::*; use cortex_m_rt::entry; use defmt::{assert_eq, panic}; -use futures::pin_mut; -use nrf52840_hal::gpio; - use embassy::executor::{task, Executor}; use embassy::traits::flash::Flash; use embassy::util::Forever; +use embassy_nrf::Peripherals; use embassy_nrf::{interrupt, qspi}; +use example_common::*; +use futures::pin_mut; const PAGE_SIZE: usize = 4096; @@ -27,52 +27,18 @@ struct AlignedBuf([u8; 4096]); #[task] async fn run() { - let p = unwrap!(embassy_nrf::pac::Peripherals::take()); + let p = Peripherals::take().unwrap(); - let port0 = gpio::p0::Parts::new(p.P0); - - let pins = qspi::Pins { - csn: port0 - .p0_17 - .into_push_pull_output(gpio::Level::High) - .degrade(), - sck: port0 - .p0_19 - .into_push_pull_output(gpio::Level::High) - .degrade(), - io0: port0 - .p0_20 - .into_push_pull_output(gpio::Level::High) - .degrade(), - io1: port0 - .p0_21 - .into_push_pull_output(gpio::Level::High) - .degrade(), - io2: Some( - port0 - .p0_22 - .into_push_pull_output(gpio::Level::High) - .degrade(), - ), - io3: Some( - port0 - .p0_23 - .into_push_pull_output(gpio::Level::High) - .degrade(), - ), - }; - - let config = qspi::Config { - pins, - read_opcode: qspi::ReadOpcode::READ4IO, - write_opcode: qspi::WriteOpcode::PP4IO, - xip_offset: 0, - write_page_size: qspi::WritePageSize::_256BYTES, - deep_power_down: None, - }; + let csn = p.P0_17; + let sck = p.P0_19; + let io0 = p.P0_20; + let io1 = p.P0_21; + let io2 = p.P0_22; + let io3 = p.P0_23; + let config = qspi::Config::default(); let irq = interrupt::take!(QSPI); - let q = qspi::Qspi::new(p.QSPI, irq, config); + let q = qspi::Qspi::new(p.QSPI, irq, sck, csn, io0, io1, io2, io3, config); pin_mut!(q); let mut id = [1; 3]; @@ -83,7 +49,7 @@ async fn run() { info!("id: {}", id); // Read status register - let mut status = [0; 1]; + let mut status = [4; 1]; q.as_mut() .custom_instruction(0x05, &[], &mut status) .await diff --git a/embassy-nrf-examples/src/bin/raw_spawn.rs b/embassy-nrf-examples/src/bin/raw_spawn.rs index 3747b49f..f8e021d9 100644 --- a/embassy-nrf-examples/src/bin/raw_spawn.rs +++ b/embassy-nrf-examples/src/bin/raw_spawn.rs @@ -3,19 +3,17 @@ #[path = "../example_common.rs"] mod example_common; -use core::mem; - -use embassy::executor::raw::Task; use example_common::*; +use core::mem; use cortex_m_rt::entry; use defmt::panic; +use embassy::executor::raw::Task; use embassy::executor::Executor; use embassy::time::{Duration, Timer}; use embassy::util::Forever; -use embassy_nrf::pac; +use embassy_nrf::peripherals; use embassy_nrf::{interrupt, rtc}; -use nrf52840_hal::clocks; async fn run1() { loop { @@ -31,24 +29,19 @@ async fn run2() { } } -static RTC: Forever> = Forever::new(); -static ALARM: Forever> = Forever::new(); +static RTC: Forever> = Forever::new(); +static ALARM: Forever> = Forever::new(); static EXECUTOR: Forever = Forever::new(); #[entry] fn main() -> ! { info!("Hello World!"); - let p = unwrap!(embassy_nrf::pac::Peripherals::take()); - - clocks::Clocks::new(p.CLOCK) - .enable_ext_hfosc() - .set_lfclk_src_external(clocks::LfOscConfiguration::NoExternalNoBypass) - .start_lfclk(); + let p = unwrap!(embassy_nrf::Peripherals::take()); + unsafe { embassy_nrf::system::configure(Default::default()) }; let rtc = RTC.put(rtc::RTC::new(p.RTC1, interrupt::take!(RTC1))); rtc.start(); - unsafe { embassy::time::set_clock(rtc) }; let alarm = ALARM.put(rtc.alarm0()); diff --git a/embassy-nrf-examples/src/bin/rtc_raw.rs b/embassy-nrf-examples/src/bin/rtc_raw.rs deleted file mode 100644 index 884ca92b..00000000 --- a/embassy-nrf-examples/src/bin/rtc_raw.rs +++ /dev/null @@ -1,62 +0,0 @@ -#![no_std] -#![no_main] -#![feature(min_type_alias_impl_trait)] -#![feature(impl_trait_in_bindings)] -#![feature(type_alias_impl_trait)] - -#[path = "../example_common.rs"] -mod example_common; -use example_common::*; - -use core::mem::MaybeUninit; -use cortex_m_rt::entry; -use defmt::panic; -use embassy::time::{Alarm, Clock}; -use embassy_nrf::{interrupt, rtc}; -use nrf52840_hal::clocks; - -static mut RTC: MaybeUninit> = MaybeUninit::uninit(); - -#[entry] -fn main() -> ! { - info!("Hello World!"); - - let p = unwrap!(embassy_nrf::pac::Peripherals::take()); - - clocks::Clocks::new(p.CLOCK) - .enable_ext_hfosc() - .set_lfclk_src_external(clocks::LfOscConfiguration::NoExternalNoBypass) - .start_lfclk(); - - let irq = interrupt::take!(RTC1); - - let rtc: &'static _ = unsafe { - let ptr = RTC.as_mut_ptr(); - ptr.write(rtc::RTC::new(p.RTC1, irq)); - &*ptr - }; - - let alarm = rtc.alarm0(); - - rtc.start(); - - alarm.set_callback(|_| info!("ALARM TRIGGERED"), core::ptr::null_mut()); - alarm.set(53719); - - info!("initialized!"); - - let mut val = 0; - let mut printval = 0; - loop { - let val2 = rtc.now(); - if val2 < val { - info!("timer ran backwards! {} -> {}", val as u32, val2 as u32); - } - val = val2; - - if val > printval + 32768 { - info!("tick {}", val as u32); - printval = val; - } - } -} diff --git a/embassy-nrf-examples/src/bin/spim.rs b/embassy-nrf-examples/src/bin/spim.rs index d3d942e4..5e2340ff 100644 --- a/embassy-nrf-examples/src/bin/spim.rs +++ b/embassy-nrf-examples/src/bin/spim.rs @@ -3,46 +3,42 @@ #![feature(min_type_alias_impl_trait)] #![feature(impl_trait_in_bindings)] #![feature(type_alias_impl_trait)] +#![allow(incomplete_features)] #[path = "../example_common.rs"] mod example_common; -use embassy_traits::spi::FullDuplex; -use example_common::*; use cortex_m_rt::entry; use defmt::panic; use embassy::executor::{task, Executor}; use embassy::util::Forever; +use embassy::util::Steal; +use embassy_nrf::gpio::{Level, Output, OutputDrive}; +use embassy_nrf::{interrupt, rtc, spim}; +use embassy_nrf::{peripherals, Peripherals}; +use embassy_traits::spi::FullDuplex; use embedded_hal::digital::v2::*; +use example_common::*; use futures::pin_mut; -use nrf52840_hal::clocks; -use nrf52840_hal::gpio; - -use embassy_nrf::{interrupt, pac, rtc, spim}; #[task] async fn run() { info!("running!"); - let p = unsafe { embassy_nrf::pac::Peripherals::steal() }; - let p0 = gpio::p0::Parts::new(p.P0); + let 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 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, OutputDrive::Standard); + // Example on how to talk to an ENC28J60 chip // softreset @@ -89,23 +85,20 @@ async fn run() { info!("erevid: {=[?]}", rx); } -static RTC: Forever> = Forever::new(); -static ALARM: Forever> = Forever::new(); +static RTC: Forever> = Forever::new(); +static ALARM: Forever> = Forever::new(); static EXECUTOR: Forever = Forever::new(); #[entry] fn main() -> ! { info!("Hello World!"); - let p = unwrap!(embassy_nrf::pac::Peripherals::take()); - - clocks::Clocks::new(p.CLOCK) - .enable_ext_hfosc() - .set_lfclk_src_external(clocks::LfOscConfiguration::NoExternalNoBypass) - .start_lfclk(); + let p = unwrap!(embassy_nrf::Peripherals::take()); + unsafe { embassy_nrf::system::configure(Default::default()) }; let rtc = RTC.put(rtc::RTC::new(p.RTC1, interrupt::take!(RTC1))); rtc.start(); + unsafe { embassy::time::set_clock(rtc) }; unsafe { embassy::time::set_clock(rtc) }; diff --git a/embassy-nrf-examples/src/bin/rtc_async.rs b/embassy-nrf-examples/src/bin/timer.rs similarity index 72% rename from embassy-nrf-examples/src/bin/rtc_async.rs rename to embassy-nrf-examples/src/bin/timer.rs index ec437425..47d5d677 100644 --- a/embassy-nrf-examples/src/bin/rtc_async.rs +++ b/embassy-nrf-examples/src/bin/timer.rs @@ -3,6 +3,7 @@ #![feature(min_type_alias_impl_trait)] #![feature(impl_trait_in_bindings)] #![feature(type_alias_impl_trait)] +#![allow(incomplete_features)] #[path = "../example_common.rs"] mod example_common; @@ -13,9 +14,7 @@ use defmt::panic; use embassy::executor::{task, Executor}; use embassy::time::{Duration, Timer}; use embassy::util::Forever; -use embassy_nrf::pac; -use embassy_nrf::{interrupt, rtc}; -use nrf52840_hal::clocks; +use embassy_nrf::{interrupt, peripherals, rtc}; #[task] async fn run1() { @@ -33,24 +32,19 @@ async fn run2() { } } -static RTC: Forever> = Forever::new(); -static ALARM: Forever> = Forever::new(); +static RTC: Forever> = Forever::new(); +static ALARM: Forever> = Forever::new(); static EXECUTOR: Forever = Forever::new(); #[entry] fn main() -> ! { info!("Hello World!"); - let p = unwrap!(embassy_nrf::pac::Peripherals::take()); - - clocks::Clocks::new(p.CLOCK) - .enable_ext_hfosc() - .set_lfclk_src_external(clocks::LfOscConfiguration::NoExternalNoBypass) - .start_lfclk(); + let p = unwrap!(embassy_nrf::Peripherals::take()); + unsafe { embassy_nrf::system::configure(Default::default()) }; let rtc = RTC.put(rtc::RTC::new(p.RTC1, interrupt::take!(RTC1))); rtc.start(); - unsafe { embassy::time::set_clock(rtc) }; let alarm = ALARM.put(rtc.alarm0()); diff --git a/embassy-nrf-examples/src/bin/uart.rs b/embassy-nrf-examples/src/bin/uart.rs index 0acd6fde..9bbcb3d4 100644 --- a/embassy-nrf-examples/src/bin/uart.rs +++ b/embassy-nrf-examples/src/bin/uart.rs @@ -3,6 +3,7 @@ #![feature(min_type_alias_impl_trait)] #![feature(impl_trait_in_bindings)] #![feature(type_alias_impl_trait)] +#![allow(incomplete_features)] #[path = "../example_common.rs"] mod example_common; @@ -11,39 +12,23 @@ use example_common::*; use cortex_m_rt::entry; use defmt::panic; use embassy::executor::{task, Executor}; -use embassy::time::{Duration, Timer}; -use embassy::traits::uart::Uart; -use embassy::util::Forever; -use embassy_nrf::{interrupt, pac, rtc, uarte}; -use futures::future::{select, Either}; -use nrf52840_hal::clocks; -use nrf52840_hal::gpio; +use embassy::traits::uart::{Read, Write}; +use embassy::util::{Forever, Steal}; +use embassy_nrf::gpio::NoPin; +use embassy_nrf::{interrupt, peripherals, rtc, uarte, Peripherals}; +use futures::pin_mut; #[task] -async fn run(uart: pac::UARTE0, port: pac::P0) { - // Init UART - let port0 = gpio::p0::Parts::new(port); +async fn run() { + let p = unsafe { Peripherals::steal() }; - let pins = uarte::Pins { - rxd: port0.p0_08.into_floating_input().degrade(), - txd: port0 - .p0_06 - .into_push_pull_output(gpio::Level::Low) - .degrade(), - cts: None, - rts: None, - }; + let mut config = uarte::Config::default(); + config.parity = uarte::Parity::EXCLUDED; + config.baudrate = uarte::Baudrate::BAUD115200; - // NOTE(unsafe): Safe becasue we do not use `mem::forget` anywhere. - let mut uart = unsafe { - uarte::Uarte::new( - uart, - interrupt::take!(UARTE0_UART0), - pins, - uarte::Parity::EXCLUDED, - uarte::Baudrate::BAUD115200, - ) - }; + let irq = interrupt::take!(UARTE0_UART0); + let uart = unsafe { uarte::Uarte::new(p.UARTE0, irq, p.P0_08, p.P0_06, NoPin, NoPin, config) }; + pin_mut!(uart); info!("uarte initialized!"); @@ -51,19 +36,22 @@ async fn run(uart: pac::UARTE0, port: pac::P0) { let mut buf = [0; 8]; buf.copy_from_slice(b"Hello!\r\n"); - unwrap!(uart.send(&buf).await); + unwrap!(uart.as_mut().write(&buf).await); info!("wrote hello in uart!"); loop { - let buf_len = buf.len(); info!("reading..."); + unwrap!(uart.as_mut().read(&mut buf).await); + info!("writing..."); + unwrap!(uart.as_mut().write(&buf).await); + /* // `receive()` doesn't return until the buffer has been completely filled with // incoming data, which in this case is 8 bytes. // // This example shows how to use `select` to run an uart receive concurrently with a // 1 second timer, effectively adding a timeout to the receive operation. - let recv_fut = uart.receive(&mut buf); + let recv_fut = uart.read(&mut buf); let timer_fut = Timer::after(Duration::from_millis(1000)); let received_len = match select(recv_fut, timer_fut).await { // recv_fut completed first, so we've received `buf_len` bytes. @@ -81,38 +69,32 @@ async fn run(uart: pac::UARTE0, port: pac::P0) { info!("read done, got {}", received); // Echo back received data - unwrap!(uart.send(received).await); + unwrap!(uart.write(received).await); } + */ } } -static RTC: Forever> = Forever::new(); -static ALARM: Forever> = Forever::new(); +static RTC: Forever> = Forever::new(); +static ALARM: Forever> = Forever::new(); static EXECUTOR: Forever = Forever::new(); #[entry] fn main() -> ! { info!("Hello World!"); - let p = unwrap!(embassy_nrf::pac::Peripherals::take()); - - clocks::Clocks::new(p.CLOCK) - .enable_ext_hfosc() - .set_lfclk_src_external(clocks::LfOscConfiguration::NoExternalNoBypass) - .start_lfclk(); + let p = unwrap!(embassy_nrf::Peripherals::take()); + unsafe { embassy_nrf::system::configure(Default::default()) }; let rtc = RTC.put(rtc::RTC::new(p.RTC1, interrupt::take!(RTC1))); rtc.start(); - unsafe { embassy::time::set_clock(rtc) }; let alarm = ALARM.put(rtc.alarm0()); let executor = EXECUTOR.put(Executor::new()); executor.set_alarm(alarm); - let uarte0 = p.UARTE0; - let p0 = p.P0; executor.run(|spawner| { - unwrap!(spawner.spawn(run(uarte0, p0))); + unwrap!(spawner.spawn(run())); }); } diff --git a/embassy-nrf-examples/src/example_common.rs b/embassy-nrf-examples/src/example_common.rs index d16964d4..54d63383 100644 --- a/embassy-nrf-examples/src/example_common.rs +++ b/embassy-nrf-examples/src/example_common.rs @@ -1,7 +1,6 @@ #![macro_use] use defmt_rtt as _; // global logger -use nrf52840_hal as _; use panic_probe as _; pub use defmt::*; diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index f26e892d..8ec5b087 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml @@ -11,11 +11,11 @@ defmt-info = [ ] defmt-warn = [ ] defmt-error = [ ] -52810 = ["nrf52810-pac", "nrf52810-hal"] -52811 = ["nrf52811-pac"] #, "nrf52811-hal"] -52832 = ["nrf52832-pac", "nrf52832-hal"] -52833 = ["nrf52833-pac", "nrf52833-hal"] -52840 = ["nrf52840-pac", "nrf52840-hal"] +52810 = ["nrf52810-pac"] +52811 = ["nrf52811-pac"] +52832 = ["nrf52832-pac"] +52833 = ["nrf52833-pac"] +52840 = ["nrf52840-pac"] [dependencies] @@ -31,14 +31,8 @@ embedded-hal = { version = "0.2.4" } embedded-dma = { version = "0.1.2" } futures = { version = "0.3.5", default-features = false } -nrf52810-pac = { version = "0.9.0", optional = true } -nrf52811-pac = { version = "0.9.1", optional = true } -nrf52832-pac = { version = "0.9.0", optional = true } -nrf52833-pac = { version = "0.9.0", optional = true } -nrf52840-pac = { version = "0.9.0", optional = true } - -nrf52810-hal = { version = "0.12.1", optional = true } -#nrf52811-hal = { version = "0.12.1", optional = true } # doesn't exist yet -nrf52832-hal = { version = "0.12.1", optional = true } -nrf52833-hal = { version = "0.12.1", optional = true } -nrf52840-hal = { version = "0.12.1", optional = true } +nrf52810-pac = { version = "0.9.0", optional = true, features = [ "rt" ]} +nrf52811-pac = { version = "0.9.1", optional = true, features = [ "rt" ]} +nrf52832-pac = { version = "0.9.0", optional = true, features = [ "rt" ]} +nrf52833-pac = { version = "0.9.0", optional = true, features = [ "rt" ]} +nrf52840-pac = { version = "0.9.0", optional = true, features = [ "rt" ]} diff --git a/embassy-nrf/src/buffered_uarte.rs b/embassy-nrf/src/buffered_uarte.rs index 6cc5f132..702ccde0 100644 --- a/embassy-nrf/src/buffered_uarte.rs +++ b/embassy-nrf/src/buffered_uarte.rs @@ -1,30 +1,24 @@ -//! HAL interface to the UARTE peripheral -//! -//! See product specification: -//! -//! - nrf52832: Section 35 -//! - nrf52840: Section 6.34 use core::cmp::min; use core::mem; -use core::ops::Deref; use core::pin::Pin; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::{Context, Poll}; use embassy::interrupt::InterruptExt; use embassy::io::{AsyncBufRead, AsyncWrite, Result}; -use embassy::util::WakerRegistration; -use embassy_extras::low_power_wait_until; +use embassy::util::{PeripheralBorrow, WakerRegistration}; use embassy_extras::peripheral::{PeripheralMutex, PeripheralState}; use embassy_extras::ring_buffer::RingBuffer; -use embedded_hal::digital::v2::OutputPin; +use embassy_extras::{low_power_wait_until, unborrow}; -use crate::fmt::*; -use crate::hal::ppi::ConfigurablePpi; -use crate::interrupt::{self, Interrupt}; +use crate::fmt::{panic, *}; +use crate::gpio::sealed::Pin as _; +use crate::gpio::{OptionalPin as GpioOptionalPin, Pin as GpioPin}; use crate::pac; +use crate::ppi::{AnyConfigurableChannel, ConfigurableChannel, Event, Ppi, Task}; +use crate::timer::Instance as TimerInstance; +use crate::uarte::{Config, Instance as UarteInstance}; // Re-export SVD variants to allow user to directly set values -pub use crate::hal::uarte::Pins; pub use pac::uarte0::{baudrate::BAUDRATE_A as Baudrate, config::PARITY_A as Parity}; #[derive(Copy, Clone, Debug, PartialEq)] @@ -39,17 +33,17 @@ enum TxState { Transmitting(usize), } -struct State<'a, U: Instance, T: TimerInstance, P1: ConfigurablePpi, P2: ConfigurablePpi> { +struct State<'d, U: UarteInstance, T: TimerInstance> { uarte: U, timer: T, - ppi_channel_1: P1, - ppi_channel_2: P2, + _ppi_ch1: Ppi<'d, AnyConfigurableChannel>, + _ppi_ch2: Ppi<'d, AnyConfigurableChannel>, - rx: RingBuffer<'a>, + rx: RingBuffer<'d>, rx_state: RxState, rx_waker: WakerRegistration, - tx: RingBuffer<'a>, + tx: RingBuffer<'d>, tx_state: TxState, tx_waker: WakerRegistration, } @@ -62,115 +56,112 @@ struct State<'a, U: Instance, T: TimerInstance, P1: ConfigurablePpi, P2: Configu /// are disabled before using `Uarte`. See product specification: /// - nrf52832: Section 15.2 /// - nrf52840: Section 6.1.2 -pub struct BufferedUarte< - 'a, - U: Instance, - T: TimerInstance, - P1: ConfigurablePpi, - P2: ConfigurablePpi, -> { - inner: PeripheralMutex>, +pub struct BufferedUarte<'d, U: UarteInstance, T: TimerInstance> { + inner: PeripheralMutex>, } -impl<'a, U: Instance, T: TimerInstance, P1: ConfigurablePpi, P2: ConfigurablePpi> - BufferedUarte<'a, U, T, P1, P2> -{ - pub fn new( - uarte: U, - timer: T, - mut ppi_channel_1: P1, - mut ppi_channel_2: P2, - irq: U::Interrupt, - rx_buffer: &'a mut [u8], - tx_buffer: &'a mut [u8], - mut pins: Pins, - parity: Parity, - baudrate: Baudrate, +impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> { + /// unsafe: may not leak self or futures + pub unsafe fn new( + uarte: impl PeripheralBorrow + 'd, + timer: impl PeripheralBorrow + 'd, + ppi_ch1: impl PeripheralBorrow + 'd, + ppi_ch2: impl PeripheralBorrow + 'd, + irq: impl PeripheralBorrow + 'd, + rxd: impl PeripheralBorrow + 'd, + txd: impl PeripheralBorrow + 'd, + cts: impl PeripheralBorrow + 'd, + rts: impl PeripheralBorrow + 'd, + config: Config, + rx_buffer: &'d mut [u8], + tx_buffer: &'d mut [u8], ) -> Self { - // Select pins - uarte.psel.rxd.write(|w| { - unsafe { w.bits(pins.rxd.psel_bits()) }; - w.connect().connected() - }); - pins.txd.set_high().unwrap(); - uarte.psel.txd.write(|w| { - unsafe { w.bits(pins.txd.psel_bits()) }; - w.connect().connected() - }); + unborrow!(uarte, timer, ppi_ch1, ppi_ch2, irq, rxd, txd, cts, rts); - // Optional pins - uarte.psel.cts.write(|w| { - if let Some(ref pin) = pins.cts { - unsafe { w.bits(pin.psel_bits()) }; - w.connect().connected() - } else { - w.connect().disconnected() - } - }); + let r = uarte.regs(); + let rt = timer.regs(); - uarte.psel.rts.write(|w| { - if let Some(ref pin) = pins.rts { - unsafe { w.bits(pin.psel_bits()) }; - w.connect().connected() - } else { - w.connect().disconnected() - } - }); + rxd.conf().write(|w| w.input().connect().drive().h0h1()); + r.psel.rxd.write(|w| unsafe { w.bits(rxd.psel_bits()) }); - // Enable UARTE instance - uarte.enable.write(|w| w.enable().enabled()); + txd.set_high(); + txd.conf().write(|w| w.dir().output().drive().h0h1()); + r.psel.txd.write(|w| unsafe { w.bits(txd.psel_bits()) }); - // Enable interrupts - uarte.intenset.write(|w| w.endrx().set().endtx().set()); + if let Some(pin) = rts.pin_mut() { + pin.set_high(); + pin.conf().write(|w| w.dir().output().drive().h0h1()); + } + r.psel.cts.write(|w| unsafe { w.bits(cts.psel_bits()) }); + + if let Some(pin) = cts.pin_mut() { + pin.conf().write(|w| w.input().connect().drive().h0h1()); + } + r.psel.rts.write(|w| unsafe { w.bits(rts.psel_bits()) }); + + r.baudrate.write(|w| w.baudrate().variant(config.baudrate)); + r.config.write(|w| w.parity().variant(config.parity)); // Configure - let hardware_flow_control = pins.rts.is_some() && pins.cts.is_some(); - uarte - .config - .write(|w| w.hwfc().bit(hardware_flow_control).parity().variant(parity)); + let hardware_flow_control = match (rts.pin().is_some(), cts.pin().is_some()) { + (false, false) => false, + (true, true) => true, + _ => panic!("RTS and CTS pins must be either both set or none set."), + }; + r.config.write(|w| { + w.hwfc().bit(hardware_flow_control); + w.parity().variant(config.parity); + w + }); + r.baudrate.write(|w| w.baudrate().variant(config.baudrate)); - // Configure frequency - uarte.baudrate.write(|w| w.baudrate().variant(baudrate)); + // Enable interrupts + r.intenset.write(|w| w.endrx().set().endtx().set()); // Disable the irq, let the Registration enable it when everything is set up. irq.disable(); irq.pend(); + // Enable UARTE instance + r.enable.write(|w| w.enable().enabled()); + // BAUDRATE register values are `baudrate * 2^32 / 16000000` // source: https://devzone.nordicsemi.com/f/nordic-q-a/391/uart-baudrate-register-values // // We want to stop RX if line is idle for 2 bytes worth of time // That is 20 bits (each byte is 1 start bit + 8 data bits + 1 stop bit) // This gives us the amount of 16M ticks for 20 bits. - let timeout = 0x8000_0000 / (baudrate as u32 / 40); + let timeout = 0x8000_0000 / (config.baudrate as u32 / 40); - timer.tasks_stop.write(|w| unsafe { w.bits(1) }); - timer.bitmode.write(|w| w.bitmode()._32bit()); - timer.prescaler.write(|w| unsafe { w.prescaler().bits(0) }); - timer.cc[0].write(|w| unsafe { w.bits(timeout) }); - timer.mode.write(|w| w.mode().timer()); - timer.shorts.write(|w| { + rt.tasks_stop.write(|w| unsafe { w.bits(1) }); + rt.bitmode.write(|w| w.bitmode()._32bit()); + rt.prescaler.write(|w| unsafe { w.prescaler().bits(0) }); + rt.cc[0].write(|w| unsafe { w.bits(timeout) }); + rt.mode.write(|w| w.mode().timer()); + rt.shorts.write(|w| { w.compare0_clear().set_bit(); w.compare0_stop().set_bit(); w }); - ppi_channel_1.set_event_endpoint(&uarte.events_rxdrdy); - ppi_channel_1.set_task_endpoint(&timer.tasks_clear); - ppi_channel_1.set_fork_task_endpoint(&timer.tasks_start); - ppi_channel_1.enable(); + let mut ppi_ch1 = Ppi::new(ppi_ch1.degrade_configurable()); + ppi_ch1.set_event(Event::from_reg(&r.events_rxdrdy)); + ppi_ch1.set_task(Task::from_reg(&rt.tasks_clear)); + ppi_ch1.set_fork_task(Task::from_reg(&rt.tasks_start)); + ppi_ch1.enable(); - ppi_channel_2.set_event_endpoint(&timer.events_compare[0]); - ppi_channel_2.set_task_endpoint(&uarte.tasks_stoprx); - ppi_channel_2.enable(); + let mut ppi_ch2 = Ppi::new(ppi_ch2.degrade_configurable()); + ppi_ch2.set_event(Event::from_reg(&rt.events_compare[0])); + ppi_ch2.set_task(Task::from_reg(&r.tasks_stoprx)); + ppi_ch2.enable(); BufferedUarte { inner: PeripheralMutex::new( State { uarte, timer, - ppi_channel_1, - ppi_channel_2, + _ppi_ch1: ppi_ch1, + _ppi_ch2: ppi_ch2, rx: RingBuffer::new(rx_buffer), rx_state: RxState::Idle, @@ -187,25 +178,23 @@ impl<'a, U: Instance, T: TimerInstance, P1: ConfigurablePpi, P2: ConfigurablePpi pub fn set_baudrate(self: Pin<&mut Self>, baudrate: Baudrate) { self.inner().with(|state, _irq| { - let timeout = 0x8000_0000 / (baudrate as u32 / 40); - state.timer.cc[0].write(|w| unsafe { w.bits(timeout) }); - state.timer.tasks_clear.write(|w| unsafe { w.bits(1) }); + let r = state.uarte.regs(); + let rt = state.timer.regs(); - state - .uarte - .baudrate - .write(|w| w.baudrate().variant(baudrate)); + let timeout = 0x8000_0000 / (baudrate as u32 / 40); + rt.cc[0].write(|w| unsafe { w.bits(timeout) }); + rt.tasks_clear.write(|w| unsafe { w.bits(1) }); + + r.baudrate.write(|w| w.baudrate().variant(baudrate)); }); } - fn inner(self: Pin<&mut Self>) -> Pin<&mut PeripheralMutex>> { + fn inner(self: Pin<&mut Self>) -> Pin<&mut PeripheralMutex>> { unsafe { Pin::new_unchecked(&mut self.get_unchecked_mut().inner) } } } -impl<'a, U: Instance, T: TimerInstance, P1: ConfigurablePpi, P2: ConfigurablePpi> AsyncBufRead - for BufferedUarte<'a, U, T, P1, P2> -{ +impl<'d, U: UarteInstance, T: TimerInstance> AsyncBufRead for BufferedUarte<'d, U, T> { fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let mut inner = self.inner(); inner.as_mut().register_interrupt(); @@ -242,9 +231,7 @@ impl<'a, U: Instance, T: TimerInstance, P1: ConfigurablePpi, P2: ConfigurablePpi } } -impl<'a, U: Instance, T: TimerInstance, P1: ConfigurablePpi, P2: ConfigurablePpi> AsyncWrite - for BufferedUarte<'a, U, T, P1, P2> -{ +impl<'d, U: UarteInstance, T: TimerInstance> AsyncWrite for BufferedUarte<'d, U, T> { fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll> { let mut inner = self.inner(); inner.as_mut().register_interrupt(); @@ -276,32 +263,36 @@ impl<'a, U: Instance, T: TimerInstance, P1: ConfigurablePpi, P2: ConfigurablePpi } } -impl<'a, U: Instance, T: TimerInstance, P1: ConfigurablePpi, P2: ConfigurablePpi> Drop - for State<'a, U, T, P1, P2> -{ +impl<'a, U: UarteInstance, T: TimerInstance> Drop for State<'a, U, T> { fn drop(&mut self) { - self.timer.tasks_stop.write(|w| unsafe { w.bits(1) }); + let r = self.uarte.regs(); + let rt = self.timer.regs(); + + // TODO this probably deadlocks. do like Uarte instead. + + rt.tasks_stop.write(|w| unsafe { w.bits(1) }); if let RxState::Receiving = self.rx_state { - self.uarte.tasks_stoprx.write(|w| unsafe { w.bits(1) }); + r.tasks_stoprx.write(|w| unsafe { w.bits(1) }); } if let TxState::Transmitting(_) = self.tx_state { - self.uarte.tasks_stoptx.write(|w| unsafe { w.bits(1) }); + r.tasks_stoptx.write(|w| unsafe { w.bits(1) }); } if let RxState::Receiving = self.rx_state { - low_power_wait_until(|| self.uarte.events_endrx.read().bits() == 1); + low_power_wait_until(|| r.events_endrx.read().bits() == 1); } if let TxState::Transmitting(_) = self.tx_state { - low_power_wait_until(|| self.uarte.events_endtx.read().bits() == 1); + low_power_wait_until(|| r.events_endtx.read().bits() == 1); } } } -impl<'a, U: Instance, T: TimerInstance, P1: ConfigurablePpi, P2: ConfigurablePpi> PeripheralState - for State<'a, U, T, P1, P2> -{ +impl<'a, U: UarteInstance, T: TimerInstance> PeripheralState for State<'a, U, T> { type Interrupt = U::Interrupt; fn on_interrupt(&mut self) { trace!("irq: start"); + let r = self.uarte.regs(); + let rt = self.timer.regs(); + loop { match self.rx_state { RxState::Idle => { @@ -313,11 +304,11 @@ impl<'a, U: Instance, T: TimerInstance, P1: ConfigurablePpi, P2: ConfigurablePpi self.rx_state = RxState::Receiving; // Set up the DMA read - self.uarte.rxd.ptr.write(|w| + r.rxd.ptr.write(|w| // The PTR field is a full 32 bits wide and accepts the full range // of values. unsafe { w.ptr().bits(buf.as_ptr() as u32) }); - self.uarte.rxd.maxcnt.write(|w| + r.rxd.maxcnt.write(|w| // We're giving it the length of the buffer, so no danger of // accessing invalid memory. We have verified that the length of the // buffer fits in an `u8`, so the cast to `u8` is also fine. @@ -328,7 +319,7 @@ impl<'a, U: Instance, T: TimerInstance, P1: ConfigurablePpi, P2: ConfigurablePpi trace!(" irq_rx: buf {:?} {:?}", buf.as_ptr() as u32, buf.len()); // Start UARTE Receive transaction - self.uarte.tasks_startrx.write(|w| + r.tasks_startrx.write(|w| // `1` is a valid value to write to task registers. unsafe { w.bits(1) }); } @@ -336,14 +327,14 @@ impl<'a, U: Instance, T: TimerInstance, P1: ConfigurablePpi, P2: ConfigurablePpi } RxState::Receiving => { trace!(" irq_rx: in state receiving"); - if self.uarte.events_endrx.read().bits() != 0 { - self.timer.tasks_stop.write(|w| unsafe { w.bits(1) }); + if r.events_endrx.read().bits() != 0 { + rt.tasks_stop.write(|w| unsafe { w.bits(1) }); - let n: usize = self.uarte.rxd.amount.read().amount().bits() as usize; + let n: usize = r.rxd.amount.read().amount().bits() as usize; trace!(" irq_rx: endrx {:?}", n); self.rx.push(n); - self.uarte.events_endrx.reset(); + r.events_endrx.reset(); self.rx_waker.wake(); self.rx_state = RxState::Idle; @@ -364,11 +355,11 @@ impl<'a, U: Instance, T: TimerInstance, P1: ConfigurablePpi, P2: ConfigurablePpi self.tx_state = TxState::Transmitting(buf.len()); // Set up the DMA write - self.uarte.txd.ptr.write(|w| + r.txd.ptr.write(|w| // The PTR field is a full 32 bits wide and accepts the full range // of values. unsafe { w.ptr().bits(buf.as_ptr() as u32) }); - self.uarte.txd.maxcnt.write(|w| + r.txd.maxcnt.write(|w| // We're giving it the length of the buffer, so no danger of // accessing invalid memory. We have verified that the length of the // buffer fits in an `u8`, so the cast to `u8` is also fine. @@ -378,7 +369,7 @@ impl<'a, U: Instance, T: TimerInstance, P1: ConfigurablePpi, P2: ConfigurablePpi unsafe { w.maxcnt().bits(buf.len() as _) }); // Start UARTE Transmit transaction - self.uarte.tasks_starttx.write(|w| + r.tasks_starttx.write(|w| // `1` is a valid value to write to task registers. unsafe { w.bits(1) }); } @@ -386,8 +377,8 @@ impl<'a, U: Instance, T: TimerInstance, P1: ConfigurablePpi, P2: ConfigurablePpi } TxState::Transmitting(n) => { trace!(" irq_tx: in state Transmitting"); - if self.uarte.events_endtx.read().bits() != 0 { - self.uarte.events_endtx.reset(); + if r.events_endtx.read().bits() != 0 { + r.events_endtx.reset(); trace!(" irq_tx: endtx {:?}", n); self.tx.pop(n); @@ -402,37 +393,3 @@ impl<'a, U: Instance, T: TimerInstance, P1: ConfigurablePpi, P2: ConfigurablePpi trace!("irq: end"); } } - -mod sealed { - pub trait Instance {} - - impl Instance for crate::pac::UARTE0 {} - #[cfg(any(feature = "52833", feature = "52840", feature = "9160"))] - impl Instance for crate::pac::UARTE1 {} - - pub trait TimerInstance {} - impl TimerInstance for crate::pac::TIMER0 {} - impl TimerInstance for crate::pac::TIMER1 {} - impl TimerInstance for crate::pac::TIMER2 {} -} - -pub trait Instance: Deref + sealed::Instance { - type Interrupt: Interrupt; -} - -impl Instance for pac::UARTE0 { - type Interrupt = interrupt::UARTE0_UART0; -} - -#[cfg(any(feature = "52833", feature = "52840", feature = "9160"))] -impl Instance for pac::UARTE1 { - type Interrupt = interrupt::UARTE1; -} - -pub trait TimerInstance: - Deref + sealed::TimerInstance -{ -} -impl TimerInstance for crate::pac::TIMER0 {} -impl TimerInstance for crate::pac::TIMER1 {} -impl TimerInstance for crate::pac::TIMER2 {} diff --git a/embassy-nrf/src/gpio.rs b/embassy-nrf/src/gpio.rs new file mode 100644 index 00000000..dcfaa47c --- /dev/null +++ b/embassy-nrf/src/gpio.rs @@ -0,0 +1,447 @@ +use core::convert::Infallible; +use core::hint::unreachable_unchecked; +use core::marker::PhantomData; + +use embassy::util::PeripheralBorrow; +use embassy_extras::{impl_unborrow, unborrow}; +use embedded_hal::digital::v2::{InputPin, OutputPin, StatefulOutputPin}; +use gpio::pin_cnf::DRIVE_A; + +use crate::pac; +use crate::pac::p0 as gpio; +use crate::peripherals; + +/// 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, +} + +/// Pull setting for an input. +#[derive(Debug, Eq, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Pull { + None, + Up, + Down, +} + +/// GPIO input driver. +pub struct Input<'d, T: Pin> { + pub(crate) pin: T, + phantom: PhantomData<&'d mut T>, +} + +impl<'d, T: Pin> Input<'d, T> { + pub fn new(pin: impl PeripheralBorrow + 'd, pull: Pull) -> Self { + unborrow!(pin); + + 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, + phantom: PhantomData, + } + } +} + +impl<'d, T: Pin> Drop for Input<'d, T> { + fn drop(&mut self) { + self.pin.conf().reset(); + } +} + +impl<'d, T: Pin> InputPin for Input<'d, T> { + 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) + } +} + +/// Digital input or output level. +#[derive(Debug, Eq, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Level { + Low, + High, +} + +#[derive(Clone, Copy, Debug, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[repr(u8)] +pub enum OutputDrive { + /// Standard '0', standard '1' + Standard = 0, + /// High drive '0', standard '1' + HighDrive0Standard1 = 1, + /// Standard '0', high drive '1' + Standard0HighDrive1 = 2, + /// High drive '0', high 'drive '1' + HighDrive = 3, + /// Disconnect '0' standard '1' (normally used for wired-or connections) + Disconnect0Standard1 = 4, + /// Disconnect '0', high drive '1' (normally used for wired-or connections) + Disconnect0HighDrive1 = 5, + /// Standard '0'. disconnect '1' (also known as "open drain", normally used for wired-and connections) + Standard0Disconnect1 = 6, + /// High drive '0', disconnect '1' (also known as "open drain", normally used for wired-and connections) + HighDrive0Disconnect1 = 7, +} + +/// GPIO output driver. +pub struct Output<'d, T: Pin> { + pub(crate) pin: T, + phantom: PhantomData<&'d mut T>, +} + +impl<'d, T: Pin> Output<'d, T> { + pub fn new( + pin: impl PeripheralBorrow + 'd, + initial_output: Level, + drive: OutputDrive, + ) -> Self { + unborrow!(pin); + + match initial_output { + Level::High => pin.set_high(), + Level::Low => pin.set_low(), + } + + 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 + }); + + Self { + pin, + phantom: PhantomData, + } + } +} + +impl<'d, T: Pin> Drop for Output<'d, T> { + fn drop(&mut self) { + self.pin.conf().reset(); + } +} + +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(()) + } + + /// 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<'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) + } + + /// 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() + } + } + + #[inline] + 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(), + } + } + } + + #[inline] + fn conf(&self) -> &gpio::PIN_CNF { + &self.block().pin_cnf[self._pin() as usize] + } + + /// Set the output as high. + #[inline] + fn set_high(&self) { + unsafe { + self.block().outset.write(|w| w.bits(1u32 << self._pin())); + } + } + + /// Set the output as low. + #[inline] + fn set_low(&self) { + unsafe { + self.block().outclr.write(|w| w.bits(1u32 << self._pin())); + } + } + } + + pub trait OptionalPin {} +} + +pub trait Pin: sealed::Pin + Sized { + /// Number of the pin within the port (0..31) + #[inline] + fn pin(&self) -> u8 { + self._pin() + } + + /// Port of the pin + #[inline] + fn port(&self) -> Port { + match self.pin_port() / 32 { + 0 => 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 + } + + /// Convert from concrete pin type PX_XX to type erased `AnyPin`. + #[inline] + fn degrade(self) -> AnyPin { + AnyPin { + pin_port: self.pin_port(), + } + } +} + +// Type-erased GPIO pin +pub struct AnyPin { + pin_port: u8, +} + +impl AnyPin { + #[inline] + pub unsafe fn steal(pin_port: u8) -> Self { + Self { pin_port } + } +} + +impl_unborrow!(AnyPin); +impl Pin for AnyPin {} +impl sealed::Pin for AnyPin { + #[inline] + fn pin_port(&self) -> u8 { + self.pin_port + } +} + +// ==================== + +pub trait OptionalPin: sealed::OptionalPin + Sized { + type Pin: Pin; + fn pin(&self) -> Option<&Self::Pin>; + fn pin_mut(&mut self) -> Option<&mut Self::Pin>; + + #[inline] + fn psel_bits(&self) -> u32 { + self.pin().map_or(1u32 << 31, |pin| Pin::psel_bits(pin)) + } + + /// Convert from concrete pin type PX_XX to type erased `Option`. + #[inline] + fn degrade_optional(mut self) -> Option { + self.pin_mut() + .map(|pin| unsafe { core::ptr::read(pin) }.degrade()) + } +} + +impl sealed::OptionalPin for T {} +impl OptionalPin for T { + type Pin = T; + + #[inline] + fn pin(&self) -> Option<&T> { + Some(self) + } + + #[inline] + fn pin_mut(&mut self) -> Option<&mut T> { + Some(self) + } +} + +// Uninhabited enum, so it's actually impossible to create a DummyPin value. +#[doc(hidden)] +pub enum DummyPin {} +impl Pin for DummyPin {} +impl sealed::Pin for DummyPin { + #[inline] + fn pin_port(&self) -> u8 { + unreachable!() + } +} + +#[derive(Clone, Copy, Debug)] +pub struct NoPin; +impl_unborrow!(NoPin); +impl sealed::OptionalPin for NoPin {} +impl OptionalPin for NoPin { + type Pin = DummyPin; + + #[inline] + fn pin(&self) -> Option<&DummyPin> { + None + } + + #[inline] + fn pin_mut(&mut self) -> Option<&mut DummyPin> { + None + } +} + +// ==================== + +macro_rules! impl_pin { + ($type:ident, $port_num:expr, $pin_num:expr) => { + impl Pin for peripherals::$type {} + impl sealed::Pin for peripherals::$type { + #[inline] + fn pin_port(&self) -> u8 { + $port_num * 32 + $pin_num + } + } + }; +} + +impl_pin!(P0_00, 0, 0); +impl_pin!(P0_01, 0, 1); +impl_pin!(P0_02, 0, 2); +impl_pin!(P0_03, 0, 3); +impl_pin!(P0_04, 0, 4); +impl_pin!(P0_05, 0, 5); +impl_pin!(P0_06, 0, 6); +impl_pin!(P0_07, 0, 7); +impl_pin!(P0_08, 0, 8); +impl_pin!(P0_09, 0, 9); +impl_pin!(P0_10, 0, 10); +impl_pin!(P0_11, 0, 11); +impl_pin!(P0_12, 0, 12); +impl_pin!(P0_13, 0, 13); +impl_pin!(P0_14, 0, 14); +impl_pin!(P0_15, 0, 15); +impl_pin!(P0_16, 0, 16); +impl_pin!(P0_17, 0, 17); +impl_pin!(P0_18, 0, 18); +impl_pin!(P0_19, 0, 19); +impl_pin!(P0_20, 0, 20); +impl_pin!(P0_21, 0, 21); +impl_pin!(P0_22, 0, 22); +impl_pin!(P0_23, 0, 23); +impl_pin!(P0_24, 0, 24); +impl_pin!(P0_25, 0, 25); +impl_pin!(P0_26, 0, 26); +impl_pin!(P0_27, 0, 27); +impl_pin!(P0_28, 0, 28); +impl_pin!(P0_29, 0, 29); +impl_pin!(P0_30, 0, 30); +impl_pin!(P0_31, 0, 31); + +#[cfg(any(feature = "52833", feature = "52840"))] +mod _p1 { + use super::*; + impl_pin!(P1_00, 1, 0); + impl_pin!(P1_01, 1, 1); + impl_pin!(P1_02, 1, 2); + impl_pin!(P1_03, 1, 3); + impl_pin!(P1_04, 1, 4); + impl_pin!(P1_05, 1, 5); + impl_pin!(P1_06, 1, 6); + impl_pin!(P1_07, 1, 7); + impl_pin!(P1_08, 1, 8); + impl_pin!(P1_09, 1, 9); + impl_pin!(P1_10, 1, 10); + impl_pin!(P1_11, 1, 11); + impl_pin!(P1_12, 1, 12); + impl_pin!(P1_13, 1, 13); + impl_pin!(P1_14, 1, 14); + impl_pin!(P1_15, 1, 15); +} diff --git a/embassy-nrf/src/gpiote.rs b/embassy-nrf/src/gpiote.rs index 5dc80a63..412eef1b 100644 --- a/embassy-nrf/src/gpiote.rs +++ b/embassy-nrf/src/gpiote.rs @@ -1,22 +1,20 @@ +use core::convert::Infallible; use core::future::Future; -use core::mem::ManuallyDrop; -use core::ops::Deref; +use core::marker::PhantomData; use core::pin::Pin; -use core::ptr; use core::task::{Context, Poll}; use embassy::interrupt::InterruptExt; use embassy::traits::gpio::{WaitForHigh, WaitForLow}; -use embassy::util::Signal; +use embassy::util::AtomicWaker; +use embassy_extras::impl_unborrow; +use embedded_hal::digital::v2::{InputPin, StatefulOutputPin}; +use futures::future::poll_fn; -use crate::hal::gpio::{Input, Level, Output, Pin as GpioPin, Port}; -use crate::interrupt; +use crate::gpio::sealed::Pin as _; +use crate::gpio::{AnyPin, Input, Output, Pin as GpioPin, Port}; use crate::pac; -use crate::pac::generic::Reg; -use crate::pac::gpiote::_TASKS_OUT; -use crate::pac::{p0 as pac_gpio, GPIOTE}; - -#[cfg(not(feature = "51"))] -use crate::pac::gpiote::{_TASKS_CLR, _TASKS_SET}; +use crate::ppi::{Event, Task}; +use crate::{interrupt, peripherals}; pub const CHANNEL_COUNT: usize = 8; @@ -25,50 +23,9 @@ pub const PIN_COUNT: usize = 48; #[cfg(not(any(feature = "52833", feature = "52840")))] pub const PIN_COUNT: usize = 32; -pub trait ChannelID { - fn number(&self) -> usize; -} - -macro_rules! impl_channel { - ($ChX:ident, $n:expr) => { - pub struct $ChX(()); - impl $ChX { - pub fn degrade(self) -> ChAny { - ChAny($n) - } - } - - impl ChannelID for $ChX { - fn number(&self) -> usize { - $n - } - } - }; -} - -impl_channel!(Ch0, 0); -impl_channel!(Ch1, 1); -impl_channel!(Ch2, 2); -impl_channel!(Ch3, 3); -impl_channel!(Ch4, 4); -impl_channel!(Ch5, 5); -impl_channel!(Ch6, 6); -impl_channel!(Ch7, 7); - -pub struct ChAny(u8); - -impl ChannelID for ChAny { - fn number(&self) -> usize { - self.0 as usize - } -} - -#[derive(Clone, Copy)] -pub struct Gpiote(()); - -const NEW_SIGNAL: Signal<()> = Signal::new(); -static CHANNEL_SIGNALS: [Signal<()>; CHANNEL_COUNT] = [NEW_SIGNAL; CHANNEL_COUNT]; -static PORT_SIGNALS: [Signal<()>; PIN_COUNT] = [NEW_SIGNAL; PIN_COUNT]; +const NEW_AW: AtomicWaker = AtomicWaker::new(); +static CHANNEL_WAKERS: [AtomicWaker; CHANNEL_COUNT] = [NEW_AW; CHANNEL_COUNT]; +static PORT_WAKERS: [AtomicWaker; PIN_COUNT] = [NEW_AW; PIN_COUNT]; pub enum InputChannelPolarity { None, @@ -84,286 +41,64 @@ pub enum OutputChannelPolarity { Toggle, } -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum NewChannelError { - NoFreeChannels, +/// Token indicating GPIOTE has been correctly initialized. +/// +/// This is not an owned singleton, it is Copy. Drivers that make use of GPIOTE require it. +#[derive(Clone, Copy)] +pub struct Initialized { + _private: (), } -pub struct Channels { - pub ch0: Ch0, - pub ch1: Ch1, - pub ch2: Ch2, - pub ch3: Ch3, - pub ch4: Ch4, - pub ch5: Ch5, - pub ch6: Ch6, - pub ch7: Ch7, +pub fn initialize(_gpiote: peripherals::GPIOTE, irq: interrupt::GPIOTE) -> Initialized { + #[cfg(any(feature = "52833", feature = "52840"))] + let ports = unsafe { &[&*pac::P0::ptr(), &*pac::P1::ptr()] }; + #[cfg(not(any(feature = "52833", feature = "52840")))] + let ports = unsafe { &[&*pac::P0::ptr()] }; + + for &p in ports { + // Enable latched detection + p.detectmode.write(|w| w.detectmode().ldetect()); + // Clear latch + p.latch.write(|w| unsafe { w.bits(0xFFFFFFFF) }) + } + + // Enable interrupts + let g = unsafe { &*pac::GPIOTE::ptr() }; + g.events_port.write(|w| w); + g.intenset.write(|w| w.port().set()); + irq.set_handler(on_irq); + irq.unpend(); + irq.enable(); + + Initialized { _private: () } } -impl Gpiote { - pub fn new(gpiote: GPIOTE, irq: interrupt::GPIOTE) -> (Self, Channels) { +unsafe fn on_irq(_ctx: *mut ()) { + let g = &*pac::GPIOTE::ptr(); + + for i in 0..CHANNEL_COUNT { + if g.events_in[i].read().bits() != 0 { + g.intenclr.write(|w| unsafe { w.bits(1 << i) }); + CHANNEL_WAKERS[i].wake(); + } + } + + if g.events_port.read().bits() != 0 { + g.events_port.write(|w| w); + #[cfg(any(feature = "52833", feature = "52840"))] - let ports = unsafe { &[&*pac::P0::ptr(), &*pac::P1::ptr()] }; + let ports = &[&*pac::P0::ptr(), &*pac::P1::ptr()]; #[cfg(not(any(feature = "52833", feature = "52840")))] - let ports = unsafe { &[&*pac::P0::ptr()] }; + let ports = &[&*pac::P0::ptr()]; - for &p in ports { - // Enable latched detection - p.detectmode.write(|w| w.detectmode().ldetect()); - // Clear latch - p.latch.write(|w| unsafe { w.bits(0xFFFFFFFF) }) - } - - // Enable interrupts - gpiote.events_port.write(|w| w); - gpiote.intenset.write(|w| w.port().set()); - irq.set_handler(Self::on_irq); - irq.unpend(); - irq.enable(); - - ( - Self(()), - Channels { - ch0: Ch0(()), - ch1: Ch1(()), - ch2: Ch2(()), - ch3: Ch3(()), - ch4: Ch4(()), - ch5: Ch5(()), - ch6: Ch6(()), - ch7: Ch7(()), - }, - ) - } - - unsafe fn on_irq(_ctx: *mut ()) { - let g = &*GPIOTE::ptr(); - - for (event_in, signal) in g.events_in.iter().zip(CHANNEL_SIGNALS.iter()) { - if event_in.read().bits() != 0 { - event_in.write(|w| w); - signal.signal(()); + for (port, &p) in ports.iter().enumerate() { + let bits = p.latch.read().bits(); + for pin in BitIter(bits) { + p.pin_cnf[pin as usize].modify(|_, w| w.sense().disabled()); + PORT_WAKERS[port * 32 + pin as usize].wake(); } + p.latch.write(|w| w.bits(bits)); } - - if g.events_port.read().bits() != 0 { - g.events_port.write(|w| w); - - #[cfg(any(feature = "52833", feature = "52840"))] - let ports = &[&*pac::P0::ptr(), &*pac::P1::ptr()]; - #[cfg(not(any(feature = "52833", feature = "52840")))] - let ports = &[&*pac::P0::ptr()]; - - let mut work = true; - while work { - work = false; - for (port, &p) in ports.iter().enumerate() { - for pin in BitIter(p.latch.read().bits()) { - work = true; - p.pin_cnf[pin as usize].modify(|_, w| w.sense().disabled()); - p.latch.write(|w| w.bits(1 << pin)); - PORT_SIGNALS[port * 32 + pin as usize].signal(()); - } - } - } - } - } -} - -fn pin_num(pin: &GpioPin) -> usize { - let port = match pin.port() { - Port::Port0 => 0, - #[cfg(any(feature = "52833", feature = "52840"))] - Port::Port1 => 32, - }; - - port + pin.pin() as usize -} - -fn pin_block(pin: &GpioPin) -> &pac_gpio::RegisterBlock { - let ptr = match pin.port() { - Port::Port0 => pac::P0::ptr(), - #[cfg(any(feature = "52833", feature = "52840"))] - Port::Port1 => pac::P1::ptr(), - }; - - unsafe { &*ptr } -} - -fn pin_conf(pin: &GpioPin) -> &pac_gpio::PIN_CNF { - &pin_block(pin).pin_cnf[pin.pin() as usize] -} - -pub struct InputChannel { - ch: C, - pin: GpioPin>, -} - -impl Drop for InputChannel { - fn drop(&mut self) { - let g = unsafe { &*GPIOTE::ptr() }; - let index = self.ch.number(); - g.config[index].write(|w| w.mode().disabled()); - g.intenclr.write(|w| unsafe { w.bits(1 << index) }); - } -} - -impl InputChannel { - pub fn new( - _gpiote: Gpiote, - ch: C, - pin: GpioPin>, - polarity: InputChannelPolarity, - ) -> Self { - let g = unsafe { &*GPIOTE::ptr() }; - let index = ch.number(); - - g.config[index].write(|w| { - match polarity { - InputChannelPolarity::HiToLo => w.mode().event().polarity().hi_to_lo(), - InputChannelPolarity::LoToHi => w.mode().event().polarity().lo_to_hi(), - InputChannelPolarity::None => w.mode().event().polarity().none(), - InputChannelPolarity::Toggle => w.mode().event().polarity().toggle(), - }; - #[cfg(any(feature = "52833", feature = "52840"))] - w.port().bit(match pin.port() { - Port::Port0 => false, - Port::Port1 => true, - }); - unsafe { w.psel().bits(pin.pin()) } - }); - - CHANNEL_SIGNALS[index].reset(); - - // Enable interrupt - g.intenset.write(|w| unsafe { w.bits(1 << index) }); - - InputChannel { ch, pin } - } - - pub fn free(self) -> (C, GpioPin>) { - let m = ManuallyDrop::new(self); - let ch = unsafe { ptr::read(&m.ch) }; - let pin = unsafe { ptr::read(&m.pin) }; - (ch, pin) - } - - pub async fn wait(&self) { - let index = self.ch.number(); - CHANNEL_SIGNALS[index].wait().await; - } - - pub fn pin(&self) -> &GpioPin> { - &self.pin - } -} - -pub struct OutputChannel { - ch: C, - pin: GpioPin>, -} - -impl Drop for OutputChannel { - fn drop(&mut self) { - let g = unsafe { &*GPIOTE::ptr() }; - let index = self.ch.number(); - g.config[index].write(|w| w.mode().disabled()); - g.intenclr.write(|w| unsafe { w.bits(1 << index) }); - } -} - -impl OutputChannel { - pub fn new( - _gpiote: Gpiote, - ch: C, - pin: GpioPin>, - level: Level, - polarity: OutputChannelPolarity, - ) -> Self { - let g = unsafe { &*GPIOTE::ptr() }; - let index = ch.number(); - - g.config[index].write(|w| { - w.mode().task(); - match level { - Level::High => w.outinit().high(), - Level::Low => w.outinit().low(), - }; - match polarity { - OutputChannelPolarity::Set => w.polarity().lo_to_hi(), - OutputChannelPolarity::Clear => w.polarity().hi_to_lo(), - OutputChannelPolarity::Toggle => w.polarity().toggle(), - }; - #[cfg(any(feature = "52833", feature = "52840"))] - w.port().bit(match pin.port() { - Port::Port0 => false, - Port::Port1 => true, - }); - unsafe { w.psel().bits(pin.pin()) } - }); - - // Enable interrupt - g.intenset.write(|w| unsafe { w.bits(1 << index) }); - - OutputChannel { ch, pin } - } - - pub fn free(self) -> (C, GpioPin>) { - let m = ManuallyDrop::new(self); - let ch = unsafe { ptr::read(&m.ch) }; - let pin = unsafe { ptr::read(&m.pin) }; - (ch, pin) - } - - /// Triggers `task out` (as configured with task_out_polarity, defaults to Toggle). - pub fn out(&self) { - let g = unsafe { &*GPIOTE::ptr() }; - let index = self.ch.number(); - - g.tasks_out[index].write(|w| unsafe { w.bits(1) }); - } - /// Triggers `task set` (set associated pin high). - #[cfg(not(feature = "51"))] - pub fn set(&self) { - let g = unsafe { &*GPIOTE::ptr() }; - let index = self.ch.number(); - - g.tasks_set[index].write(|w| unsafe { w.bits(1) }); - } - /// Triggers `task clear` (set associated pin low). - #[cfg(not(feature = "51"))] - pub fn clear(&self) { - let g = unsafe { &*GPIOTE::ptr() }; - let index = self.ch.number(); - - g.tasks_clr[index].write(|w| unsafe { w.bits(1) }); - } - - /// Returns reference to task_out endpoint for PPI. - pub fn task_out(&self) -> &Reg { - let g = unsafe { &*GPIOTE::ptr() }; - let index = self.ch.number(); - - &g.tasks_out[index] - } - - /// Returns reference to task_clr endpoint for PPI. - #[cfg(not(feature = "51"))] - pub fn task_clr(&self) -> &Reg { - let g = unsafe { &*GPIOTE::ptr() }; - let index = self.ch.number(); - - &g.tasks_clr[index] - } - - /// Returns reference to task_set endpoint for PPI. - #[cfg(not(feature = "51"))] - pub fn task_set(&self) -> &Reg { - let g = unsafe { &*GPIOTE::ptr() }; - let index = self.ch.number(); - - &g.tasks_set[index] } } @@ -383,72 +118,296 @@ impl Iterator for BitIter { } } -pub struct GpiotePin { - pin: GpioPin>, +/// GPIOTE channel driver in input mode +pub struct InputChannel<'d, C: Channel, T: GpioPin> { + ch: C, + pin: Input<'d, T>, } -impl Unpin for GpiotePin {} +impl<'d, C: Channel, T: GpioPin> Drop for InputChannel<'d, C, T> { + fn drop(&mut self) { + let g = unsafe { &*pac::GPIOTE::ptr() }; + let num = self.ch.number(); + g.config[num].write(|w| w.mode().disabled()); + g.intenclr.write(|w| unsafe { w.bits(1 << num) }); + } +} -impl GpiotePin { - pub fn new(_gpiote: Gpiote, pin: GpioPin>) -> Self { +impl<'d, C: Channel, T: GpioPin> InputChannel<'d, C, T> { + pub fn new( + _init: Initialized, + ch: C, + pin: Input<'d, T>, + polarity: InputChannelPolarity, + ) -> Self { + let g = unsafe { &*pac::GPIOTE::ptr() }; + let num = ch.number(); + + g.config[num].write(|w| { + match polarity { + InputChannelPolarity::HiToLo => w.mode().event().polarity().hi_to_lo(), + InputChannelPolarity::LoToHi => w.mode().event().polarity().lo_to_hi(), + InputChannelPolarity::None => w.mode().event().polarity().none(), + InputChannelPolarity::Toggle => w.mode().event().polarity().toggle(), + }; + #[cfg(any(feature = "52833", feature = "52840"))] + w.port().bit(match pin.pin.port() { + Port::Port0 => false, + Port::Port1 => true, + }); + unsafe { w.psel().bits(pin.pin.pin()) } + }); + + g.events_in[num].reset(); + + InputChannel { ch, pin } + } + + pub async fn wait(&self) { + let g = unsafe { &*pac::GPIOTE::ptr() }; + let num = self.ch.number(); + + // Enable interrupt + g.events_in[num].reset(); + g.intenset.write(|w| unsafe { w.bits(1 << num) }); + + poll_fn(|cx| { + CHANNEL_WAKERS[num].register(cx.waker()); + + if g.events_in[num].read().bits() != 0 { + Poll::Ready(()) + } else { + Poll::Pending + } + }) + .await; + } + + /// Returns the IN event, for use with PPI. + pub fn event_in(&self) -> Event { + let g = unsafe { &*pac::GPIOTE::ptr() }; + Event::from_reg(&g.events_in[self.ch.number()]) + } +} + +impl<'d, C: Channel, T: GpioPin> InputPin for InputChannel<'d, C, T> { + type Error = Infallible; + + fn is_high(&self) -> Result { + self.pin.is_high() + } + + fn is_low(&self) -> Result { + self.pin.is_low() + } +} + +/// GPIOTE channel driver in output mode +pub struct OutputChannel<'d, C: Channel, T: GpioPin> { + ch: C, + _pin: Output<'d, T>, +} + +impl<'d, C: Channel, T: GpioPin> Drop for OutputChannel<'d, C, T> { + fn drop(&mut self) { + let g = unsafe { &*pac::GPIOTE::ptr() }; + let num = self.ch.number(); + g.config[num].write(|w| w.mode().disabled()); + g.intenclr.write(|w| unsafe { w.bits(1 << num) }); + } +} + +impl<'d, C: Channel, T: GpioPin> OutputChannel<'d, C, T> { + pub fn new( + _init: Initialized, + ch: C, + pin: Output<'d, T>, + polarity: OutputChannelPolarity, + ) -> Self { + let g = unsafe { &*pac::GPIOTE::ptr() }; + let num = ch.number(); + + g.config[num].write(|w| { + w.mode().task(); + match pin.is_set_high().unwrap() { + true => w.outinit().high(), + false => w.outinit().low(), + }; + match polarity { + OutputChannelPolarity::Set => w.polarity().lo_to_hi(), + OutputChannelPolarity::Clear => w.polarity().hi_to_lo(), + OutputChannelPolarity::Toggle => w.polarity().toggle(), + }; + #[cfg(any(feature = "52833", feature = "52840"))] + w.port().bit(match pin.pin.port() { + Port::Port0 => false, + Port::Port1 => true, + }); + unsafe { w.psel().bits(pin.pin.pin()) } + }); + + OutputChannel { ch, _pin: pin } + } + + /// Triggers `task out` (as configured with task_out_polarity, defaults to Toggle). + pub fn out(&self) { + let g = unsafe { &*pac::GPIOTE::ptr() }; + g.tasks_out[self.ch.number()].write(|w| unsafe { w.bits(1) }); + } + + /// Triggers `task set` (set associated pin high). + #[cfg(not(feature = "51"))] + pub fn set(&self) { + let g = unsafe { &*pac::GPIOTE::ptr() }; + g.tasks_set[self.ch.number()].write(|w| unsafe { w.bits(1) }); + } + + /// Triggers `task clear` (set associated pin low). + #[cfg(not(feature = "51"))] + pub fn clear(&self) { + let g = unsafe { &*pac::GPIOTE::ptr() }; + g.tasks_clr[self.ch.number()].write(|w| unsafe { w.bits(1) }); + } + + /// Returns the OUT task, for use with PPI. + pub fn task_out(&self) -> Task { + let g = unsafe { &*pac::GPIOTE::ptr() }; + Task::from_reg(&g.tasks_out[self.ch.number()]) + } + + /// Returns the CLR task, for use with PPI. + #[cfg(not(feature = "51"))] + pub fn task_clr(&self) -> Task { + let g = unsafe { &*pac::GPIOTE::ptr() }; + Task::from_reg(&g.tasks_clr[self.ch.number()]) + } + + /// Returns the SET task, for use with PPI. + #[cfg(not(feature = "51"))] + pub fn task_set(&self) -> Task { + let g = unsafe { &*pac::GPIOTE::ptr() }; + Task::from_reg(&g.tasks_set[self.ch.number()]) + } +} + +/// GPIOTE port input driver +pub struct PortInput<'d, T: GpioPin> { + pin: Input<'d, T>, +} + +impl<'d, T: GpioPin> Unpin for PortInput<'d, T> {} + +impl<'d, T: GpioPin> PortInput<'d, T> { + pub fn new(_init: Initialized, pin: Input<'d, T>) -> Self { Self { pin } } } -impl WaitForHigh for GpiotePin { - type Future<'a> = PortInputFuture<'a, T>; +impl<'d, T: GpioPin> InputPin for PortInput<'d, T> { + type Error = Infallible; + + fn is_high(&self) -> Result { + self.pin.is_high() + } + + fn is_low(&self) -> Result { + self.pin.is_low() + } +} + +impl<'d, T: GpioPin> WaitForHigh for PortInput<'d, T> { + type Future<'a> = PortInputFuture<'a>; fn wait_for_high<'a>(self: Pin<&'a mut Self>) -> Self::Future<'a> { + self.pin.pin.conf().modify(|_, w| w.sense().high()); + PortInputFuture { - pin: &self.get_mut().pin, - polarity: PortInputPolarity::High, + pin_port: self.pin.pin.pin_port(), + phantom: PhantomData, } } } -impl WaitForLow for GpiotePin { - type Future<'a> = PortInputFuture<'a, T>; +impl<'d, T: GpioPin> WaitForLow for PortInput<'d, T> { + type Future<'a> = PortInputFuture<'a>; fn wait_for_low<'a>(self: Pin<&'a mut Self>) -> Self::Future<'a> { + self.pin.pin.conf().modify(|_, w| w.sense().low()); + PortInputFuture { - pin: &self.get_mut().pin, - polarity: PortInputPolarity::Low, + pin_port: self.pin.pin.pin_port(), + phantom: PhantomData, } } } -impl Deref for GpiotePin { - type Target = GpioPin>; - fn deref(&self) -> &Self::Target { - &self.pin - } +pub struct PortInputFuture<'a> { + pin_port: u8, + phantom: PhantomData<&'a mut AnyPin>, } -enum PortInputPolarity { - High, - Low, -} - -pub struct PortInputFuture<'a, T> { - pin: &'a GpioPin>, - polarity: PortInputPolarity, -} - -impl<'a, T> Drop for PortInputFuture<'a, T> { +impl<'a> Drop for PortInputFuture<'a> { fn drop(&mut self) { - pin_conf(&self.pin).modify(|_, w| w.sense().disabled()); - PORT_SIGNALS[pin_num(&self.pin)].reset(); + let pin = unsafe { AnyPin::steal(self.pin_port) }; + pin.conf().modify(|_, w| w.sense().disabled()); } } -impl<'a, T> Future for PortInputFuture<'a, T> { +impl<'a> Future for PortInputFuture<'a> { type Output = (); fn poll(self: core::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - pin_conf(&self.pin).modify(|_, w| match self.polarity { - PortInputPolarity::Low => w.sense().low(), - PortInputPolarity::High => w.sense().high(), - }); - PORT_SIGNALS[pin_num(&self.pin)].poll_wait(cx) + PORT_WAKERS[self.pin_port as usize].register(cx.waker()); + + let pin = unsafe { AnyPin::steal(self.pin_port) }; + if pin.conf().read().sense().is_disabled() { + Poll::Ready(()) + } else { + Poll::Pending + } } } + +mod sealed { + pub trait Channel {} +} + +pub trait Channel: sealed::Channel + Sized { + fn number(&self) -> usize; + fn degrade(self) -> AnyChannel { + AnyChannel { + number: self.number() as u8, + } + } +} + +pub struct AnyChannel { + number: u8, +} +impl_unborrow!(AnyChannel); +impl sealed::Channel for AnyChannel {} +impl Channel for AnyChannel { + fn number(&self) -> usize { + self.number as usize + } +} + +macro_rules! impl_channel { + ($type:ident, $number:expr) => { + impl sealed::Channel for peripherals::$type {} + impl Channel for peripherals::$type { + fn number(&self) -> usize { + $number as usize + } + } + }; +} + +impl_channel!(GPIOTE_CH0, 0); +impl_channel!(GPIOTE_CH1, 1); +impl_channel!(GPIOTE_CH2, 2); +impl_channel!(GPIOTE_CH3, 3); +impl_channel!(GPIOTE_CH4, 4); +impl_channel!(GPIOTE_CH5, 5); +impl_channel!(GPIOTE_CH6, 6); +impl_channel!(GPIOTE_CH7, 7); diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index 2c72b912..33d764fb 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -40,17 +40,6 @@ pub use nrf52833_pac as pac; #[cfg(feature = "52840")] pub use nrf52840_pac as pac; -#[cfg(feature = "52810")] -pub use nrf52810_hal as hal; -#[cfg(feature = "52811")] -pub use nrf52811_hal as hal; -#[cfg(feature = "52832")] -pub use nrf52832_hal as hal; -#[cfg(feature = "52833")] -pub use nrf52833_hal as hal; -#[cfg(feature = "52840")] -pub use nrf52840_hal as hal; - /// Length of Nordic EasyDMA differs for MCUs #[cfg(any( feature = "52810", @@ -94,10 +83,181 @@ 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 ppi; #[cfg(feature = "52840")] pub mod qspi; pub mod rtc; +pub mod saadc; pub mod spim; +pub mod system; +pub mod timer; pub mod uarte; + +embassy_extras::peripherals! { + // RTC + RTC0, + RTC1, + #[cfg(any(feature = "52832", feature = "52833", feature = "52840"))] + RTC2, + + // QSPI + #[cfg(feature = "52840")] + QSPI, + + // UARTE + UARTE0, + #[cfg(any(feature = "52833", feature = "52840", feature = "9160"))] + 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, + #[cfg(any(feature = "52832", feature = "52833", feature = "52840"))] + SPIM1, + #[cfg(any(feature = "52832", feature = "52833", feature = "52840"))] + SPIM2, + #[cfg(any(feature = "52833", feature = "52840"))] + SPIM3, + + // SAADC + SAADC, + + // TIMER + TIMER0, + TIMER1, + TIMER2, + #[cfg(any(feature = "52832", feature = "52833", feature = "52840"))] + TIMER3, + #[cfg(any(feature = "52832", feature = "52833", feature = "52840"))] + TIMER4, + + // GPIOTE + GPIOTE, + GPIOTE_CH0, + GPIOTE_CH1, + GPIOTE_CH2, + GPIOTE_CH3, + GPIOTE_CH4, + GPIOTE_CH5, + GPIOTE_CH6, + GPIOTE_CH7, + + // PPI + PPI_CH0, + PPI_CH1, + PPI_CH2, + PPI_CH3, + PPI_CH4, + PPI_CH5, + PPI_CH6, + PPI_CH7, + PPI_CH8, + PPI_CH9, + PPI_CH10, + PPI_CH11, + PPI_CH12, + PPI_CH13, + PPI_CH14, + PPI_CH15, + #[cfg(not(feature = "51"))] + PPI_CH16, + #[cfg(not(feature = "51"))] + PPI_CH17, + #[cfg(not(feature = "51"))] + PPI_CH18, + #[cfg(not(feature = "51"))] + PPI_CH19, + PPI_CH20, + PPI_CH21, + PPI_CH22, + PPI_CH23, + PPI_CH24, + PPI_CH25, + PPI_CH26, + PPI_CH27, + PPI_CH28, + PPI_CH29, + PPI_CH30, + PPI_CH31, + + PPI_GROUP0, + PPI_GROUP1, + PPI_GROUP2, + PPI_GROUP3, + #[cfg(not(feature = "51"))] + PPI_GROUP4, + #[cfg(not(feature = "51"))] + PPI_GROUP5, + + // GPIO port 0 + P0_00, + P0_01, + P0_02, + P0_03, + P0_04, + P0_05, + P0_06, + P0_07, + P0_08, + P0_09, + P0_10, + P0_11, + P0_12, + P0_13, + P0_14, + P0_15, + P0_16, + P0_17, + P0_18, + P0_19, + P0_20, + P0_21, + P0_22, + P0_23, + P0_24, + P0_25, + P0_26, + P0_27, + P0_28, + P0_29, + P0_30, + P0_31, + + // GPIO port 1 + #[cfg(any(feature = "52833", feature = "52840"))] + P1_00, + #[cfg(any(feature = "52833", feature = "52840"))] + P1_01, + #[cfg(any(feature = "52833", feature = "52840"))] + P1_02, + #[cfg(any(feature = "52833", feature = "52840"))] + P1_03, + #[cfg(any(feature = "52833", feature = "52840"))] + P1_04, + #[cfg(any(feature = "52833", feature = "52840"))] + P1_05, + #[cfg(any(feature = "52833", feature = "52840"))] + P1_06, + #[cfg(any(feature = "52833", feature = "52840"))] + P1_07, + #[cfg(any(feature = "52833", feature = "52840"))] + P1_08, + #[cfg(any(feature = "52833", feature = "52840"))] + P1_09, + #[cfg(any(feature = "52833", feature = "52840"))] + P1_10, + #[cfg(any(feature = "52833", feature = "52840"))] + P1_11, + #[cfg(any(feature = "52833", feature = "52840"))] + P1_12, + #[cfg(any(feature = "52833", feature = "52840"))] + P1_13, + #[cfg(any(feature = "52833", feature = "52840"))] + P1_14, + #[cfg(any(feature = "52833", feature = "52840"))] + P1_15, +} diff --git a/embassy-nrf/src/ppi.rs b/embassy-nrf/src/ppi.rs new file mode 100644 index 00000000..6487267d --- /dev/null +++ b/embassy-nrf/src/ppi.rs @@ -0,0 +1,255 @@ +//! HAL interface for the PPI peripheral. +//! +//! The Programmable Peripheral Interconnect interface allows for an autonomous interoperability +//! between peripherals through their events and tasks. There are fixed PPI channels and fully +//! configurable ones, fixed channels can only connect specific events to specific tasks. For fully +//! configurable channels, it is possible to choose, via software, the event and the task that it +//! will triggered by the event. +//! +//! On nRF52 devices, there is also a fork task endpoint, where the user can configure one more task +//! to be triggered by the same event, even fixed PPI channels have a configurable fork task. + +use core::marker::PhantomData; +use core::ptr::NonNull; +use embassy::util::PeripheralBorrow; +use embassy_extras::{impl_unborrow, unborrow}; + +use crate::{pac, peripherals}; + +// ====================== +// driver + +pub struct Ppi<'d, C: Channel> { + ch: C, + phantom: PhantomData<&'d mut C>, +} + +impl<'d, C: Channel> Ppi<'d, C> { + pub fn new(ch: impl PeripheralBorrow + 'd) -> Self { + unborrow!(ch); + let mut this = Self { + ch, + phantom: PhantomData, + }; + #[cfg(not(feature = "51"))] + this.clear_fork_task(); + this + } + + /// Enables the channel. + pub fn enable(&mut self) { + let r = unsafe { &*pac::PPI::ptr() }; + r.chenset + .write(|w| unsafe { w.bits(1 << self.ch.number()) }); + } + + /// Disables the channel. + pub fn disable(&mut self) { + let r = unsafe { &*pac::PPI::ptr() }; + r.chenclr + .write(|w| unsafe { w.bits(1 << self.ch.number()) }); + } + + #[cfg(not(feature = "51"))] + /// Sets the fork task that must be triggered when the configured event occurs. The user must + /// provide a reference to the task. + pub fn set_fork_task(&mut self, task: Task) { + let r = unsafe { &*pac::PPI::ptr() }; + r.fork[self.ch.number()] + .tep + .write(|w| unsafe { w.bits(task.0.as_ptr() as u32) }) + } + + #[cfg(not(feature = "51"))] + /// Clear the fork task endpoint. Previously set task will no longer be triggered. + pub fn clear_fork_task(&mut self) { + let r = unsafe { &*pac::PPI::ptr() }; + r.fork[self.ch.number()].tep.write(|w| unsafe { w.bits(0) }) + } +} + +impl<'d, C: Channel> Drop for Ppi<'d, C> { + fn drop(&mut self) { + self.disable() + } +} + +impl<'d, C: ConfigurableChannel> Ppi<'d, C> { + /// Sets the task to be triggered when the configured event occurs. + pub fn set_task(&mut self, task: Task) { + let r = unsafe { &*pac::PPI::ptr() }; + r.ch[self.ch.number()] + .tep + .write(|w| unsafe { w.bits(task.0.as_ptr() as u32) }) + } + + /// Sets the event that will trigger the chosen task(s). + pub fn set_event(&mut self, event: Event) { + let r = unsafe { &*pac::PPI::ptr() }; + r.ch[self.ch.number()] + .eep + .write(|w| unsafe { w.bits(event.0.as_ptr() as u32) }) + } +} + +// ====================== +// traits + +pub struct Task(pub NonNull<()>); +impl Task { + pub(crate) fn from_reg(reg: &T) -> Self { + Self(unsafe { NonNull::new_unchecked(reg as *const _ as *mut ()) }) + } +} + +pub struct Event(pub NonNull<()>); +impl Event { + pub(crate) fn from_reg(reg: &T) -> Self { + Self(unsafe { NonNull::new_unchecked(reg as *const _ as *mut ()) }) + } +} + +mod sealed { + pub trait ConfigurableChannel {} + pub trait Channel {} + pub trait Group {} +} + +pub trait Channel: sealed::Channel + Sized { + fn number(&self) -> usize; + fn degrade(self) -> AnyChannel { + AnyChannel { + number: self.number() as u8, + } + } +} +pub trait ConfigurableChannel: Channel + sealed::ConfigurableChannel { + fn degrade_configurable(self) -> AnyConfigurableChannel { + AnyConfigurableChannel { + number: self.number() as u8, + } + } +} + +pub trait Group: sealed::Group + Sized { + fn number(&self) -> usize; + fn degrade(self) -> AnyGroup { + AnyGroup { + number: self.number() as u8, + } + } +} + +// ====================== +// channels + +pub struct AnyChannel { + number: u8, +} +impl_unborrow!(AnyChannel); +impl sealed::Channel for AnyChannel {} +impl Channel for AnyChannel { + fn number(&self) -> usize { + self.number as usize + } +} + +pub struct AnyConfigurableChannel { + number: u8, +} +impl_unborrow!(AnyConfigurableChannel); +impl sealed::Channel for AnyConfigurableChannel {} +impl sealed::ConfigurableChannel for AnyConfigurableChannel {} +impl ConfigurableChannel for AnyConfigurableChannel {} +impl Channel for AnyConfigurableChannel { + fn number(&self) -> usize { + self.number as usize + } +} + +macro_rules! impl_channel { + ($type:ident, $number:expr, configurable) => { + impl_channel!($type, $number); + impl sealed::ConfigurableChannel for peripherals::$type {} + impl ConfigurableChannel for peripherals::$type {} + }; + ($type:ident, $number:expr) => { + impl sealed::Channel for peripherals::$type {} + impl Channel for peripherals::$type { + fn number(&self) -> usize { + $number + } + } + }; +} + +impl_channel!(PPI_CH0, 0, configurable); +impl_channel!(PPI_CH1, 1, configurable); +impl_channel!(PPI_CH2, 2, configurable); +impl_channel!(PPI_CH3, 3, configurable); +impl_channel!(PPI_CH4, 4, configurable); +impl_channel!(PPI_CH5, 5, configurable); +impl_channel!(PPI_CH6, 6, configurable); +impl_channel!(PPI_CH7, 7, configurable); +impl_channel!(PPI_CH8, 8, configurable); +impl_channel!(PPI_CH9, 9, configurable); +impl_channel!(PPI_CH10, 10, configurable); +impl_channel!(PPI_CH11, 11, configurable); +impl_channel!(PPI_CH12, 12, configurable); +impl_channel!(PPI_CH13, 13, configurable); +impl_channel!(PPI_CH14, 14, configurable); +impl_channel!(PPI_CH15, 15, configurable); +#[cfg(not(feature = "51"))] +impl_channel!(PPI_CH16, 16, configurable); +#[cfg(not(feature = "51"))] +impl_channel!(PPI_CH17, 17, configurable); +#[cfg(not(feature = "51"))] +impl_channel!(PPI_CH18, 18, configurable); +#[cfg(not(feature = "51"))] +impl_channel!(PPI_CH19, 19, configurable); +impl_channel!(PPI_CH20, 20); +impl_channel!(PPI_CH21, 21); +impl_channel!(PPI_CH22, 22); +impl_channel!(PPI_CH23, 23); +impl_channel!(PPI_CH24, 24); +impl_channel!(PPI_CH25, 25); +impl_channel!(PPI_CH26, 26); +impl_channel!(PPI_CH27, 27); +impl_channel!(PPI_CH28, 28); +impl_channel!(PPI_CH29, 29); +impl_channel!(PPI_CH30, 30); +impl_channel!(PPI_CH31, 31); + +// ====================== +// groups + +pub struct AnyGroup { + number: u8, +} +impl_unborrow!(AnyGroup); +impl sealed::Group for AnyGroup {} +impl Group for AnyGroup { + fn number(&self) -> usize { + self.number as usize + } +} + +macro_rules! impl_group { + ($type:ident, $number:expr) => { + impl sealed::Group for peripherals::$type {} + impl Group for peripherals::$type { + fn number(&self) -> usize { + $number + } + } + }; +} + +impl_group!(PPI_GROUP0, 0); +impl_group!(PPI_GROUP1, 1); +impl_group!(PPI_GROUP2, 2); +impl_group!(PPI_GROUP3, 3); +#[cfg(not(feature = "51"))] +impl_group!(PPI_GROUP4, 4); +#[cfg(not(feature = "51"))] +impl_group!(PPI_GROUP5, 5); diff --git a/embassy-nrf/src/qspi.rs b/embassy-nrf/src/qspi.rs index 026660cb..033c9e01 100644 --- a/embassy-nrf/src/qspi.rs +++ b/embassy-nrf/src/qspi.rs @@ -1,13 +1,16 @@ use core::future::Future; +use core::marker::PhantomData; use core::pin::Pin; use core::task::Poll; +use embassy::interrupt::Interrupt; use embassy_extras::peripheral::{PeripheralMutex, PeripheralState}; +use embassy_extras::unborrow; use crate::fmt::{assert, assert_eq, *}; -use crate::hal::gpio::{Output, Pin as GpioPin, PushPull}; +use crate::gpio::Pin as GpioPin; use crate::interrupt::{self}; -use crate::pac::QSPI; +use crate::{pac, peripherals}; pub use crate::pac::qspi::ifconfig0::ADDRMODE_A as AddressMode; pub use crate::pac::qspi::ifconfig0::PPSIZE_A as WritePageSize; @@ -25,25 +28,16 @@ pub use crate::pac::qspi::ifconfig0::WRITEOC_A as WriteOpcode; // - set gpio in high drive use embassy::traits::flash::{Error, Flash}; -use embassy::util::{DropBomb, WakerRegistration}; +use embassy::util::{wake_on_interrupt, DropBomb, PeripheralBorrow, WakerRegistration}; use futures::future::poll_fn; -pub struct Pins { - pub sck: GpioPin>, - pub csn: GpioPin>, - pub io0: GpioPin>, - pub io1: GpioPin>, - pub io2: Option>>, - pub io3: Option>>, -} - pub struct DeepPowerDownConfig { pub enter_time: u16, pub exit_time: u16, } +#[non_exhaustive] pub struct Config { - pub pins: Pins, pub xip_offset: u32, pub read_opcode: ReadOpcode, pub write_opcode: WriteOpcode, @@ -51,55 +45,59 @@ pub struct Config { pub deep_power_down: Option, } -struct State { - inner: QSPI, - waker: WakerRegistration, +impl Default for Config { + fn default() -> Self { + Self { + read_opcode: ReadOpcode::READ4IO, + write_opcode: WriteOpcode::PP4IO, + xip_offset: 0, + write_page_size: WritePageSize::_256BYTES, + deep_power_down: None, + } + } } -pub struct Qspi { - inner: PeripheralMutex, +pub struct Qspi<'d, T: Instance> { + peri: T, + irq: T::Interrupt, + phantom: PhantomData<&'d mut T>, } -impl Qspi { - pub fn new(qspi: QSPI, irq: interrupt::QSPI, config: Config) -> Self { - qspi.psel.sck.write(|w| { - let pin = &config.pins.sck; - unsafe { w.bits(pin.psel_bits()) }; - w.connect().connected() - }); - qspi.psel.csn.write(|w| { - let pin = &config.pins.csn; - unsafe { w.bits(pin.psel_bits()) }; - w.connect().connected() - }); - qspi.psel.io0.write(|w| { - let pin = &config.pins.io0; - unsafe { w.bits(pin.psel_bits()) }; - w.connect().connected() - }); - qspi.psel.io1.write(|w| { - let pin = &config.pins.io1; - unsafe { w.bits(pin.psel_bits()) }; - w.connect().connected() - }); - qspi.psel.io2.write(|w| { - if let Some(ref pin) = config.pins.io2 { - unsafe { w.bits(pin.psel_bits()) }; - w.connect().connected() - } else { - w.connect().disconnected() - } - }); - qspi.psel.io3.write(|w| { - if let Some(ref pin) = config.pins.io3 { - unsafe { w.bits(pin.psel_bits()) }; - w.connect().connected() - } else { - w.connect().disconnected() - } - }); +impl<'d, T: Instance> Qspi<'d, T> { + pub fn new( + qspi: impl PeripheralBorrow + 'd, + irq: impl PeripheralBorrow + 'd, + sck: impl PeripheralBorrow + 'd, + csn: impl PeripheralBorrow + 'd, + io0: impl PeripheralBorrow + 'd, + io1: impl PeripheralBorrow + 'd, + io2: impl PeripheralBorrow + 'd, + io3: impl PeripheralBorrow + 'd, + config: Config, + ) -> Self { + unborrow!(qspi, irq, sck, csn, io0, io1, io2, io3); - qspi.ifconfig0.write(|mut w| { + let r = qspi.regs(); + + for cnf in &[ + sck.conf(), + csn.conf(), + io0.conf(), + io1.conf(), + io2.conf(), + io3.conf(), + ] { + cnf.write(|w| w.dir().output().drive().h0h1()); + } + + r.psel.sck.write(|w| unsafe { w.bits(sck.psel_bits()) }); + r.psel.csn.write(|w| unsafe { w.bits(csn.psel_bits()) }); + r.psel.io0.write(|w| unsafe { w.bits(io0.psel_bits()) }); + r.psel.io1.write(|w| unsafe { w.bits(io1.psel_bits()) }); + r.psel.io2.write(|w| unsafe { w.bits(io2.psel_bits()) }); + r.psel.io3.write(|w| unsafe { w.bits(io3.psel_bits()) }); + + r.ifconfig0.write(|mut w| { w = w.addrmode().variant(AddressMode::_24BIT); if config.deep_power_down.is_some() { w = w.dpmenable().enable(); @@ -113,14 +111,14 @@ impl Qspi { }); if let Some(dpd) = &config.deep_power_down { - qspi.dpmdur.write(|mut w| unsafe { + r.dpmdur.write(|mut w| unsafe { w = w.enter().bits(dpd.enter_time); w = w.exit().bits(dpd.exit_time); w }) } - qspi.ifconfig1.write(|w| { + r.ifconfig1.write(|w| { let w = unsafe { w.sckdelay().bits(80) }; let w = w.dpmen().exit(); let w = w.spimode().mode0(); @@ -128,48 +126,42 @@ impl Qspi { w }); - qspi.xipoffset + r.xipoffset .write(|w| unsafe { w.xipoffset().bits(config.xip_offset) }); // Enable it - qspi.enable.write(|w| w.enable().enabled()); + r.enable.write(|w| w.enable().enabled()); - qspi.events_ready.reset(); - qspi.tasks_activate.write(|w| w.tasks_activate().bit(true)); - while qspi.events_ready.read().bits() == 0 {} - qspi.events_ready.reset(); + r.events_ready.reset(); + r.tasks_activate.write(|w| w.tasks_activate().bit(true)); + while r.events_ready.read().bits() == 0 {} + r.events_ready.reset(); Self { - inner: PeripheralMutex::new( - State { - inner: qspi, - waker: WakerRegistration::new(), - }, - irq, - ), + peri: qspi, + irq, + phantom: PhantomData, } } - pub fn sleep(self: Pin<&mut Self>) { - self.inner().with(|s, _| { - info!("flash: sleeping"); - info!("flash: state = {:?}", s.inner.status.read().bits()); - s.inner.ifconfig1.modify(|_, w| w.dpmen().enter()); - info!("flash: state = {:?}", s.inner.status.read().bits()); - cortex_m::asm::delay(1000000); - info!("flash: state = {:?}", s.inner.status.read().bits()); + pub fn sleep(mut self: Pin<&mut Self>) { + let r = unsafe { self.as_mut().get_unchecked_mut() }.peri.regs(); - s.inner - .tasks_deactivate - .write(|w| w.tasks_deactivate().set_bit()); - }); + info!("flash: sleeping"); + info!("flash: state = {:?}", r.status.read().bits()); + r.ifconfig1.modify(|_, w| w.dpmen().enter()); + info!("flash: state = {:?}", r.status.read().bits()); + cortex_m::asm::delay(1000000); + info!("flash: state = {:?}", r.status.read().bits()); + + r.tasks_deactivate.write(|w| w.tasks_deactivate().set_bit()); } - pub async fn custom_instruction<'a>( - mut self: Pin<&'a mut Self>, + pub async fn custom_instruction( + mut self: Pin<&mut Self>, opcode: u8, - req: &'a [u8], - resp: &'a mut [u8], + req: &[u8], + resp: &mut [u8], ) -> Result<(), Error> { let bomb = DropBomb::new(); @@ -192,69 +184,73 @@ impl Qspi { let len = core::cmp::max(req.len(), resp.len()) as u8; - self.as_mut().inner().with(|s, _| { - s.inner.cinstrdat0.write(|w| unsafe { w.bits(dat0) }); - s.inner.cinstrdat1.write(|w| unsafe { w.bits(dat1) }); + let r = unsafe { self.as_mut().get_unchecked_mut() }.peri.regs(); + r.cinstrdat0.write(|w| unsafe { w.bits(dat0) }); + r.cinstrdat1.write(|w| unsafe { w.bits(dat1) }); - s.inner.events_ready.reset(); - s.inner.intenset.write(|w| w.ready().set()); + r.events_ready.reset(); + r.intenset.write(|w| w.ready().set()); - s.inner.cinstrconf.write(|w| { - let w = unsafe { w.opcode().bits(opcode) }; - let w = unsafe { w.length().bits(len + 1) }; - let w = w.lio2().bit(true); - let w = w.lio3().bit(true); - let w = w.wipwait().bit(true); - let w = w.wren().bit(true); - let w = w.lfen().bit(false); - let w = w.lfstop().bit(false); - w - }); + r.cinstrconf.write(|w| { + let w = unsafe { w.opcode().bits(opcode) }; + let w = unsafe { w.length().bits(len + 1) }; + let w = w.lio2().bit(true); + let w = w.lio3().bit(true); + let w = w.wipwait().bit(true); + let w = w.wren().bit(true); + let w = w.lfen().bit(false); + let w = w.lfstop().bit(false); + w }); self.as_mut().wait_ready().await; - self.as_mut().inner().with(|s, _| { - let dat0 = s.inner.cinstrdat0.read().bits(); - let dat1 = s.inner.cinstrdat1.read().bits(); - for i in 0..4 { - if i < resp.len() { - resp[i] = (dat0 >> (i * 8)) as u8; - } + let r = unsafe { self.as_mut().get_unchecked_mut() }.peri.regs(); + + let dat0 = r.cinstrdat0.read().bits(); + let dat1 = r.cinstrdat1.read().bits(); + for i in 0..4 { + if i < resp.len() { + resp[i] = (dat0 >> (i * 8)) as u8; } - for i in 0..4 { - if i + 4 < resp.len() { - resp[i] = (dat1 >> (i * 8)) as u8; - } + } + for i in 0..4 { + if i + 4 < resp.len() { + resp[i] = (dat1 >> (i * 8)) as u8; } - }); + } bomb.defuse(); Ok(()) } - fn inner(self: Pin<&mut Self>) -> Pin<&mut PeripheralMutex> { - unsafe { Pin::new_unchecked(&mut self.get_unchecked_mut().inner) } - } + async fn wait_ready(self: Pin<&mut Self>) { + let this = unsafe { self.get_unchecked_mut() }; - fn wait_ready<'a>(mut self: Pin<&'a mut Self>) -> impl Future + 'a { poll_fn(move |cx| { - self.as_mut().inner().with(|s, _irq| { - if s.inner.events_ready.read().bits() != 0 { - return Poll::Ready(()); - } - s.waker.register(cx.waker()); - Poll::Pending - }) + let r = this.peri.regs(); + + if r.events_ready.read().bits() != 0 { + r.events_ready.reset(); + return Poll::Ready(()); + } + + wake_on_interrupt(&mut this.irq, cx.waker()); + + Poll::Pending }) + .await } } -impl Flash for Qspi { - type ReadFuture<'a> = impl Future> + 'a; - type WriteFuture<'a> = impl Future> + 'a; - type ErasePageFuture<'a> = impl Future> + 'a; +impl<'d, T: Instance> Flash for Qspi<'d, T> { + #[rustfmt::skip] + type ReadFuture<'a> where Self: 'a = impl Future> + 'a; + #[rustfmt::skip] + type WriteFuture<'a> where Self: 'a = impl Future> + 'a; + #[rustfmt::skip] + type ErasePageFuture<'a> where Self: 'a = impl Future> + 'a; fn read<'a>( mut self: Pin<&'a mut Self>, @@ -268,26 +264,21 @@ impl Flash for Qspi { assert_eq!(data.len() as u32 % 4, 0); assert_eq!(address as u32 % 4, 0); - self.as_mut().inner().with(|s, _| { - s.inner - .read - .src - .write(|w| unsafe { w.src().bits(address as u32) }); - s.inner - .read - .dst - .write(|w| unsafe { w.dst().bits(data.as_ptr() as u32) }); - s.inner - .read - .cnt - .write(|w| unsafe { w.cnt().bits(data.len() as u32) }); + let r = unsafe { self.as_mut().get_unchecked_mut() }.peri.regs(); - s.inner.events_ready.reset(); - s.inner.intenset.write(|w| w.ready().set()); - s.inner - .tasks_readstart - .write(|w| w.tasks_readstart().bit(true)); - }); + r.read + .src + .write(|w| unsafe { w.src().bits(address as u32) }); + r.read + .dst + .write(|w| unsafe { w.dst().bits(data.as_ptr() as u32) }); + r.read + .cnt + .write(|w| unsafe { w.cnt().bits(data.len() as u32) }); + + r.events_ready.reset(); + r.intenset.write(|w| w.ready().set()); + r.tasks_readstart.write(|w| w.tasks_readstart().bit(true)); self.as_mut().wait_ready().await; @@ -309,26 +300,20 @@ impl Flash for Qspi { assert_eq!(data.len() as u32 % 4, 0); assert_eq!(address as u32 % 4, 0); - self.as_mut().inner().with(|s, _| { - s.inner - .write - .src - .write(|w| unsafe { w.src().bits(data.as_ptr() as u32) }); - s.inner - .write - .dst - .write(|w| unsafe { w.dst().bits(address as u32) }); - s.inner - .write - .cnt - .write(|w| unsafe { w.cnt().bits(data.len() as u32) }); + let r = unsafe { self.as_mut().get_unchecked_mut() }.peri.regs(); + r.write + .src + .write(|w| unsafe { w.src().bits(data.as_ptr() as u32) }); + r.write + .dst + .write(|w| unsafe { w.dst().bits(address as u32) }); + r.write + .cnt + .write(|w| unsafe { w.cnt().bits(data.len() as u32) }); - s.inner.events_ready.reset(); - s.inner.intenset.write(|w| w.ready().set()); - s.inner - .tasks_writestart - .write(|w| w.tasks_writestart().bit(true)); - }); + r.events_ready.reset(); + r.intenset.write(|w| w.ready().set()); + r.tasks_writestart.write(|w| w.tasks_writestart().bit(true)); self.as_mut().wait_ready().await; @@ -344,19 +329,15 @@ impl Flash for Qspi { assert_eq!(address as u32 % 4096, 0); - self.as_mut().inner().with(|s, _| { - s.inner - .erase - .ptr - .write(|w| unsafe { w.ptr().bits(address as u32) }); - s.inner.erase.len.write(|w| w.len()._4kb()); + let r = unsafe { self.as_mut().get_unchecked_mut() }.peri.regs(); + r.erase + .ptr + .write(|w| unsafe { w.ptr().bits(address as u32) }); + r.erase.len.write(|w| w.len()._4kb()); - s.inner.events_ready.reset(); - s.inner.intenset.write(|w| w.ready().set()); - s.inner - .tasks_erasestart - .write(|w| w.tasks_erasestart().bit(true)); - }); + r.events_ready.reset(); + r.intenset.write(|w| w.ready().set()); + r.tasks_erasestart.write(|w| w.tasks_erasestart().bit(true)); self.as_mut().wait_ready().await; @@ -383,13 +364,29 @@ impl Flash for Qspi { } } -impl PeripheralState for State { - type Interrupt = interrupt::QSPI; +mod sealed { + use super::*; - fn on_interrupt(&mut self) { - if self.inner.events_ready.read().bits() != 0 { - self.inner.intenclr.write(|w| w.ready().clear()); - self.waker.wake() - } + pub trait Instance { + fn regs(&self) -> &pac::qspi::RegisterBlock; } } + +pub trait Instance: sealed::Instance + 'static { + type Interrupt: Interrupt; +} + +macro_rules! impl_instance { + ($type:ident, $irq:ident) => { + impl sealed::Instance for peripherals::$type { + fn regs(&self) -> &pac::qspi::RegisterBlock { + unsafe { &*pac::$type::ptr() } + } + } + impl Instance for peripherals::$type { + type Interrupt = interrupt::$irq; + } + }; +} + +impl_instance!(QSPI, QSPI); diff --git a/embassy-nrf/src/rtc.rs b/embassy-nrf/src/rtc.rs index e0e645fe..cbb5a87e 100644 --- a/embassy-nrf/src/rtc.rs +++ b/embassy-nrf/src/rtc.rs @@ -1,13 +1,12 @@ use core::cell::Cell; -use core::ops::Deref; use core::sync::atomic::{compiler_fence, AtomicU32, Ordering}; use embassy::interrupt::InterruptExt; use embassy::time::Clock; -use crate::interrupt; use crate::interrupt::{CriticalSection, Interrupt, Mutex}; -use crate::pac::rtc0; +use crate::pac; +use crate::{interrupt, peripherals}; // RTC timekeeping works with something we call "periods", which are time intervals // of 2^23 ticks. The RTC counter value is 24 bits, so one "overflow cycle" is 2 periods. @@ -96,19 +95,20 @@ impl RTC { } pub fn start(&'static self) { - self.rtc.cc[3].write(|w| unsafe { w.bits(0x800000) }); + let r = self.rtc.regs(); + r.cc[3].write(|w| unsafe { w.bits(0x800000) }); - self.rtc.intenset.write(|w| { + r.intenset.write(|w| { let w = w.ovrflw().set(); let w = w.compare3().set(); w }); - self.rtc.tasks_clear.write(|w| unsafe { w.bits(1) }); - self.rtc.tasks_start.write(|w| unsafe { w.bits(1) }); + r.tasks_clear.write(|w| unsafe { w.bits(1) }); + r.tasks_start.write(|w| unsafe { w.bits(1) }); // Wait for clear - while self.rtc.counter.read().bits() != 0 {} + while r.counter.read().bits() != 0 {} self.irq.set_handler(|ptr| unsafe { let this = &*(ptr as *const () as *const Self); @@ -120,19 +120,20 @@ impl RTC { } fn on_interrupt(&self) { - if self.rtc.events_ovrflw.read().bits() == 1 { - self.rtc.events_ovrflw.write(|w| w); + let r = self.rtc.regs(); + if r.events_ovrflw.read().bits() == 1 { + r.events_ovrflw.write(|w| w); self.next_period(); } - if self.rtc.events_compare[3].read().bits() == 1 { - self.rtc.events_compare[3].write(|w| w); + if r.events_compare[3].read().bits() == 1 { + r.events_compare[3].write(|w| w); self.next_period(); } for n in 0..ALARM_COUNT { - if self.rtc.events_compare[n].read().bits() == 1 { - self.rtc.events_compare[n].write(|w| w); + if r.events_compare[n].read().bits() == 1 { + r.events_compare[n].write(|w| w); interrupt::free(|cs| { self.trigger_alarm(n, cs); }) @@ -142,6 +143,7 @@ impl RTC { fn next_period(&self) { interrupt::free(|cs| { + let r = self.rtc.regs(); let period = self.period.fetch_add(1, Ordering::Relaxed) + 1; let t = (period as u64) << 23; @@ -151,15 +153,16 @@ impl RTC { let diff = at - t; if diff < 0xc00000 { - self.rtc.cc[n].write(|w| unsafe { w.bits(at as u32 & 0xFFFFFF) }); - self.rtc.intenset.write(|w| unsafe { w.bits(compare_n(n)) }); + r.cc[n].write(|w| unsafe { w.bits(at as u32 & 0xFFFFFF) }); + r.intenset.write(|w| unsafe { w.bits(compare_n(n)) }); } } }) } fn trigger_alarm(&self, n: usize, cs: &CriticalSection) { - self.rtc.intenclr.write(|w| unsafe { w.bits(compare_n(n)) }); + let r = self.rtc.regs(); + r.intenclr.write(|w| unsafe { w.bits(compare_n(n)) }); let alarm = &self.alarms.borrow(cs)[n]; alarm.timestamp.set(u64::MAX); @@ -190,6 +193,8 @@ impl RTC { return; } + let r = self.rtc.regs(); + // If it hasn't triggered yet, setup it in the compare channel. let diff = timestamp - t; if diff < 0xc00000 { @@ -206,12 +211,12 @@ impl RTC { // by the Alarm trait contract. What's not allowed is triggering alarms *before* their scheduled time, // and we don't do that here. let safe_timestamp = timestamp.max(t + 3); - self.rtc.cc[n].write(|w| unsafe { w.bits(safe_timestamp as u32 & 0xFFFFFF) }); - self.rtc.intenset.write(|w| unsafe { w.bits(compare_n(n)) }); + r.cc[n].write(|w| unsafe { w.bits(safe_timestamp as u32 & 0xFFFFFF) }); + r.intenset.write(|w| unsafe { w.bits(compare_n(n)) }); } else { // If it's too far in the future, don't setup the compare channel yet. // It will be setup later by `next_period`. - self.rtc.intenclr.write(|w| unsafe { w.bits(compare_n(n)) }); + r.intenclr.write(|w| unsafe { w.bits(compare_n(n)) }); } }) } @@ -232,7 +237,7 @@ impl embassy::time::Clock for RTC { // `period` MUST be read before `counter`, see comment at the top for details. let period = self.period.load(Ordering::Relaxed); compiler_fence(Ordering::Acquire); - let counter = self.rtc.counter.read().bits(); + let counter = self.rtc.regs().counter.read().bits(); calc_now(period, counter) } } @@ -257,31 +262,32 @@ impl embassy::time::Alarm for Alarm { } mod sealed { - pub trait Instance {} + use super::*; + pub trait Instance { + fn regs(&self) -> &pac::rtc0::RegisterBlock; + } +} - impl Instance for crate::pac::RTC0 {} - impl Instance for crate::pac::RTC1 {} - #[cfg(any(feature = "52832", feature = "52833", feature = "52840"))] - impl Instance for crate::pac::RTC2 {} +macro_rules! impl_instance { + ($type:ident, $irq:ident) => { + impl sealed::Instance for peripherals::$type { + fn regs(&self) -> &pac::rtc0::RegisterBlock { + unsafe { &*pac::$type::ptr() } + } + } + impl Instance for peripherals::$type { + type Interrupt = interrupt::$irq; + } + }; } /// Implemented by all RTC instances. -pub trait Instance: - sealed::Instance + Deref + Sized + 'static -{ +pub trait Instance: sealed::Instance + 'static { /// The interrupt associated with this RTC instance. type Interrupt: Interrupt; } -impl Instance for crate::pac::RTC0 { - type Interrupt = interrupt::RTC0; -} - -impl Instance for crate::pac::RTC1 { - type Interrupt = interrupt::RTC1; -} - +impl_instance!(RTC0, RTC0); +impl_instance!(RTC1, RTC1); #[cfg(any(feature = "52832", feature = "52833", feature = "52840"))] -impl Instance for crate::pac::RTC2 { - type Interrupt = interrupt::RTC2; -} +impl_instance!(RTC2, RTC2); diff --git a/embassy-nrf/src/saadc.rs b/embassy-nrf/src/saadc.rs new file mode 100644 index 00000000..b854ce78 --- /dev/null +++ b/embassy-nrf/src/saadc.rs @@ -0,0 +1,242 @@ +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::{wake_on_interrupt, PeripheralBorrow}; +use embassy_extras::unborrow; +use futures::future::poll_fn; +use traits::spi::FullDuplex; + +use crate::gpio::Pin as GpioPin; +use crate::interrupt::{self, Interrupt}; +use crate::{pac, peripherals, slice_in_ram_or}; + +#[cfg(feature = "9160")] +use pac::{saadc_ns as saadc, SAADC_NS as SAADC}; + +#[cfg(not(feature = "9160"))] +use pac::{saadc, SAADC}; + +pub use saadc::{ + ch::{ + config::{GAIN_A as Gain, REFSEL_A as Reference, RESP_A as Resistor, TACQ_A as Time}, + pselp::PSELP_A as PositiveChannel, + }, + oversample::OVERSAMPLE_A as Oversample, + resolution::VAL_A as Resolution, +}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] +pub enum Error {} + +/// One-shot saadc. Continuous sample mode TODO. +pub struct OneShot<'d, T: PositivePin> { + peri: peripherals::SAADC, + positive_pin: T, + irq: interrupt::SAADC, + phantom: PhantomData<(&'d mut peripherals::SAADC, &'d mut T)>, +} + +/// Used to configure the SAADC peripheral. +/// +/// See the `Default` impl for suitable default values. +pub struct Config { + /// Output resolution in bits. + pub resolution: Resolution, + /// Average 2^`oversample` input samples before transferring the result into memory. + pub oversample: Oversample, + /// Reference voltage of the SAADC input. + pub reference: Reference, + /// Gain used to control the effective input range of the SAADC. + pub gain: Gain, + /// Positive channel resistor control. + pub resistor: Resistor, + /// Acquisition time in microseconds. + pub time: Time, +} + +impl Default for Config { + fn default() -> Self { + Self { + resolution: Resolution::_14BIT, + oversample: Oversample::OVER8X, + reference: Reference::VDD1_4, + gain: Gain::GAIN1_4, + resistor: Resistor::BYPASS, + time: Time::_20US, + } + } +} + +impl<'d, T: PositivePin> OneShot<'d, T> { + pub fn new( + saadc: impl PeripheralBorrow + 'd, + irq: impl PeripheralBorrow + 'd, + positive_pin: impl PeripheralBorrow + 'd, + config: Config, + ) -> Self { + unborrow!(saadc, irq, positive_pin); + + let r = unsafe { &*SAADC::ptr() }; + + let Config { + resolution, + oversample, + reference, + gain, + resistor, + time, + } = config; + + // Configure pins + r.enable.write(|w| w.enable().enabled()); + r.resolution.write(|w| w.val().variant(resolution)); + r.oversample.write(|w| w.oversample().variant(oversample)); + + r.ch[0].config.write(|w| { + w.refsel().variant(reference); + w.gain().variant(gain); + w.tacq().variant(time); + w.mode().se(); + w.resp().variant(resistor); + w.resn().bypass(); + if !matches!(oversample, Oversample::BYPASS) { + w.burst().enabled(); + } else { + w.burst().disabled(); + } + w + }); + + // Set positive channel + r.ch[0] + .pselp + .write(|w| w.pselp().variant(positive_pin.channel())); + + // Disable all events interrupts + r.intenclr.write(|w| unsafe { w.bits(0x003F_FFFF) }); + + Self { + peri: saadc, + positive_pin, + irq, + phantom: PhantomData, + } + } + + fn regs(&self) -> &saadc::RegisterBlock { + unsafe { &*SAADC::ptr() } + } +} + +impl<'d, T: PositivePin> Drop for OneShot<'d, T> { + fn drop(&mut self) { + let r = self.regs(); + r.enable.write(|w| w.enable().disabled()); + } +} + +pub trait Sample { + type SampleFuture<'a>: Future + 'a + where + Self: 'a; + + fn sample<'a>(self: Pin<&'a mut Self>) -> Self::SampleFuture<'a>; +} + +impl<'d, T: PositivePin> Sample for OneShot<'d, T> { + #[rustfmt::skip] + type SampleFuture<'a> where Self: 'a = impl Future + 'a; + + fn sample<'a>(self: Pin<&'a mut Self>) -> Self::SampleFuture<'a> { + async move { + let this = unsafe { self.get_unchecked_mut() }; + let r = this.regs(); + + // Set up the DMA + let mut val: i16 = 0; + r.result + .ptr + .write(|w| unsafe { w.ptr().bits(((&mut val) as *mut _) as u32) }); + r.result.maxcnt.write(|w| unsafe { w.maxcnt().bits(1) }); + + // Reset and enable the end event + r.events_end.reset(); + r.intenset.write(|w| w.end().set()); + + // Don't reorder the ADC start event before the previous writes. Hopefully this + // wouldn't happen anyway. + compiler_fence(Ordering::SeqCst); + + r.tasks_start.write(|w| unsafe { w.bits(1) }); + r.tasks_sample.write(|w| unsafe { w.bits(1) }); + + // Wait for 'end' event. + poll_fn(|cx| { + let r = this.regs(); + + if r.events_end.read().bits() != 0 { + r.events_end.reset(); + return Poll::Ready(()); + } + + wake_on_interrupt(&mut this.irq, cx.waker()); + + Poll::Pending + }) + .await; + + // The DMA wrote the sampled value to `val`. + val + } + } +} + +/// A pin that can be used as the positive end of a ADC differential in the SAADC periperhal. +/// +/// Currently negative is always shorted to ground (0V). +pub trait PositivePin { + fn channel(&self) -> PositiveChannel; +} + +macro_rules! positive_pin_mappings { + ( $($ch:ident => $pin:ident,)*) => { + $( + impl PositivePin for crate::peripherals::$pin { + fn channel(&self) -> PositiveChannel { + PositiveChannel::$ch + } + } + )* + }; +} + +// TODO the variant names are unchecked +// the pins are copied from nrf hal +#[cfg(feature = "9160")] +positive_pin_mappings! { + ANALOGINPUT0 => P0_13, + ANALOGINPUT1 => P0_14, + ANALOGINPUT2 => P0_15, + ANALOGINPUT3 => P0_16, + ANALOGINPUT4 => P0_17, + ANALOGINPUT5 => P0_18, + ANALOGINPUT6 => P0_19, + ANALOGINPUT7 => P0_20, +} + +#[cfg(not(feature = "9160"))] +positive_pin_mappings! { + ANALOGINPUT0 => P0_02, + ANALOGINPUT1 => P0_03, + ANALOGINPUT2 => P0_04, + ANALOGINPUT3 => P0_05, + ANALOGINPUT4 => P0_28, + ANALOGINPUT5 => P0_29, + ANALOGINPUT6 => P0_30, + ANALOGINPUT7 => P0_31, +} diff --git a/embassy-nrf/src/spim.rs b/embassy-nrf/src/spim.rs index b236f538..93ca52c6 100644 --- a/embassy-nrf/src/spim.rs +++ b/embassy-nrf/src/spim.rs @@ -1,19 +1,21 @@ 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_extras::peripheral::{PeripheralMutex, PeripheralState}; +use embassy::util::{wake_on_interrupt, PeripheralBorrow}; +use embassy_extras::unborrow; use futures::future::poll_fn; use traits::spi::FullDuplex; +use crate::gpio::sealed::Pin as _; +use crate::gpio::{OptionalPin, 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))] @@ -25,47 +27,61 @@ pub enum Error { DMABufferNotInDataMemory, } -struct State { - spim: T, - waker: WakerRegistration, -} - -pub struct Spim { - inner: PeripheralMutex>, +pub struct Spim<'d, T: Instance> { + peri: T, + irq: T::Interrupt, + 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 { + unborrow!(spim, irq, sck, miso, mosi); + let r = spim.regs(); - // Select pins. - r.psel.sck.write(|w| { - unsafe { w.bits(config.pins.sck.psel_bits()) }; - w.connect().connected() - }); + // Configure pins + sck.conf().write(|w| w.dir().output().drive().h0h1()); + if let Some(mosi) = mosi.pin_mut() { + mosi.conf().write(|w| w.dir().output().drive().h0h1()); + } + if let Some(miso) = miso.pin_mut() { + miso.conf().write(|w| w.input().connect().drive().h0h1()); + } - 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()), + match config.mode.polarity { + Polarity::IdleHigh => { + sck.set_high(); + if let Some(mosi) = mosi.pin_mut() { + mosi.set_high(); + } + } + Polarity::IdleLow => { + sck.set_low(); + if let Some(mosi) = mosi.pin_mut() { + mosi.set_low(); + } + } } + // Select pins. + // Note: OptionalPin reports 'disabled' for psel_bits when no pin was selected. + r.psel.sck.write(|w| unsafe { w.bits(sck.psel_bits()) }); + r.psel.mosi.write(|w| unsafe { w.bits(mosi.psel_bits()) }); + r.psel.miso.write(|w| unsafe { w.bits(miso.psel_bits()) }); + // Enable SPIM instance. r.enable.write(|w| w.enable().enabled()); @@ -107,22 +123,14 @@ impl Spim { r.intenclr.write(|w| unsafe { w.bits(0xFFFF_FFFF) }); Self { - inner: PeripheralMutex::new( - State { - spim, - waker: WakerRegistration::new(), - }, - irq, - ), + peri: spim, + irq, + phantom: PhantomData, } } - - fn inner(self: Pin<&mut Self>) -> Pin<&mut PeripheralMutex>> { - unsafe { Pin::new_unchecked(&mut self.get_unchecked_mut().inner) } - } } -impl FullDuplex for Spim { +impl<'d, T: Instance> FullDuplex for Spim<'d, T> { type Error = Error; #[rustfmt::skip] @@ -133,69 +141,69 @@ impl FullDuplex for Spim { type WriteReadFuture<'a> where Self: 'a = impl Future> + 'a; fn read<'a>(self: Pin<&'a mut Self>, data: &'a mut [u8]) -> Self::ReadFuture<'a> { - async move { todo!() } + self.read_write(data, &[]) } fn write<'a>(self: Pin<&'a mut Self>, data: &'a [u8]) -> Self::WriteFuture<'a> { - async move { todo!() } + self.read_write(&mut [], data) } fn read_write<'a>( - mut self: Pin<&'a mut Self>, + self: Pin<&'a mut Self>, rx: &'a mut [u8], tx: &'a [u8], ) -> Self::WriteReadFuture<'a> { async move { + let this = unsafe { self.get_unchecked_mut() }; slice_in_ram_or(rx, Error::DMABufferNotInDataMemory)?; slice_in_ram_or(tx, Error::DMABufferNotInDataMemory)?; - self.as_mut().inner().register_interrupt(); - self.as_mut().inner().with(|s, _irq| { - // Conservative compiler fence to prevent optimizations that do not - // take in to account actions by DMA. The fence has been placed here, - // before any DMA action has started. - compiler_fence(Ordering::SeqCst); + // Conservative compiler fence to prevent optimizations that do not + // take in to account actions by DMA. The fence has been placed here, + // before any DMA action has started. + compiler_fence(Ordering::SeqCst); - let r = s.spim.regs(); + let r = this.peri.regs(); - // Set up the DMA write. - r.txd - .ptr - .write(|w| unsafe { w.ptr().bits(tx.as_ptr() as u32) }); - r.txd - .maxcnt - .write(|w| unsafe { w.maxcnt().bits(tx.len() as _) }); + // Set up the DMA write. + r.txd + .ptr + .write(|w| unsafe { w.ptr().bits(tx.as_ptr() as u32) }); + r.txd + .maxcnt + .write(|w| unsafe { w.maxcnt().bits(tx.len() as _) }); - // Set up the DMA read. - r.rxd - .ptr - .write(|w| unsafe { w.ptr().bits(rx.as_mut_ptr() as u32) }); - r.rxd - .maxcnt - .write(|w| unsafe { w.maxcnt().bits(rx.len() as _) }); + // Set up the DMA read. + r.rxd + .ptr + .write(|w| unsafe { w.ptr().bits(rx.as_mut_ptr() as u32) }); + r.rxd + .maxcnt + .write(|w| unsafe { w.maxcnt().bits(rx.len() as _) }); - // Reset and enable the event - r.events_end.reset(); - r.intenset.write(|w| w.end().set()); + // Reset and enable the event + r.events_end.reset(); + r.intenset.write(|w| w.end().set()); - // Start SPI transaction. - r.tasks_start.write(|w| unsafe { w.bits(1) }); + // Start SPI transaction. + r.tasks_start.write(|w| unsafe { w.bits(1) }); - // Conservative compiler fence to prevent optimizations that do not - // take in to account actions by DMA. The fence has been placed here, - // after all possible DMA actions have completed. - compiler_fence(Ordering::SeqCst); - }); + // Conservative compiler fence to prevent optimizations that do not + // take in to account actions by DMA. The fence has been placed here, + // after all possible DMA actions have completed. + compiler_fence(Ordering::SeqCst); // Wait for 'end' event. poll_fn(|cx| { - self.as_mut().inner().with(|s, _irq| { - let r = s.spim.regs(); - if r.events_end.read().bits() != 0 { - return Poll::Ready(()); - } - s.waker.register(cx.waker()); - Poll::Pending - }) + let r = this.peri.regs(); + + if r.events_end.read().bits() != 0 { + r.events_end.reset(); + return Poll::Ready(()); + } + + wake_on_interrupt(&mut this.irq, cx.waker()); + + Poll::Pending }) .await; @@ -204,51 +212,41 @@ impl FullDuplex for Spim { } } -impl PeripheralState for State { - type Interrupt = U::Interrupt; - fn on_interrupt(&mut self) { - if self.spim.regs().events_end.read().bits() != 0 { - self.spim.regs().intenclr.write(|w| w.end().clear()); - self.waker.wake() - } - } -} - mod sealed { use super::*; pub trait Instance { - fn regs(&mut self) -> &pac::spim0::RegisterBlock; + fn regs(&self) -> &pac::spim0::RegisterBlock; } } -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 { - fn regs(&mut self) -> &pac::spim0::RegisterBlock { - self +macro_rules! impl_instance { + ($type:ident, $irq:ident) => { + impl sealed::Instance for peripherals::$type { + fn regs(&self) -> &pac::spim0::RegisterBlock { + unsafe { &*pac::$type::ptr() } } } - impl Instance for pac::$SPIMx { - type Interrupt = interrupt::$IRQ; + impl Instance for peripherals::$type { + type Interrupt = interrupt::$irq; } }; } #[cfg(feature = "52810")] -make_impl!(SPIM0, SPIM0_SPIS0_SPI0); +impl_instance!(SPIM0, SPIM0_SPIS0_SPI0); #[cfg(not(feature = "52810"))] -make_impl!(SPIM0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); +impl_instance!(SPIM0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); #[cfg(any(feature = "52832", feature = "52833", feature = "52840"))] -make_impl!(SPIM1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); +impl_instance!(SPIM1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); #[cfg(any(feature = "52832", feature = "52833", feature = "52840"))] -make_impl!(SPIM2, SPIM2_SPIS2_SPI2); +impl_instance!(SPIM2, SPIM2_SPIS2_SPI2); #[cfg(any(feature = "52833", feature = "52840"))] -make_impl!(SPIM3, SPIM3); +impl_instance!(SPIM3, SPIM3); diff --git a/embassy-nrf/src/system.rs b/embassy-nrf/src/system.rs new file mode 100644 index 00000000..5d36e66f --- /dev/null +++ b/embassy-nrf/src/system.rs @@ -0,0 +1,76 @@ +use crate::pac; + +pub enum HfclkSource { + Internal, + ExternalXtal, +} + +pub enum LfclkSource { + InternalRC, + Synthesized, + ExternalXtal, + ExternalLowSwing, + ExternalFullSwing, +} + +#[non_exhaustive] +pub struct Config { + pub hfclk_source: HfclkSource, + pub lfclk_source: LfclkSource, +} + +impl Default for Config { + fn default() -> Self { + Self { + // There are hobby nrf52 boards out there without external XTALs... + // Default everything to internal so it Just Works. User can enable external + // xtals if they know they have them. + hfclk_source: HfclkSource::Internal, + lfclk_source: LfclkSource::InternalRC, + } + } +} + +/// safety: must only call once. +pub unsafe fn configure(config: Config) { + let r = &*pac::CLOCK::ptr(); + + // Start HFCLK. + match config.hfclk_source { + HfclkSource::Internal => {} + HfclkSource::ExternalXtal => { + // Datasheet says this is likely to take 0.36ms + r.events_hfclkstarted.write(|w| unsafe { w.bits(0) }); + r.tasks_hfclkstart.write(|w| unsafe { w.bits(1) }); + while r.events_hfclkstarted.read().bits() == 0 {} + } + } + + // Configure LFCLK. + match config.lfclk_source { + LfclkSource::InternalRC => r.lfclksrc.write(|w| w.src().rc()), + LfclkSource::Synthesized => r.lfclksrc.write(|w| w.src().synth()), + + LfclkSource::ExternalXtal => r.lfclksrc.write(move |w| w.src().xtal()), + + LfclkSource::ExternalLowSwing => r.lfclksrc.write(move |w| { + w.src().xtal(); + w.external().enabled(); + w.bypass().disabled(); + w + }), + LfclkSource::ExternalFullSwing => r.lfclksrc.write(move |w| { + w.src().xtal(); + w.external().enabled(); + w.bypass().enabled(); + w + }), + } + + // Start LFCLK. + // Datasheet says this could take 100us from synth source + // 600us from rc source, 0.25s from an external source. + r.events_lfclkstarted.write(|w| unsafe { w.bits(0) }); + r.tasks_lfclkstart.write(|w| unsafe { w.bits(1) }); + while r.events_lfclkstarted.read().bits() == 0 {} +} diff --git a/embassy-nrf/src/timer.rs b/embassy-nrf/src/timer.rs new file mode 100644 index 00000000..d74e3cfa --- /dev/null +++ b/embassy-nrf/src/timer.rs @@ -0,0 +1,43 @@ +use embassy::interrupt::Interrupt; + +use crate::{interrupt, pac, peripherals}; + +mod sealed { + use super::*; + + pub trait Instance { + fn regs(&self) -> &pac::timer0::RegisterBlock; + } + pub trait ExtendedInstance {} +} + +pub trait Instance: sealed::Instance + 'static { + type Interrupt: Interrupt; +} +pub trait ExtendedInstance: Instance + sealed::ExtendedInstance {} + +macro_rules! impl_instance { + ($type:ident, $irq:ident) => { + impl sealed::Instance for peripherals::$type { + fn regs(&self) -> &pac::timer0::RegisterBlock { + unsafe { &*(pac::$type::ptr() as *const pac::timer0::RegisterBlock) } + } + } + impl Instance for peripherals::$type { + type Interrupt = interrupt::$irq; + } + }; + ($type:ident, $irq:ident, extended) => { + impl_instance!($type, $irq); + impl sealed::ExtendedInstance for peripherals::$type {} + impl ExtendedInstance for peripherals::$type {} + }; +} + +impl_instance!(TIMER0, TIMER0); +impl_instance!(TIMER1, TIMER1); +impl_instance!(TIMER2, TIMER2); +#[cfg(any(feature = "52832", feature = "52833", feature = "52840"))] +impl_instance!(TIMER3, TIMER3, extended); +#[cfg(any(feature = "52832", feature = "52833", feature = "52840"))] +impl_instance!(TIMER4, TIMER4, extended); diff --git a/embassy-nrf/src/uarte.rs b/embassy-nrf/src/uarte.rs index b5e4da86..957fa4c7 100644 --- a/embassy-nrf/src/uarte.rs +++ b/embassy-nrf/src/uarte.rs @@ -1,47 +1,57 @@ -//! Async low power UARTE. -//! -//! The peripheral is automatically enabled and disabled as required to save power. -//! Lowest power consumption can only be guaranteed if the send receive futures -//! are dropped correctly (e.g. not using `mem::forget()`). +//! Async UART use core::future::Future; -use core::ops::Deref; -use core::sync::atomic::{compiler_fence, Ordering}; -use core::task::{Context, Poll}; +use core::marker::PhantomData; +use core::pin::Pin; +use core::sync::atomic::{compiler_fence, AtomicBool, Ordering}; +use core::task::Poll; +use embassy::traits::uart::{Error, Read, Write}; +use embassy::util::{AtomicWaker, OnDrop, PeripheralBorrow}; +use embassy_extras::peripheral_shared::{Peripheral, PeripheralState}; +use embassy_extras::unborrow; +use futures::future::poll_fn; -use embassy::interrupt::InterruptExt; -use embassy::util::Signal; - -use crate::fmt::{assert, *}; -use crate::hal::pac; -use crate::hal::prelude::*; -use crate::hal::target_constants::EASY_DMA_SIZE; +use crate::fmt::{assert, panic, *}; +use crate::gpio::sealed::Pin as _; +use crate::gpio::{OptionalPin as GpioOptionalPin, Pin as GpioPin}; use crate::interrupt; use crate::interrupt::Interrupt; +use crate::pac; +use crate::peripherals; +use crate::target_constants::EASY_DMA_SIZE; -pub use crate::hal::uarte::Pins; // Re-export SVD variants to allow user to directly set values. pub use pac::uarte0::{baudrate::BAUDRATE_A as Baudrate, config::PARITY_A as Parity}; +#[non_exhaustive] +pub struct Config { + pub parity: Parity, + pub baudrate: Baudrate, +} + +impl Default for Config { + fn default() -> Self { + Self { + parity: Parity::EXCLUDED, + baudrate: Baudrate::BAUD115200, + } + } +} + +struct State { + peri: T, + + endrx_waker: AtomicWaker, + endtx_waker: AtomicWaker, +} + /// Interface to the UARTE peripheral -pub struct Uarte -where - T: Instance, -{ - instance: T, - irq: T::Interrupt, - pins: Pins, +pub struct Uarte<'d, T: Instance> { + inner: Peripheral>, + phantom: PhantomData<&'d mut T>, } -pub struct State { - tx_done: Signal<()>, - rx_done: Signal, -} - -impl Uarte -where - T: Instance, -{ +impl<'d, T: Instance> Uarte<'d, T> { /// Creates the interface to a UARTE instance. /// Sets the baud rate, parity and assigns the pins to the UARTE peripheral. /// @@ -52,389 +62,272 @@ where /// or [`receive`](Uarte::receive). #[allow(unused_unsafe)] pub unsafe fn new( - uarte: T, - irq: T::Interrupt, - mut pins: Pins, - parity: Parity, - baudrate: Baudrate, + uarte: impl PeripheralBorrow + 'd, + irq: impl PeripheralBorrow + 'd, + rxd: impl PeripheralBorrow + 'd, + txd: impl PeripheralBorrow + 'd, + cts: impl PeripheralBorrow + 'd, + rts: impl PeripheralBorrow + 'd, + config: Config, ) -> Self { - assert!(uarte.enable.read().enable().is_disabled()); + unborrow!(uarte, irq, rxd, txd, cts, rts); - uarte.psel.rxd.write(|w| { - unsafe { w.bits(pins.rxd.psel_bits()) }; - w.connect().connected() + let r = uarte.regs(); + + assert!(r.enable.read().enable().is_disabled()); + + rxd.conf().write(|w| w.input().connect().drive().h0h1()); + r.psel.rxd.write(|w| unsafe { w.bits(rxd.psel_bits()) }); + + txd.set_high(); + txd.conf().write(|w| w.dir().output().drive().h0h1()); + r.psel.txd.write(|w| unsafe { w.bits(txd.psel_bits()) }); + + if let Some(pin) = rts.pin_mut() { + pin.set_high(); + pin.conf().write(|w| w.dir().output().drive().h0h1()); + } + r.psel.cts.write(|w| unsafe { w.bits(cts.psel_bits()) }); + + if let Some(pin) = cts.pin_mut() { + pin.conf().write(|w| w.input().connect().drive().h0h1()); + } + r.psel.rts.write(|w| unsafe { w.bits(rts.psel_bits()) }); + + // Configure + let hardware_flow_control = match (rts.pin().is_some(), cts.pin().is_some()) { + (false, false) => false, + (true, true) => true, + _ => panic!("RTS and CTS pins must be either both set or none set."), + }; + r.config.write(|w| { + w.hwfc().bit(hardware_flow_control); + w.parity().variant(config.parity); + w }); + r.baudrate.write(|w| w.baudrate().variant(config.baudrate)); - pins.txd.set_high().unwrap(); - uarte.psel.txd.write(|w| { - unsafe { w.bits(pins.txd.psel_bits()) }; - w.connect().connected() - }); + // Disable all interrupts + r.intenclr.write(|w| unsafe { w.bits(0xFFFF_FFFF) }); - // Optional pins - uarte.psel.cts.write(|w| { - if let Some(ref pin) = pins.cts { - unsafe { w.bits(pin.psel_bits()) }; - w.connect().connected() - } else { - w.connect().disconnected() - } - }); + // Reset rxstarted, txstarted. These are used by drop to know whether a transfer was + // stopped midway or not. + r.events_rxstarted.reset(); + r.events_txstarted.reset(); - uarte.psel.rts.write(|w| { - if let Some(ref pin) = pins.rts { - unsafe { w.bits(pin.psel_bits()) }; - w.connect().connected() - } else { - w.connect().disconnected() - } - }); + // Enable + r.enable.write(|w| w.enable().enabled()); - uarte.baudrate.write(|w| w.baudrate().variant(baudrate)); - uarte.config.write(|w| w.parity().variant(parity)); - - // Enable interrupts - uarte.events_endtx.reset(); - uarte.events_endrx.reset(); - uarte - .intenset - .write(|w| w.endtx().set().txstopped().set().endrx().set().rxto().set()); - - // Register ISR - irq.set_handler(Self::on_irq); - irq.unpend(); - irq.enable(); - - Uarte { - instance: uarte, - irq, - pins, + Self { + inner: Peripheral::new( + irq, + State { + peri: uarte, + endrx_waker: AtomicWaker::new(), + endtx_waker: AtomicWaker::new(), + }, + ), + phantom: PhantomData, } } - pub fn free(self) -> (T, T::Interrupt, Pins) { - // Wait for the peripheral to be disabled from the ISR. - while self.instance.enable.read().enable().is_enabled() {} - (self.instance, self.irq, self.pins) + fn inner(self: Pin<&mut Self>) -> Pin<&mut Peripheral>> { + unsafe { Pin::new_unchecked(&mut self.get_unchecked_mut().inner) } } +} - fn enable(&mut self) { - trace!("enable"); - self.instance.enable.write(|w| w.enable().enabled()); - } +impl PeripheralState for State { + type Interrupt = T::Interrupt; - fn tx_started(&self) -> bool { - self.instance.events_txstarted.read().bits() != 0 - } - - fn rx_started(&self) -> bool { - self.instance.events_rxstarted.read().bits() != 0 - } - - unsafe fn on_irq(_ctx: *mut ()) { - let uarte = &*pac::UARTE0::ptr(); - - let mut try_disable = false; - - if uarte.events_endtx.read().bits() != 0 { - uarte.events_endtx.reset(); - trace!("endtx"); - compiler_fence(Ordering::SeqCst); - - if uarte.events_txstarted.read().bits() != 0 { - // The ENDTX was signal triggered because DMA has finished. - uarte.events_txstarted.reset(); - try_disable = true; - } - - T::state().tx_done.signal(()); + fn on_interrupt(&self) { + let r = self.peri.regs(); + if r.events_endrx.read().bits() != 0 { + self.endrx_waker.wake(); + r.intenclr.write(|w| w.endrx().clear()); + } + if r.events_endtx.read().bits() != 0 { + self.endtx_waker.wake(); + r.intenclr.write(|w| w.endtx().clear()); } - if uarte.events_txstopped.read().bits() != 0 { - uarte.events_txstopped.reset(); - trace!("txstopped"); - try_disable = true; + if r.events_rxto.read().bits() != 0 { + r.intenclr.write(|w| w.rxto().clear()); } - - if uarte.events_endrx.read().bits() != 0 { - uarte.events_endrx.reset(); - trace!("endrx"); - let len = uarte.rxd.amount.read().bits(); - compiler_fence(Ordering::SeqCst); - - if uarte.events_rxstarted.read().bits() != 0 { - // The ENDRX was signal triggered because DMA buffer is full. - uarte.events_rxstarted.reset(); - try_disable = true; - } - - T::state().rx_done.signal(len); + if r.events_txstopped.read().bits() != 0 { + r.intenclr.write(|w| w.txstopped().clear()); } + } +} - if uarte.events_rxto.read().bits() != 0 { - uarte.events_rxto.reset(); - trace!("rxto"); - try_disable = true; - } +impl<'a, T: Instance> Drop for Uarte<'a, T> { + fn drop(&mut self) { + info!("uarte drop"); - // Disable the peripheral if not active. - if try_disable - && uarte.events_txstarted.read().bits() == 0 - && uarte.events_rxstarted.read().bits() == 0 + let s = unsafe { Pin::new_unchecked(&mut self.inner) }.state(); + let r = s.peri.regs(); + + let did_stoprx = r.events_rxstarted.read().bits() != 0; + let did_stoptx = r.events_txstarted.read().bits() != 0; + info!("did_stoprx {} did_stoptx {}", did_stoprx, did_stoptx); + + // Wait for rxto or txstopped, if needed. + r.intenset.write(|w| w.rxto().set().txstopped().set()); + while (did_stoprx && r.events_rxto.read().bits() == 0) + || (did_stoptx && r.events_txstopped.read().bits() == 0) { - trace!("disable"); - uarte.enable.write(|w| w.enable().disabled()); + info!("uarte drop: wfe"); + cortex_m::asm::wfe(); + } + + cortex_m::asm::sev(); + + // Finally we can disable! + r.enable.write(|w| w.enable().disabled()); + + info!("uarte drop: done"); + + // TODO: disable pins + } +} + +impl<'d, T: Instance> Read for Uarte<'d, T> { + #[rustfmt::skip] + type ReadFuture<'a> where Self: 'a = impl Future> + 'a; + + fn read<'a>(mut self: Pin<&'a mut Self>, rx_buffer: &'a mut [u8]) -> Self::ReadFuture<'a> { + self.as_mut().inner().register_interrupt(); + + async move { + let ptr = rx_buffer.as_ptr(); + let len = rx_buffer.len(); + assert!(len <= EASY_DMA_SIZE); + + let s = self.inner().state(); + let r = s.peri.regs(); + + let drop = OnDrop::new(move || { + info!("read drop: stopping"); + + r.intenclr.write(|w| w.endrx().clear()); + r.events_rxto.reset(); + r.tasks_stoprx.write(|w| unsafe { w.bits(1) }); + + while r.events_endrx.read().bits() == 0 {} + + info!("read drop: stopped"); + }); + + r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) }); + r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) }); + + r.events_endrx.reset(); + r.intenset.write(|w| w.endrx().set()); + + compiler_fence(Ordering::SeqCst); + + trace!("startrx"); + r.tasks_startrx.write(|w| unsafe { w.bits(1) }); + + poll_fn(|cx| { + s.endrx_waker.register(cx.waker()); + if r.events_endrx.read().bits() != 0 { + return Poll::Ready(()); + } + Poll::Pending + }) + .await; + + compiler_fence(Ordering::SeqCst); + r.events_rxstarted.reset(); + drop.defuse(); + + Ok(()) } } } -impl embassy::traits::uart::Uart for Uarte { - type ReceiveFuture<'a> = ReceiveFuture<'a, T>; - type SendFuture<'a> = SendFuture<'a, T>; +impl<'d, T: Instance> Write for Uarte<'d, T> { + #[rustfmt::skip] + type WriteFuture<'a> where Self: 'a = impl Future> + 'a; - /// Sends serial data. - /// - /// `tx_buffer` is marked as static as per `embedded-dma` requirements. - /// It it safe to use a buffer with a non static lifetime if memory is not - /// reused until the future has finished. - fn send<'a>(&'a mut self, tx_buffer: &'a [u8]) -> SendFuture<'a, T> { - // Panic if TX is running which can happen if the user has called - // `mem::forget()` on a previous future after polling it once. - assert!(!self.tx_started()); + fn write<'a>(mut self: Pin<&'a mut Self>, tx_buffer: &'a [u8]) -> Self::WriteFuture<'a> { + self.as_mut().inner().register_interrupt(); - T::state().tx_done.reset(); - - SendFuture { - uarte: self, - buf: tx_buffer, - } - } - - /// Receives serial data. - /// - /// The future is pending until the buffer is completely filled. - /// A common pattern is to use [`stop()`](ReceiveFuture::stop) to cancel - /// unfinished transfers after a timeout to prevent lockup when no more data - /// is incoming. - /// - /// `rx_buffer` is marked as static as per `embedded-dma` requirements. - /// It it safe to use a buffer with a non static lifetime if memory is not - /// reused until the future has finished. - fn receive<'a>(&'a mut self, rx_buffer: &'a mut [u8]) -> ReceiveFuture<'a, T> { - // Panic if RX is running which can happen if the user has called - // `mem::forget()` on a previous future after polling it once. - assert!(!self.rx_started()); - - T::state().rx_done.reset(); - - ReceiveFuture { - uarte: self, - buf: rx_buffer, - } - } -} - -/// Future for the [`Uarte::send()`] method. -pub struct SendFuture<'a, T> -where - T: Instance, -{ - uarte: &'a mut Uarte, - buf: &'a [u8], -} - -impl<'a, T> Drop for SendFuture<'a, T> -where - T: Instance, -{ - fn drop(self: &mut Self) { - if self.uarte.tx_started() { - trace!("stoptx"); - - // Stop the transmitter to minimize the current consumption. - self.uarte.instance.events_txstarted.reset(); - self.uarte - .instance - .tasks_stoptx - .write(|w| unsafe { w.bits(1) }); - - // TX is stopped almost instantly, spinning is fine. - while !T::state().tx_done.signaled() {} - } - } -} - -impl<'a, T> Future for SendFuture<'a, T> -where - T: Instance, -{ - type Output = Result<(), embassy::traits::uart::Error>; - - fn poll(self: core::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let Self { uarte, buf } = unsafe { self.get_unchecked_mut() }; - - if T::state().tx_done.poll_wait(cx).is_pending() { - let ptr = buf.as_ptr(); - let len = buf.len(); + async move { + let ptr = tx_buffer.as_ptr(); + let len = tx_buffer.len(); assert!(len <= EASY_DMA_SIZE); // TODO: panic if buffer is not in SRAM - uarte.enable(); + let s = self.inner().state(); + let r = s.peri.regs(); + + let drop = OnDrop::new(move || { + info!("write drop: stopping"); + + r.intenclr.write(|w| w.endtx().clear()); + r.events_txstopped.reset(); + r.tasks_stoptx.write(|w| unsafe { w.bits(1) }); + + // TX is stopped almost instantly, spinning is fine. + while r.events_endtx.read().bits() == 0 {} + info!("write drop: stopped"); + }); + + r.txd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) }); + r.txd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) }); + + r.events_endtx.reset(); + r.intenset.write(|w| w.endtx().set()); compiler_fence(Ordering::SeqCst); - uarte - .instance - .txd - .ptr - .write(|w| unsafe { w.ptr().bits(ptr as u32) }); - uarte - .instance - .txd - .maxcnt - .write(|w| unsafe { w.maxcnt().bits(len as _) }); trace!("starttx"); - uarte.instance.tasks_starttx.write(|w| unsafe { w.bits(1) }); - while !uarte.tx_started() {} // Make sure transmission has started - - Poll::Pending - } else { - Poll::Ready(Ok(())) - } - } -} - -/// Future for the [`Uarte::receive()`] method. -pub struct ReceiveFuture<'a, T> -where - T: Instance, -{ - uarte: &'a mut Uarte, - buf: &'a mut [u8], -} - -impl<'a, T> Drop for ReceiveFuture<'a, T> -where - T: Instance, -{ - fn drop(self: &mut Self) { - if self.uarte.rx_started() { - trace!("stoprx (drop)"); - - self.uarte.instance.events_rxstarted.reset(); - self.uarte - .instance - .tasks_stoprx - .write(|w| unsafe { w.bits(1) }); - - embassy_extras::low_power_wait_until(|| T::state().rx_done.signaled()) - } - } -} - -impl<'a, T> Future for ReceiveFuture<'a, T> -where - T: Instance, -{ - type Output = Result<(), embassy::traits::uart::Error>; - - fn poll(self: core::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let Self { uarte, buf } = unsafe { self.get_unchecked_mut() }; - - match T::state().rx_done.poll_wait(cx) { - Poll::Pending if !uarte.rx_started() => { - let ptr = buf.as_ptr(); - let len = buf.len(); - assert!(len <= EASY_DMA_SIZE); - - uarte.enable(); - - compiler_fence(Ordering::SeqCst); - uarte - .instance - .rxd - .ptr - .write(|w| unsafe { w.ptr().bits(ptr as u32) }); - uarte - .instance - .rxd - .maxcnt - .write(|w| unsafe { w.maxcnt().bits(len as _) }); - - trace!("startrx"); - uarte.instance.tasks_startrx.write(|w| unsafe { w.bits(1) }); - while !uarte.rx_started() {} // Make sure reception has started + r.tasks_starttx.write(|w| unsafe { w.bits(1) }); + poll_fn(|cx| { + s.endtx_waker.register(cx.waker()); + if r.events_endtx.read().bits() != 0 { + return Poll::Ready(()); + } Poll::Pending - } - Poll::Pending => Poll::Pending, - Poll::Ready(_) => Poll::Ready(Ok(())), + }) + .await; + + compiler_fence(Ordering::SeqCst); + r.events_txstarted.reset(); + drop.defuse(); + + Ok(()) } } } -/// Future for the [`receive()`] method. -impl<'a, T> ReceiveFuture<'a, T> -where - T: Instance, -{ - /// Stops the ongoing reception and returns the number of bytes received. - pub async fn stop(self) -> usize { - let len = if self.uarte.rx_started() { - trace!("stoprx (stop)"); +mod sealed { + use super::*; - self.uarte.instance.events_rxstarted.reset(); - self.uarte - .instance - .tasks_stoprx - .write(|w| unsafe { w.bits(1) }); - T::state().rx_done.wait().await - } else { - // Transfer was stopped before it even started. No bytes were sent. - 0 - }; - len as _ + pub trait Instance { + fn regs(&self) -> &pac::uarte0::RegisterBlock; } } -mod private { - pub trait Sealed {} -} - -pub trait Instance: - Deref + Sized + private::Sealed + 'static -{ +pub trait Instance: sealed::Instance + 'static { type Interrupt: Interrupt; - - #[doc(hidden)] - fn state() -> &'static State; } -static UARTE0_STATE: State = State { - tx_done: Signal::new(), - rx_done: Signal::new(), -}; -impl private::Sealed for pac::UARTE0 {} -impl Instance for pac::UARTE0 { - type Interrupt = interrupt::UARTE0_UART0; - - fn state() -> &'static State { - &UARTE0_STATE - } +macro_rules! impl_instance { + ($type:ident, $irq:ident) => { + impl sealed::Instance for peripherals::$type { + fn regs(&self) -> &pac::uarte0::RegisterBlock { + unsafe { &*pac::$type::ptr() } + } + } + impl Instance for peripherals::$type { + type Interrupt = interrupt::$irq; + } + }; } +impl_instance!(UARTE0, UARTE0_UART0); #[cfg(any(feature = "52833", feature = "52840", feature = "9160"))] -static UARTE1_STATE: State = State { - tx_done: Signal::new(), - rx_done: Signal::new(), -}; -#[cfg(any(feature = "52833", feature = "52840", feature = "9160"))] -impl private::Sealed for pac::UARTE1 {} -#[cfg(any(feature = "52833", feature = "52840", feature = "9160"))] -impl Instance for pac::UARTE1 { - type Interrupt = interrupt::UARTE1; - - fn state() -> &'static State { - &UARTE1_STATE - } -} +impl_instance!(UARTE1, UARTE1); diff --git a/embassy-stm32f4-examples/src/bin/serial.rs b/embassy-stm32f4-examples/src/bin/serial.rs index 49a588c2..c8e7f288 100644 --- a/embassy-stm32f4-examples/src/bin/serial.rs +++ b/embassy-stm32f4-examples/src/bin/serial.rs @@ -12,10 +12,11 @@ use example_common::{panic, *}; use cortex_m::singleton; use cortex_m_rt::entry; use embassy::executor::{task, Executor}; -use embassy::traits::uart::Uart; +use embassy::traits::uart::{Read, Write}; use embassy::util::Forever; use embassy_stm32f4::interrupt; use embassy_stm32f4::serial; +use futures::pin_mut; use stm32f4xx_hal::dma::StreamsTuple; use stm32f4xx_hal::prelude::*; use stm32f4xx_hal::serial::config::Config; @@ -76,10 +77,12 @@ async fn run(dp: stm32::Peripherals, _cp: cortex_m::Peripherals) { clocks, ) }; + pin_mut!(serial); + let buf = singleton!(: [u8; 30] = [0; 30]).unwrap(); buf[5] = 0x01; - serial.send(buf).await.unwrap(); + serial.write(buf).await.unwrap(); } static EXECUTOR: Forever = Forever::new(); diff --git a/embassy-stm32f4/src/serial.rs b/embassy-stm32f4/src/serial.rs index c8dc0598..7539abf5 100644 --- a/embassy-stm32f4/src/serial.rs +++ b/embassy-stm32f4/src/serial.rs @@ -1,17 +1,12 @@ -//! Async low power Serial. -//! -//! The peripheral is autmatically enabled and disabled as required to save power. -//! Lowest power consumption can only be guaranteed if the send receive futures -//! are dropped correctly (e.g. not using `mem::forget()`). +//! Async Serial. use core::future::Future; use core::marker::PhantomData; - -use futures::{select_biased, FutureExt}; - +use core::pin::Pin; use embassy::interrupt::Interrupt; -use embassy::traits::uart::{Error, IdleUart, Uart}; +use embassy::traits::uart::{Error, Read, ReadUntilIdle, Write}; use embassy::util::InterruptFuture; +use futures::{select_biased, FutureExt}; use crate::hal::{ dma, @@ -89,7 +84,7 @@ where } } -impl Uart for Serial +impl Read for Serial where USART: serial::Instance + PeriAddress @@ -101,56 +96,19 @@ where RSTREAM: Stream + WithInterrupt + 'static, CHANNEL: Channel + 'static, { - type SendFuture<'a> = impl Future> + 'a; - type ReceiveFuture<'a> = impl Future> + 'a; - - /// Sends serial data. - fn send<'a>(&'a mut self, buf: &'a [u8]) -> Self::SendFuture<'a> { - #[allow(mutable_transmutes)] - let static_buf = unsafe { core::mem::transmute::<&'a [u8], &'static mut [u8]>(buf) }; - - let tx_stream = self.tx_stream.take().unwrap(); - let usart = self.usart.take().unwrap(); - - async move { - let mut tx_transfer = Transfer::init( - tx_stream, - usart, - static_buf, - None, - DmaConfig::default() - .transfer_complete_interrupt(true) - .memory_increment(true) - .double_buffer(false), - ); - - let fut = InterruptFuture::new(&mut self.tx_int); - - tx_transfer.start(|_usart| {}); - fut.await; - - let (tx_stream, usart, _buf, _) = tx_transfer.free(); - - self.tx_stream.replace(tx_stream); - self.usart.replace(usart); - - Ok(()) - } - } + type ReadFuture<'a> = impl Future> + 'a; /// Receives serial data. /// /// The future is pending until the buffer is completely filled. - /// A common pattern is to use [`stop()`](ReceiveFuture::stop) to cancel - /// unfinished transfers after a timeout to prevent lockup when no more data - /// is incoming. - fn receive<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReceiveFuture<'a> { + fn read<'a>(self: Pin<&'a mut Self>, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { + let this = unsafe { self.get_unchecked_mut() }; let static_buf = unsafe { core::mem::transmute::<&'a mut [u8], &'static mut [u8]>(buf) }; - let rx_stream = self.rx_stream.take().unwrap(); - let usart = self.usart.take().unwrap(); - async move { + let rx_stream = this.rx_stream.take().unwrap(); + let usart = this.usart.take().unwrap(); + let mut rx_transfer = Transfer::init( rx_stream, usart, @@ -162,20 +120,20 @@ where .double_buffer(false), ); - let fut = InterruptFuture::new(&mut self.rx_int); + let fut = InterruptFuture::new(&mut this.rx_int); rx_transfer.start(|_usart| {}); fut.await; let (rx_stream, usart, _, _) = rx_transfer.free(); - self.rx_stream.replace(rx_stream); - self.usart.replace(usart); + this.rx_stream.replace(rx_stream); + this.usart.replace(usart); Ok(()) } } } -impl IdleUart for Serial +impl Write for Serial where USART: serial::Instance + PeriAddress @@ -187,20 +145,74 @@ where RSTREAM: Stream + WithInterrupt + 'static, CHANNEL: Channel + 'static, { - type ReceiveFuture<'a> = impl Future> + 'a; + type WriteFuture<'a> = impl Future> + 'a; + + /// Sends serial data. + fn write<'a>(self: Pin<&'a mut Self>, buf: &'a [u8]) -> Self::WriteFuture<'a> { + let this = unsafe { self.get_unchecked_mut() }; + #[allow(mutable_transmutes)] + let static_buf = unsafe { core::mem::transmute::<&'a [u8], &'static mut [u8]>(buf) }; + + async move { + let tx_stream = this.tx_stream.take().unwrap(); + let usart = this.usart.take().unwrap(); + + let mut tx_transfer = Transfer::init( + tx_stream, + usart, + static_buf, + None, + DmaConfig::default() + .transfer_complete_interrupt(true) + .memory_increment(true) + .double_buffer(false), + ); + + let fut = InterruptFuture::new(&mut this.tx_int); + + tx_transfer.start(|_usart| {}); + fut.await; + + let (tx_stream, usart, _buf, _) = tx_transfer.free(); + + this.tx_stream.replace(tx_stream); + this.usart.replace(usart); + + Ok(()) + } + } +} + +impl ReadUntilIdle for Serial +where + USART: serial::Instance + + PeriAddress + + DMASet + + DMASet + + WithInterrupt + + 'static, + TSTREAM: Stream + WithInterrupt + 'static, + RSTREAM: Stream + WithInterrupt + 'static, + CHANNEL: Channel + 'static, +{ + type ReadUntilIdleFuture<'a> = impl Future> + 'a; /// Receives serial data. /// /// The future is pending until either the buffer is completely full, or the RX line falls idle after receiving some data. /// /// Returns the number of bytes read. - fn receive_until_idle<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReceiveFuture<'a> { + fn read_until_idle<'a>( + self: Pin<&'a mut Self>, + buf: &'a mut [u8], + ) -> Self::ReadUntilIdleFuture<'a> { + let this = unsafe { self.get_unchecked_mut() }; let static_buf = unsafe { core::mem::transmute::<&'a mut [u8], &'static mut [u8]>(buf) }; - let rx_stream = self.rx_stream.take().unwrap(); - let usart = self.usart.take().unwrap(); - async move { + let rx_stream = this.rx_stream.take().unwrap(); + let usart = this.usart.take().unwrap(); + unsafe { /* __HAL_UART_ENABLE_IT(&uart->UartHandle, UART_IT_IDLE); */ (*USART::ptr()).cr1.modify(|_, w| w.idleie().set_bit()); @@ -223,15 +235,12 @@ where let total_bytes = RSTREAM::get_number_of_transfers() as usize; - let fut = InterruptFuture::new(&mut self.rx_int); - let fut_idle = InterruptFuture::new(&mut self.usart_int); + let fut = InterruptFuture::new(&mut this.rx_int); + let fut_idle = InterruptFuture::new(&mut this.usart_int); rx_transfer.start(|_usart| {}); - select_biased! { - () = fut.fuse() => {}, - () = fut_idle.fuse() => {}, - } + futures::future::select(fut, fut_idle).await; let (rx_stream, usart, _, _) = rx_transfer.free(); @@ -240,8 +249,8 @@ where unsafe { (*USART::ptr()).cr1.modify(|_, w| w.idleie().clear_bit()); } - self.rx_stream.replace(rx_stream); - self.usart.replace(usart); + this.rx_stream.replace(rx_stream); + this.usart.replace(usart); Ok(total_bytes - remaining_bytes) } diff --git a/embassy-traits/src/flash.rs b/embassy-traits/src/flash.rs index 145cc768..3adaa3a0 100644 --- a/embassy-traits/src/flash.rs +++ b/embassy-traits/src/flash.rs @@ -11,9 +11,17 @@ pub enum Error { } pub trait Flash { - type ReadFuture<'a>: Future>; - type WriteFuture<'a>: Future>; - type ErasePageFuture<'a>: Future>; + type ReadFuture<'a>: Future> + where + Self: 'a; + + type WriteFuture<'a>: Future> + where + Self: 'a; + + type ErasePageFuture<'a>: Future> + where + Self: 'a; /// Reads data from the flash device. /// diff --git a/embassy-traits/src/uart.rs b/embassy-traits/src/uart.rs index 44174718..5676e3fc 100644 --- a/embassy-traits/src/uart.rs +++ b/embassy-traits/src/uart.rs @@ -1,4 +1,5 @@ use core::future::Future; +use core::pin::Pin; #[derive(Copy, Clone, Debug, Eq, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -7,18 +8,31 @@ pub enum Error { Other, } -pub trait Uart { - type ReceiveFuture<'a>: Future>; - type SendFuture<'a>: Future>; - /// Receive into the buffer until the buffer is full - fn receive<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReceiveFuture<'a>; - /// Send the specified buffer, and return when the transmission has completed - fn send<'a>(&'a mut self, buf: &'a [u8]) -> Self::SendFuture<'a>; +pub trait Read { + type ReadFuture<'a>: Future> + where + Self: 'a; + + fn read<'a>(self: Pin<&'a mut Self>, buf: &'a mut [u8]) -> Self::ReadFuture<'a>; } -pub trait IdleUart { - type ReceiveFuture<'a>: Future>; +pub trait ReadUntilIdle { + type ReadUntilIdleFuture<'a>: Future> + where + Self: 'a; + /// Receive into the buffer until the buffer is full or the line is idle after some bytes are received /// Return the number of bytes received - fn receive_until_idle<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReceiveFuture<'a>; + fn read_until_idle<'a>( + self: Pin<&'a mut Self>, + buf: &'a mut [u8], + ) -> Self::ReadUntilIdleFuture<'a>; +} + +pub trait Write { + type WriteFuture<'a>: Future> + where + Self: 'a; + + fn write<'a>(self: Pin<&'a mut Self>, buf: &'a [u8]) -> Self::WriteFuture<'a>; } diff --git a/embassy/src/executor/raw.rs b/embassy/src/executor/raw.rs index 7e981b08..52512c53 100644 --- a/embassy/src/executor/raw.rs +++ b/embassy/src/executor/raw.rs @@ -4,9 +4,9 @@ use core::cmp::min; use core::future::Future; use core::marker::PhantomData; use core::pin::Pin; -use core::ptr; use core::ptr::NonNull; use core::task::{Context, Poll, Waker}; +use core::{mem, ptr}; use super::run_queue::{RunQueue, RunQueueItem}; use super::timer_queue::{TimerQueue, TimerQueueItem}; @@ -143,6 +143,10 @@ impl Task { } Poll::Pending => {} } + + // the compiler is emitting a virtual call for waker drop, but we know + // it's a noop for our waker. + mem::forget(waker); } } diff --git a/embassy/src/interrupt.rs b/embassy/src/interrupt.rs index a4285a9f..99d7af75 100644 --- a/embassy/src/interrupt.rs +++ b/embassy/src/interrupt.rs @@ -1,7 +1,7 @@ use core::ptr; use cortex_m::peripheral::NVIC; -use atomic_polyfill::{AtomicPtr, Ordering}; +use atomic_polyfill::{compiler_fence, AtomicPtr, Ordering}; pub use embassy_macros::interrupt_declare as declare; pub use embassy_macros::interrupt_take as take; @@ -58,22 +58,27 @@ pub trait InterruptExt: Interrupt { impl InterruptExt for T { fn set_handler(&self, func: unsafe fn(*mut ())) { + compiler_fence(Ordering::SeqCst); let handler = unsafe { self.__handler() }; - handler.func.store(func as *mut (), Ordering::Release); + handler.func.store(func as *mut (), Ordering::Relaxed); + compiler_fence(Ordering::SeqCst); } fn remove_handler(&self) { + compiler_fence(Ordering::SeqCst); let handler = unsafe { self.__handler() }; - handler.func.store(ptr::null_mut(), Ordering::Release); + handler.func.store(ptr::null_mut(), Ordering::Relaxed); + compiler_fence(Ordering::SeqCst); } fn set_handler_context(&self, ctx: *mut ()) { let handler = unsafe { self.__handler() }; - handler.ctx.store(ctx, Ordering::Release); + handler.ctx.store(ctx, Ordering::Relaxed); } #[inline] fn enable(&self) { + compiler_fence(Ordering::SeqCst); unsafe { NVIC::unmask(NrWrap(self.number())); } @@ -82,6 +87,7 @@ impl InterruptExt for T { #[inline] fn disable(&self) { NVIC::mask(NrWrap(self.number())); + compiler_fence(Ordering::SeqCst); } #[inline] diff --git a/embassy/src/util/forever.rs b/embassy/src/util/forever.rs index efa96f30..0432fa51 100644 --- a/embassy/src/util/forever.rs +++ b/embassy/src/util/forever.rs @@ -31,6 +31,7 @@ unsafe impl Send for Forever {} unsafe impl Sync for Forever {} impl Forever { + #[inline(always)] pub const fn new() -> Self { Self { used: AtomicBool::new(false), @@ -43,10 +44,11 @@ impl Forever { /// Panics if this `Forever` already has a value. /// /// Returns a mutable reference to the stored value. + #[inline(always)] pub fn put(&'static self, val: T) -> &'static mut T { if self .used - .compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire) + .compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed) .is_err() { panic!("Forever.put() called multiple times"); @@ -60,6 +62,25 @@ impl Forever { } } + #[inline(always)] + pub fn put_with(&'static self, val: impl FnOnce() -> T) -> &'static mut T { + if self + .used + .compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed) + .is_err() + { + panic!("Forever.put() called multiple times"); + } + + unsafe { + let p = self.t.get(); + let p = (&mut *p).as_mut_ptr(); + p.write(val()); + &mut *p + } + } + + #[inline(always)] pub unsafe fn steal(&'static self) -> &'static mut T { let p = self.t.get(); let p = (&mut *p).as_mut_ptr(); diff --git a/embassy/src/util/mod.rs b/embassy/src/util/mod.rs index 6917e999..9f82422d 100644 --- a/embassy/src/util/mod.rs +++ b/embassy/src/util/mod.rs @@ -2,6 +2,7 @@ mod drop_bomb; mod forever; mod mutex; +mod on_drop; mod portal; mod signal; @@ -11,6 +12,16 @@ mod waker; pub use drop_bomb::*; pub use forever::*; pub use mutex::*; +pub use on_drop::*; pub use portal::*; pub use signal::*; pub use waker::*; + +pub trait PeripheralBorrow { + type Target; + unsafe fn unborrow(self) -> Self::Target; +} + +pub trait Steal { + unsafe fn steal() -> Self; +} diff --git a/embassy/src/util/on_drop.rs b/embassy/src/util/on_drop.rs new file mode 100644 index 00000000..10f3407f --- /dev/null +++ b/embassy/src/util/on_drop.rs @@ -0,0 +1,24 @@ +use core::mem; +use core::mem::MaybeUninit; + +pub struct OnDrop { + f: MaybeUninit, +} + +impl OnDrop { + pub fn new(f: F) -> Self { + Self { + f: MaybeUninit::new(f), + } + } + + pub fn defuse(self) { + mem::forget(self) + } +} + +impl Drop for OnDrop { + fn drop(&mut self) { + unsafe { self.f.as_ptr().read()() } + } +} diff --git a/embassy/src/util/signal.rs b/embassy/src/util/signal.rs index 0fd5c927..749b3224 100644 --- a/embassy/src/util/signal.rs +++ b/embassy/src/util/signal.rs @@ -5,6 +5,8 @@ use core::ptr; use core::task::{Context, Poll, Waker}; use cortex_m::peripheral::NVIC; use cortex_m::peripheral::{scb, SCB}; +use executor::raw::TaskHeader; +use ptr::NonNull; use crate::executor; use crate::fmt::panic; @@ -79,6 +81,30 @@ impl Signal { } } +// ========== + +pub fn wake_on_interrupt(interrupt: &mut impl Interrupt, waker: &Waker) { + interrupt.disable(); + interrupt.set_handler(irq_wake_handler); + interrupt.set_handler_context(unsafe { executor::raw::task_from_waker(waker) }.as_ptr() as _); + interrupt.enable(); +} + +unsafe fn irq_wake_handler(ctx: *mut ()) { + if let Some(task) = NonNull::new(ctx as *mut TaskHeader) { + executor::raw::wake_task(task); + } + + let irq = match SCB::vect_active() { + scb::VectActive::Interrupt { irqn } => irqn, + _ => unreachable!(), + }; + + NVIC::mask(crate::interrupt::NrWrap(irq as u16)); +} + +// ========== + struct NrWrap(u8); unsafe impl cortex_m::interrupt::Nr for NrWrap { fn nr(&self) -> u8 { @@ -119,26 +145,13 @@ impl<'a, I: Interrupt> Drop for InterruptFuture<'a, I> { impl<'a, I: Interrupt> InterruptFuture<'a, I> { pub fn new(interrupt: &'a mut I) -> Self { interrupt.disable(); - interrupt.set_handler(Self::interrupt_handler); + interrupt.set_handler(irq_wake_handler); interrupt.set_handler_context(ptr::null_mut()); interrupt.unpend(); interrupt.enable(); Self { interrupt } } - - unsafe fn interrupt_handler(ctx: *mut ()) { - let irq = match SCB::vect_active() { - scb::VectActive::Interrupt { irqn } => irqn, - _ => unreachable!(), - }; - - if !ctx.is_null() { - executor::raw::wake_task(ptr::NonNull::new_unchecked(ctx as _)); - } - - NVIC::mask(crate::interrupt::NrWrap(irq as u16)); - } } impl<'a, I: Interrupt> Unpin for InterruptFuture<'a, I> {} @@ -148,7 +161,6 @@ impl<'a, I: Interrupt> Future for InterruptFuture<'a, I> { fn poll(self: core::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> { let s = unsafe { self.get_unchecked_mut() }; - s.interrupt.set_handler(Self::interrupt_handler); s.interrupt.set_handler_context(unsafe { executor::raw::task_from_waker(&cx.waker()).cast().as_ptr() }); diff --git a/embassy/src/util/waker.rs b/embassy/src/util/waker.rs index 2b72fd56..39315509 100644 --- a/embassy/src/util/waker.rs +++ b/embassy/src/util/waker.rs @@ -1,7 +1,7 @@ use core::ptr::{self, NonNull}; use core::task::Waker; -use atomic_polyfill::{AtomicPtr, Ordering}; +use atomic_polyfill::{compiler_fence, AtomicPtr, Ordering}; use crate::executor::raw::{task_from_waker, wake_task, TaskHeader}; @@ -48,11 +48,11 @@ impl WakerRegistration { } } -pub struct AtomicWakerRegistration { +pub struct AtomicWaker { waker: AtomicPtr, } -impl AtomicWakerRegistration { +impl AtomicWaker { pub const fn new() -> Self { Self { waker: AtomicPtr::new(ptr::null_mut()), @@ -62,17 +62,15 @@ impl AtomicWakerRegistration { /// Register a waker. Overwrites the previous waker, if any. pub fn register(&self, w: &Waker) { let w = unsafe { task_from_waker(w) }; - let w2 = self.waker.swap(w.as_ptr(), Ordering::Relaxed); - if !w2.is_null() && w2 != w.as_ptr() { - unsafe { wake_task(NonNull::new_unchecked(w2)) }; - } + self.waker.store(w.as_ptr(), Ordering::Relaxed); + compiler_fence(Ordering::SeqCst); } /// Wake the registered waker, if any. pub fn wake(&self) { - let w2 = self.waker.swap(ptr::null_mut(), Ordering::Relaxed); - if !w2.is_null() { - unsafe { wake_task(NonNull::new_unchecked(w2)) }; + let w2 = self.waker.load(Ordering::Relaxed); + if let Some(w2) = NonNull::new(w2) { + unsafe { wake_task(w2) }; } } } diff --git a/embassy/src/util/waker_agnostic.rs b/embassy/src/util/waker_agnostic.rs index b4234c0f..3f6ad373 100644 --- a/embassy/src/util/waker_agnostic.rs +++ b/embassy/src/util/waker_agnostic.rs @@ -49,11 +49,11 @@ impl WakerRegistration { } /// Utility struct to register and wake a waker. -pub struct AtomicWakerRegistration { +pub struct AtomicWaker { waker: Mutex>>, } -impl AtomicWakerRegistration { +impl AtomicWaker { pub const fn new() -> Self { Self { waker: Mutex::new(Cell::new(None)), @@ -66,11 +66,7 @@ impl AtomicWakerRegistration { let cell = self.waker.borrow(cs); cell.set(match cell.replace(None) { Some(w2) if (w2.will_wake(w)) => Some(w2), - Some(w2) => { - w2.wake(); - Some(w.clone()) - } - None => Some(w.clone()), + _ => Some(w.clone()), }) }) } @@ -80,7 +76,8 @@ impl AtomicWakerRegistration { cortex_m::interrupt::free(|cs| { let cell = self.waker.borrow(cs); if let Some(w) = cell.replace(None) { - w.wake() + w.wake_by_ref(); + cell.set(Some(w)); } }) }