Merge pull request #91 from embassy-rs/borrow-v3
nrf: New API supporting borrowed peripherals
This commit is contained in:
commit
af6d708c93
@ -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;
|
||||
|
||||
|
107
embassy-extras/src/macros.rs
Normal file
107
embassy-extras/src/macros.rs
Normal file
@ -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<Self> {
|
||||
|
||||
#[no_mangle]
|
||||
static mut _EMBASSY_DEVICE_PERIPHERALS: bool = false;
|
||||
|
||||
cortex_m::interrupt::free(|_| {
|
||||
if unsafe { _EMBASSY_DEVICE_PERIPHERALS } {
|
||||
None
|
||||
} else {
|
||||
Some(unsafe { <Self as embassy::util::Steal>::steal() })
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl embassy::util::Steal for Peripherals {
|
||||
#[inline]
|
||||
unsafe fn steal() -> Self {
|
||||
Self {
|
||||
$(
|
||||
$(#[$cfg])?
|
||||
$name: <peripherals::$name as embassy::util::Steal>::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) }
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
@ -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<S: PeripheralState> PeripheralMutex<S> {
|
||||
}
|
||||
|
||||
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<S: PeripheralState> PeripheralMutex<S> {
|
||||
});
|
||||
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<S: PeripheralState> PeripheralMutex<S> {
|
||||
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
|
||||
|
63
embassy-extras/src/peripheral_shared.rs
Normal file
63
embassy-extras/src/peripheral_shared.rs
Normal file
@ -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<S: PeripheralState> {
|
||||
state: UnsafeCell<S>,
|
||||
|
||||
irq_setup_done: bool,
|
||||
irq: S::Interrupt,
|
||||
|
||||
_not_send: PhantomData<*mut ()>,
|
||||
_pinned: PhantomPinned,
|
||||
}
|
||||
|
||||
impl<S: PeripheralState> Peripheral<S> {
|
||||
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<S: PeripheralState> Drop for Peripheral<S> {
|
||||
fn drop(&mut self) {
|
||||
self.irq.disable();
|
||||
self.irq.remove_handler();
|
||||
}
|
||||
}
|
@ -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,13 +172,11 @@ 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 = 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);
|
||||
|
||||
|
@ -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"] }
|
@ -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(
|
||||
let u = unsafe {
|
||||
BufferedUarte::new(
|
||||
p.UARTE0,
|
||||
p.TIMER0,
|
||||
ppi.ppi0,
|
||||
ppi.ppi1,
|
||||
p.PPI_CH0,
|
||||
p.PPI_CH1,
|
||||
irq,
|
||||
unsafe { &mut RX_BUFFER },
|
||||
unsafe { &mut TX_BUFFER },
|
||||
pins,
|
||||
buffered_uarte::Parity::EXCLUDED,
|
||||
buffered_uarte::Baudrate::BAUD115200,
|
||||
);
|
||||
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<rtc::RTC<peripherals::RTC1>> = Forever::new();
|
||||
static ALARM: Forever<rtc::Alarm<peripherals::RTC1>> = Forever::new();
|
||||
static EXECUTOR: Forever<Executor> = 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()));
|
||||
});
|
||||
|
@ -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<rtc::RTC<pac::RTC1>> = Forever::new();
|
||||
static ALARM: Forever<rtc::Alarm<pac::RTC1>> = Forever::new();
|
||||
static RTC: Forever<rtc::RTC<peripherals::RTC1>> = Forever::new();
|
||||
static ALARM: Forever<rtc::Alarm<peripherals::RTC1>> = Forever::new();
|
||||
static EXECUTOR: Forever<Executor> = 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());
|
||||
|
@ -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 {
|
||||
|
@ -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<gpio::PullUp>) {
|
||||
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<gpio::PullUp>) {
|
||||
|
||||
#[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);
|
||||
}
|
||||
|
@ -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<rtc::RTC<pac::RTC1>> = Forever::new();
|
||||
static ALARM_HIGH: Forever<rtc::Alarm<pac::RTC1>> = Forever::new();
|
||||
static RTC: Forever<rtc::RTC<peripherals::RTC1>> = Forever::new();
|
||||
static ALARM_HIGH: Forever<rtc::Alarm<peripherals::RTC1>> = Forever::new();
|
||||
static EXECUTOR_HIGH: Forever<InterruptExecutor<interrupt::SWI1_EGU1>> = Forever::new();
|
||||
static ALARM_MED: Forever<rtc::Alarm<pac::RTC1>> = Forever::new();
|
||||
static ALARM_MED: Forever<rtc::Alarm<peripherals::RTC1>> = Forever::new();
|
||||
static EXECUTOR_MED: Forever<InterruptExecutor<interrupt::SWI0_EGU0>> = Forever::new();
|
||||
static ALARM_LOW: Forever<rtc::Alarm<pac::RTC1>> = Forever::new();
|
||||
static ALARM_LOW: Forever<rtc::Alarm<peripherals::RTC1>> = Forever::new();
|
||||
static EXECUTOR_LOW: Forever<Executor> = 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) };
|
||||
|
111
embassy-nrf-examples/src/bin/ppi.rs
Normal file
111
embassy-nrf-examples/src/bin/ppi.rs
Normal file
@ -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<Executor> = Forever::new();
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
info!("Hello World!");
|
||||
|
||||
let executor = EXECUTOR.put(Executor::new());
|
||||
executor.run(|spawner| {
|
||||
unwrap!(spawner.spawn(run()));
|
||||
});
|
||||
}
|
@ -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
|
||||
|
@ -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<rtc::RTC<pac::RTC1>> = Forever::new();
|
||||
static ALARM: Forever<rtc::Alarm<pac::RTC1>> = Forever::new();
|
||||
static RTC: Forever<rtc::RTC<peripherals::RTC1>> = Forever::new();
|
||||
static ALARM: Forever<rtc::Alarm<peripherals::RTC1>> = Forever::new();
|
||||
static EXECUTOR: Forever<Executor> = 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());
|
||||
|
@ -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<rtc::RTC<embassy_nrf::pac::RTC1>> = 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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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<rtc::RTC<pac::RTC1>> = Forever::new();
|
||||
static ALARM: Forever<rtc::Alarm<pac::RTC1>> = Forever::new();
|
||||
static RTC: Forever<rtc::RTC<peripherals::RTC1>> = Forever::new();
|
||||
static ALARM: Forever<rtc::Alarm<peripherals::RTC1>> = Forever::new();
|
||||
static EXECUTOR: Forever<Executor> = 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) };
|
||||
|
||||
|
@ -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<rtc::RTC<pac::RTC1>> = Forever::new();
|
||||
static ALARM: Forever<rtc::Alarm<pac::RTC1>> = Forever::new();
|
||||
static RTC: Forever<rtc::RTC<peripherals::RTC1>> = Forever::new();
|
||||
static ALARM: Forever<rtc::Alarm<peripherals::RTC1>> = Forever::new();
|
||||
static EXECUTOR: Forever<Executor> = 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());
|
@ -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<rtc::RTC<pac::RTC1>> = Forever::new();
|
||||
static ALARM: Forever<rtc::Alarm<pac::RTC1>> = Forever::new();
|
||||
static RTC: Forever<rtc::RTC<peripherals::RTC1>> = Forever::new();
|
||||
static ALARM: Forever<rtc::Alarm<peripherals::RTC1>> = Forever::new();
|
||||
static EXECUTOR: Forever<Executor> = 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()));
|
||||
});
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
#![macro_use]
|
||||
|
||||
use defmt_rtt as _; // global logger
|
||||
use nrf52840_hal as _;
|
||||
use panic_probe as _;
|
||||
|
||||
pub use defmt::*;
|
||||
|
@ -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" ]}
|
||||
|
@ -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<State<'a, U, T, P1, P2>>,
|
||||
pub struct BufferedUarte<'d, U: UarteInstance, T: TimerInstance> {
|
||||
inner: PeripheralMutex<State<'d, U, T>>,
|
||||
}
|
||||
|
||||
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<Target = U> + 'd,
|
||||
timer: impl PeripheralBorrow<Target = T> + 'd,
|
||||
ppi_ch1: impl PeripheralBorrow<Target = impl ConfigurableChannel> + 'd,
|
||||
ppi_ch2: impl PeripheralBorrow<Target = impl ConfigurableChannel> + 'd,
|
||||
irq: impl PeripheralBorrow<Target = U::Interrupt> + 'd,
|
||||
rxd: impl PeripheralBorrow<Target = impl GpioPin> + 'd,
|
||||
txd: impl PeripheralBorrow<Target = impl GpioPin> + 'd,
|
||||
cts: impl PeripheralBorrow<Target = impl GpioOptionalPin> + 'd,
|
||||
rts: impl PeripheralBorrow<Target = impl GpioOptionalPin> + '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();
|
||||
|
||||
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()) });
|
||||
|
||||
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()
|
||||
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()) });
|
||||
|
||||
// Enable UARTE instance
|
||||
uarte.enable.write(|w| w.enable().enabled());
|
||||
|
||||
// Enable interrupts
|
||||
uarte.intenset.write(|w| w.endrx().set().endtx().set());
|
||||
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<State<'a, U, T, P1, P2>>> {
|
||||
fn inner(self: Pin<&mut Self>) -> Pin<&mut PeripheralMutex<State<'d, U, T>>> {
|
||||
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<Result<&[u8]>> {
|
||||
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<Result<usize>> {
|
||||
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<Target = pac::uarte0::RegisterBlock> + 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<Target = pac::timer0::RegisterBlock> + sealed::TimerInstance
|
||||
{
|
||||
}
|
||||
impl TimerInstance for crate::pac::TIMER0 {}
|
||||
impl TimerInstance for crate::pac::TIMER1 {}
|
||||
impl TimerInstance for crate::pac::TIMER2 {}
|
||||
|
447
embassy-nrf/src/gpio.rs
Normal file
447
embassy-nrf/src/gpio.rs
Normal file
@ -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<Target = T> + '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<bool, Self::Error> {
|
||||
self.is_low().map(|v| !v)
|
||||
}
|
||||
|
||||
fn is_low(&self) -> Result<bool, Self::Error> {
|
||||
Ok(self.pin.block().in_.read().bits() & (1 << self.pin.pin()) == 0)
|
||||
}
|
||||
}
|
||||
|
||||
/// 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<Target = T> + '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<bool, Self::Error> {
|
||||
self.is_set_low().map(|v| !v)
|
||||
}
|
||||
|
||||
/// Is the output pin set as low?
|
||||
fn is_set_low(&self) -> Result<bool, Self::Error> {
|
||||
Ok(self.pin.block().out.read().bits() & (1 << self.pin.pin()) == 0)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) mod sealed {
|
||||
use super::*;
|
||||
|
||||
pub trait Pin {
|
||||
fn pin_port(&self) -> u8;
|
||||
|
||||
#[inline]
|
||||
fn _pin(&self) -> u8 {
|
||||
#[cfg(any(feature = "52833", feature = "52840"))]
|
||||
{
|
||||
self.pin_port() % 32
|
||||
}
|
||||
|
||||
#[cfg(not(any(feature = "52833", feature = "52840")))]
|
||||
{
|
||||
self.pin_port()
|
||||
}
|
||||
}
|
||||
|
||||
#[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<AnyPin>`.
|
||||
#[inline]
|
||||
fn degrade_optional(mut self) -> Option<AnyPin> {
|
||||
self.pin_mut()
|
||||
.map(|pin| unsafe { core::ptr::read(pin) }.degrade())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Pin> sealed::OptionalPin for T {}
|
||||
impl<T: Pin> 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);
|
||||
}
|
@ -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,25 +41,15 @@ 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,
|
||||
}
|
||||
|
||||
impl Gpiote {
|
||||
pub fn new(gpiote: GPIOTE, irq: interrupt::GPIOTE) -> (Self, Channels) {
|
||||
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")))]
|
||||
@ -116,34 +63,23 @@ impl Gpiote {
|
||||
}
|
||||
|
||||
// Enable interrupts
|
||||
gpiote.events_port.write(|w| w);
|
||||
gpiote.intenset.write(|w| w.port().set());
|
||||
irq.set_handler(Self::on_irq);
|
||||
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();
|
||||
|
||||
(
|
||||
Self(()),
|
||||
Channels {
|
||||
ch0: Ch0(()),
|
||||
ch1: Ch1(()),
|
||||
ch2: Ch2(()),
|
||||
ch3: Ch3(()),
|
||||
ch4: Ch4(()),
|
||||
ch5: Ch5(()),
|
||||
ch6: Ch6(()),
|
||||
ch7: Ch7(()),
|
||||
},
|
||||
)
|
||||
}
|
||||
Initialized { _private: () }
|
||||
}
|
||||
|
||||
unsafe fn on_irq(_ctx: *mut ()) {
|
||||
let g = &*GPIOTE::ptr();
|
||||
unsafe fn on_irq(_ctx: *mut ()) {
|
||||
let g = &*pac::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 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();
|
||||
}
|
||||
}
|
||||
|
||||
@ -155,216 +91,15 @@ impl Gpiote {
|
||||
#[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;
|
||||
let bits = p.latch.read().bits();
|
||||
for pin in BitIter(bits) {
|
||||
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(());
|
||||
PORT_WAKERS[port * 32 + pin as usize].wake();
|
||||
}
|
||||
p.latch.write(|w| w.bits(bits));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn pin_num<T>(pin: &GpioPin<T>) -> 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<T>(pin: &GpioPin<T>) -> &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<T>(pin: &GpioPin<T>) -> &pac_gpio::PIN_CNF {
|
||||
&pin_block(pin).pin_cnf[pin.pin() as usize]
|
||||
}
|
||||
|
||||
pub struct InputChannel<C: ChannelID, T> {
|
||||
ch: C,
|
||||
pin: GpioPin<Input<T>>,
|
||||
}
|
||||
|
||||
impl<C: ChannelID, T> Drop for InputChannel<C, T> {
|
||||
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<C: ChannelID, T> InputChannel<C, T> {
|
||||
pub fn new(
|
||||
_gpiote: Gpiote,
|
||||
ch: C,
|
||||
pin: GpioPin<Input<T>>,
|
||||
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<Input<T>>) {
|
||||
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<Input<T>> {
|
||||
&self.pin
|
||||
}
|
||||
}
|
||||
|
||||
pub struct OutputChannel<C: ChannelID, T> {
|
||||
ch: C,
|
||||
pin: GpioPin<Output<T>>,
|
||||
}
|
||||
|
||||
impl<C: ChannelID, T> Drop for OutputChannel<C, T> {
|
||||
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<C: ChannelID, T> OutputChannel<C, T> {
|
||||
pub fn new(
|
||||
_gpiote: Gpiote,
|
||||
ch: C,
|
||||
pin: GpioPin<Output<T>>,
|
||||
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<Output<T>>) {
|
||||
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<u32, _TASKS_OUT> {
|
||||
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<u32, _TASKS_CLR> {
|
||||
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<u32, _TASKS_SET> {
|
||||
let g = unsafe { &*GPIOTE::ptr() };
|
||||
let index = self.ch.number();
|
||||
|
||||
&g.tasks_set[index]
|
||||
}
|
||||
}
|
||||
|
||||
struct BitIter(u32);
|
||||
@ -383,72 +118,296 @@ impl Iterator for BitIter {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct GpiotePin<T> {
|
||||
pin: GpioPin<Input<T>>,
|
||||
/// GPIOTE channel driver in input mode
|
||||
pub struct InputChannel<'d, C: Channel, T: GpioPin> {
|
||||
ch: C,
|
||||
pin: Input<'d, T>,
|
||||
}
|
||||
|
||||
impl<T> Unpin for GpiotePin<T> {}
|
||||
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<T> GpiotePin<T> {
|
||||
pub fn new(_gpiote: Gpiote, pin: GpioPin<Input<T>>) -> 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<bool, Self::Error> {
|
||||
self.pin.is_high()
|
||||
}
|
||||
|
||||
fn is_low(&self) -> Result<bool, Self::Error> {
|
||||
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<T: 'static> WaitForHigh for GpiotePin<T> {
|
||||
type Future<'a> = PortInputFuture<'a, T>;
|
||||
impl<'d, T: GpioPin> InputPin for PortInput<'d, T> {
|
||||
type Error = Infallible;
|
||||
|
||||
fn is_high(&self) -> Result<bool, Self::Error> {
|
||||
self.pin.is_high()
|
||||
}
|
||||
|
||||
fn is_low(&self) -> Result<bool, Self::Error> {
|
||||
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<T: 'static> WaitForLow for GpiotePin<T> {
|
||||
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<T> Deref for GpiotePin<T> {
|
||||
type Target = GpioPin<Input<T>>;
|
||||
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<Input<T>>,
|
||||
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<Self::Output> {
|
||||
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);
|
||||
|
@ -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<T>(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,
|
||||
}
|
||||
|
255
embassy-nrf/src/ppi.rs
Normal file
255
embassy-nrf/src/ppi.rs
Normal file
@ -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<Target = C> + '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<T>(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<T>(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);
|
@ -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<Output<PushPull>>,
|
||||
pub csn: GpioPin<Output<PushPull>>,
|
||||
pub io0: GpioPin<Output<PushPull>>,
|
||||
pub io1: GpioPin<Output<PushPull>>,
|
||||
pub io2: Option<GpioPin<Output<PushPull>>>,
|
||||
pub io3: Option<GpioPin<Output<PushPull>>>,
|
||||
}
|
||||
|
||||
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<DeepPowerDownConfig>,
|
||||
}
|
||||
|
||||
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<State>,
|
||||
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<Target = T> + 'd,
|
||||
irq: impl PeripheralBorrow<Target = T::Interrupt> + 'd,
|
||||
sck: impl PeripheralBorrow<Target = impl GpioPin> + 'd,
|
||||
csn: impl PeripheralBorrow<Target = impl GpioPin> + 'd,
|
||||
io0: impl PeripheralBorrow<Target = impl GpioPin> + 'd,
|
||||
io1: impl PeripheralBorrow<Target = impl GpioPin> + 'd,
|
||||
io2: impl PeripheralBorrow<Target = impl GpioPin> + 'd,
|
||||
io3: impl PeripheralBorrow<Target = impl GpioPin> + '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(),
|
||||
},
|
||||
peri: qspi,
|
||||
irq,
|
||||
),
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sleep(self: Pin<&mut Self>) {
|
||||
self.inner().with(|s, _| {
|
||||
pub fn sleep(mut self: Pin<&mut Self>) {
|
||||
let r = unsafe { self.as_mut().get_unchecked_mut() }.peri.regs();
|
||||
|
||||
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());
|
||||
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 = {:?}", s.inner.status.read().bits());
|
||||
info!("flash: state = {:?}", r.status.read().bits());
|
||||
|
||||
s.inner
|
||||
.tasks_deactivate
|
||||
.write(|w| w.tasks_deactivate().set_bit());
|
||||
});
|
||||
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,14 +184,14 @@ 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| {
|
||||
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);
|
||||
@ -210,13 +202,13 @@ impl Qspi {
|
||||
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();
|
||||
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;
|
||||
@ -227,34 +219,38 @@ impl Qspi {
|
||||
resp[i] = (dat1 >> (i * 8)) as u8;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
bomb.defuse();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn inner(self: Pin<&mut Self>) -> Pin<&mut PeripheralMutex<State>> {
|
||||
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<Output = ()> + 'a {
|
||||
poll_fn(move |cx| {
|
||||
self.as_mut().inner().with(|s, _irq| {
|
||||
if s.inner.events_ready.read().bits() != 0 {
|
||||
let r = this.peri.regs();
|
||||
|
||||
if r.events_ready.read().bits() != 0 {
|
||||
r.events_ready.reset();
|
||||
return Poll::Ready(());
|
||||
}
|
||||
s.waker.register(cx.waker());
|
||||
|
||||
wake_on_interrupt(&mut this.irq, cx.waker());
|
||||
|
||||
Poll::Pending
|
||||
})
|
||||
})
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
impl Flash for Qspi {
|
||||
type ReadFuture<'a> = impl Future<Output = Result<(), Error>> + 'a;
|
||||
type WriteFuture<'a> = impl Future<Output = Result<(), Error>> + 'a;
|
||||
type ErasePageFuture<'a> = impl Future<Output = Result<(), Error>> + 'a;
|
||||
impl<'d, T: Instance> Flash for Qspi<'d, T> {
|
||||
#[rustfmt::skip]
|
||||
type ReadFuture<'a> where Self: 'a = impl Future<Output = Result<(), Error>> + 'a;
|
||||
#[rustfmt::skip]
|
||||
type WriteFuture<'a> where Self: 'a = impl Future<Output = Result<(), Error>> + 'a;
|
||||
#[rustfmt::skip]
|
||||
type ErasePageFuture<'a> where Self: 'a = impl Future<Output = Result<(), Error>> + '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
|
||||
let r = unsafe { self.as_mut().get_unchecked_mut() }.peri.regs();
|
||||
|
||||
r.read
|
||||
.src
|
||||
.write(|w| unsafe { w.src().bits(address as u32) });
|
||||
s.inner
|
||||
.read
|
||||
r.read
|
||||
.dst
|
||||
.write(|w| unsafe { w.dst().bits(data.as_ptr() as u32) });
|
||||
s.inner
|
||||
.read
|
||||
r.read
|
||||
.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_readstart
|
||||
.write(|w| w.tasks_readstart().bit(true));
|
||||
});
|
||||
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
|
||||
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) });
|
||||
s.inner
|
||||
.write
|
||||
r.write
|
||||
.dst
|
||||
.write(|w| unsafe { w.dst().bits(address as u32) });
|
||||
s.inner
|
||||
.write
|
||||
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
|
||||
let r = unsafe { self.as_mut().get_unchecked_mut() }.peri.regs();
|
||||
r.erase
|
||||
.ptr
|
||||
.write(|w| unsafe { w.ptr().bits(address as u32) });
|
||||
s.inner.erase.len.write(|w| w.len()._4kb());
|
||||
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);
|
||||
|
@ -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<T: Instance> RTC<T> {
|
||||
}
|
||||
|
||||
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<T: Instance> RTC<T> {
|
||||
}
|
||||
|
||||
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<T: Instance> RTC<T> {
|
||||
|
||||
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<T: Instance> RTC<T> {
|
||||
|
||||
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<T: Instance> RTC<T> {
|
||||
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<T: Instance> RTC<T> {
|
||||
// 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<T: Instance> embassy::time::Clock for RTC<T> {
|
||||
// `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<T: Instance> embassy::time::Alarm for Alarm<T> {
|
||||
}
|
||||
|
||||
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<Target = rtc0::RegisterBlock> + 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);
|
||||
|
242
embassy-nrf/src/saadc.rs
Normal file
242
embassy-nrf/src/saadc.rs
Normal file
@ -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<Target = peripherals::SAADC> + 'd,
|
||||
irq: impl PeripheralBorrow<Target = interrupt::SAADC> + 'd,
|
||||
positive_pin: impl PeripheralBorrow<Target = T> + '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<Output = i16> + '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<Output = i16> + '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,
|
||||
}
|
@ -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,46 +27,60 @@ pub enum Error {
|
||||
DMABufferNotInDataMemory,
|
||||
}
|
||||
|
||||
struct State<T: Instance> {
|
||||
spim: T,
|
||||
waker: WakerRegistration,
|
||||
}
|
||||
|
||||
pub struct Spim<T: Instance> {
|
||||
inner: PeripheralMutex<State<T>>,
|
||||
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<T: Instance> Spim<T> {
|
||||
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<Target = T> + 'd,
|
||||
irq: impl PeripheralBorrow<Target = T::Interrupt> + 'd,
|
||||
sck: impl PeripheralBorrow<Target = impl GpioPin> + 'd,
|
||||
miso: impl PeripheralBorrow<Target = impl OptionalPin> + 'd,
|
||||
mosi: impl PeripheralBorrow<Target = impl OptionalPin> + '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.mode.polarity {
|
||||
Polarity::IdleHigh => {
|
||||
sck.set_high();
|
||||
if let Some(mosi) = mosi.pin_mut() {
|
||||
mosi.set_high();
|
||||
}
|
||||
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()),
|
||||
}
|
||||
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<T: Instance> Spim<T> {
|
||||
r.intenclr.write(|w| unsafe { w.bits(0xFFFF_FFFF) });
|
||||
|
||||
Self {
|
||||
inner: PeripheralMutex::new(
|
||||
State {
|
||||
spim,
|
||||
waker: WakerRegistration::new(),
|
||||
},
|
||||
peri: spim,
|
||||
irq,
|
||||
),
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
fn inner(self: Pin<&mut Self>) -> Pin<&mut PeripheralMutex<State<T>>> {
|
||||
unsafe { Pin::new_unchecked(&mut self.get_unchecked_mut().inner) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Instance> FullDuplex<u8> for Spim<T> {
|
||||
impl<'d, T: Instance> FullDuplex<u8> for Spim<'d, T> {
|
||||
type Error = Error;
|
||||
|
||||
#[rustfmt::skip]
|
||||
@ -133,29 +141,28 @@ impl<T: Instance> FullDuplex<u8> for Spim<T> {
|
||||
type WriteReadFuture<'a> where Self: 'a = impl Future<Output = Result<(), Self::Error>> + '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);
|
||||
|
||||
let r = s.spim.regs();
|
||||
let r = this.peri.regs();
|
||||
|
||||
// Set up the DMA write.
|
||||
r.txd
|
||||
@ -184,19 +191,20 @@ impl<T: Instance> FullDuplex<u8> for Spim<T> {
|
||||
// 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();
|
||||
let r = this.peri.regs();
|
||||
|
||||
if r.events_end.read().bits() != 0 {
|
||||
r.events_end.reset();
|
||||
return Poll::Ready(());
|
||||
}
|
||||
s.waker.register(cx.waker());
|
||||
|
||||
wake_on_interrupt(&mut this.irq, cx.waker());
|
||||
|
||||
Poll::Pending
|
||||
})
|
||||
})
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
@ -204,51 +212,41 @@ impl<T: Instance> FullDuplex<u8> for Spim<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<U: Instance> PeripheralState for State<U> {
|
||||
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);
|
||||
|
76
embassy-nrf/src/system.rs
Normal file
76
embassy-nrf/src/system.rs
Normal file
@ -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 {}
|
||||
}
|
43
embassy-nrf/src/timer.rs
Normal file
43
embassy-nrf/src/timer.rs
Normal file
@ -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);
|
@ -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<T: Instance> {
|
||||
peri: T,
|
||||
|
||||
endrx_waker: AtomicWaker,
|
||||
endtx_waker: AtomicWaker,
|
||||
}
|
||||
|
||||
/// Interface to the UARTE peripheral
|
||||
pub struct Uarte<T>
|
||||
where
|
||||
T: Instance,
|
||||
{
|
||||
instance: T,
|
||||
irq: T::Interrupt,
|
||||
pins: Pins,
|
||||
pub struct Uarte<'d, T: Instance> {
|
||||
inner: Peripheral<State<T>>,
|
||||
phantom: PhantomData<&'d mut T>,
|
||||
}
|
||||
|
||||
pub struct State {
|
||||
tx_done: Signal<()>,
|
||||
rx_done: Signal<u32>,
|
||||
}
|
||||
|
||||
impl<T> Uarte<T>
|
||||
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<Target = T> + 'd,
|
||||
irq: impl PeripheralBorrow<Target = T::Interrupt> + 'd,
|
||||
rxd: impl PeripheralBorrow<Target = impl GpioPin> + 'd,
|
||||
txd: impl PeripheralBorrow<Target = impl GpioPin> + 'd,
|
||||
cts: impl PeripheralBorrow<Target = impl GpioOptionalPin> + 'd,
|
||||
rts: impl PeripheralBorrow<Target = impl GpioOptionalPin> + '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();
|
||||
|
||||
pins.txd.set_high().unwrap();
|
||||
uarte.psel.txd.write(|w| {
|
||||
unsafe { w.bits(pins.txd.psel_bits()) };
|
||||
w.connect().connected()
|
||||
});
|
||||
assert!(r.enable.read().enable().is_disabled());
|
||||
|
||||
// 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()
|
||||
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()) });
|
||||
|
||||
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()
|
||||
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));
|
||||
|
||||
uarte.baudrate.write(|w| w.baudrate().variant(baudrate));
|
||||
uarte.config.write(|w| w.parity().variant(parity));
|
||||
// Disable all interrupts
|
||||
r.intenclr.write(|w| unsafe { w.bits(0xFFFF_FFFF) });
|
||||
|
||||
// Enable interrupts
|
||||
uarte.events_endtx.reset();
|
||||
uarte.events_endrx.reset();
|
||||
uarte
|
||||
.intenset
|
||||
.write(|w| w.endtx().set().txstopped().set().endrx().set().rxto().set());
|
||||
// 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();
|
||||
|
||||
// Register ISR
|
||||
irq.set_handler(Self::on_irq);
|
||||
irq.unpend();
|
||||
irq.enable();
|
||||
// Enable
|
||||
r.enable.write(|w| w.enable().enabled());
|
||||
|
||||
Uarte {
|
||||
instance: uarte,
|
||||
Self {
|
||||
inner: Peripheral::new(
|
||||
irq,
|
||||
pins,
|
||||
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<State<T>>> {
|
||||
unsafe { Pin::new_unchecked(&mut self.get_unchecked_mut().inner) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Instance> PeripheralState for State<T> {
|
||||
type Interrupt = T::Interrupt;
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
fn enable(&mut self) {
|
||||
trace!("enable");
|
||||
self.instance.enable.write(|w| w.enable().enabled());
|
||||
if r.events_rxto.read().bits() != 0 {
|
||||
r.intenclr.write(|w| w.rxto().clear());
|
||||
}
|
||||
|
||||
fn tx_started(&self) -> bool {
|
||||
self.instance.events_txstarted.read().bits() != 0
|
||||
if r.events_txstopped.read().bits() != 0 {
|
||||
r.intenclr.write(|w| w.txstopped().clear());
|
||||
}
|
||||
|
||||
fn rx_started(&self) -> bool {
|
||||
self.instance.events_rxstarted.read().bits() != 0
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn on_irq(_ctx: *mut ()) {
|
||||
let uarte = &*pac::UARTE0::ptr();
|
||||
impl<'a, T: Instance> Drop for Uarte<'a, T> {
|
||||
fn drop(&mut self) {
|
||||
info!("uarte drop");
|
||||
|
||||
let mut try_disable = false;
|
||||
let s = unsafe { Pin::new_unchecked(&mut self.inner) }.state();
|
||||
let r = s.peri.regs();
|
||||
|
||||
if uarte.events_endtx.read().bits() != 0 {
|
||||
uarte.events_endtx.reset();
|
||||
trace!("endtx");
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
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);
|
||||
|
||||
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(());
|
||||
}
|
||||
|
||||
if uarte.events_txstopped.read().bits() != 0 {
|
||||
uarte.events_txstopped.reset();
|
||||
trace!("txstopped");
|
||||
try_disable = true;
|
||||
}
|
||||
|
||||
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 uarte.events_rxto.read().bits() != 0 {
|
||||
uarte.events_rxto.reset();
|
||||
trace!("rxto");
|
||||
try_disable = true;
|
||||
}
|
||||
|
||||
// Disable the peripheral if not active.
|
||||
if try_disable
|
||||
&& uarte.events_txstarted.read().bits() == 0
|
||||
&& uarte.events_rxstarted.read().bits() == 0
|
||||
// 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<Output = Result<(), Error>> + '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<T: Instance> embassy::traits::uart::Uart for Uarte<T> {
|
||||
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<Output = Result<(), Error>> + '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<T>,
|
||||
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<Self::Output> {
|
||||
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
|
||||
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
|
||||
} else {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Future for the [`Uarte::receive()`] method.
|
||||
pub struct ReceiveFuture<'a, T>
|
||||
where
|
||||
T: Instance,
|
||||
{
|
||||
uarte: &'a mut Uarte<T>,
|
||||
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<Self::Output> {
|
||||
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();
|
||||
})
|
||||
.await;
|
||||
|
||||
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 _) });
|
||||
r.events_txstarted.reset();
|
||||
drop.defuse();
|
||||
|
||||
trace!("startrx");
|
||||
uarte.instance.tasks_startrx.write(|w| unsafe { w.bits(1) });
|
||||
while !uarte.rx_started() {} // Make sure reception has started
|
||||
|
||||
Poll::Pending
|
||||
}
|
||||
Poll::Pending => Poll::Pending,
|
||||
Poll::Ready(_) => Poll::Ready(Ok(())),
|
||||
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<Target = pac::uarte0::RegisterBlock> + 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() }
|
||||
}
|
||||
}
|
||||
|
||||
#[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 for peripherals::$type {
|
||||
type Interrupt = interrupt::$irq;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_instance!(UARTE0, UARTE0_UART0);
|
||||
#[cfg(any(feature = "52833", feature = "52840", feature = "9160"))]
|
||||
impl_instance!(UARTE1, UARTE1);
|
||||
|
@ -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<Executor> = Forever::new();
|
||||
|
@ -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<USART, TSTREAM, RSTREAM, CHANNEL> Uart for Serial<USART, TSTREAM, RSTREAM, CHANNEL>
|
||||
impl<USART, TSTREAM, RSTREAM, CHANNEL> Read for Serial<USART, TSTREAM, RSTREAM, CHANNEL>
|
||||
where
|
||||
USART: serial::Instance
|
||||
+ PeriAddress<MemSize = u8>
|
||||
@ -101,56 +96,19 @@ where
|
||||
RSTREAM: Stream + WithInterrupt + 'static,
|
||||
CHANNEL: Channel + 'static,
|
||||
{
|
||||
type SendFuture<'a> = impl Future<Output = Result<(), Error>> + 'a;
|
||||
type ReceiveFuture<'a> = impl Future<Output = Result<(), Error>> + '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<Output = Result<(), Error>> + '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<USART, TSTREAM, RSTREAM, CHANNEL> IdleUart for Serial<USART, TSTREAM, RSTREAM, CHANNEL>
|
||||
impl<USART, TSTREAM, RSTREAM, CHANNEL> Write for Serial<USART, TSTREAM, RSTREAM, CHANNEL>
|
||||
where
|
||||
USART: serial::Instance
|
||||
+ PeriAddress<MemSize = u8>
|
||||
@ -187,20 +145,74 @@ where
|
||||
RSTREAM: Stream + WithInterrupt + 'static,
|
||||
CHANNEL: Channel + 'static,
|
||||
{
|
||||
type ReceiveFuture<'a> = impl Future<Output = Result<usize, Error>> + 'a;
|
||||
type WriteFuture<'a> = impl Future<Output = Result<(), Error>> + '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<USART, TSTREAM, RSTREAM, CHANNEL> ReadUntilIdle for Serial<USART, TSTREAM, RSTREAM, CHANNEL>
|
||||
where
|
||||
USART: serial::Instance
|
||||
+ PeriAddress<MemSize = u8>
|
||||
+ DMASet<TSTREAM, CHANNEL, MemoryToPeripheral>
|
||||
+ DMASet<RSTREAM, CHANNEL, PeripheralToMemory>
|
||||
+ WithInterrupt
|
||||
+ 'static,
|
||||
TSTREAM: Stream + WithInterrupt + 'static,
|
||||
RSTREAM: Stream + WithInterrupt + 'static,
|
||||
CHANNEL: Channel + 'static,
|
||||
{
|
||||
type ReadUntilIdleFuture<'a> = impl Future<Output = Result<usize, Error>> + '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)
|
||||
}
|
||||
|
@ -11,9 +11,17 @@ pub enum Error {
|
||||
}
|
||||
|
||||
pub trait Flash {
|
||||
type ReadFuture<'a>: Future<Output = Result<(), Error>>;
|
||||
type WriteFuture<'a>: Future<Output = Result<(), Error>>;
|
||||
type ErasePageFuture<'a>: Future<Output = Result<(), Error>>;
|
||||
type ReadFuture<'a>: Future<Output = Result<(), Error>>
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
type WriteFuture<'a>: Future<Output = Result<(), Error>>
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
type ErasePageFuture<'a>: Future<Output = Result<(), Error>>
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
/// Reads data from the flash device.
|
||||
///
|
||||
|
@ -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<Output = Result<(), Error>>;
|
||||
type SendFuture<'a>: Future<Output = Result<(), Error>>;
|
||||
/// 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<Output = Result<(), Error>>
|
||||
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<Output = Result<usize, Error>>;
|
||||
pub trait ReadUntilIdle {
|
||||
type ReadUntilIdleFuture<'a>: Future<Output = Result<usize, Error>>
|
||||
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<Output = Result<(), Error>>
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
fn write<'a>(self: Pin<&'a mut Self>, buf: &'a [u8]) -> Self::WriteFuture<'a>;
|
||||
}
|
||||
|
@ -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<F: Future + 'static> Task<F> {
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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<T: Interrupt + ?Sized> 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<T: Interrupt + ?Sized> InterruptExt for T {
|
||||
#[inline]
|
||||
fn disable(&self) {
|
||||
NVIC::mask(NrWrap(self.number()));
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -31,6 +31,7 @@ unsafe impl<T> Send for Forever<T> {}
|
||||
unsafe impl<T> Sync for Forever<T> {}
|
||||
|
||||
impl<T> Forever<T> {
|
||||
#[inline(always)]
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
used: AtomicBool::new(false),
|
||||
@ -43,10 +44,11 @@ impl<T> Forever<T> {
|
||||
/// 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<T> Forever<T> {
|
||||
}
|
||||
}
|
||||
|
||||
#[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();
|
||||
|
@ -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;
|
||||
}
|
||||
|
24
embassy/src/util/on_drop.rs
Normal file
24
embassy/src/util/on_drop.rs
Normal file
@ -0,0 +1,24 @@
|
||||
use core::mem;
|
||||
use core::mem::MaybeUninit;
|
||||
|
||||
pub struct OnDrop<F: FnOnce()> {
|
||||
f: MaybeUninit<F>,
|
||||
}
|
||||
|
||||
impl<F: FnOnce()> OnDrop<F> {
|
||||
pub fn new(f: F) -> Self {
|
||||
Self {
|
||||
f: MaybeUninit::new(f),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn defuse(self) {
|
||||
mem::forget(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: FnOnce()> Drop for OnDrop<F> {
|
||||
fn drop(&mut self) {
|
||||
unsafe { self.f.as_ptr().read()() }
|
||||
}
|
||||
}
|
@ -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<T: Send> Signal<T> {
|
||||
}
|
||||
}
|
||||
|
||||
// ==========
|
||||
|
||||
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()
|
||||
});
|
||||
|
@ -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<TaskHeader>,
|
||||
}
|
||||
|
||||
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) };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -49,11 +49,11 @@ impl WakerRegistration {
|
||||
}
|
||||
|
||||
/// Utility struct to register and wake a waker.
|
||||
pub struct AtomicWakerRegistration {
|
||||
pub struct AtomicWaker {
|
||||
waker: Mutex<Cell<Option<Waker>>>,
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
})
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user