From 7be4337de96de9948632bdc2fc5067d0c4a76b33 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Fri, 24 Feb 2023 13:01:41 -0600 Subject: [PATCH 01/51] Add `#[must_use]` to all futures --- embassy-futures/src/yield_now.rs | 1 + embassy-nrf/src/gpiote.rs | 1 + embassy-rp/src/dma.rs | 1 + embassy-rp/src/gpio.rs | 1 + embassy-rp/src/pio.rs | 3 +++ embassy-stm32/src/dma/mod.rs | 1 + embassy-stm32/src/exti.rs | 1 + embassy-sync/src/channel.rs | 4 ++++ embassy-sync/src/pipe.rs | 2 ++ embassy-time/src/timer.rs | 1 + 10 files changed, 16 insertions(+) diff --git a/embassy-futures/src/yield_now.rs b/embassy-futures/src/yield_now.rs index 13b10377..bb3c67d1 100644 --- a/embassy-futures/src/yield_now.rs +++ b/embassy-futures/src/yield_now.rs @@ -24,6 +24,7 @@ pub fn yield_now() -> impl Future { YieldNowFuture { yielded: false } } +#[must_use = "futures do nothing unless you `.await` or poll them"] struct YieldNowFuture { yielded: bool, } diff --git a/embassy-nrf/src/gpiote.rs b/embassy-nrf/src/gpiote.rs index e1816eb9..66c682b4 100644 --- a/embassy-nrf/src/gpiote.rs +++ b/embassy-nrf/src/gpiote.rs @@ -315,6 +315,7 @@ impl<'d, C: Channel, T: GpioPin> OutputChannel<'d, C, T> { // ======================= +#[must_use = "futures do nothing unless you `.await` or poll them"] pub(crate) struct PortInputFuture<'a> { pin: PeripheralRef<'a, AnyPin>, } diff --git a/embassy-rp/src/dma.rs b/embassy-rp/src/dma.rs index fd281fd5..05adcecd 100644 --- a/embassy-rp/src/dma.rs +++ b/embassy-rp/src/dma.rs @@ -151,6 +151,7 @@ fn copy_inner<'a, C: Channel>( Transfer::new(ch) } +#[must_use = "futures do nothing unless you `.await` or poll them"] pub struct Transfer<'a, C: Channel> { channel: PeripheralRef<'a, C>, } diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs index 76d4281f..fd3b0556 100644 --- a/embassy-rp/src/gpio.rs +++ b/embassy-rp/src/gpio.rs @@ -193,6 +193,7 @@ unsafe fn IO_IRQ_BANK0() { } } +#[must_use = "futures do nothing unless you `.await` or poll them"] struct InputFuture<'a, T: Pin> { pin: PeripheralRef<'a, T>, level: InterruptTrigger, diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index 2fb2783d..3c7abea2 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -120,6 +120,7 @@ unsafe fn PIO1_IRQ_0() { } /// Future that waits for TX-FIFO to become writable +#[must_use = "futures do nothing unless you `.await` or poll them"] pub struct FifoOutFuture<'a, PIO: PioInstance, SM: PioStateMachine + Unpin> { sm: &'a mut SM, pio: PhantomData, @@ -182,6 +183,7 @@ impl<'d, PIO: PioInstance, SM: PioStateMachine + Unpin> Drop for FifoOutFuture<' } /// Future that waits for RX-FIFO to become readable +#[must_use = "futures do nothing unless you `.await` or poll them"] pub struct FifoInFuture<'a, PIO: PioInstance, SM: PioStateMachine> { sm: &'a mut SM, pio: PhantomData, @@ -241,6 +243,7 @@ impl<'d, PIO: PioInstance, SM: PioStateMachine> Drop for FifoInFuture<'d, PIO, S } /// Future that waits for IRQ +#[must_use = "futures do nothing unless you `.await` or poll them"] pub struct IrqFuture { pio: PhantomData, irq_no: u8, diff --git a/embassy-stm32/src/dma/mod.rs b/embassy-stm32/src/dma/mod.rs index f5a82fb7..0030bd57 100644 --- a/embassy-stm32/src/dma/mod.rs +++ b/embassy-stm32/src/dma/mod.rs @@ -273,6 +273,7 @@ mod transfers { Transfer::new(channel) } + #[must_use = "futures do nothing unless you `.await` or poll them"] pub(crate) struct Transfer<'a, C: Channel> { channel: PeripheralRef<'a, C>, } diff --git a/embassy-stm32/src/exti.rs b/embassy-stm32/src/exti.rs index c9c3ef62..e1ce09a4 100644 --- a/embassy-stm32/src/exti.rs +++ b/embassy-stm32/src/exti.rs @@ -198,6 +198,7 @@ mod eha { } } +#[must_use = "futures do nothing unless you `.await` or poll them"] struct ExtiInputFuture<'a> { pin: u8, phantom: PhantomData<&'a mut AnyPin>, diff --git a/embassy-sync/src/channel.rs b/embassy-sync/src/channel.rs index 76f42d0e..77352874 100644 --- a/embassy-sync/src/channel.rs +++ b/embassy-sync/src/channel.rs @@ -181,6 +181,7 @@ where } /// Future returned by [`Channel::recv`] and [`Receiver::recv`]. +#[must_use = "futures do nothing unless you `.await` or poll them"] pub struct RecvFuture<'ch, M, T, const N: usize> where M: RawMutex, @@ -203,6 +204,7 @@ where } /// Future returned by [`DynamicReceiver::recv`]. +#[must_use = "futures do nothing unless you `.await` or poll them"] pub struct DynamicRecvFuture<'ch, T> { channel: &'ch dyn DynamicChannel, } @@ -219,6 +221,7 @@ impl<'ch, T> Future for DynamicRecvFuture<'ch, T> { } /// Future returned by [`Channel::send`] and [`Sender::send`]. +#[must_use = "futures do nothing unless you `.await` or poll them"] pub struct SendFuture<'ch, M, T, const N: usize> where M: RawMutex, @@ -250,6 +253,7 @@ where impl<'ch, M, T, const N: usize> Unpin for SendFuture<'ch, M, T, N> where M: RawMutex {} /// Future returned by [`DynamicSender::send`]. +#[must_use = "futures do nothing unless you `.await` or poll them"] pub struct DynamicSendFuture<'ch, T> { channel: &'ch dyn DynamicChannel, message: Option, diff --git a/embassy-sync/src/pipe.rs b/embassy-sync/src/pipe.rs index 905686ac..1977005f 100644 --- a/embassy-sync/src/pipe.rs +++ b/embassy-sync/src/pipe.rs @@ -48,6 +48,7 @@ where } /// Future returned by [`Pipe::write`] and [`Writer::write`]. +#[must_use = "futures do nothing unless you `.await` or poll them"] pub struct WriteFuture<'p, M, const N: usize> where M: RawMutex, @@ -110,6 +111,7 @@ where } /// Future returned by [`Pipe::read`] and [`Reader::read`]. +#[must_use = "futures do nothing unless you `.await` or poll them"] pub struct ReadFuture<'p, M, const N: usize> where M: RawMutex, diff --git a/embassy-time/src/timer.rs b/embassy-time/src/timer.rs index bd791b81..f74b5cb2 100644 --- a/embassy-time/src/timer.rs +++ b/embassy-time/src/timer.rs @@ -26,6 +26,7 @@ pub async fn with_timeout(timeout: Duration, fut: F) -> Result Date: Sat, 25 Feb 2023 20:58:28 +0100 Subject: [PATCH 02/51] embassy-net: DNS resolver detects when name is just an IP address and returns immediately --- embassy-net/src/lib.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index bda5f9e1..4ec1b5a7 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -236,6 +236,22 @@ impl Stack { /// Make a query for a given name and return the corresponding IP addresses. #[cfg(feature = "dns")] pub async fn dns_query(&self, name: &str, qtype: dns::DnsQueryType) -> Result, dns::Error> { + // For A and AAAA queries we try detect whether `name` is just an IP address + match qtype { + dns::DnsQueryType::A => { + if let Ok(ip) = name.parse().map(IpAddress::Ipv4) { + return Ok([ip].into_iter().collect()); + } + } + #[cfg(feature = "proto-ipv6")] + dns::DnsQueryType::Aaaa => { + if let Ok(ip) = name.parse().map(IpAddress::Ipv6) { + return Ok([ip].into_iter().collect()); + } + } + _ => {} + } + let query = poll_fn(|cx| { self.with_mut(|s, i| { let socket = s.sockets.get_mut::(i.dns_socket); From bc71230cd07296468f2e03c00f9ceddbab67c9d9 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 26 Feb 2023 21:50:12 +0100 Subject: [PATCH 03/51] examples/std: fix net running out of sockets. --- examples/std/src/bin/net.rs | 2 +- examples/std/src/bin/net_dns.rs | 2 +- examples/std/src/bin/net_udp.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/std/src/bin/net.rs b/examples/std/src/bin/net.rs index 451850d9..e018e18c 100644 --- a/examples/std/src/bin/net.rs +++ b/examples/std/src/bin/net.rs @@ -65,7 +65,7 @@ async fn main_task(spawner: Spawner) { let seed = u64::from_le_bytes(seed); // Init network stack - let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<2>::new()), seed)); + let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<3>::new()), seed)); // Launch network task spawner.spawn(net_task(stack)).unwrap(); diff --git a/examples/std/src/bin/net_dns.rs b/examples/std/src/bin/net_dns.rs index e1cc45a3..d1e1f821 100644 --- a/examples/std/src/bin/net_dns.rs +++ b/examples/std/src/bin/net_dns.rs @@ -65,7 +65,7 @@ async fn main_task(spawner: Spawner) { let seed = u64::from_le_bytes(seed); // Init network stack - let stack: &Stack<_> = &*singleton!(Stack::new(device, config, singleton!(StackResources::<2>::new()), seed)); + let stack: &Stack<_> = &*singleton!(Stack::new(device, config, singleton!(StackResources::<3>::new()), seed)); // Launch network task spawner.spawn(net_task(stack)).unwrap(); diff --git a/examples/std/src/bin/net_udp.rs b/examples/std/src/bin/net_udp.rs index f1923f18..328a0536 100644 --- a/examples/std/src/bin/net_udp.rs +++ b/examples/std/src/bin/net_udp.rs @@ -62,7 +62,7 @@ async fn main_task(spawner: Spawner) { let seed = u64::from_le_bytes(seed); // Init network stack - let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<2>::new()), seed)); + let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<3>::new()), seed)); // Launch network task spawner.spawn(net_task(stack)).unwrap(); From 8fb380b18052c2393ae1dc3466bb87e9402181d8 Mon Sep 17 00:00:00 2001 From: Andres Hurtado Lopez Date: Sun, 26 Feb 2023 18:40:23 -0500 Subject: [PATCH 04/51] RP-PICO UART adding set_baudrate --- embassy-rp/src/uart/mod.rs | 50 +++++++++++++++++++++++++++----------- 1 file changed, 36 insertions(+), 14 deletions(-) diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index bbbf97c0..7540052b 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -296,7 +296,7 @@ impl<'d, T: Instance> Uart<'d, T, Async> { Some(rx_dma.map_into()), config, ) - } + } } impl<'d, T: Instance, M: Mode> Uart<'d, T, M> { @@ -324,6 +324,25 @@ impl<'d, T: Instance, M: Mode> Uart<'d, T, M> { } } + fn baudrate_calculations(baudrate: u32) -> (u32, u32) { + + let clk_base = crate::clocks::clk_peri_freq(); + + let baud_rate_div = (8 * clk_base) / baudrate; + let mut baud_ibrd = baud_rate_div >> 7; + let mut baud_fbrd = ((baud_rate_div & 0x7f) + 1) / 2; + + if baud_ibrd == 0 { + baud_ibrd = 1; + baud_fbrd = 0; + } else if baud_ibrd >= 65535 { + baud_ibrd = 65535; + baud_fbrd = 0; + } + + (baud_ibrd, baud_fbrd) + } + fn init( tx: Option>, rx: Option>, @@ -350,19 +369,7 @@ impl<'d, T: Instance, M: Mode> Uart<'d, T, M> { pin.pad_ctrl().write(|w| w.set_ie(true)); } - let clk_base = crate::clocks::clk_peri_freq(); - - let baud_rate_div = (8 * clk_base) / config.baudrate; - let mut baud_ibrd = baud_rate_div >> 7; - let mut baud_fbrd = ((baud_rate_div & 0x7f) + 1) / 2; - - if baud_ibrd == 0 { - baud_ibrd = 1; - baud_fbrd = 0; - } else if baud_ibrd >= 65535 { - baud_ibrd = 65535; - baud_fbrd = 0; - } + let (baud_ibrd, baud_fbrd) = Uart::::baudrate_calculations(config.baudrate); // Load PL011's baud divisor registers r.uartibrd().write_value(pac::uart::regs::Uartibrd(baud_ibrd)); @@ -400,6 +407,21 @@ impl<'d, T: Instance, M: Mode> Uart<'d, T, M> { }); } } + + pub fn set_baudrate(&mut self, baudrate: u32) { + + let r = T::regs(); + + let (baud_ibrd, baud_fbrd) = Self::baudrate_calculations(baudrate); + + unsafe { + + // Load PL011's baud divisor registers + r.uartibrd().write_value(pac::uart::regs::Uartibrd(baud_ibrd)); + r.uartfbrd().write_value(pac::uart::regs::Uartfbrd(baud_fbrd)); + } + + } } impl<'d, T: Instance, M: Mode> Uart<'d, T, M> { From 7172dfd08379a3732372ada61e25d1c5678fa59f Mon Sep 17 00:00:00 2001 From: Andres Hurtado Lopez Date: Sun, 26 Feb 2023 19:14:25 -0500 Subject: [PATCH 05/51] RP-PICO UART adding set_baudrate: refactoring of methods --- embassy-rp/src/uart/mod.rs | 51 +++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 28 deletions(-) diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index 7540052b..f9cce5c6 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -324,25 +324,6 @@ impl<'d, T: Instance, M: Mode> Uart<'d, T, M> { } } - fn baudrate_calculations(baudrate: u32) -> (u32, u32) { - - let clk_base = crate::clocks::clk_peri_freq(); - - let baud_rate_div = (8 * clk_base) / baudrate; - let mut baud_ibrd = baud_rate_div >> 7; - let mut baud_fbrd = ((baud_rate_div & 0x7f) + 1) / 2; - - if baud_ibrd == 0 { - baud_ibrd = 1; - baud_fbrd = 0; - } else if baud_ibrd >= 65535 { - baud_ibrd = 65535; - baud_fbrd = 0; - } - - (baud_ibrd, baud_fbrd) - } - fn init( tx: Option>, rx: Option>, @@ -369,11 +350,7 @@ impl<'d, T: Instance, M: Mode> Uart<'d, T, M> { pin.pad_ctrl().write(|w| w.set_ie(true)); } - let (baud_ibrd, baud_fbrd) = Uart::::baudrate_calculations(config.baudrate); - - // Load PL011's baud divisor registers - r.uartibrd().write_value(pac::uart::regs::Uartibrd(baud_ibrd)); - r.uartfbrd().write_value(pac::uart::regs::Uartfbrd(baud_fbrd)); + Uart::::set_baudrate_inner(config.baudrate); let (pen, eps) = match config.parity { Parity::ParityNone => (false, false), @@ -408,20 +385,38 @@ impl<'d, T: Instance, M: Mode> Uart<'d, T, M> { } } + + /// sets baudrate on runtime pub fn set_baudrate(&mut self, baudrate: u32) { - + Self::set_baudrate_inner(baudrate); + } + + + fn set_baudrate_inner(baudrate: u32) { let r = T::regs(); - let (baud_ibrd, baud_fbrd) = Self::baudrate_calculations(baudrate); - + let clk_base = crate::clocks::clk_peri_freq(); + + let baud_rate_div = (8 * clk_base) / baudrate; + let mut baud_ibrd = baud_rate_div >> 7; + let mut baud_fbrd = ((baud_rate_div & 0x7f) + 1) / 2; + + if baud_ibrd == 0 { + baud_ibrd = 1; + baud_fbrd = 0; + } else if baud_ibrd >= 65535 { + baud_ibrd = 65535; + baud_fbrd = 0; + } + unsafe { // Load PL011's baud divisor registers r.uartibrd().write_value(pac::uart::regs::Uartibrd(baud_ibrd)); r.uartfbrd().write_value(pac::uart::regs::Uartfbrd(baud_fbrd)); } - } + } impl<'d, T: Instance, M: Mode> Uart<'d, T, M> { From 482ba835c4eec6d3bb006eea3a715360c170e418 Mon Sep 17 00:00:00 2001 From: Andres Hurtado Lopez Date: Sun, 26 Feb 2023 19:20:08 -0500 Subject: [PATCH 06/51] RP-PICO UART adding set_baudrate: Changing static call from specific type to a Self (requires adding lifetime specifier) --- embassy-rp/src/uart/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index f9cce5c6..6b3e2406 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -299,7 +299,7 @@ impl<'d, T: Instance> Uart<'d, T, Async> { } } -impl<'d, T: Instance, M: Mode> Uart<'d, T, M> { +impl<'d, T: Instance + 'd, M: Mode> Uart<'d, T, M> { fn new_inner( _uart: impl Peripheral

+ 'd, mut tx: PeripheralRef<'d, AnyPin>, @@ -350,7 +350,7 @@ impl<'d, T: Instance, M: Mode> Uart<'d, T, M> { pin.pad_ctrl().write(|w| w.set_ie(true)); } - Uart::::set_baudrate_inner(config.baudrate); + Self::set_baudrate_inner(config.baudrate); let (pen, eps) = match config.parity { Parity::ParityNone => (false, false), From 2331d58aa667d31ce74a2e10582a93b710c2aef7 Mon Sep 17 00:00:00 2001 From: Andres Hurtado Lopez Date: Sun, 26 Feb 2023 21:23:51 -0500 Subject: [PATCH 07/51] RP-PICO UART adding set_baudrate: missing to run rust-fmt --- embassy-rp/src/uart/mod.rs | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index 6b3e2406..42b3671a 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -296,7 +296,7 @@ impl<'d, T: Instance> Uart<'d, T, Async> { Some(rx_dma.map_into()), config, ) - } + } } impl<'d, T: Instance + 'd, M: Mode> Uart<'d, T, M> { @@ -350,7 +350,7 @@ impl<'d, T: Instance + 'd, M: Mode> Uart<'d, T, M> { pin.pad_ctrl().write(|w| w.set_ie(true)); } - Self::set_baudrate_inner(config.baudrate); + Self::set_baudrate_inner(config.baudrate); let (pen, eps) = match config.parity { Parity::ParityNone => (false, false), @@ -385,22 +385,20 @@ impl<'d, T: Instance + 'd, M: Mode> Uart<'d, T, M> { } } - /// sets baudrate on runtime pub fn set_baudrate(&mut self, baudrate: u32) { - Self::set_baudrate_inner(baudrate); + Self::set_baudrate_inner(baudrate); } - fn set_baudrate_inner(baudrate: u32) { - let r = T::regs(); - + let r = T::regs(); + let clk_base = crate::clocks::clk_peri_freq(); - + let baud_rate_div = (8 * clk_base) / baudrate; let mut baud_ibrd = baud_rate_div >> 7; let mut baud_fbrd = ((baud_rate_div & 0x7f) + 1) / 2; - + if baud_ibrd == 0 { baud_ibrd = 1; baud_fbrd = 0; @@ -408,15 +406,13 @@ impl<'d, T: Instance + 'd, M: Mode> Uart<'d, T, M> { baud_ibrd = 65535; baud_fbrd = 0; } - + unsafe { - - // Load PL011's baud divisor registers + // Load PL011's baud divisor registers r.uartibrd().write_value(pac::uart::regs::Uartibrd(baud_ibrd)); r.uartfbrd().write_value(pac::uart::regs::Uartfbrd(baud_fbrd)); - } + } } - } impl<'d, T: Instance, M: Mode> Uart<'d, T, M> { From 5cb0c8cc01583137ccb8b6a64a452b52316b626f Mon Sep 17 00:00:00 2001 From: Lachezar Lechev Date: Tue, 28 Feb 2023 09:22:38 +0200 Subject: [PATCH 08/51] fix: rp - disable Pull-down/up resistors for ADC read Signed-off-by: Lachezar Lechev --- embassy-rp/src/adc.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/embassy-rp/src/adc.rs b/embassy-rp/src/adc.rs index 025c6f91..145ba9c5 100644 --- a/embassy-rp/src/adc.rs +++ b/embassy-rp/src/adc.rs @@ -7,6 +7,7 @@ use embassy_hal_common::into_ref; use embassy_sync::waitqueue::AtomicWaker; use embedded_hal_02::adc::{Channel, OneShot}; +use crate::gpio::Pin; use crate::interrupt::{self, InterruptExt}; use crate::peripherals::ADC; use crate::{pac, peripherals, Peripheral}; @@ -90,9 +91,17 @@ impl<'d> Adc<'d> { } } - pub async fn read, ID = u8>>(&mut self, _pin: &mut PIN) -> u16 { + pub async fn read, ID = u8> + Pin>(&mut self, pin: &mut PIN) -> u16 { let r = Self::regs(); unsafe { + // disable pull-down and pull-up resistors + // pull-down resistors are enabled by default + pin.pad_ctrl().modify(|w| { + w.set_ie(true); + let (pu, pd) = (false, false); + w.set_pue(pu); + w.set_pde(pd); + }); r.cs().modify(|w| { w.set_ainsel(PIN::channel()); w.set_start_once(true) From 90f2939bf6ad02c68c3ada800d4ed7ddb66619a0 Mon Sep 17 00:00:00 2001 From: Davide Della Giustina Date: Tue, 28 Feb 2023 14:22:54 +0000 Subject: [PATCH 09/51] Added PacketQueue::init() --- embassy-stm32/src/eth/mod.rs | 18 ++++++++++++++++++ embassy-stm32/src/lib.rs | 8 +++++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/src/eth/mod.rs b/embassy-stm32/src/eth/mod.rs index 9f62b61e..9b500bfd 100644 --- a/embassy-stm32/src/eth/mod.rs +++ b/embassy-stm32/src/eth/mod.rs @@ -39,6 +39,24 @@ impl PacketQueue { rx_buf: [Packet([0; RX_BUFFER_SIZE]); RX], } } + + // Allow to initialize a Self without requiring it to go on the stack + #[cfg(feature = "nightly")] + pub const unsafe fn init(this: &mut core::mem::MaybeUninit) { + let this: &mut Self = unsafe { this.assume_init_mut() }; + let mut i = 0; + while i < TX { + this.tx_desc[i] = TDes::new(); + this.tx_buf[i] = Packet([0; TX_BUFFER_SIZE]); + i += 1; + } + i = 0; + while i < RX { + this.rx_desc[i] = RDes::new(); + this.rx_buf[i] = Packet([0; RX_BUFFER_SIZE]); + i += 1; + } + } } static WAKER: AtomicWaker = AtomicWaker::new(); diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index eeaa04f6..7c0b2d51 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -1,7 +1,13 @@ #![no_std] #![cfg_attr( feature = "nightly", - feature(type_alias_impl_trait, async_fn_in_trait, impl_trait_projections) + feature( + type_alias_impl_trait, + async_fn_in_trait, + impl_trait_projections, + const_mut_refs, + const_maybe_uninit_assume_init + ) )] #![cfg_attr(feature = "nightly", allow(incomplete_features))] From c1e93c0904706a7497046ba25d82fcfda6576734 Mon Sep 17 00:00:00 2001 From: Davide Della Giustina Date: Tue, 28 Feb 2023 14:34:26 +0000 Subject: [PATCH 10/51] PacketQueue::new() uses ::init() when in nightly --- embassy-stm32/src/eth/mod.rs | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/embassy-stm32/src/eth/mod.rs b/embassy-stm32/src/eth/mod.rs index 9b500bfd..04c74e60 100644 --- a/embassy-stm32/src/eth/mod.rs +++ b/embassy-stm32/src/eth/mod.rs @@ -30,13 +30,24 @@ pub struct PacketQueue { impl PacketQueue { pub const fn new() -> Self { - const NEW_TDES: TDes = TDes::new(); - const NEW_RDES: RDes = RDes::new(); - Self { - tx_desc: [NEW_TDES; TX], - rx_desc: [NEW_RDES; RX], - tx_buf: [Packet([0; TX_BUFFER_SIZE]); TX], - rx_buf: [Packet([0; RX_BUFFER_SIZE]); RX], + #[cfg(feature = "nightly")] + { + let mut this = core::mem::MaybeUninit::uninit(); + unsafe { + Self::init(&mut this); + this.assume_init() + } + } + #[cfg(not(feature = "nightly"))] + { + const NEW_TDES: TDes = TDes::new(); + const NEW_RDES: RDes = RDes::new(); + Self { + tx_desc: [NEW_TDES; TX], + rx_desc: [NEW_RDES; RX], + tx_buf: [Packet([0; TX_BUFFER_SIZE]); TX], + rx_buf: [Packet([0; RX_BUFFER_SIZE]); RX], + } } } From 4e212c7a0b171cae116d9a95353a0611f424c68d Mon Sep 17 00:00:00 2001 From: kbleeke Date: Tue, 28 Feb 2023 17:04:56 +0100 Subject: [PATCH 11/51] embassy-time: add async tick() method to Ticker --- embassy-time/src/timer.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/embassy-time/src/timer.rs b/embassy-time/src/timer.rs index f74b5cb2..416830a7 100644 --- a/embassy-time/src/timer.rs +++ b/embassy-time/src/timer.rs @@ -3,7 +3,7 @@ use core::pin::Pin; use core::task::{Context, Poll, Waker}; use futures_util::future::{select, Either}; -use futures_util::{pin_mut, Stream}; +use futures_util::{pin_mut, Stream, StreamExt}; use crate::{Duration, Instant}; @@ -132,6 +132,11 @@ impl Ticker { let expires_at = Instant::now() + duration; Self { expires_at, duration } } + + /// Waits for the next tick + pub async fn next(&mut self) { + ::next(self).await; + } } impl Unpin for Ticker {} From 485bb76e467aaa2522907bcb1bad538b4374d672 Mon Sep 17 00:00:00 2001 From: Davide Della Giustina Date: Tue, 28 Feb 2023 17:39:02 +0000 Subject: [PATCH 12/51] Implemented suggestions from @Dirbaio --- embassy-stm32/src/eth/mod.rs | 43 +++++++++--------------------------- embassy-stm32/src/lib.rs | 8 +------ 2 files changed, 11 insertions(+), 40 deletions(-) diff --git a/embassy-stm32/src/eth/mod.rs b/embassy-stm32/src/eth/mod.rs index 04c74e60..89d2c5a3 100644 --- a/embassy-stm32/src/eth/mod.rs +++ b/embassy-stm32/src/eth/mod.rs @@ -5,6 +5,7 @@ mod _version; pub mod generic_smi; +use core::mem::MaybeUninit; use core::task::Context; use embassy_net_driver::{Capabilities, LinkState}; @@ -30,43 +31,19 @@ pub struct PacketQueue { impl PacketQueue { pub const fn new() -> Self { - #[cfg(feature = "nightly")] - { - let mut this = core::mem::MaybeUninit::uninit(); - unsafe { - Self::init(&mut this); - this.assume_init() - } - } - #[cfg(not(feature = "nightly"))] - { - const NEW_TDES: TDes = TDes::new(); - const NEW_RDES: RDes = RDes::new(); - Self { - tx_desc: [NEW_TDES; TX], - rx_desc: [NEW_RDES; RX], - tx_buf: [Packet([0; TX_BUFFER_SIZE]); TX], - rx_buf: [Packet([0; RX_BUFFER_SIZE]); RX], - } + const NEW_TDES: TDes = TDes::new(); + const NEW_RDES: RDes = RDes::new(); + Self { + tx_desc: [NEW_TDES; TX], + rx_desc: [NEW_RDES; RX], + tx_buf: [Packet([0; TX_BUFFER_SIZE]); TX], + rx_buf: [Packet([0; RX_BUFFER_SIZE]); RX], } } // Allow to initialize a Self without requiring it to go on the stack - #[cfg(feature = "nightly")] - pub const unsafe fn init(this: &mut core::mem::MaybeUninit) { - let this: &mut Self = unsafe { this.assume_init_mut() }; - let mut i = 0; - while i < TX { - this.tx_desc[i] = TDes::new(); - this.tx_buf[i] = Packet([0; TX_BUFFER_SIZE]); - i += 1; - } - i = 0; - while i < RX { - this.rx_desc[i] = RDes::new(); - this.rx_buf[i] = Packet([0; RX_BUFFER_SIZE]); - i += 1; - } + pub unsafe fn init(this: &mut MaybeUninit) { + this.as_mut_ptr().write_bytes(0u8, core::mem::size_of::()); } } diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 7c0b2d51..eeaa04f6 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -1,13 +1,7 @@ #![no_std] #![cfg_attr( feature = "nightly", - feature( - type_alias_impl_trait, - async_fn_in_trait, - impl_trait_projections, - const_mut_refs, - const_maybe_uninit_assume_init - ) + feature(type_alias_impl_trait, async_fn_in_trait, impl_trait_projections) )] #![cfg_attr(feature = "nightly", allow(incomplete_features))] From 3c601bf8d2624d6fa8fd8e68dfee5cb9348ab4f9 Mon Sep 17 00:00:00 2001 From: Davide Della Giustina Date: Tue, 28 Feb 2023 18:04:43 +0000 Subject: [PATCH 13/51] PacketQueue::init() does not need to be unsafe --- embassy-stm32/src/eth/mod.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/eth/mod.rs b/embassy-stm32/src/eth/mod.rs index 89d2c5a3..b632861b 100644 --- a/embassy-stm32/src/eth/mod.rs +++ b/embassy-stm32/src/eth/mod.rs @@ -42,8 +42,10 @@ impl PacketQueue { } // Allow to initialize a Self without requiring it to go on the stack - pub unsafe fn init(this: &mut MaybeUninit) { - this.as_mut_ptr().write_bytes(0u8, core::mem::size_of::()); + pub fn init(this: &mut MaybeUninit) { + unsafe { + this.as_mut_ptr().write_bytes(0u8, core::mem::size_of::()); + } } } From 4dfa32b1e0572c03a5f97f0ed4a4a0acd6f12cca Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 27 Feb 2023 01:08:16 +0100 Subject: [PATCH 14/51] cortex-m/executor: don't use the owned interrupts system. Preparation for #1224. --- embassy-cortex-m/src/executor.rs | 99 ++++++++++++------- examples/nrf52840/src/bin/multiprio.rs | 32 +++--- examples/stm32f0/Cargo.toml | 2 +- .../src/bin/{priority.rs => multiprio.rs} | 32 +++--- examples/stm32f3/src/bin/multiprio.rs | 36 ++++--- examples/stm32f4/src/bin/multiprio.rs | 36 ++++--- 6 files changed, 152 insertions(+), 85 deletions(-) rename examples/stm32f0/src/bin/{priority.rs => multiprio.rs} (85%) diff --git a/embassy-cortex-m/src/executor.rs b/embassy-cortex-m/src/executor.rs index 0d1745d8..558539e7 100644 --- a/embassy-cortex-m/src/executor.rs +++ b/embassy-cortex-m/src/executor.rs @@ -1,18 +1,22 @@ //! Executor specific to cortex-m devices. -use core::marker::PhantomData; +use core::cell::UnsafeCell; +use core::mem::MaybeUninit; + +use atomic_polyfill::{AtomicBool, Ordering}; +use cortex_m::interrupt::InterruptNumber; +use cortex_m::peripheral::NVIC; pub use embassy_executor::*; -use crate::interrupt::{Interrupt, InterruptExt}; +#[derive(Clone, Copy)] +struct N(u16); +unsafe impl cortex_m::interrupt::InterruptNumber for N { + fn number(self) -> u16 { + self.0 + } +} fn pend_by_number(n: u16) { - #[derive(Clone, Copy)] - struct N(u16); - unsafe impl cortex_m::interrupt::InterruptNumber for N { - fn number(self) -> u16 { - self.0 - } - } cortex_m::peripheral::NVIC::pend(N(n)) } @@ -37,26 +41,37 @@ fn pend_by_number(n: u16) { /// /// It is somewhat more complex to use, it's recommended to use the thread-mode /// [`Executor`] instead, if it works for your use case. -pub struct InterruptExecutor { - irq: I, - inner: raw::Executor, - not_send: PhantomData<*mut ()>, +pub struct InterruptExecutor { + started: AtomicBool, + executor: UnsafeCell>, } -impl InterruptExecutor { - /// Create a new Executor. - pub fn new(irq: I) -> Self { - let ctx = irq.number() as *mut (); +unsafe impl Send for InterruptExecutor {} +unsafe impl Sync for InterruptExecutor {} + +impl InterruptExecutor { + /// Create a new, not started `InterruptExecutor`. + #[inline] + pub const fn new() -> Self { Self { - irq, - inner: raw::Executor::new(|ctx| pend_by_number(ctx as u16), ctx), - not_send: PhantomData, + started: AtomicBool::new(false), + executor: UnsafeCell::new(MaybeUninit::uninit()), } } + /// Executor interrupt callback. + /// + /// # Safety + /// + /// You MUST call this from the interrupt handler, and from nowhere else. + pub unsafe fn on_interrupt(&'static self) { + let executor = unsafe { (&*self.executor.get()).assume_init_ref() }; + executor.poll(); + } + /// Start the executor. /// - /// This initializes the executor, configures and enables the interrupt, and returns. + /// This initializes the executor, enables the interrupt, and returns. /// The executor keeps running in the background through the interrupt. /// /// This returns a [`SendSpawner`] you can use to spawn tasks on it. A [`SendSpawner`] @@ -67,23 +82,35 @@ impl InterruptExecutor { /// To obtain a [`Spawner`](embassy_executor::Spawner) for this executor, use [`Spawner::for_current_executor()`](embassy_executor::Spawner::for_current_executor()) from /// a task running in it. /// - /// This function requires `&'static mut self`. This means you have to store the - /// Executor instance in a place where it'll live forever and grants you mutable - /// access. There's a few ways to do this: + /// # Interrupt requirements /// - /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe) - /// - a `static mut` (unsafe) - /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) - pub fn start(&'static mut self) -> SendSpawner { - self.irq.disable(); + /// You must write the interrupt handler yourself, and make it call [`on_interrupt()`](Self::on_interrupt). + /// + /// This method already enables (unmasks) the interrupt, you must NOT do it yourself. + /// + /// You must set the interrupt priority before calling this method. You MUST NOT + /// do it after. + /// + pub fn start(&'static self, irq: impl InterruptNumber) -> SendSpawner { + if self + .started + .compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed) + .is_err() + { + panic!("InterruptExecutor::start() called multiple times on the same executor."); + } - self.irq.set_handler(|ctx| unsafe { - let executor = &*(ctx as *const raw::Executor); - executor.poll(); - }); - self.irq.set_handler_context(&self.inner as *const _ as _); - self.irq.enable(); + unsafe { + (&mut *self.executor.get()).as_mut_ptr().write(raw::Executor::new( + |ctx| pend_by_number(ctx as u16), + irq.number() as *mut (), + )) + } - self.inner.spawner().make_send() + let executor = unsafe { (&*self.executor.get()).assume_init_ref() }; + + unsafe { NVIC::unmask(irq) } + + executor.spawner().make_send() } } diff --git a/examples/nrf52840/src/bin/multiprio.rs b/examples/nrf52840/src/bin/multiprio.rs index 25806ae4..851e189e 100644 --- a/examples/nrf52840/src/bin/multiprio.rs +++ b/examples/nrf52840/src/bin/multiprio.rs @@ -57,11 +57,14 @@ #![no_main] #![feature(type_alias_impl_trait)] +use core::mem; + +use cortex_m::peripheral::NVIC; use cortex_m_rt::entry; use defmt::{info, unwrap}; use embassy_nrf::executor::{Executor, InterruptExecutor}; use embassy_nrf::interrupt; -use embassy_nrf::interrupt::InterruptExt; +use embassy_nrf::pac::Interrupt; use embassy_time::{Duration, Instant, Timer}; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; @@ -108,28 +111,35 @@ async fn run_low() { } } -static EXECUTOR_HIGH: StaticCell> = StaticCell::new(); -static EXECUTOR_MED: StaticCell> = StaticCell::new(); +static EXECUTOR_HIGH: InterruptExecutor = InterruptExecutor::new(); +static EXECUTOR_MED: InterruptExecutor = InterruptExecutor::new(); static EXECUTOR_LOW: StaticCell = StaticCell::new(); +#[interrupt] +unsafe fn SWI1_EGU1() { + EXECUTOR_HIGH.on_interrupt() +} + +#[interrupt] +unsafe fn SWI0_EGU0() { + EXECUTOR_MED.on_interrupt() +} + #[entry] fn main() -> ! { info!("Hello World!"); let _p = embassy_nrf::init(Default::default()); + let mut nvic: NVIC = unsafe { mem::transmute(()) }; // High-priority executor: SWI1_EGU1, priority level 6 - let irq = interrupt::take!(SWI1_EGU1); - irq.set_priority(interrupt::Priority::P6); - let executor = EXECUTOR_HIGH.init(InterruptExecutor::new(irq)); - let spawner = executor.start(); + unsafe { nvic.set_priority(Interrupt::SWI1_EGU1, 6 << 5) }; + let spawner = EXECUTOR_HIGH.start(Interrupt::SWI1_EGU1); unwrap!(spawner.spawn(run_high())); // Medium-priority executor: SWI0_EGU0, priority level 7 - let irq = interrupt::take!(SWI0_EGU0); - irq.set_priority(interrupt::Priority::P7); - let executor = EXECUTOR_MED.init(InterruptExecutor::new(irq)); - let spawner = executor.start(); + unsafe { nvic.set_priority(Interrupt::SWI0_EGU0, 7 << 5) }; + let spawner = EXECUTOR_MED.start(Interrupt::SWI0_EGU0); unwrap!(spawner.spawn(run_med())); // Low priority executor: runs in thread mode, using WFE/SEV diff --git a/examples/stm32f0/Cargo.toml b/examples/stm32f0/Cargo.toml index d4afbb8f..89d99b6d 100644 --- a/examples/stm32f0/Cargo.toml +++ b/examples/stm32f0/Cargo.toml @@ -15,5 +15,5 @@ panic-probe = "0.3" embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } -embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "memory-x", "stm32f091rc", "time-driver-any", "exti"] } +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "memory-x", "stm32f091rc", "time-driver-any", "exti", "unstable-pac"] } static_cell = "1.0" diff --git a/examples/stm32f0/src/bin/priority.rs b/examples/stm32f0/src/bin/multiprio.rs similarity index 85% rename from examples/stm32f0/src/bin/priority.rs rename to examples/stm32f0/src/bin/multiprio.rs index 7fed6a77..e0dc8c98 100644 --- a/examples/stm32f0/src/bin/priority.rs +++ b/examples/stm32f0/src/bin/multiprio.rs @@ -57,11 +57,14 @@ #![no_main] #![feature(type_alias_impl_trait)] +use core::mem; + +use cortex_m::peripheral::NVIC; use cortex_m_rt::entry; use defmt::*; use embassy_stm32::executor::{Executor, InterruptExecutor}; use embassy_stm32::interrupt; -use embassy_stm32::interrupt::InterruptExt; +use embassy_stm32::pac::Interrupt; use embassy_time::{Duration, Instant, Timer}; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; @@ -108,27 +111,34 @@ async fn run_low() { } } -static EXECUTOR_HIGH: StaticCell> = StaticCell::new(); -static EXECUTOR_MED: StaticCell> = StaticCell::new(); +static EXECUTOR_HIGH: InterruptExecutor = InterruptExecutor::new(); +static EXECUTOR_MED: InterruptExecutor = InterruptExecutor::new(); static EXECUTOR_LOW: StaticCell = StaticCell::new(); +#[interrupt] +unsafe fn USART1() { + EXECUTOR_HIGH.on_interrupt() +} + +#[interrupt] +unsafe fn USART2() { + EXECUTOR_MED.on_interrupt() +} + #[entry] fn main() -> ! { // Initialize and create handle for devicer peripherals let _p = embassy_stm32::init(Default::default()); + let mut nvic: NVIC = unsafe { mem::transmute(()) }; // High-priority executor: USART1, priority level 6 - let irq = interrupt::take!(USART1); - irq.set_priority(interrupt::Priority::P6); - let executor = EXECUTOR_HIGH.init(InterruptExecutor::new(irq)); - let spawner = executor.start(); + unsafe { nvic.set_priority(Interrupt::USART1, 6 << 4) }; + let spawner = EXECUTOR_HIGH.start(Interrupt::USART1); unwrap!(spawner.spawn(run_high())); // Medium-priority executor: USART2, priority level 7 - let irq = interrupt::take!(USART2); - irq.set_priority(interrupt::Priority::P7); - let executor = EXECUTOR_MED.init(InterruptExecutor::new(irq)); - let spawner = executor.start(); + unsafe { nvic.set_priority(Interrupt::USART2, 7 << 4) }; + let spawner = EXECUTOR_MED.start(Interrupt::USART2); unwrap!(spawner.spawn(run_med())); // Low priority executor: runs in thread mode, using WFE/SEV diff --git a/examples/stm32f3/src/bin/multiprio.rs b/examples/stm32f3/src/bin/multiprio.rs index 9e8228a4..77df51ac 100644 --- a/examples/stm32f3/src/bin/multiprio.rs +++ b/examples/stm32f3/src/bin/multiprio.rs @@ -57,11 +57,14 @@ #![no_main] #![feature(type_alias_impl_trait)] +use core::mem; + +use cortex_m::peripheral::NVIC; use cortex_m_rt::entry; use defmt::*; use embassy_stm32::executor::{Executor, InterruptExecutor}; use embassy_stm32::interrupt; -use embassy_stm32::interrupt::InterruptExt; +use embassy_stm32::pac::Interrupt; use embassy_time::{Duration, Instant, Timer}; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; @@ -108,28 +111,35 @@ async fn run_low() { } } -static EXECUTOR_HIGH: StaticCell> = StaticCell::new(); -static EXECUTOR_MED: StaticCell> = StaticCell::new(); +static EXECUTOR_HIGH: InterruptExecutor = InterruptExecutor::new(); +static EXECUTOR_MED: InterruptExecutor = InterruptExecutor::new(); static EXECUTOR_LOW: StaticCell = StaticCell::new(); +#[interrupt] +unsafe fn UART4() { + EXECUTOR_HIGH.on_interrupt() +} + +#[interrupt] +unsafe fn UART5() { + EXECUTOR_MED.on_interrupt() +} + #[entry] fn main() -> ! { info!("Hello World!"); let _p = embassy_stm32::init(Default::default()); + let mut nvic: NVIC = unsafe { mem::transmute(()) }; - // High-priority executor: SWI1_EGU1, priority level 6 - let irq = interrupt::take!(UART4); - irq.set_priority(interrupt::Priority::P6); - let executor = EXECUTOR_HIGH.init(InterruptExecutor::new(irq)); - let spawner = executor.start(); + // High-priority executor: UART4, priority level 6 + unsafe { nvic.set_priority(Interrupt::UART4, 6 << 4) }; + let spawner = EXECUTOR_HIGH.start(Interrupt::UART4); unwrap!(spawner.spawn(run_high())); - // Medium-priority executor: SWI0_EGU0, priority level 7 - let irq = interrupt::take!(UART5); - irq.set_priority(interrupt::Priority::P7); - let executor = EXECUTOR_MED.init(InterruptExecutor::new(irq)); - let spawner = executor.start(); + // Medium-priority executor: UART5, priority level 7 + unsafe { nvic.set_priority(Interrupt::UART5, 7 << 4) }; + let spawner = EXECUTOR_MED.start(Interrupt::UART5); unwrap!(spawner.spawn(run_med())); // Low priority executor: runs in thread mode, using WFE/SEV diff --git a/examples/stm32f4/src/bin/multiprio.rs b/examples/stm32f4/src/bin/multiprio.rs index 9e8228a4..77df51ac 100644 --- a/examples/stm32f4/src/bin/multiprio.rs +++ b/examples/stm32f4/src/bin/multiprio.rs @@ -57,11 +57,14 @@ #![no_main] #![feature(type_alias_impl_trait)] +use core::mem; + +use cortex_m::peripheral::NVIC; use cortex_m_rt::entry; use defmt::*; use embassy_stm32::executor::{Executor, InterruptExecutor}; use embassy_stm32::interrupt; -use embassy_stm32::interrupt::InterruptExt; +use embassy_stm32::pac::Interrupt; use embassy_time::{Duration, Instant, Timer}; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; @@ -108,28 +111,35 @@ async fn run_low() { } } -static EXECUTOR_HIGH: StaticCell> = StaticCell::new(); -static EXECUTOR_MED: StaticCell> = StaticCell::new(); +static EXECUTOR_HIGH: InterruptExecutor = InterruptExecutor::new(); +static EXECUTOR_MED: InterruptExecutor = InterruptExecutor::new(); static EXECUTOR_LOW: StaticCell = StaticCell::new(); +#[interrupt] +unsafe fn UART4() { + EXECUTOR_HIGH.on_interrupt() +} + +#[interrupt] +unsafe fn UART5() { + EXECUTOR_MED.on_interrupt() +} + #[entry] fn main() -> ! { info!("Hello World!"); let _p = embassy_stm32::init(Default::default()); + let mut nvic: NVIC = unsafe { mem::transmute(()) }; - // High-priority executor: SWI1_EGU1, priority level 6 - let irq = interrupt::take!(UART4); - irq.set_priority(interrupt::Priority::P6); - let executor = EXECUTOR_HIGH.init(InterruptExecutor::new(irq)); - let spawner = executor.start(); + // High-priority executor: UART4, priority level 6 + unsafe { nvic.set_priority(Interrupt::UART4, 6 << 4) }; + let spawner = EXECUTOR_HIGH.start(Interrupt::UART4); unwrap!(spawner.spawn(run_high())); - // Medium-priority executor: SWI0_EGU0, priority level 7 - let irq = interrupt::take!(UART5); - irq.set_priority(interrupt::Priority::P7); - let executor = EXECUTOR_MED.init(InterruptExecutor::new(irq)); - let spawner = executor.start(); + // Medium-priority executor: UART5, priority level 7 + unsafe { nvic.set_priority(Interrupt::UART5, 7 << 4) }; + let spawner = EXECUTOR_MED.start(Interrupt::UART5); unwrap!(spawner.spawn(run_med())); // Low priority executor: runs in thread mode, using WFE/SEV From 6dbb631f1ecb75361ee70da91f50779c29f23482 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 1 Mar 2023 01:32:42 +0100 Subject: [PATCH 15/51] Example fixes. --- examples/nrf52840/src/bin/usb_ethernet.rs | 23 ----------------------- examples/nrf5340/Cargo.toml | 23 +++++++---------------- examples/stm32f4/Cargo.toml | 6 +----- examples/stm32f4/src/bin/usb_ethernet.rs | 11 +++-------- examples/stm32l4/Cargo.toml | 2 -- examples/stm32l5/Cargo.toml | 2 -- 6 files changed, 11 insertions(+), 56 deletions(-) diff --git a/examples/nrf52840/src/bin/usb_ethernet.rs b/examples/nrf52840/src/bin/usb_ethernet.rs index 430468ad..97978089 100644 --- a/examples/nrf52840/src/bin/usb_ethernet.rs +++ b/examples/nrf52840/src/bin/usb_ethernet.rs @@ -46,31 +46,8 @@ async fn net_task(stack: &'static Stack>) -> ! { stack.run().await } -#[inline(never)] -pub fn test_function() -> (usize, u32, [u32; 2]) { - let mut array = [3; 2]; - - let mut index = 0; - let mut result = 0; - - for x in [1, 2] { - if x == 1 { - array[1] = 99; - } else { - index = if x == 2 { 1 } else { 0 }; - - // grabs value from array[0], not array[1] - result = array[index]; - } - } - - (index, result, array) -} - #[embassy_executor::main] async fn main(spawner: Spawner) { - info!("{:?}", test_function()); - let p = embassy_nrf::init(Default::default()); let clock: pac::CLOCK = unsafe { mem::transmute(()) }; diff --git a/examples/nrf5340/Cargo.toml b/examples/nrf5340/Cargo.toml index eed49301..e88ddf2f 100644 --- a/examples/nrf5340/Cargo.toml +++ b/examples/nrf5340/Cargo.toml @@ -4,24 +4,13 @@ name = "embassy-nrf5340-examples" version = "0.1.0" license = "MIT OR Apache-2.0" -[features] -default = ["nightly"] -nightly = [ - "embassy-executor/nightly", - "embassy-nrf/nightly", - "embassy-net/nightly", - "embassy-nrf/unstable-traits", - "embassy-usb", - "embedded-io/async", - "embassy-net", -] - [dependencies] embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = [ "defmt", ] } embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = [ + "nightly", "defmt", "integrated-timers", ] } @@ -30,6 +19,8 @@ embassy-time = { version = "0.1.0", path = "../../embassy-time", features = [ "defmt-timestamp-uptime", ] } embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = [ + "nightly", + "unstable-traits", "defmt", "nrf5340-app-s", "time-driver-rtc1", @@ -37,16 +28,16 @@ embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = [ "unstable-pac", ] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = [ + "nightly", "defmt", "tcp", "dhcpv4", "medium-ethernet", -], optional = true } +] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = [ "defmt", -], optional = true } -embedded-io = "0.4.0" - +] } +embedded-io = { version = "0.4.0", features = [ "async" ]} defmt = "0.3" defmt-rtt = "0.4" diff --git a/examples/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml index e2b17bfc..7a7bab5b 100644 --- a/examples/stm32f4/Cargo.toml +++ b/examples/stm32f4/Cargo.toml @@ -10,7 +10,7 @@ embassy-executor = { version = "0.1.0", path = "../../embassy-executor", feature embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "unstable-traits", "defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-any", "exti"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } -embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "nightly"], optional = true } +embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "nightly"] } defmt = "0.3" defmt-rtt = "0.4" @@ -27,9 +27,5 @@ embedded-storage = "0.3.0" micromath = "2.0.0" static_cell = "1.0" -[[bin]] -name = "usb_ethernet" -required-features = ["embassy-net"] - [profile.release] debug = 2 diff --git a/examples/stm32f4/src/bin/usb_ethernet.rs b/examples/stm32f4/src/bin/usb_ethernet.rs index 4a16aac0..db9e1839 100644 --- a/examples/stm32f4/src/bin/usb_ethernet.rs +++ b/examples/stm32f4/src/bin/usb_ethernet.rs @@ -100,8 +100,8 @@ async fn main(spawner: Spawner) { let (runner, device) = class.into_embassy_net_device::(singleton!(NetState::new()), our_mac_addr); unwrap!(spawner.spawn(usb_ncm_task(runner))); - let config = embassy_net::ConfigStrategy::Dhcp; - //let config = embassy_net::ConfigStrategy::Static(embassy_net::Config { + let config = embassy_net::Config::Dhcp(Default::default()); + //let config = embassy_net::Config::Static(embassy_net::StaticConfig { // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), // dns_servers: Vec::new(), // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), @@ -114,12 +114,7 @@ async fn main(spawner: Spawner) { let seed = u64::from_le_bytes(seed); // Init network stack - let stack = &*singleton!(Stack::new( - device, - config, - singleton!(StackResources::<1, 2, 8>::new()), - seed - )); + let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<2>::new()), seed)); unwrap!(spawner.spawn(net_task(stack))); diff --git a/examples/stm32l4/Cargo.toml b/examples/stm32l4/Cargo.toml index 5627760e..644c90b1 100644 --- a/examples/stm32l4/Cargo.toml +++ b/examples/stm32l4/Cargo.toml @@ -4,8 +4,6 @@ name = "embassy-stm32l4-examples" version = "0.1.0" license = "MIT OR Apache-2.0" -[features] - [dependencies] embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } diff --git a/examples/stm32l5/Cargo.toml b/examples/stm32l5/Cargo.toml index c0accb0d..f880328d 100644 --- a/examples/stm32l5/Cargo.toml +++ b/examples/stm32l5/Cargo.toml @@ -4,8 +4,6 @@ name = "embassy-stm32l5-examples" version = "0.1.0" license = "MIT OR Apache-2.0" -[features] - [dependencies] embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } From aabc275186aff8a640ee6591c3572c00f508b3b7 Mon Sep 17 00:00:00 2001 From: Patrick Oppenlander Date: Wed, 1 Mar 2023 10:10:40 +1100 Subject: [PATCH 16/51] stm32/spi: fix occasional data corruption Need to clear the rx fifo before enabling rx dma. --- embassy-stm32/src/spi/mod.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index e5ba746e..1f170887 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -456,13 +456,14 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { T::REGS.cr1().modify(|w| { w.set_spe(false); }); - set_rxdmaen(T::REGS, true); } // SPIv3 clears rxfifo on SPE=0 #[cfg(not(any(spi_v3, spi_v4)))] flush_rx_fifo(T::REGS); + set_rxdmaen(T::REGS, true); + let clock_byte_count = data.len(); let rx_request = self.rxdma.request(); @@ -510,13 +511,14 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { T::REGS.cr1().modify(|w| { w.set_spe(false); }); - set_rxdmaen(T::REGS, true); } // SPIv3 clears rxfifo on SPE=0 #[cfg(not(any(spi_v3, spi_v4)))] flush_rx_fifo(T::REGS); + set_rxdmaen(T::REGS, true); + let rx_request = self.rxdma.request(); let rx_src = T::REGS.rx_ptr(); unsafe { self.rxdma.start_read(rx_request, rx_src, read, Default::default()) }; From c0e40b887bf4975512cc40e13ea17cb57f34f099 Mon Sep 17 00:00:00 2001 From: Davide Della Giustina Date: Wed, 1 Mar 2023 20:57:13 +0000 Subject: [PATCH 17/51] Apply fix --- embassy-stm32/src/eth/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/eth/mod.rs b/embassy-stm32/src/eth/mod.rs index b632861b..e1d7a09b 100644 --- a/embassy-stm32/src/eth/mod.rs +++ b/embassy-stm32/src/eth/mod.rs @@ -44,7 +44,7 @@ impl PacketQueue { // Allow to initialize a Self without requiring it to go on the stack pub fn init(this: &mut MaybeUninit) { unsafe { - this.as_mut_ptr().write_bytes(0u8, core::mem::size_of::()); + this.as_mut_ptr().write_bytes(0u8, 1); } } } From f95aafc90e365bbdd243c0ed5069ff23abfa8be4 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 2 Mar 2023 00:57:54 +0100 Subject: [PATCH 18/51] common: allow atomic ringbuf to fill up to N instead of just N-1. This allows the ringbuf to be filled up to `N` instead of just `N-1`, using some fun tricks on the indices. The advantage is better performance: Before, the first write would fill N-1 bytes, The second would write just the 1 byte left before wrapping, then N-2. Then 2, then N-3, and so on. This would result in more smaller chunks, so worse perf. This problem is gone now. --- embassy-hal-common/src/atomic_ring_buffer.rs | 98 ++++++++++++++------ 1 file changed, 69 insertions(+), 29 deletions(-) diff --git a/embassy-hal-common/src/atomic_ring_buffer.rs b/embassy-hal-common/src/atomic_ring_buffer.rs index 4c944d76..ccbc3736 100644 --- a/embassy-hal-common/src/atomic_ring_buffer.rs +++ b/embassy-hal-common/src/atomic_ring_buffer.rs @@ -14,10 +14,18 @@ use core::sync::atomic::{AtomicPtr, AtomicUsize, Ordering}; /// One concurrent writer and one concurrent reader are supported, even at /// different execution priorities (like main and irq). pub struct RingBuffer { - buf: AtomicPtr, - len: AtomicUsize, - start: AtomicUsize, - end: AtomicUsize, + pub buf: AtomicPtr, + pub len: AtomicUsize, + + // start and end wrap at len*2, not at len. + // This allows distinguishing "full" and "empty". + // full is when start+len == end (modulo len*2) + // empty is when start == end + // + // This avoids having to consider the ringbuffer "full" at len-1 instead of len. + // The usual solution is adding a "full" flag, but that can't be made atomic + pub start: AtomicUsize, + pub end: AtomicUsize, } pub struct Reader<'a>(&'a RingBuffer); @@ -90,7 +98,7 @@ impl RingBuffer { let start = self.start.load(Ordering::Relaxed); let end = self.end.load(Ordering::Relaxed); - len == 0 || self.wrap(end + 1) == start + self.wrap(start + len) == end } pub fn is_empty(&self) -> bool { @@ -100,15 +108,13 @@ impl RingBuffer { start == end } - fn wrap(&self, n: usize) -> usize { + fn wrap(&self, mut n: usize) -> usize { let len = self.len.load(Ordering::Relaxed); - assert!(n <= len); - if n == len { - 0 - } else { - n + if n >= len * 2 { + n -= len * 2 } + n } } @@ -161,16 +167,25 @@ impl<'a> Writer<'a> { pub fn push_buf(&mut self) -> (*mut u8, usize) { // Ordering: popping writes `start` last, so we read `start` first. // Read it with Acquire ordering, so that the next accesses can't be reordered up past it. - let start = self.0.start.load(Ordering::Acquire); + let mut start = self.0.start.load(Ordering::Acquire); let buf = self.0.buf.load(Ordering::Relaxed); let len = self.0.len.load(Ordering::Relaxed); - let end = self.0.end.load(Ordering::Relaxed); + let mut end = self.0.end.load(Ordering::Relaxed); - let n = if start <= end { - len - end - (start == 0 && len != 0) as usize - } else { - start - end - 1 - }; + let empty = start == end; + + if start >= len { + start -= len + } + if end >= len { + end -= len + } + + if start == end && !empty { + // full + return (buf, 0); + } + let n = if start > end { start - end } else { len - end }; trace!(" ringbuf: push_buf {:?}..{:?}", end, end + n); (unsafe { buf.add(end) }, n) @@ -239,12 +254,23 @@ impl<'a> Reader<'a> { // Ordering: pushing writes `end` last, so we read `end` first. // Read it with Acquire ordering, so that the next accesses can't be reordered up past it. // This is needed to guarantee we "see" the data written by the writer. - let end = self.0.end.load(Ordering::Acquire); + let mut end = self.0.end.load(Ordering::Acquire); let buf = self.0.buf.load(Ordering::Relaxed); let len = self.0.len.load(Ordering::Relaxed); - let start = self.0.start.load(Ordering::Relaxed); + let mut start = self.0.start.load(Ordering::Relaxed); - let n = if end < start { len - start } else { end - start }; + if start == end { + return (buf, 0); + } + + if start >= len { + start -= len + } + if end >= len { + end -= len + } + + let n = if end > start { end - start } else { len - start }; trace!(" ringbuf: pop_buf {:?}..{:?}", start, start + n); (unsafe { buf.add(start) }, n) @@ -280,12 +306,12 @@ mod tests { assert_eq!(rb.is_full(), false); rb.writer().push(|buf| { - // If capacity is 4, we can fill it up to 3. - assert_eq!(3, buf.len()); + assert_eq!(4, buf.len()); buf[0] = 1; buf[1] = 2; buf[2] = 3; - 3 + buf[3] = 4; + 4 }); assert_eq!(rb.is_empty(), false); @@ -301,7 +327,7 @@ mod tests { assert_eq!(rb.is_full(), true); rb.reader().pop(|buf| { - assert_eq!(3, buf.len()); + assert_eq!(4, buf.len()); assert_eq!(1, buf[0]); 1 }); @@ -310,7 +336,7 @@ mod tests { assert_eq!(rb.is_full(), false); rb.reader().pop(|buf| { - assert_eq!(2, buf.len()); + assert_eq!(3, buf.len()); 0 }); @@ -318,11 +344,16 @@ mod tests { assert_eq!(rb.is_full(), false); rb.reader().pop(|buf| { - assert_eq!(2, buf.len()); + assert_eq!(3, buf.len()); assert_eq!(2, buf[0]); assert_eq!(3, buf[1]); 2 }); + rb.reader().pop(|buf| { + assert_eq!(1, buf.len()); + assert_eq!(4, buf[0]); + 1 + }); assert_eq!(rb.is_empty(), true); assert_eq!(rb.is_full(), false); @@ -333,18 +364,27 @@ mod tests { }); rb.writer().push(|buf| { - assert_eq!(1, buf.len()); + assert_eq!(4, buf.len()); buf[0] = 10; 1 }); rb.writer().push(|buf| { - assert_eq!(2, buf.len()); + assert_eq!(3, buf.len()); buf[0] = 11; buf[1] = 12; 2 }); + assert_eq!(rb.is_empty(), false); + assert_eq!(rb.is_full(), false); + + rb.writer().push(|buf| { + assert_eq!(1, buf.len()); + buf[0] = 13; + 1 + }); + assert_eq!(rb.is_empty(), false); assert_eq!(rb.is_full(), true); } From 78974dfeb4db4a36bf9478397fd65370eb1dcf0c Mon Sep 17 00:00:00 2001 From: Patrick Oppenlander Date: Tue, 7 Feb 2023 14:49:44 +1100 Subject: [PATCH 19/51] hal-common/atomic_ring_buffer: add push_bufs() push_slices() This allows a writer to access all of the free space in the buffer, even when it's wrapping around. --- embassy-hal-common/src/atomic_ring_buffer.rs | 147 +++++++++++++++++++ 1 file changed, 147 insertions(+) diff --git a/embassy-hal-common/src/atomic_ring_buffer.rs b/embassy-hal-common/src/atomic_ring_buffer.rs index ccbc3736..afd3ce1d 100644 --- a/embassy-hal-common/src/atomic_ring_buffer.rs +++ b/embassy-hal-common/src/atomic_ring_buffer.rs @@ -153,6 +153,14 @@ impl<'a> Writer<'a> { unsafe { slice::from_raw_parts_mut(data, len) } } + /// Get up to two buffers where data can be pushed to. + /// + /// Equivalent to [`Self::push_bufs`] but returns slices. + pub fn push_slices(&mut self) -> [&mut [u8]; 2] { + let [(d0, l0), (d1, l1)] = self.push_bufs(); + unsafe { [slice::from_raw_parts_mut(d0, l0), slice::from_raw_parts_mut(d1, l1)] } + } + /// Get a buffer where data can be pushed to. /// /// Write data to the start of the buffer, then call `push_done` with @@ -191,6 +199,45 @@ impl<'a> Writer<'a> { (unsafe { buf.add(end) }, n) } + /// Get up to two buffers where data can be pushed to. + /// + /// Write data starting at the beginning of the first buffer, then call + /// `push_done` with however many bytes you've pushed. + /// + /// The buffers are suitable to DMA to. + /// + /// If the ringbuf is full, both buffers will be zero length. + /// If there is only area available, the second buffer will be zero length. + /// + /// The buffer stays valid as long as no other `Writer` method is called + /// and `init`/`deinit` aren't called on the ringbuf. + pub fn push_bufs(&mut self) -> [(*mut u8, usize); 2] { + // Ordering: as per push_buf() + let mut start = self.0.start.load(Ordering::Acquire); + let buf = self.0.buf.load(Ordering::Relaxed); + let len = self.0.len.load(Ordering::Relaxed); + let mut end = self.0.end.load(Ordering::Relaxed); + + let empty = start == end; + + if start >= len { + start -= len + } + if end >= len { + end -= len + } + + if start == end && !empty { + // full + return [(buf, 0), (buf, 0)]; + } + let n0 = if start > end { start - end } else { len - end }; + let n1 = if start <= end { start } else { 0 }; + + trace!(" ringbuf: push_bufs [{:?}..{:?}, {:?}..{:?}]", end, end + n0, 0, n1); + [(unsafe { buf.add(end) }, n0), (buf, n1)] + } + pub fn push_done(&mut self, n: usize) { trace!(" ringbuf: push {:?}", n); let end = self.0.end.load(Ordering::Relaxed); @@ -408,4 +455,104 @@ mod tests { }); } } + + #[test] + fn push_slices() { + init(); + + let mut b = [0; 4]; + let rb = RingBuffer::new(); + unsafe { + rb.init(b.as_mut_ptr(), 4); + + /* push 3 -> [1 2 3 x] */ + let mut w = rb.writer(); + let ps = w.push_slices(); + assert_eq!(4, ps[0].len()); + assert_eq!(0, ps[1].len()); + ps[0][0] = 1; + ps[0][1] = 2; + ps[0][2] = 3; + w.push_done(3); + drop(w); + + /* pop 2 -> [x x 3 x] */ + rb.reader().pop(|buf| { + assert_eq!(3, buf.len()); + assert_eq!(1, buf[0]); + assert_eq!(2, buf[1]); + assert_eq!(3, buf[2]); + 2 + }); + + /* push 3 -> [5 6 3 4] */ + let mut w = rb.writer(); + let ps = w.push_slices(); + assert_eq!(1, ps[0].len()); + assert_eq!(2, ps[1].len()); + ps[0][0] = 4; + ps[1][0] = 5; + ps[1][1] = 6; + w.push_done(3); + drop(w); + + /* buf is now full */ + let mut w = rb.writer(); + let ps = w.push_slices(); + assert_eq!(0, ps[0].len()); + assert_eq!(0, ps[1].len()); + + /* pop 2 -> [5 6 x x] */ + rb.reader().pop(|buf| { + assert_eq!(2, buf.len()); + assert_eq!(3, buf[0]); + assert_eq!(4, buf[1]); + 2 + }); + + /* should now have one push slice again */ + let mut w = rb.writer(); + let ps = w.push_slices(); + assert_eq!(2, ps[0].len()); + assert_eq!(0, ps[1].len()); + drop(w); + + /* pop 2 -> [x x x x] */ + rb.reader().pop(|buf| { + assert_eq!(2, buf.len()); + assert_eq!(5, buf[0]); + assert_eq!(6, buf[1]); + 2 + }); + + /* should now have two push slices */ + let mut w = rb.writer(); + let ps = w.push_slices(); + assert_eq!(2, ps[0].len()); + assert_eq!(2, ps[1].len()); + drop(w); + + /* make sure we exercise all wrap around cases properly */ + for _ in 0..10 { + /* should be empty, push 1 */ + let mut w = rb.writer(); + let ps = w.push_slices(); + assert_eq!(4, ps[0].len() + ps[1].len()); + w.push_done(1); + drop(w); + + /* should have 1 element */ + let mut w = rb.writer(); + let ps = w.push_slices(); + assert_eq!(3, ps[0].len() + ps[1].len()); + drop(w); + + /* pop 1 */ + rb.reader().pop(|buf| { + assert_eq!(1, buf.len()); + 1 + }); + } + } + } } From 64003cdfd60439195d75b4d510d3027c463fdb0a Mon Sep 17 00:00:00 2001 From: Scott Mabin Date: Thu, 2 Mar 2023 11:36:41 +0000 Subject: [PATCH 20/51] Add embassy-esp README --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 938f2f4a..6d5e75ea 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,9 @@ Rust's async/await allows - **Hardware Abstraction Layers** - HALs implement safe, idiomatic Rust APIs to use the hardware capabilities, so raw register manipulation is not needed. The Embassy project maintains HALs for select hardware, but you can still use HALs from other projects with Embassy. - embassy-stm32, for all STM32 microcontroller families. - embassy-nrf, for the Nordic Semiconductor nRF52, nRF53, nRF91 series. + - esp-rs, for the Espressif Systems ESP32 series of chips. + - Embassy HAL support for Espressif chips is being developed in the [esp-rs/esp-hal](https://github.com/esp-rs/esp-hal) repository. + - Async WiFi, Bluetooth and ESP-NOW is being developed in the [esp-rs/esp-wifi](https://github.com/esp-rs/esp-wifi) repository. - **Time that Just Works** - No more messing with hardware timers. embassy_time provides Instant, Duration and Timer types that are globally available and never overflow. From 7bdb3abad7bbf89a55c73cfb0b475b78c5a7488d Mon Sep 17 00:00:00 2001 From: Caleb Jamison Date: Thu, 2 Mar 2023 13:59:52 -0500 Subject: [PATCH 21/51] Swap debug! for trace! in rp gpio When using gpio pin changes for things like peripheral interrupts these debug! calls flood defmt, making it difficult to find what you're actually looking for. --- embassy-rp/src/gpio.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs index fd3b0556..fb45ef7c 100644 --- a/embassy-rp/src/gpio.rs +++ b/embassy-rp/src/gpio.rs @@ -174,7 +174,7 @@ unsafe fn IO_IRQ_BANK0() { w.set_edge_low(pin_group, false); } InterruptTrigger::LevelHigh => { - debug!("IO_IRQ_BANK0 pin {} LevelHigh triggered", pin); + trace!("IO_IRQ_BANK0 pin {} LevelHigh triggered", pin); w.set_level_high(pin_group, false); } InterruptTrigger::LevelLow => { @@ -214,7 +214,7 @@ impl<'d, T: Pin> InputFuture<'d, T> { critical_section::with(|_| { pin.int_proc().inte((pin.pin() / 8) as usize).modify(|w| match level { InterruptTrigger::LevelHigh => { - debug!("InputFuture::new enable LevelHigh for pin {}", pin.pin()); + trace!("InputFuture::new enable LevelHigh for pin {}", pin.pin()); w.set_level_high(pin_group, true); } InterruptTrigger::LevelLow => { @@ -261,45 +261,45 @@ impl<'d, T: Pin> Future for InputFuture<'d, T> { // the pin and if it has been disabled that means it was done by the // interrupt service routine, so we then know that the event/trigger // happened and Poll::Ready will be returned. - debug!("{:?} for pin {}", self.level, self.pin.pin()); + trace!("{:?} for pin {}", self.level, self.pin.pin()); match self.level { InterruptTrigger::AnyEdge => { if !inte.edge_high(pin_group) && !inte.edge_low(pin_group) { #[rustfmt::skip] - debug!("{:?} for pin {} was cleared, return Poll::Ready", self.level, self.pin.pin()); + trace!("{:?} for pin {} was cleared, return Poll::Ready", self.level, self.pin.pin()); return Poll::Ready(()); } } InterruptTrigger::LevelHigh => { if !inte.level_high(pin_group) { #[rustfmt::skip] - debug!("{:?} for pin {} was cleared, return Poll::Ready", self.level, self.pin.pin()); + trace!("{:?} for pin {} was cleared, return Poll::Ready", self.level, self.pin.pin()); return Poll::Ready(()); } } InterruptTrigger::LevelLow => { if !inte.level_low(pin_group) { #[rustfmt::skip] - debug!("{:?} for pin {} was cleared, return Poll::Ready", self.level, self.pin.pin()); + trace!("{:?} for pin {} was cleared, return Poll::Ready", self.level, self.pin.pin()); return Poll::Ready(()); } } InterruptTrigger::EdgeHigh => { if !inte.edge_high(pin_group) { #[rustfmt::skip] - debug!("{:?} for pin {} was cleared, return Poll::Ready", self.level, self.pin.pin()); + trace!("{:?} for pin {} was cleared, return Poll::Ready", self.level, self.pin.pin()); return Poll::Ready(()); } } InterruptTrigger::EdgeLow => { if !inte.edge_low(pin_group) { #[rustfmt::skip] - debug!("{:?} for pin {} was cleared, return Poll::Ready", self.level, self.pin.pin()); + trace!("{:?} for pin {} was cleared, return Poll::Ready", self.level, self.pin.pin()); return Poll::Ready(()); } } } - debug!("InputFuture::poll return Poll::Pending"); + trace!("InputFuture::poll return Poll::Pending"); Poll::Pending } } From 4314b823aaadc54387fbc1379f89300c70007d83 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 4 Mar 2023 05:24:38 +0100 Subject: [PATCH 22/51] nrf: add PPI channel group driver. --- embassy-nrf/src/ppi/dppi.rs | 2 +- embassy-nrf/src/ppi/mod.rs | 104 +++++++++++++++++++++++++++++++++--- embassy-nrf/src/ppi/ppi.rs | 2 +- 3 files changed, 98 insertions(+), 10 deletions(-) diff --git a/embassy-nrf/src/ppi/dppi.rs b/embassy-nrf/src/ppi/dppi.rs index 0908cd7b..3a1e7f17 100644 --- a/embassy-nrf/src/ppi/dppi.rs +++ b/embassy-nrf/src/ppi/dppi.rs @@ -6,7 +6,7 @@ use crate::{pac, Peripheral}; const DPPI_ENABLE_BIT: u32 = 0x8000_0000; const DPPI_CHANNEL_MASK: u32 = 0x0000_00FF; -fn regs() -> &'static pac::dppic::RegisterBlock { +pub(crate) fn regs() -> &'static pac::dppic::RegisterBlock { unsafe { &*pac::DPPIC::ptr() } } diff --git a/embassy-nrf/src/ppi/mod.rs b/embassy-nrf/src/ppi/mod.rs index b76eccf0..7c18da6e 100644 --- a/embassy-nrf/src/ppi/mod.rs +++ b/embassy-nrf/src/ppi/mod.rs @@ -17,16 +17,16 @@ use core::ptr::NonNull; -use embassy_hal_common::{impl_peripheral, PeripheralRef}; +use embassy_hal_common::{impl_peripheral, into_ref, PeripheralRef}; use crate::{peripherals, Peripheral}; -#[cfg(feature = "_dppi")] -mod dppi; -#[cfg(feature = "_ppi")] -mod ppi; +#[cfg_attr(feature = "_dppi", path = "dppi.rs")] +#[cfg_attr(feature = "_ppi", path = "ppi.rs")] +mod _version; +pub(crate) use _version::*; -/// An instance of the Programmable peripheral interconnect on nRF devices. +/// PPI channel driver. pub struct Ppi<'d, C: Channel, const EVENT_COUNT: usize, const TASK_COUNT: usize> { ch: PeripheralRef<'d, C>, #[cfg(feature = "_dppi")] @@ -35,6 +35,88 @@ pub struct Ppi<'d, C: Channel, const EVENT_COUNT: usize, const TASK_COUNT: usize tasks: [Task; TASK_COUNT], } +/// PPI channel group driver. +pub struct PpiGroup<'d, G: Group> { + g: PeripheralRef<'d, G>, +} + +impl<'d, G: Group> PpiGroup<'d, G> { + /// Create a new PPI group driver. + /// + /// The group is initialized as containing no channels. + pub fn new(g: impl Peripheral

+ 'd) -> Self { + into_ref!(g); + + let r = regs(); + let n = g.number(); + r.chg[n].write(|w| unsafe { w.bits(0) }); + + Self { g } + } + + /// Add a PPI channel to this group. + /// + /// If the channel is already in the group, this is a no-op. + pub fn add_channel( + &mut self, + ch: &Ppi<'_, C, EVENT_COUNT, TASK_COUNT>, + ) { + let r = regs(); + let ng = self.g.number(); + let nc = ch.ch.number(); + r.chg[ng].modify(|r, w| unsafe { w.bits(r.bits() | 1 << nc) }); + } + + /// Remove a PPI channel from this group. + /// + /// If the channel is already not in the group, this is a no-op. + pub fn remove_channel( + &mut self, + ch: &Ppi<'_, C, EVENT_COUNT, TASK_COUNT>, + ) { + let r = regs(); + let ng = self.g.number(); + let nc = ch.ch.number(); + r.chg[ng].modify(|r, w| unsafe { w.bits(r.bits() & !(1 << nc)) }); + } + + /// Enable all the channels in this group. + pub fn enable_all(&mut self) { + let n = self.g.number(); + regs().tasks_chg[n].en.write(|w| unsafe { w.bits(1) }); + } + + /// Disable all the channels in this group. + pub fn disable_all(&mut self) { + let n = self.g.number(); + regs().tasks_chg[n].dis.write(|w| unsafe { w.bits(1) }); + } + + /// Get a reference to the "enable all" task. + /// + /// When triggered, it will enable all the channels in this group. + pub fn task_enable_all(&self) -> Task { + let n = self.g.number(); + Task::from_reg(®s().tasks_chg[n].en) + } + + /// Get a reference to the "disable all" task. + /// + /// When triggered, it will disable all the channels in this group. + pub fn task_disable_all(&self) -> Task { + let n = self.g.number(); + Task::from_reg(®s().tasks_chg[n].dis) + } +} + +impl<'d, G: Group> Drop for PpiGroup<'d, G> { + fn drop(&mut self) { + let r = regs(); + let n = self.g.number(); + r.chg[n].write(|w| unsafe { w.bits(0) }); + } +} + #[cfg(feature = "_dppi")] const REGISTER_DPPI_CONFIG_OFFSET: usize = 0x80 / core::mem::size_of::(); @@ -112,7 +194,7 @@ pub(crate) mod sealed { } /// Interface for PPI channels. -pub trait Channel: sealed::Channel + Peripheral

+ Sized { +pub trait Channel: sealed::Channel + Peripheral

+ Sized + 'static { /// Returns the number of the channel fn number(&self) -> usize; } @@ -130,7 +212,7 @@ pub trait StaticChannel: Channel + Into { } /// Interface for a group of PPI channels. -pub trait Group: sealed::Group + Sized { +pub trait Group: sealed::Group + Peripheral

+ Into + Sized + 'static { /// Returns the number of the group. fn number(&self) -> usize; /// Convert into a type erased group. @@ -248,6 +330,12 @@ macro_rules! impl_group { $number } } + + impl From for crate::ppi::AnyGroup { + fn from(val: peripherals::$type) -> Self { + crate::ppi::Group::degrade(val) + } + } }; } diff --git a/embassy-nrf/src/ppi/ppi.rs b/embassy-nrf/src/ppi/ppi.rs index a96ab50b..f1eeaee1 100644 --- a/embassy-nrf/src/ppi/ppi.rs +++ b/embassy-nrf/src/ppi/ppi.rs @@ -14,7 +14,7 @@ impl Event { } } -fn regs() -> &'static pac::ppi::RegisterBlock { +pub(crate) fn regs() -> &'static pac::ppi::RegisterBlock { unsafe { &*pac::PPI::ptr() } } From 51478caad843e4b0be1d1baf83d1f0e8c0d96a30 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 4 Mar 2023 05:25:57 +0100 Subject: [PATCH 23/51] nrf/timer: add support for counter mode. --- embassy-nrf/src/timer.rs | 43 +++++++++++++++++++++++++++++++++++----- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/embassy-nrf/src/timer.rs b/embassy-nrf/src/timer.rs index d1ae5723..3b0d2f1c 100644 --- a/embassy-nrf/src/timer.rs +++ b/embassy-nrf/src/timer.rs @@ -132,7 +132,21 @@ impl<'d, T: Instance> Timer<'d, T, Awaitable> { irq.unpend(); irq.enable(); - Self::new_inner(timer) + Self::new_inner(timer, false) + } + + /// Create a new async-capable timer driver in counter mode. + pub fn new_awaitable_counter( + timer: impl Peripheral

+ 'd, + irq: impl Peripheral

+ 'd, + ) -> Self { + into_ref!(irq); + + irq.set_handler(Self::on_interrupt); + irq.unpend(); + irq.enable(); + + Self::new_inner(timer, true) } } @@ -142,7 +156,15 @@ impl<'d, T: Instance> Timer<'d, T, NotAwaitable> { /// This can be useful for triggering tasks via PPI /// `Uarte` uses this internally. pub fn new(timer: impl Peripheral

+ 'd) -> Self { - Self::new_inner(timer) + Self::new_inner(timer, false) + } + + /// Create a `Timer` driver in counter mode without an interrupt, meaning `Cc::wait` won't work. + /// + /// This can be useful for triggering tasks via PPI + /// `Uarte` uses this internally. + pub fn new_counter(timer: impl Peripheral

+ 'd) -> Self { + Self::new_inner(timer, true) } } @@ -150,7 +172,7 @@ impl<'d, T: Instance, I: TimerType> Timer<'d, T, I> { /// Create a `Timer` without an interrupt, meaning `Cc::wait` won't work. /// /// This is used by the public constructors. - fn new_inner(timer: impl Peripheral

+ 'd) -> Self { + fn new_inner(timer: impl Peripheral

+ 'd, is_counter: bool) -> Self { into_ref!(timer); let regs = T::regs(); @@ -164,8 +186,11 @@ impl<'d, T: Instance, I: TimerType> Timer<'d, T, I> { // since changing BITMODE while running can cause 'unpredictable behaviour' according to the specification. this.stop(); - // Set the instance to timer mode. - regs.mode.write(|w| w.mode().timer()); + if is_counter { + regs.mode.write(|w| w.mode().counter()); + } else { + regs.mode.write(|w| w.mode().timer()); + } // Make the counter's max value as high as possible. // TODO: is there a reason someone would want to set this lower? @@ -225,6 +250,14 @@ impl<'d, T: Instance, I: TimerType> Timer<'d, T, I> { Task::from_reg(&T::regs().tasks_clear) } + /// Returns the COUNT task, for use with PPI. + /// + /// When triggered, this task increments the timer's counter by 1. + /// Only works in counter mode. + pub fn task_count(&self) -> Task { + Task::from_reg(&T::regs().tasks_count) + } + /// Change the timer's frequency. /// /// This will stop the timer if it isn't already stopped, From 7b9075130e72e0f6fab6b07c0171e42e1b9802a1 Mon Sep 17 00:00:00 2001 From: Lasse Dalegaard Date: Sat, 4 Mar 2023 10:36:10 +0100 Subject: [PATCH 24/51] embassy_usb: Add split() for cdc_acm --- embassy-usb/src/class/cdc_acm.rs | 100 +++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) diff --git a/embassy-usb/src/class/cdc_acm.rs b/embassy-usb/src/class/cdc_acm.rs index ff82ad40..a341e10d 100644 --- a/embassy-usb/src/class/cdc_acm.rs +++ b/embassy-usb/src/class/cdc_acm.rs @@ -276,6 +276,106 @@ impl<'d, D: Driver<'d>> CdcAcmClass<'d, D> { pub async fn wait_connection(&mut self) { self.read_ep.wait_enabled().await } + + /// Split the class into a sender and receiver. + /// + /// This allows concurrently sending and receiving packets from separate tasks. + pub fn split(self) -> (Sender<'d, D>, Receiver<'d, D>) { + ( + Sender { + write_ep: self.write_ep, + control: self.control, + }, + Receiver { + read_ep: self.read_ep, + control: self.control, + }, + ) + } +} + +/// CDC ACM class packet sender. +/// +/// You can obtain a `Sender` with [`CdcAcmClass::split`] +pub struct Sender<'d, D: Driver<'d>> { + write_ep: D::EndpointIn, + control: &'d ControlShared, +} + +impl<'d, D: Driver<'d>> Sender<'d, D> { + /// Gets the maximum packet size in bytes. + pub fn max_packet_size(&self) -> u16 { + // The size is the same for both endpoints. + self.write_ep.info().max_packet_size + } + + /// Gets the current line coding. The line coding contains information that's mainly relevant + /// for USB to UART serial port emulators, and can be ignored if not relevant. + pub fn line_coding(&self) -> LineCoding { + self.control.line_coding.lock(|x| x.get()) + } + + /// Gets the DTR (data terminal ready) state + pub fn dtr(&self) -> bool { + self.control.dtr.load(Ordering::Relaxed) + } + + /// Gets the RTS (request to send) state + pub fn rts(&self) -> bool { + self.control.rts.load(Ordering::Relaxed) + } + + /// Writes a single packet into the IN endpoint. + pub async fn write_packet(&mut self, data: &[u8]) -> Result<(), EndpointError> { + self.write_ep.write(data).await + } + + /// Waits for the USB host to enable this interface + pub async fn wait_connection(&mut self) { + self.write_ep.wait_enabled().await + } +} + +/// CDC ACM class packet receiver. +/// +/// You can obtain a `Receiver` with [`CdcAcmClass::split`] +pub struct Receiver<'d, D: Driver<'d>> { + read_ep: D::EndpointOut, + control: &'d ControlShared, +} + +impl<'d, D: Driver<'d>> Receiver<'d, D> { + /// Gets the maximum packet size in bytes. + pub fn max_packet_size(&self) -> u16 { + // The size is the same for both endpoints. + self.read_ep.info().max_packet_size + } + + /// Gets the current line coding. The line coding contains information that's mainly relevant + /// for USB to UART serial port emulators, and can be ignored if not relevant. + pub fn line_coding(&self) -> LineCoding { + self.control.line_coding.lock(|x| x.get()) + } + + /// Gets the DTR (data terminal ready) state + pub fn dtr(&self) -> bool { + self.control.dtr.load(Ordering::Relaxed) + } + + /// Gets the RTS (request to send) state + pub fn rts(&self) -> bool { + self.control.rts.load(Ordering::Relaxed) + } + + /// Reads a single packet from the OUT endpoint. + pub async fn read_packet(&mut self, data: &mut [u8]) -> Result { + self.read_ep.read(data).await + } + + /// Waits for the USB host to enable this interface + pub async fn wait_connection(&mut self) { + self.read_ep.wait_enabled().await + } } /// Number of stop bits for LineCoding From ccc224c81ff1a56296576f4a249fe91a37c03fd8 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 4 Mar 2023 05:27:29 +0100 Subject: [PATCH 25/51] nrf/buffered_uarte: remove PeripheralMutex, make it work without rts/cts. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit > dirbaio: so I was checking how zephyr does UARTE RX on nRF > dirbaio: because currently we have the ugly "restart DMA on line idle to flush it" hack > dirbaio: because according to the docs "For each byte received over the RXD line, an RXDRDY event will be generated. This event is likely to occur before the corresponding data has been transferred to Data RAM." > dirbaio: so as I understood it, the only way to guarantee the data is actually transferred to RAM is to stop+restart DMA > dirbaio: well, guess what? > dirbaio: they just count RXDRDY's, and process that amount of data without restarting DMA > dirbaio: with a timer configured as counter https://github.com/zephyrproject-rtos/zephyr/blob/main/drivers/serial/uart_nrfx_uarte.c#L650-L692 > dirbaio: 🤔🤷⁉️ > dirbaio: someone saying you can do the "hook up rxdrdy to a counter" trick, someone else saying it's wrong 🤪 https://devzone.nordicsemi.com/f/nordic-q-a/28420/uarte-in-circular-mode So we're going to do just that! - BufferedUarte is lock-free now. No PeripheralMutex. - The "restart DMA on line idle to flush it" hack is GONE. This means - It'll work correctly without RTS/CTS now. - It'll have better throughput when using RTS/CTS. --- embassy-nrf/src/buffered_uarte.rs | 668 ++++++++++++--------- embassy-nrf/src/uarte.rs | 5 + examples/nrf52840/src/bin/buffered_uart.rs | 12 +- 3 files changed, 379 insertions(+), 306 deletions(-) diff --git a/embassy-nrf/src/buffered_uarte.rs b/embassy-nrf/src/buffered_uarte.rs index 112f084c..79f9a1f7 100644 --- a/embassy-nrf/src/buffered_uarte.rs +++ b/embassy-nrf/src/buffered_uarte.rs @@ -1,10 +1,5 @@ //! Async buffered UART driver. //! -//! WARNING!!! The functionality provided here is intended to be used only -//! in situations where hardware flow control are available i.e. CTS and RTS. -//! This is a problem that should be addressed at a later stage and can be -//! fully explained at . -//! //! Note that discarding a future from a read or write operation may lead to losing //! data. For example, when using `futures_util::future::select` and completion occurs //! on the "other" future, you should capture the incomplete future and continue to use @@ -13,82 +8,120 @@ //! //! Please also see [crate::uarte] to understand when [BufferedUarte] should be used. -use core::cell::RefCell; use core::cmp::min; use core::future::poll_fn; -use core::sync::atomic::{compiler_fence, Ordering}; +use core::slice; +use core::sync::atomic::{compiler_fence, AtomicU8, AtomicUsize, Ordering}; use core::task::Poll; -use embassy_cortex_m::peripheral::{PeripheralMutex, PeripheralState, StateStorage}; -use embassy_hal_common::ring_buffer::RingBuffer; +use embassy_cortex_m::interrupt::Interrupt; +use embassy_hal_common::atomic_ring_buffer::RingBuffer; use embassy_hal_common::{into_ref, PeripheralRef}; -use embassy_sync::waitqueue::WakerRegistration; +use embassy_sync::waitqueue::AtomicWaker; // 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}; -use crate::gpio::{self, Pin as GpioPin}; +use crate::gpio::sealed::Pin; +use crate::gpio::{self, AnyPin, Pin as GpioPin, PselBits}; use crate::interrupt::InterruptExt; -use crate::ppi::{AnyConfigurableChannel, ConfigurableChannel, Event, Ppi, Task}; -use crate::timer::{Frequency, Instance as TimerInstance, Timer}; +use crate::ppi::{ + self, AnyConfigurableChannel, AnyGroup, Channel, ConfigurableChannel, Event, Group, Ppi, PpiGroup, Task, +}; +use crate::timer::{Instance as TimerInstance, Timer}; use crate::uarte::{apply_workaround_for_enable_anomaly, Config, Instance as UarteInstance}; use crate::{pac, Peripheral}; -#[derive(Copy, Clone, Debug, PartialEq)] -enum RxState { - Idle, - Receiving, -} +mod sealed { + use super::*; -#[derive(Copy, Clone, Debug, PartialEq)] -enum TxState { - Idle, - Transmitting(usize), -} + pub struct State { + pub tx_waker: AtomicWaker, + pub tx_buf: RingBuffer, + pub tx_count: AtomicUsize, -/// A type for storing the state of the UARTE peripheral that can be stored in a static. -pub struct State<'d, U: UarteInstance, T: TimerInstance>(StateStorage>); -impl<'d, U: UarteInstance, T: TimerInstance> State<'d, U, T> { - /// Create an instance for storing UARTE peripheral state. - pub fn new() -> Self { - Self(StateStorage::new()) + pub rx_waker: AtomicWaker, + pub rx_buf: RingBuffer, + pub rx_bufs: AtomicU8, + pub rx_ppi_ch: AtomicU8, } } -struct StateInner<'d, U: UarteInstance, T: TimerInstance> { - _peri: PeripheralRef<'d, U>, - timer: Timer<'d, T>, - _ppi_ch1: Ppi<'d, AnyConfigurableChannel, 1, 2>, - _ppi_ch2: Ppi<'d, AnyConfigurableChannel, 1, 1>, +pub(crate) use sealed::State; - rx: RingBuffer<'d>, - rx_state: RxState, - rx_waker: WakerRegistration, +impl State { + pub(crate) const fn new() -> Self { + Self { + tx_waker: AtomicWaker::new(), + tx_buf: RingBuffer::new(), + tx_count: AtomicUsize::new(0), - tx: RingBuffer<'d>, - tx_state: TxState, - tx_waker: WakerRegistration, + rx_waker: AtomicWaker::new(), + rx_buf: RingBuffer::new(), + rx_bufs: AtomicU8::new(0), + rx_ppi_ch: AtomicU8::new(0), + } + } } /// Buffered UARTE driver. pub struct BufferedUarte<'d, U: UarteInstance, T: TimerInstance> { - inner: RefCell>>, + _peri: PeripheralRef<'d, U>, + timer: Timer<'d, T>, + _ppi_ch1: Ppi<'d, AnyConfigurableChannel, 1, 1>, + _ppi_ch2: Ppi<'d, AnyConfigurableChannel, 1, 2>, + _ppi_group: PpiGroup<'d, AnyGroup>, } impl<'d, U: UarteInstance, T: TimerInstance> Unpin for BufferedUarte<'d, U, T> {} impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> { - /// Create a new instance of a BufferedUarte. + /// Create a new BufferedUarte without hardware flow control. /// - /// See the [module documentation](crate::buffered_uarte) for more details about the intended use. + /// # Panics /// - /// The BufferedUarte uses the provided state to store the buffers and peripheral state. The timer and ppi channels are used to 'emulate' idle line detection so that read operations - /// can return early if there is no data to receive. + /// Panics if `rx_buffer.len()` is odd. pub fn new( - state: &'d mut State<'d, U, T>, - peri: impl Peripheral

+ 'd, + uarte: impl Peripheral

+ 'd, timer: impl Peripheral

+ 'd, - ppi_ch1: impl Peripheral

+ 'd, - ppi_ch2: impl Peripheral

+ 'd, + ppi_ch1: impl Peripheral

+ 'd, + ppi_ch2: impl Peripheral

+ 'd, + ppi_group: impl Peripheral

+ 'd, + irq: impl Peripheral

+ 'd, + rxd: impl Peripheral

+ 'd, + txd: impl Peripheral

+ 'd, + config: Config, + rx_buffer: &'d mut [u8], + tx_buffer: &'d mut [u8], + ) -> Self { + into_ref!(rxd, txd, ppi_ch1, ppi_ch2, ppi_group); + Self::new_inner( + uarte, + timer, + ppi_ch1.map_into(), + ppi_ch2.map_into(), + ppi_group.map_into(), + irq, + rxd.map_into(), + txd.map_into(), + None, + None, + config, + rx_buffer, + tx_buffer, + ) + } + + /// Create a new BufferedUarte with hardware flow control (RTS/CTS) + /// + /// # Panics + /// + /// Panics if `rx_buffer.len()` is odd. + pub fn new_with_rtscts( + uarte: impl Peripheral

+ 'd, + timer: impl Peripheral

+ 'd, + ppi_ch1: impl Peripheral

+ 'd, + ppi_ch2: impl Peripheral

+ 'd, + ppi_group: impl Peripheral

+ 'd, irq: impl Peripheral

+ 'd, rxd: impl Peripheral

+ 'd, txd: impl Peripheral

+ 'd, @@ -98,12 +131,45 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> { rx_buffer: &'d mut [u8], tx_buffer: &'d mut [u8], ) -> Self { - into_ref!(peri, ppi_ch1, ppi_ch2, irq, rxd, txd, cts, rts); + into_ref!(rxd, txd, cts, rts, ppi_ch1, ppi_ch2, ppi_group); + Self::new_inner( + uarte, + timer, + ppi_ch1.map_into(), + ppi_ch2.map_into(), + ppi_group.map_into(), + irq, + rxd.map_into(), + txd.map_into(), + Some(cts.map_into()), + Some(rts.map_into()), + config, + rx_buffer, + tx_buffer, + ) + } + + fn new_inner( + peri: impl Peripheral

+ 'd, + timer: impl Peripheral

+ 'd, + ppi_ch1: PeripheralRef<'d, AnyConfigurableChannel>, + ppi_ch2: PeripheralRef<'d, AnyConfigurableChannel>, + ppi_group: PeripheralRef<'d, AnyGroup>, + irq: impl Peripheral

+ 'd, + rxd: PeripheralRef<'d, AnyPin>, + txd: PeripheralRef<'d, AnyPin>, + cts: Option>, + rts: Option>, + config: Config, + rx_buffer: &'d mut [u8], + tx_buffer: &'d mut [u8], + ) -> Self { + into_ref!(peri, timer, irq); + + assert!(rx_buffer.len() % 2 == 0); let r = U::regs(); - let mut timer = Timer::new(timer); - rxd.conf().write(|w| w.input().connect().drive().h0h1()); r.psel.rxd.write(|w| unsafe { w.bits(rxd.psel_bits()) }); @@ -111,92 +177,200 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> { txd.conf().write(|w| w.dir().output().drive().h0h1()); r.psel.txd.write(|w| unsafe { w.bits(txd.psel_bits()) }); - cts.conf().write(|w| w.input().connect().drive().h0h1()); + if let Some(pin) = &cts { + pin.conf().write(|w| w.input().connect().drive().h0h1()); + } r.psel.cts.write(|w| unsafe { w.bits(cts.psel_bits()) }); - rts.set_high(); - rts.conf().write(|w| w.dir().output().drive().h0h1()); + if let Some(pin) = &rts { + pin.set_high(); + pin.conf().write(|w| w.dir().output().drive().h0h1()); + } r.psel.rts.write(|w| unsafe { w.bits(rts.psel_bits()) }); - r.baudrate.write(|w| w.baudrate().variant(config.baudrate)); - r.config.write(|w| w.parity().variant(config.parity)); + // Initialize state + let s = U::buffered_state(); + s.tx_count.store(0, Ordering::Relaxed); + s.rx_bufs.store(0, Ordering::Relaxed); + let len = tx_buffer.len(); + unsafe { s.tx_buf.init(tx_buffer.as_mut_ptr(), len) }; + let len = rx_buffer.len(); + unsafe { s.rx_buf.init(rx_buffer.as_mut_ptr(), len) }; // Configure r.config.write(|w| { - w.hwfc().bit(true); + w.hwfc().bit(false); w.parity().variant(config.parity); w }); r.baudrate.write(|w| w.baudrate().variant(config.baudrate)); - // Enable interrupts - r.intenset.write(|w| w.endrx().set().endtx().set()); + // clear errors + let errors = r.errorsrc.read().bits(); + r.errorsrc.write(|w| unsafe { w.bits(errors) }); - // Disable the irq, let the Registration enable it when everything is set up. - irq.disable(); - irq.pend(); + r.events_rxstarted.reset(); + r.events_txstarted.reset(); + r.events_error.reset(); + r.events_endrx.reset(); + r.events_endtx.reset(); + + // Enable interrupts + r.intenclr.write(|w| unsafe { w.bits(!0) }); + r.intenset.write(|w| { + w.endtx().set(); + w.rxstarted().set(); + w.error().set(); + w + }); // Enable UARTE instance apply_workaround_for_enable_anomaly(&r); 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 / (config.baudrate as u32 / 40); + // Configure byte counter. + let mut timer = Timer::new_counter(timer); + timer.cc(1).write(rx_buffer.len() as u32 * 2); + timer.cc(1).short_compare_clear(); + timer.clear(); + timer.start(); - timer.set_frequency(Frequency::F16MHz); - timer.cc(0).write(timeout); - timer.cc(0).short_compare_clear(); - timer.cc(0).short_compare_stop(); - - let mut ppi_ch1 = Ppi::new_one_to_two( - ppi_ch1.map_into(), - Event::from_reg(&r.events_rxdrdy), - timer.task_clear(), - timer.task_start(), - ); + let mut ppi_ch1 = Ppi::new_one_to_one(ppi_ch1, Event::from_reg(&r.events_rxdrdy), timer.task_count()); ppi_ch1.enable(); - let mut ppi_ch2 = Ppi::new_one_to_one( - ppi_ch2.map_into(), - timer.cc(0).event_compare(), - Task::from_reg(&r.tasks_stoprx), + s.rx_ppi_ch.store(ppi_ch2.number() as u8, Ordering::Relaxed); + let mut ppi_group = PpiGroup::new(ppi_group); + let mut ppi_ch2 = Ppi::new_one_to_two( + ppi_ch2, + Event::from_reg(&r.events_endrx), + Task::from_reg(&r.tasks_startrx), + ppi_group.task_disable_all(), ); - ppi_ch2.enable(); + ppi_ch2.disable(); + ppi_group.add_channel(&ppi_ch2); + + irq.disable(); + irq.set_handler(Self::on_interrupt); + irq.pend(); + irq.enable(); Self { - inner: RefCell::new(PeripheralMutex::new(irq, &mut state.0, move || StateInner { - _peri: peri, - timer, - _ppi_ch1: ppi_ch1, - _ppi_ch2: ppi_ch2, - - rx: RingBuffer::new(rx_buffer), - rx_state: RxState::Idle, - rx_waker: WakerRegistration::new(), - - tx: RingBuffer::new(tx_buffer), - tx_state: TxState::Idle, - tx_waker: WakerRegistration::new(), - })), + _peri: peri, + timer, + _ppi_ch1: ppi_ch1, + _ppi_ch2: ppi_ch2, + _ppi_group: ppi_group, } } + fn pend_irq() { + unsafe { ::steal() }.pend() + } + + fn on_interrupt(_: *mut ()) { + //trace!("irq: start"); + let r = U::regs(); + let s = U::buffered_state(); + + let buf_len = s.rx_buf.len(); + let half_len = buf_len / 2; + let mut tx = unsafe { s.tx_buf.reader() }; + let mut rx = unsafe { s.rx_buf.writer() }; + + if r.events_error.read().bits() != 0 { + r.events_error.reset(); + let errs = r.errorsrc.read(); + r.errorsrc.write(|w| unsafe { w.bits(errs.bits()) }); + + if errs.overrun().bit() { + panic!("BufferedUarte overrun"); + } + } + + // Received some bytes, wake task. + if r.inten.read().rxdrdy().bit_is_set() && r.events_rxdrdy.read().events_rxdrdy().bit_is_set() { + r.intenclr.write(|w| w.rxdrdy().clear()); + r.events_rxdrdy.reset(); + s.rx_waker.wake(); + } + + // If not RXing, start. + if s.rx_bufs.load(Ordering::Relaxed) == 0 { + let (ptr, len) = rx.push_buf(); + if len >= half_len { + //trace!(" irq_rx: starting {:?}", half_len); + s.rx_bufs.store(1, Ordering::Relaxed); + + // Set up the DMA read + r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) }); + r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(half_len as _) }); + + // Start UARTE Receive transaction + r.tasks_startrx.write(|w| unsafe { w.bits(1) }); + rx.push_done(half_len); + r.intenset.write(|w| w.rxstarted().set()); + } + } + + if r.events_rxstarted.read().bits() != 0 { + //trace!(" irq_rx: rxstarted"); + let (ptr, len) = rx.push_buf(); + if len >= half_len { + //trace!(" irq_rx: starting second {:?}", half_len); + + // Set up the DMA read + r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) }); + r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(half_len as _) }); + + let chn = s.rx_ppi_ch.load(Ordering::Relaxed); + + ppi::regs().chenset.write(|w| unsafe { w.bits(1 << chn) }); + + rx.push_done(half_len); + + r.events_rxstarted.reset(); + } else { + //trace!(" irq_rx: rxstarted no buf"); + r.intenclr.write(|w| w.rxstarted().clear()); + } + } + + // ============================= + + // TX end + if r.events_endtx.read().bits() != 0 { + r.events_endtx.reset(); + + let n = s.tx_count.load(Ordering::Relaxed); + //trace!(" irq_tx: endtx {:?}", n); + tx.pop_done(n); + s.tx_waker.wake(); + s.tx_count.store(0, Ordering::Relaxed); + } + + // If not TXing, start. + if s.tx_count.load(Ordering::Relaxed) == 0 { + let (ptr, len) = tx.pop_buf(); + if len != 0 { + //trace!(" irq_tx: starting {:?}", len); + s.tx_count.store(len, Ordering::Relaxed); + + // Set up the DMA write + r.txd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) }); + r.txd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) }); + + // Start UARTE Transmit transaction + r.tasks_starttx.write(|w| unsafe { w.bits(1) }); + } + } + + //trace!("irq: end"); + } + /// Adjust the baud rate to the provided value. pub fn set_baudrate(&mut self, baudrate: Baudrate) { - self.inner.borrow_mut().with(|state| { - let r = U::regs(); - - let timeout = 0x8000_0000 / (baudrate as u32 / 40); - state.timer.cc(0).write(timeout); - state.timer.clear(); - - r.baudrate.write(|w| w.baudrate().variant(baudrate)); - }); + let r = U::regs(); + r.baudrate.write(|w| w.baudrate().variant(baudrate)); } /// Split the UART in reader and writer parts. @@ -206,120 +380,117 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> { (BufferedUarteRx { inner: self }, BufferedUarteTx { inner: self }) } - async fn inner_read<'a>(&'a self, buf: &'a mut [u8]) -> Result { - poll_fn(move |cx| { - let mut do_pend = false; - let mut inner = self.inner.borrow_mut(); - let res = inner.with(|state| { - compiler_fence(Ordering::SeqCst); - trace!("poll_read"); - - // We have data ready in buffer? Return it. - let data = state.rx.pop_buf(); - if !data.is_empty() { - trace!(" got {:?} {:?}", data.as_ptr() as u32, data.len()); - let len = data.len().min(buf.len()); - buf[..len].copy_from_slice(&data[..len]); - state.rx.pop(len); - do_pend = true; - return Poll::Ready(Ok(len)); - } - - trace!(" empty"); - state.rx_waker.register(cx.waker()); - Poll::Pending - }); - if do_pend { - inner.pend(); - } - - res - }) - .await + async fn inner_read(&self, buf: &mut [u8]) -> Result { + let data = self.inner_fill_buf().await?; + let n = data.len().min(buf.len()); + buf[..n].copy_from_slice(&data[..n]); + self.inner_consume(n); + Ok(n) } async fn inner_write<'a>(&'a self, buf: &'a [u8]) -> Result { poll_fn(move |cx| { - let mut inner = self.inner.borrow_mut(); - let res = inner.with(|state| { - trace!("poll_write: {:?}", buf.len()); + //trace!("poll_write: {:?}", buf.len()); + let s = U::buffered_state(); + let mut tx = unsafe { s.tx_buf.writer() }; - let tx_buf = state.tx.push_buf(); - if tx_buf.is_empty() { - trace!("poll_write: pending"); - state.tx_waker.register(cx.waker()); - return Poll::Pending; - } + let tx_buf = tx.push_slice(); + if tx_buf.is_empty() { + //trace!("poll_write: pending"); + s.tx_waker.register(cx.waker()); + return Poll::Pending; + } - let n = min(tx_buf.len(), buf.len()); - tx_buf[..n].copy_from_slice(&buf[..n]); - state.tx.push(n); + let n = min(tx_buf.len(), buf.len()); + tx_buf[..n].copy_from_slice(&buf[..n]); + tx.push_done(n); - trace!("poll_write: queued {:?}", n); + //trace!("poll_write: queued {:?}", n); - compiler_fence(Ordering::SeqCst); + compiler_fence(Ordering::SeqCst); + Self::pend_irq(); - Poll::Ready(Ok(n)) - }); - - inner.pend(); - - res + Poll::Ready(Ok(n)) }) .await } async fn inner_flush<'a>(&'a self) -> Result<(), core::convert::Infallible> { poll_fn(move |cx| { - self.inner.borrow_mut().with(|state| { - trace!("poll_flush"); + //trace!("poll_flush"); + let s = U::buffered_state(); + if !s.tx_buf.is_empty() { + //trace!("poll_flush: pending"); + s.tx_waker.register(cx.waker()); + return Poll::Pending; + } - if !state.tx.is_empty() { - trace!("poll_flush: pending"); - state.tx_waker.register(cx.waker()); - return Poll::Pending; - } - - Poll::Ready(Ok(())) - }) + Poll::Ready(Ok(())) }) .await } async fn inner_fill_buf<'a>(&'a self) -> Result<&'a [u8], core::convert::Infallible> { poll_fn(move |cx| { - self.inner.borrow_mut().with(|state| { - compiler_fence(Ordering::SeqCst); - trace!("fill_buf"); + compiler_fence(Ordering::SeqCst); + //trace!("poll_read"); - // We have data ready in buffer? Return it. - let buf = state.rx.pop_buf(); - if !buf.is_empty() { - trace!(" got {:?} {:?}", buf.as_ptr() as u32, buf.len()); - let buf: &[u8] = buf; - // Safety: buffer lives as long as uart - let buf: &[u8] = unsafe { core::mem::transmute(buf) }; - return Poll::Ready(Ok(buf)); - } + let r = U::regs(); + let s = U::buffered_state(); - trace!(" empty"); - state.rx_waker.register(cx.waker()); - Poll::>::Pending - }) + // Read the RXDRDY counter. + T::regs().tasks_capture[0].write(|w| unsafe { w.bits(1) }); + let mut end = T::regs().cc[0].read().bits() as usize; + //trace!(" rxdrdy count = {:?}", end); + + // We've set a compare channel that resets the counter to 0 when it reaches `len*2`. + // However, it's unclear if that's instant, or there's a small window where you can + // still read `len()*2`. + // This could happen if in one clock cycle the counter is updated, and in the next the + // clear takes effect. The docs are very sparse, they just say "Task delays: After TIMER + // is started, the CLEAR, COUNT, and STOP tasks are guaranteed to take effect within one + // clock cycle of the PCLK16M." :shrug: + // So, we wrap the counter ourselves, just in case. + if end > s.rx_buf.len() * 2 { + end = 0 + } + + // This logic mirrors `atomic_ring_buffer::Reader::pop_buf()` + let mut start = s.rx_buf.start.load(Ordering::Relaxed); + let len = s.rx_buf.len(); + if start == end { + //trace!(" empty"); + s.rx_waker.register(cx.waker()); + r.intenset.write(|w| w.rxdrdy().set_bit()); + return Poll::Pending; + } + + if start >= len { + start -= len + } + if end >= len { + end -= len + } + + let n = if end > start { end - start } else { len - start }; + assert!(n != 0); + //trace!(" uarte ringbuf: pop_buf {:?}..{:?}", start, start + n); + + let buf = s.rx_buf.buf.load(Ordering::Relaxed); + Poll::Ready(Ok(unsafe { slice::from_raw_parts(buf.add(start), n) })) }) .await } fn inner_consume(&self, amt: usize) { - let mut inner = self.inner.borrow_mut(); - let signal = inner.with(|state| { - let full = state.rx.is_full(); - state.rx.pop(amt); - full - }); - if signal { - inner.pend(); + if amt == 0 { + return; } + + let s = U::buffered_state(); + let mut rx = unsafe { s.rx_buf.reader() }; + rx.pop_done(amt); + U::regs().intenset.write(|w| w.rxstarted().set()); } } @@ -397,7 +568,7 @@ impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Write } } -impl<'a, U: UarteInstance, T: TimerInstance> Drop for StateInner<'a, U, T> { +impl<'a, U: UarteInstance, T: TimerInstance> Drop for BufferedUarte<'a, U, T> { fn drop(&mut self) { let r = U::regs(); @@ -418,108 +589,11 @@ impl<'a, U: UarteInstance, T: TimerInstance> Drop for StateInner<'a, U, T> { gpio::deconfigure_pin(r.psel.txd.read().bits()); gpio::deconfigure_pin(r.psel.rts.read().bits()); gpio::deconfigure_pin(r.psel.cts.read().bits()); - } -} - -impl<'a, U: UarteInstance, T: TimerInstance> PeripheralState for StateInner<'a, U, T> { - type Interrupt = U::Interrupt; - fn on_interrupt(&mut self) { - trace!("irq: start"); - let r = U::regs(); - - loop { - match self.rx_state { - RxState::Idle => { - trace!(" irq_rx: in state idle"); - - let buf = self.rx.push_buf(); - if !buf.is_empty() { - trace!(" irq_rx: starting {:?}", buf.len()); - self.rx_state = RxState::Receiving; - - // Set up the DMA read - 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) }); - 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. - // - // The MAXCNT field is at least 8 bits wide and accepts the full - // range of values. - unsafe { w.maxcnt().bits(buf.len() as _) }); - trace!(" irq_rx: buf {:?} {:?}", buf.as_ptr() as u32, buf.len()); - - // Start UARTE Receive transaction - r.tasks_startrx.write(|w| unsafe { w.bits(1) }); - } - break; - } - RxState::Receiving => { - trace!(" irq_rx: in state receiving"); - if r.events_endrx.read().bits() != 0 { - self.timer.stop(); - - let n: usize = r.rxd.amount.read().amount().bits() as usize; - trace!(" irq_rx: endrx {:?}", n); - self.rx.push(n); - - r.events_endrx.reset(); - - self.rx_waker.wake(); - self.rx_state = RxState::Idle; - } else { - break; - } - } - } - } - - loop { - match self.tx_state { - TxState::Idle => { - trace!(" irq_tx: in state Idle"); - let buf = self.tx.pop_buf(); - if !buf.is_empty() { - trace!(" irq_tx: starting {:?}", buf.len()); - self.tx_state = TxState::Transmitting(buf.len()); - - // Set up the DMA write - 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) }); - 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. - // - // The MAXCNT field is 8 bits wide and accepts the full range of - // values. - unsafe { w.maxcnt().bits(buf.len() as _) }); - - // Start UARTE Transmit transaction - r.tasks_starttx.write(|w| unsafe { w.bits(1) }); - } - break; - } - TxState::Transmitting(n) => { - trace!(" irq_tx: in state Transmitting"); - if r.events_endtx.read().bits() != 0 { - r.events_endtx.reset(); - - trace!(" irq_tx: endtx {:?}", n); - self.tx.pop(n); - self.tx_waker.wake(); - self.tx_state = TxState::Idle; - } else { - break; - } - } - } - } - trace!("irq: end"); + + let s = U::buffered_state(); + unsafe { + s.rx_buf.deinit(); + s.tx_buf.deinit(); + } } } diff --git a/embassy-nrf/src/uarte.rs b/embassy-nrf/src/uarte.rs index 48457744..00afbd05 100644 --- a/embassy-nrf/src/uarte.rs +++ b/embassy-nrf/src/uarte.rs @@ -883,6 +883,7 @@ pub(crate) mod sealed { pub trait Instance { fn regs() -> &'static pac::uarte0::RegisterBlock; fn state() -> &'static State; + fn buffered_state() -> &'static crate::buffered_uarte::State; } } @@ -902,6 +903,10 @@ macro_rules! impl_uarte { static STATE: crate::uarte::sealed::State = crate::uarte::sealed::State::new(); &STATE } + fn buffered_state() -> &'static crate::buffered_uarte::State { + static STATE: crate::buffered_uarte::State = crate::buffered_uarte::State::new(); + &STATE + } } impl crate::uarte::Instance for peripherals::$type { type Interrupt = crate::interrupt::$irq; diff --git a/examples/nrf52840/src/bin/buffered_uart.rs b/examples/nrf52840/src/bin/buffered_uart.rs index ea566f4b..584e6b2b 100644 --- a/examples/nrf52840/src/bin/buffered_uart.rs +++ b/examples/nrf52840/src/bin/buffered_uart.rs @@ -4,10 +4,9 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_nrf::buffered_uarte::{BufferedUarte, State}; +use embassy_nrf::buffered_uarte::BufferedUarte; use embassy_nrf::{interrupt, uarte}; use embedded_io::asynch::{BufRead, Write}; -use futures::pin_mut; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] @@ -21,24 +20,19 @@ async fn main(_spawner: Spawner) { let mut rx_buffer = [0u8; 4096]; let irq = interrupt::take!(UARTE0_UART0); - let mut state = State::new(); - // Please note - important to have hardware flow control (https://github.com/embassy-rs/embassy/issues/536) - let u = BufferedUarte::new( - &mut state, + let mut u = BufferedUarte::new( p.UARTE0, p.TIMER0, p.PPI_CH0, p.PPI_CH1, + p.PPI_GROUP0, irq, p.P0_08, p.P0_06, - p.P0_07, - p.P0_05, config, &mut rx_buffer, &mut tx_buffer, ); - pin_mut!(u); info!("uarte initialized!"); From 916f94b36663cbb638654b34acd53e30beb5c7b6 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 4 Mar 2023 05:59:16 +0100 Subject: [PATCH 26/51] nrf/buffered_uarte: make available on stable. --- embassy-nrf/src/buffered_uarte.rs | 175 +++++++++++++++------ embassy-nrf/src/lib.rs | 1 - examples/nrf52840/src/bin/buffered_uart.rs | 2 +- 3 files changed, 125 insertions(+), 53 deletions(-) diff --git a/embassy-nrf/src/buffered_uarte.rs b/embassy-nrf/src/buffered_uarte.rs index 79f9a1f7..07f292c4 100644 --- a/embassy-nrf/src/buffered_uarte.rs +++ b/embassy-nrf/src/buffered_uarte.rs @@ -46,6 +46,14 @@ mod sealed { } } +/// UART error. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] +pub enum Error { + // No errors for now +} + pub(crate) use sealed::State; impl State { @@ -380,7 +388,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> { (BufferedUarteRx { inner: self }, BufferedUarteTx { inner: self }) } - async fn inner_read(&self, buf: &mut [u8]) -> Result { + async fn inner_read(&self, buf: &mut [u8]) -> Result { let data = self.inner_fill_buf().await?; let n = data.len().min(buf.len()); buf[..n].copy_from_slice(&data[..n]); @@ -388,7 +396,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> { Ok(n) } - async fn inner_write<'a>(&'a self, buf: &'a [u8]) -> Result { + async fn inner_write<'a>(&'a self, buf: &'a [u8]) -> Result { poll_fn(move |cx| { //trace!("poll_write: {:?}", buf.len()); let s = U::buffered_state(); @@ -415,7 +423,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> { .await } - async fn inner_flush<'a>(&'a self) -> Result<(), core::convert::Infallible> { + async fn inner_flush<'a>(&'a self) -> Result<(), Error> { poll_fn(move |cx| { //trace!("poll_flush"); let s = U::buffered_state(); @@ -430,7 +438,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> { .await } - async fn inner_fill_buf<'a>(&'a self) -> Result<&'a [u8], core::convert::Infallible> { + async fn inner_fill_buf<'a>(&'a self) -> Result<&'a [u8], Error> { poll_fn(move |cx| { compiler_fence(Ordering::SeqCst); //trace!("poll_read"); @@ -492,6 +500,31 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> { rx.pop_done(amt); U::regs().intenset.write(|w| w.rxstarted().set()); } + + /// Pull some bytes from this source into the specified buffer, returning how many bytes were read. + pub async fn read(&mut self, buf: &mut [u8]) -> Result { + self.inner_read(buf).await + } + + /// Return the contents of the internal buffer, filling it with more data from the inner reader if it is empty. + pub async fn fill_buf(&mut self) -> Result<&[u8], Error> { + self.inner_fill_buf().await + } + + /// Tell this buffer that `amt` bytes have been consumed from the buffer, so they should no longer be returned in calls to `fill_buf`. + pub fn consume(&mut self, amt: usize) { + self.inner_consume(amt) + } + + /// Write a buffer into this writer, returning how many bytes were written. + pub async fn write(&mut self, buf: &[u8]) -> Result { + self.inner_write(buf).await + } + + /// Flush this output stream, ensuring that all intermediately buffered contents reach their destination. + pub async fn flush(&mut self) -> Result<(), Error> { + self.inner_flush().await + } } /// Reader part of the buffered UARTE driver. @@ -499,72 +532,112 @@ pub struct BufferedUarteTx<'u, 'd, U: UarteInstance, T: TimerInstance> { inner: &'u BufferedUarte<'d, U, T>, } +impl<'u, 'd, U: UarteInstance, T: TimerInstance> BufferedUarteTx<'u, 'd, U, T> { + /// Write a buffer into this writer, returning how many bytes were written. + pub async fn write(&mut self, buf: &[u8]) -> Result { + self.inner.inner_write(buf).await + } + + /// Flush this output stream, ensuring that all intermediately buffered contents reach their destination. + pub async fn flush(&mut self) -> Result<(), Error> { + self.inner.inner_flush().await + } +} + /// Writer part of the buffered UARTE driver. pub struct BufferedUarteRx<'u, 'd, U: UarteInstance, T: TimerInstance> { inner: &'u BufferedUarte<'d, U, T>, } -impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::Io for BufferedUarte<'d, U, T> { - type Error = core::convert::Infallible; -} - -impl<'u, 'd, U: UarteInstance, T: TimerInstance> embedded_io::Io for BufferedUarteRx<'u, 'd, U, T> { - type Error = core::convert::Infallible; -} - -impl<'u, 'd, U: UarteInstance, T: TimerInstance> embedded_io::Io for BufferedUarteTx<'u, 'd, U, T> { - type Error = core::convert::Infallible; -} - -impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Read for BufferedUarte<'d, U, T> { - async fn read(&mut self, buf: &mut [u8]) -> Result { - self.inner_read(buf).await - } -} - -impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Read for BufferedUarteRx<'u, 'd, U, T> { - async fn read(&mut self, buf: &mut [u8]) -> Result { +impl<'u, 'd, U: UarteInstance, T: TimerInstance> BufferedUarteRx<'u, 'd, U, T> { + /// Pull some bytes from this source into the specified buffer, returning how many bytes were read. + pub async fn read(&mut self, buf: &mut [u8]) -> Result { self.inner.inner_read(buf).await } -} -impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::BufRead for BufferedUarte<'d, U, T> { - async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { - self.inner_fill_buf().await - } - - fn consume(&mut self, amt: usize) { - self.inner_consume(amt) - } -} - -impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io::asynch::BufRead for BufferedUarteRx<'u, 'd, U, T> { - async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { + /// Return the contents of the internal buffer, filling it with more data from the inner reader if it is empty. + pub async fn fill_buf(&mut self) -> Result<&[u8], Error> { self.inner.inner_fill_buf().await } - fn consume(&mut self, amt: usize) { + /// Tell this buffer that `amt` bytes have been consumed from the buffer, so they should no longer be returned in calls to `fill_buf`. + pub fn consume(&mut self, amt: usize) { self.inner.inner_consume(amt) } } -impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Write for BufferedUarte<'d, U, T> { - async fn write(&mut self, buf: &[u8]) -> Result { - self.inner_write(buf).await +#[cfg(feature = "nightly")] +mod _embedded_io { + use super::*; + + impl embedded_io::Error for Error { + fn kind(&self) -> embedded_io::ErrorKind { + match *self {} + } } - async fn flush(&mut self) -> Result<(), Self::Error> { - self.inner_flush().await - } -} - -impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Write for BufferedUarteTx<'u, 'd, U, T> { - async fn write(&mut self, buf: &[u8]) -> Result { - self.inner.inner_write(buf).await + impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::Io for BufferedUarte<'d, U, T> { + type Error = Error; } - async fn flush(&mut self) -> Result<(), Self::Error> { - self.inner.inner_flush().await + impl<'u, 'd, U: UarteInstance, T: TimerInstance> embedded_io::Io for BufferedUarteRx<'u, 'd, U, T> { + type Error = Error; + } + + impl<'u, 'd, U: UarteInstance, T: TimerInstance> embedded_io::Io for BufferedUarteTx<'u, 'd, U, T> { + type Error = Error; + } + + impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Read for BufferedUarte<'d, U, T> { + async fn read(&mut self, buf: &mut [u8]) -> Result { + self.inner_read(buf).await + } + } + + impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Read for BufferedUarteRx<'u, 'd, U, T> { + async fn read(&mut self, buf: &mut [u8]) -> Result { + self.inner.inner_read(buf).await + } + } + + impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::BufRead for BufferedUarte<'d, U, T> { + async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { + self.inner_fill_buf().await + } + + fn consume(&mut self, amt: usize) { + self.inner_consume(amt) + } + } + + impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io::asynch::BufRead for BufferedUarteRx<'u, 'd, U, T> { + async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { + self.inner.inner_fill_buf().await + } + + fn consume(&mut self, amt: usize) { + self.inner.inner_consume(amt) + } + } + + impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Write for BufferedUarte<'d, U, T> { + async fn write(&mut self, buf: &[u8]) -> Result { + self.inner_write(buf).await + } + + async fn flush(&mut self) -> Result<(), Self::Error> { + self.inner_flush().await + } + } + + impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Write for BufferedUarteTx<'u, 'd, U, T> { + async fn write(&mut self, buf: &[u8]) -> Result { + self.inner.inner_write(buf).await + } + + async fn flush(&mut self) -> Result<(), Self::Error> { + self.inner.inner_flush().await + } } } diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index a9683df4..6b7dc779 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -37,7 +37,6 @@ pub(crate) mod util; #[cfg(feature = "_time-driver")] mod time_driver; -#[cfg(feature = "nightly")] pub mod buffered_uarte; pub mod gpio; #[cfg(feature = "gpiote")] diff --git a/examples/nrf52840/src/bin/buffered_uart.rs b/examples/nrf52840/src/bin/buffered_uart.rs index 584e6b2b..5b934b7d 100644 --- a/examples/nrf52840/src/bin/buffered_uart.rs +++ b/examples/nrf52840/src/bin/buffered_uart.rs @@ -6,7 +6,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_nrf::buffered_uarte::BufferedUarte; use embassy_nrf::{interrupt, uarte}; -use embedded_io::asynch::{BufRead, Write}; +use embedded_io::asynch::Write; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] From 7650fea5f2bed1c39a0ff6c5934709d316547a23 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 4 Mar 2023 05:37:13 +0100 Subject: [PATCH 27/51] nrf/buffered_uarte: add HIL tests. --- ci.sh | 1 + embassy-nrf/src/buffered_uarte.rs | 2 +- tests/nrf/.cargo/config.toml | 9 + tests/nrf/Cargo.toml | 20 ++ tests/nrf/build.rs | 16 ++ tests/nrf/link_ram.x | 254 ++++++++++++++++++++++++ tests/nrf/memory.x | 5 + tests/nrf/src/bin/buffered_uart.rs | 74 +++++++ tests/nrf/src/bin/buffered_uart_spam.rs | 86 ++++++++ 9 files changed, 466 insertions(+), 1 deletion(-) create mode 100644 tests/nrf/.cargo/config.toml create mode 100644 tests/nrf/Cargo.toml create mode 100644 tests/nrf/build.rs create mode 100644 tests/nrf/link_ram.x create mode 100644 tests/nrf/memory.x create mode 100644 tests/nrf/src/bin/buffered_uart.rs create mode 100644 tests/nrf/src/bin/buffered_uart_spam.rs diff --git a/ci.sh b/ci.sh index 417937d0..bbcb26bd 100755 --- a/ci.sh +++ b/ci.sh @@ -133,6 +133,7 @@ cargo batch \ --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb55rg --out-dir out/tests/nucleo-stm32wb55rg \ --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32u585ai --out-dir out/tests/iot-stm32u585ai \ --- build --release --manifest-path tests/rp/Cargo.toml --target thumbv6m-none-eabi --out-dir out/tests/rpi-pico \ + --- build --release --manifest-path tests/nrf/Cargo.toml --target thumbv7em-none-eabi --out-dir out/tests/nrf52840-dk \ $BUILD_EXTRA diff --git a/embassy-nrf/src/buffered_uarte.rs b/embassy-nrf/src/buffered_uarte.rs index 07f292c4..ab639aee 100644 --- a/embassy-nrf/src/buffered_uarte.rs +++ b/embassy-nrf/src/buffered_uarte.rs @@ -296,7 +296,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> { } // Received some bytes, wake task. - if r.inten.read().rxdrdy().bit_is_set() && r.events_rxdrdy.read().events_rxdrdy().bit_is_set() { + if r.inten.read().rxdrdy().bit_is_set() && r.events_rxdrdy.read().bits() != 0 { r.intenclr.write(|w| w.rxdrdy().clear()); r.events_rxdrdy.reset(); s.rx_waker.wake(); diff --git a/tests/nrf/.cargo/config.toml b/tests/nrf/.cargo/config.toml new file mode 100644 index 00000000..4eec189d --- /dev/null +++ b/tests/nrf/.cargo/config.toml @@ -0,0 +1,9 @@ +[target.'cfg(all(target_arch = "arm", target_os = "none"))'] +#runner = "teleprobe local run --chip nRF52840_xxAA --elf" +runner = "teleprobe client run --target nrf52840-dk --elf" + +[build] +target = "thumbv7em-none-eabi" + +[env] +DEFMT_LOG = "trace" diff --git a/tests/nrf/Cargo.toml b/tests/nrf/Cargo.toml new file mode 100644 index 00000000..2a4e8cf4 --- /dev/null +++ b/tests/nrf/Cargo.toml @@ -0,0 +1,20 @@ +[package] +edition = "2021" +name = "embassy-nrf-examples" +version = "0.1.0" +license = "MIT OR Apache-2.0" + +[dependencies] +embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } +embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt", "nightly"] } +embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "nightly", "integrated-timers"] } +embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "nightly", "defmt-timestamp-uptime"] } +embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nightly", "unstable-traits", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } +embedded-io = { version = "0.4.0", features = ["async"] } + +defmt = "0.3" +defmt-rtt = "0.4" + +cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m-rt = "0.7.0" +panic-probe = { version = "0.3", features = ["print-defmt"] } \ No newline at end of file diff --git a/tests/nrf/build.rs b/tests/nrf/build.rs new file mode 100644 index 00000000..6f487224 --- /dev/null +++ b/tests/nrf/build.rs @@ -0,0 +1,16 @@ +use std::error::Error; +use std::path::PathBuf; +use std::{env, fs}; + +fn main() -> Result<(), Box> { + let out = PathBuf::from(env::var("OUT_DIR").unwrap()); + fs::write(out.join("link_ram.x"), include_bytes!("link_ram.x")).unwrap(); + println!("cargo:rustc-link-search={}", out.display()); + println!("cargo:rerun-if-changed=link_ram.x"); + + println!("cargo:rustc-link-arg-bins=--nmagic"); + println!("cargo:rustc-link-arg-bins=-Tlink_ram.x"); + println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); + + Ok(()) +} diff --git a/tests/nrf/link_ram.x b/tests/nrf/link_ram.x new file mode 100644 index 00000000..26da86ba --- /dev/null +++ b/tests/nrf/link_ram.x @@ -0,0 +1,254 @@ +/* ##### EMBASSY NOTE + Originally from https://github.com/rust-embedded/cortex-m-rt/blob/master/link.x.in + Adjusted to put everything in RAM +*/ + +/* # Developer notes + +- Symbols that start with a double underscore (__) are considered "private" + +- Symbols that start with a single underscore (_) are considered "semi-public"; they can be + overridden in a user linker script, but should not be referred from user code (e.g. `extern "C" { + static mut __sbss }`). + +- `EXTERN` forces the linker to keep a symbol in the final binary. We use this to make sure a + symbol if not dropped if it appears in or near the front of the linker arguments and "it's not + needed" by any of the preceding objects (linker arguments) + +- `PROVIDE` is used to provide default values that can be overridden by a user linker script + +- On alignment: it's important for correctness that the VMA boundaries of both .bss and .data *and* + the LMA of .data are all 4-byte aligned. These alignments are assumed by the RAM initialization + routine. There's also a second benefit: 4-byte aligned boundaries means that you won't see + "Address (..) is out of bounds" in the disassembly produced by `objdump`. +*/ + +/* Provides information about the memory layout of the device */ +/* This will be provided by the user (see `memory.x`) or by a Board Support Crate */ +INCLUDE memory.x + +/* # Entry point = reset vector */ +EXTERN(__RESET_VECTOR); +EXTERN(Reset); +ENTRY(Reset); + +/* # Exception vectors */ +/* This is effectively weak aliasing at the linker level */ +/* The user can override any of these aliases by defining the corresponding symbol themselves (cf. + the `exception!` macro) */ +EXTERN(__EXCEPTIONS); /* depends on all the these PROVIDED symbols */ + +EXTERN(DefaultHandler); + +PROVIDE(NonMaskableInt = DefaultHandler); +EXTERN(HardFaultTrampoline); +PROVIDE(MemoryManagement = DefaultHandler); +PROVIDE(BusFault = DefaultHandler); +PROVIDE(UsageFault = DefaultHandler); +PROVIDE(SecureFault = DefaultHandler); +PROVIDE(SVCall = DefaultHandler); +PROVIDE(DebugMonitor = DefaultHandler); +PROVIDE(PendSV = DefaultHandler); +PROVIDE(SysTick = DefaultHandler); + +PROVIDE(DefaultHandler = DefaultHandler_); +PROVIDE(HardFault = HardFault_); + +/* # Interrupt vectors */ +EXTERN(__INTERRUPTS); /* `static` variable similar to `__EXCEPTIONS` */ + +/* # Pre-initialization function */ +/* If the user overrides this using the `pre_init!` macro or by creating a `__pre_init` function, + then the function this points to will be called before the RAM is initialized. */ +PROVIDE(__pre_init = DefaultPreInit); + +/* # Sections */ +SECTIONS +{ + PROVIDE(_stack_start = ORIGIN(RAM) + LENGTH(RAM)); + + /* ## Sections in RAM */ + /* ### Vector table */ + .vector_table ORIGIN(RAM) : + { + /* Initial Stack Pointer (SP) value */ + LONG(_stack_start); + + /* Reset vector */ + KEEP(*(.vector_table.reset_vector)); /* this is the `__RESET_VECTOR` symbol */ + __reset_vector = .; + + /* Exceptions */ + KEEP(*(.vector_table.exceptions)); /* this is the `__EXCEPTIONS` symbol */ + __eexceptions = .; + + /* Device specific interrupts */ + KEEP(*(.vector_table.interrupts)); /* this is the `__INTERRUPTS` symbol */ + } > RAM + + PROVIDE(_stext = ADDR(.vector_table) + SIZEOF(.vector_table)); + + /* ### .text */ + .text _stext : + { + __stext = .; + *(.Reset); + + *(.text .text.*); + + /* The HardFaultTrampoline uses the `b` instruction to enter `HardFault`, + so must be placed close to it. */ + *(.HardFaultTrampoline); + *(.HardFault.*); + + . = ALIGN(4); /* Pad .text to the alignment to workaround overlapping load section bug in old lld */ + __etext = .; + } > RAM + + /* ### .rodata */ + .rodata : ALIGN(4) + { + . = ALIGN(4); + __srodata = .; + *(.rodata .rodata.*); + + /* 4-byte align the end (VMA) of this section. + This is required by LLD to ensure the LMA of the following .data + section will have the correct alignment. */ + . = ALIGN(4); + __erodata = .; + } > RAM + + /* ## Sections in RAM */ + /* ### .data */ + .data : ALIGN(4) + { + . = ALIGN(4); + __sdata = .; + __edata = .; + *(.data .data.*); + . = ALIGN(4); /* 4-byte align the end (VMA) of this section */ + } > RAM + /* Allow sections from user `memory.x` injected using `INSERT AFTER .data` to + * use the .data loading mechanism by pushing __edata. Note: do not change + * output region or load region in those user sections! */ + . = ALIGN(4); + + /* LMA of .data */ + __sidata = LOADADDR(.data); + + /* ### .gnu.sgstubs + This section contains the TrustZone-M veneers put there by the Arm GNU linker. */ + /* Security Attribution Unit blocks must be 32 bytes aligned. */ + /* Note that this pads the RAM usage to 32 byte alignment. */ + .gnu.sgstubs : ALIGN(32) + { + . = ALIGN(32); + __veneer_base = .; + *(.gnu.sgstubs*) + . = ALIGN(32); + __veneer_limit = .; + } > RAM + + /* ### .bss */ + .bss (NOLOAD) : ALIGN(4) + { + . = ALIGN(4); + __sbss = .; + *(.bss .bss.*); + *(COMMON); /* Uninitialized C statics */ + . = ALIGN(4); /* 4-byte align the end (VMA) of this section */ + } > RAM + /* Allow sections from user `memory.x` injected using `INSERT AFTER .bss` to + * use the .bss zeroing mechanism by pushing __ebss. Note: do not change + * output region or load region in those user sections! */ + . = ALIGN(4); + __ebss = .; + + /* ### .uninit */ + .uninit (NOLOAD) : ALIGN(4) + { + . = ALIGN(4); + __suninit = .; + *(.uninit .uninit.*); + . = ALIGN(4); + __euninit = .; + } > RAM + + /* Place the heap right after `.uninit` in RAM */ + PROVIDE(__sheap = __euninit); + + /* ## .got */ + /* Dynamic relocations are unsupported. This section is only used to detect relocatable code in + the input files and raise an error if relocatable code is found */ + .got (NOLOAD) : + { + KEEP(*(.got .got.*)); + } + + /* ## Discarded sections */ + /DISCARD/ : + { + /* Unused exception related info that only wastes space */ + *(.ARM.exidx); + *(.ARM.exidx.*); + *(.ARM.extab.*); + } +} + +/* Do not exceed this mark in the error messages below | */ +/* # Alignment checks */ +ASSERT(ORIGIN(RAM) % 4 == 0, " +ERROR(cortex-m-rt): the start of the RAM region must be 4-byte aligned"); + +ASSERT(__sdata % 4 == 0 && __edata % 4 == 0, " +BUG(cortex-m-rt): .data is not 4-byte aligned"); + +ASSERT(__sidata % 4 == 0, " +BUG(cortex-m-rt): the LMA of .data is not 4-byte aligned"); + +ASSERT(__sbss % 4 == 0 && __ebss % 4 == 0, " +BUG(cortex-m-rt): .bss is not 4-byte aligned"); + +ASSERT(__sheap % 4 == 0, " +BUG(cortex-m-rt): start of .heap is not 4-byte aligned"); + +/* # Position checks */ + +/* ## .vector_table */ +ASSERT(__reset_vector == ADDR(.vector_table) + 0x8, " +BUG(cortex-m-rt): the reset vector is missing"); + +ASSERT(__eexceptions == ADDR(.vector_table) + 0x40, " +BUG(cortex-m-rt): the exception vectors are missing"); + +ASSERT(SIZEOF(.vector_table) > 0x40, " +ERROR(cortex-m-rt): The interrupt vectors are missing. +Possible solutions, from most likely to less likely: +- Link to a svd2rust generated device crate +- Check that you actually use the device/hal/bsp crate in your code +- Disable the 'device' feature of cortex-m-rt to build a generic application (a dependency +may be enabling it) +- Supply the interrupt handlers yourself. Check the documentation for details."); + +/* ## .text */ +ASSERT(ADDR(.vector_table) + SIZEOF(.vector_table) <= _stext, " +ERROR(cortex-m-rt): The .text section can't be placed inside the .vector_table section +Set _stext to an address greater than the end of .vector_table (See output of `nm`)"); + +ASSERT(_stext + SIZEOF(.text) < ORIGIN(RAM) + LENGTH(RAM), " +ERROR(cortex-m-rt): The .text section must be placed inside the RAM memory. +Set _stext to an address smaller than 'ORIGIN(RAM) + LENGTH(RAM)'"); + +/* # Other checks */ +ASSERT(SIZEOF(.got) == 0, " +ERROR(cortex-m-rt): .got section detected in the input object files +Dynamic relocations are not supported. If you are linking to C code compiled using +the 'cc' crate then modify your build script to compile the C code _without_ +the -fPIC flag. See the documentation of the `cc::Build.pic` method for details."); +/* Do not exceed this mark in the error messages above | */ + + +/* Provides weak aliases (cf. PROVIDED) for device specific interrupt handlers */ +/* This will usually be provided by a device crate generated using svd2rust (see `device.x`) */ +INCLUDE device.x \ No newline at end of file diff --git a/tests/nrf/memory.x b/tests/nrf/memory.x new file mode 100644 index 00000000..58900a7b --- /dev/null +++ b/tests/nrf/memory.x @@ -0,0 +1,5 @@ +MEMORY +{ + FLASH : ORIGIN = 0x00000000, LENGTH = 1024K + RAM : ORIGIN = 0x20000000, LENGTH = 256K +} diff --git a/tests/nrf/src/bin/buffered_uart.rs b/tests/nrf/src/bin/buffered_uart.rs new file mode 100644 index 00000000..0550b0bb --- /dev/null +++ b/tests/nrf/src/bin/buffered_uart.rs @@ -0,0 +1,74 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::{assert_eq, *}; +use embassy_executor::Spawner; +use embassy_futures::join::join; +use embassy_nrf::buffered_uarte::BufferedUarte; +use embassy_nrf::{interrupt, uarte}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_nrf::init(Default::default()); + let mut config = uarte::Config::default(); + config.parity = uarte::Parity::EXCLUDED; + config.baudrate = uarte::Baudrate::BAUD1M; + + let mut tx_buffer = [0u8; 1024]; + let mut rx_buffer = [0u8; 1024]; + + let mut u = BufferedUarte::new( + p.UARTE0, + p.TIMER0, + p.PPI_CH0, + p.PPI_CH1, + p.PPI_GROUP0, + interrupt::take!(UARTE0_UART0), + p.P1_03, + p.P1_02, + config.clone(), + &mut rx_buffer, + &mut tx_buffer, + ); + + info!("uarte initialized!"); + + let (mut rx, mut tx) = u.split(); + + const COUNT: usize = 40_000; + + let tx_fut = async { + let mut tx_buf = [0; 215]; + let mut i = 0; + while i < COUNT { + let n = tx_buf.len().min(COUNT - i); + let tx_buf = &mut tx_buf[..n]; + for (j, b) in tx_buf.iter_mut().enumerate() { + *b = (i + j) as u8; + } + let n = unwrap!(tx.write(tx_buf).await); + i += n; + } + }; + let rx_fut = async { + let mut i = 0; + while i < COUNT { + let buf = unwrap!(rx.fill_buf().await); + + for &b in buf { + assert_eq!(b, i as u8); + i = i + 1; + } + + let n = buf.len(); + rx.consume(n); + } + }; + + join(rx_fut, tx_fut).await; + + info!("Test OK"); + cortex_m::asm::bkpt(); +} diff --git a/tests/nrf/src/bin/buffered_uart_spam.rs b/tests/nrf/src/bin/buffered_uart_spam.rs new file mode 100644 index 00000000..57aaeca4 --- /dev/null +++ b/tests/nrf/src/bin/buffered_uart_spam.rs @@ -0,0 +1,86 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use core::mem; +use core::ptr::NonNull; + +use defmt::{assert_eq, *}; +use embassy_executor::Spawner; +use embassy_nrf::buffered_uarte::BufferedUarte; +use embassy_nrf::gpio::{Level, Output, OutputDrive}; +use embassy_nrf::ppi::{Event, Ppi, Task}; +use embassy_nrf::uarte::Uarte; +use embassy_nrf::{interrupt, pac, uarte}; +use embassy_time::{Duration, Timer}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let mut p = embassy_nrf::init(Default::default()); + let mut config = uarte::Config::default(); + config.parity = uarte::Parity::EXCLUDED; + config.baudrate = uarte::Baudrate::BAUD1M; + + let mut tx_buffer = [0u8; 1024]; + let mut rx_buffer = [0u8; 1024]; + + mem::forget(Output::new(&mut p.P1_02, Level::High, OutputDrive::Standard)); + + let mut u = BufferedUarte::new( + p.UARTE0, + p.TIMER0, + p.PPI_CH0, + p.PPI_CH1, + p.PPI_GROUP0, + interrupt::take!(UARTE0_UART0), + p.P1_03, + p.P1_04, + config.clone(), + &mut rx_buffer, + &mut tx_buffer, + ); + + info!("uarte initialized!"); + + // uarte needs some quiet time to start rxing properly. + Timer::after(Duration::from_millis(10)).await; + + // Tx spam in a loop. + const NSPAM: usize = 17; + static mut TX_BUF: [u8; NSPAM] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]; + let _spam = Uarte::new(p.UARTE1, interrupt::take!(UARTE1), p.P1_01, p.P1_02, config.clone()); + let spam_peri: pac::UARTE1 = unsafe { mem::transmute(()) }; + let event = unsafe { Event::new_unchecked(NonNull::new_unchecked(&spam_peri.events_endtx as *const _ as _)) }; + let task = unsafe { Task::new_unchecked(NonNull::new_unchecked(&spam_peri.tasks_starttx as *const _ as _)) }; + let mut spam_ppi = Ppi::new_one_to_one(p.PPI_CH2, event, task); + spam_ppi.enable(); + let p = unsafe { TX_BUF.as_mut_ptr() }; + spam_peri.txd.ptr.write(|w| unsafe { w.ptr().bits(p as u32) }); + spam_peri.txd.maxcnt.write(|w| unsafe { w.maxcnt().bits(NSPAM as _) }); + spam_peri.tasks_starttx.write(|w| unsafe { w.bits(1) }); + + let mut i = 0; + let mut total = 0; + while total < 256 * 1024 { + let buf = unwrap!(u.fill_buf().await); + //info!("rx {}", buf); + + for &b in buf { + assert_eq!(b, unsafe { TX_BUF[i] }); + + i = i + 1; + if i == NSPAM { + i = 0; + } + } + + // Read bytes have to be explicitly consumed, otherwise fill_buf() will return them again + let n = buf.len(); + u.consume(n); + total += n; + } + + info!("Test OK"); + cortex_m::asm::bkpt(); +} From 9eb65b11cb29ec85224c129660d8ae2e01e06119 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 23 Feb 2023 22:23:01 +0100 Subject: [PATCH 28/51] nrf/qspi: remove cfg_if hack --- embassy-nrf/src/qspi.rs | 59 +++++++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/embassy-nrf/src/qspi.rs b/embassy-nrf/src/qspi.rs index d434327f..5b68aa4d 100644 --- a/embassy-nrf/src/qspi.rs +++ b/embassy-nrf/src/qspi.rs @@ -525,42 +525,43 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> NorFlash for Qspi<'d, T, FLASH_SI } } -cfg_if::cfg_if! { - if #[cfg(feature = "nightly")] - { - use embedded_storage_async::nor_flash::{AsyncNorFlash, AsyncReadNorFlash}; - use core::future::Future; +#[cfg(feature = "nightly")] +mod _eh1 { + use core::future::Future; - impl<'d, T: Instance, const FLASH_SIZE: usize> AsyncNorFlash for Qspi<'d, T, FLASH_SIZE> { - const WRITE_SIZE: usize = ::WRITE_SIZE; - const ERASE_SIZE: usize = ::ERASE_SIZE; + use embedded_storage_async::nor_flash::{AsyncNorFlash, AsyncReadNorFlash}; - type WriteFuture<'a> = impl Future> + 'a where Self: 'a; - fn write<'a>(&'a mut self, offset: u32, data: &'a [u8]) -> Self::WriteFuture<'a> { - async move { self.write(offset as usize, data).await } - } + use super::*; - type EraseFuture<'a> = impl Future> + 'a where Self: 'a; - fn erase<'a>(&'a mut self, from: u32, to: u32) -> Self::EraseFuture<'a> { - async move { - for address in (from as usize..to as usize).step_by(::ERASE_SIZE) { - self.erase(address).await? - } - Ok(()) - } - } + impl<'d, T: Instance, const FLASH_SIZE: usize> AsyncNorFlash for Qspi<'d, T, FLASH_SIZE> { + const WRITE_SIZE: usize = ::WRITE_SIZE; + const ERASE_SIZE: usize = ::ERASE_SIZE; + + type WriteFuture<'a> = impl Future> + 'a where Self: 'a; + fn write<'a>(&'a mut self, offset: u32, data: &'a [u8]) -> Self::WriteFuture<'a> { + async move { self.write(offset as usize, data).await } } - impl<'d, T: Instance, const FLASH_SIZE: usize> AsyncReadNorFlash for Qspi<'d, T, FLASH_SIZE> { - const READ_SIZE: usize = 4; - type ReadFuture<'a> = impl Future> + 'a where Self: 'a; - fn read<'a>(&'a mut self, address: u32, data: &'a mut [u8]) -> Self::ReadFuture<'a> { - async move { self.read(address as usize, data).await } + type EraseFuture<'a> = impl Future> + 'a where Self: 'a; + fn erase<'a>(&'a mut self, from: u32, to: u32) -> Self::EraseFuture<'a> { + async move { + for address in (from as usize..to as usize).step_by(::ERASE_SIZE) { + self.erase(address).await? + } + Ok(()) } + } + } - fn capacity(&self) -> usize { - FLASH_SIZE - } + impl<'d, T: Instance, const FLASH_SIZE: usize> AsyncReadNorFlash for Qspi<'d, T, FLASH_SIZE> { + const READ_SIZE: usize = 4; + type ReadFuture<'a> = impl Future> + 'a where Self: 'a; + fn read<'a>(&'a mut self, address: u32, data: &'a mut [u8]) -> Self::ReadFuture<'a> { + async move { self.read(address as usize, data).await } + } + + fn capacity(&self) -> usize { + FLASH_SIZE } } } From 1955a225e86d358d39f00620c770631c2186576f Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 23 Feb 2023 22:36:10 +0100 Subject: [PATCH 29/51] nrf/qspi: add nrf53 support. --- embassy-nrf/src/chips/nrf5340_app.rs | 5 ++++ embassy-nrf/src/lib.rs | 2 +- embassy-nrf/src/qspi.rs | 37 ++++++++++++++-------------- 3 files changed, 25 insertions(+), 19 deletions(-) diff --git a/embassy-nrf/src/chips/nrf5340_app.rs b/embassy-nrf/src/chips/nrf5340_app.rs index 2e1c7f38..9c7b738e 100644 --- a/embassy-nrf/src/chips/nrf5340_app.rs +++ b/embassy-nrf/src/chips/nrf5340_app.rs @@ -250,6 +250,9 @@ embassy_hal_common::peripherals! { TIMER1, TIMER2, + // QSPI + QSPI, + // GPIOTE GPIOTE_CH0, GPIOTE_CH1, @@ -393,6 +396,8 @@ impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); +impl_qspi!(QSPI, QSPI, QSPI); + impl_pin!(P0_00, 0, 0); impl_pin!(P0_01, 0, 1); #[cfg(feature = "nfc-pins-as-gpio")] diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index a9683df4..26f22b61 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -58,7 +58,7 @@ pub mod ppi; pub mod pwm; #[cfg(not(any(feature = "nrf51", feature = "_nrf9160", feature = "_nrf5340")))] pub mod qdec; -#[cfg(feature = "nrf52840")] +#[cfg(any(feature = "nrf52840", feature = "_nrf5340-app"))] pub mod qspi; #[cfg(not(any(feature = "_nrf5340", feature = "_nrf9160")))] pub mod rng; diff --git a/embassy-nrf/src/qspi.rs b/embassy-nrf/src/qspi.rs index 5b68aa4d..9a4e614f 100644 --- a/embassy-nrf/src/qspi.rs +++ b/embassy-nrf/src/qspi.rs @@ -133,25 +133,26 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> { let r = T::regs(); - sck.set_high(); - csn.set_high(); - io0.set_high(); - io1.set_high(); - io2.set_high(); - io3.set_high(); - sck.conf().write(|w| w.dir().output().drive().h0h1()); - csn.conf().write(|w| w.dir().output().drive().h0h1()); - io0.conf().write(|w| w.dir().output().drive().h0h1()); - io1.conf().write(|w| w.dir().output().drive().h0h1()); - io2.conf().write(|w| w.dir().output().drive().h0h1()); - io3.conf().write(|w| w.dir().output().drive().h0h1()); + macro_rules! config_pin { + ($pin:ident) => { + $pin.set_high(); + $pin.conf().write(|w| { + w.dir().output(); + w.drive().h0h1(); + #[cfg(feature = "_nrf5340-s")] + w.mcusel().peripheral(); + w + }); + r.psel.$pin.write(|w| unsafe { w.bits($pin.psel_bits()) }); + }; + } - 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()) }); + config_pin!(sck); + config_pin!(csn); + config_pin!(io0); + config_pin!(io1); + config_pin!(io2); + config_pin!(io3); r.ifconfig0.write(|w| { w.addrmode().variant(config.address_mode); From 75f69803af244329ba6dd9093599be357f12ae60 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 5 Mar 2023 02:24:52 +0100 Subject: [PATCH 30/51] nrf/qspi: always use u32 for addresses. --- embassy-nrf/src/qspi.rs | 62 +++++++++++++++---------------- examples/nrf52840/src/bin/qspi.rs | 10 ++--- 2 files changed, 36 insertions(+), 36 deletions(-) diff --git a/embassy-nrf/src/qspi.rs b/embassy-nrf/src/qspi.rs index 9a4e614f..404438ea 100644 --- a/embassy-nrf/src/qspi.rs +++ b/embassy-nrf/src/qspi.rs @@ -111,12 +111,12 @@ pub enum Error { } /// QSPI flash driver. -pub struct Qspi<'d, T: Instance, const FLASH_SIZE: usize> { +pub struct Qspi<'d, T: Instance, const FLASH_SIZE: u32> { irq: PeripheralRef<'d, T::Interrupt>, dpm_enabled: bool, } -impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> { +impl<'d, T: Instance, const FLASH_SIZE: u32> Qspi<'d, T, FLASH_SIZE> { /// Create a new QSPI driver. pub fn new( _qspi: impl Peripheral

+ 'd, @@ -322,17 +322,17 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> { } } - fn start_read(&mut self, address: usize, data: &mut [u8]) -> Result<(), Error> { + fn start_read(&mut self, address: u32, data: &mut [u8]) -> Result<(), Error> { assert_eq!(data.as_ptr() as u32 % 4, 0); assert_eq!(data.len() as u32 % 4, 0); - assert_eq!(address as u32 % 4, 0); + assert_eq!(address % 4, 0); if address > FLASH_SIZE { return Err(Error::OutOfBounds); } let r = T::regs(); - r.read.src.write(|w| unsafe { w.src().bits(address as u32) }); + r.read.src.write(|w| unsafe { w.src().bits(address) }); r.read.dst.write(|w| unsafe { w.dst().bits(data.as_ptr() as u32) }); r.read.cnt.write(|w| unsafe { w.cnt().bits(data.len() as u32) }); @@ -343,10 +343,10 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> { Ok(()) } - fn start_write(&mut self, address: usize, data: &[u8]) -> Result<(), Error> { + fn start_write(&mut self, address: u32, data: &[u8]) -> Result<(), Error> { assert_eq!(data.as_ptr() as u32 % 4, 0); assert_eq!(data.len() as u32 % 4, 0); - assert_eq!(address as u32 % 4, 0); + assert_eq!(address % 4, 0); if address > FLASH_SIZE { return Err(Error::OutOfBounds); @@ -354,7 +354,7 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> { let r = T::regs(); r.write.src.write(|w| unsafe { w.src().bits(data.as_ptr() as u32) }); - r.write.dst.write(|w| unsafe { w.dst().bits(address as u32) }); + r.write.dst.write(|w| unsafe { w.dst().bits(address) }); r.write.cnt.write(|w| unsafe { w.cnt().bits(data.len() as u32) }); r.events_ready.reset(); @@ -364,14 +364,14 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> { Ok(()) } - fn start_erase(&mut self, address: usize) -> Result<(), Error> { - assert_eq!(address as u32 % 4096, 0); + fn start_erase(&mut self, address: u32) -> Result<(), Error> { + assert_eq!(address % 4096, 0); if address > FLASH_SIZE { return Err(Error::OutOfBounds); } let r = T::regs(); - r.erase.ptr.write(|w| unsafe { w.ptr().bits(address as u32) }); + r.erase.ptr.write(|w| unsafe { w.ptr().bits(address) }); r.erase.len.write(|w| w.len()._4kb()); r.events_ready.reset(); @@ -382,7 +382,7 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> { } /// Read data from the flash memory. - pub async fn read(&mut self, address: usize, data: &mut [u8]) -> Result<(), Error> { + pub async fn read(&mut self, address: u32, data: &mut [u8]) -> Result<(), Error> { let ondrop = OnDrop::new(Self::blocking_wait_ready); self.start_read(address, data)?; @@ -394,7 +394,7 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> { } /// Write data to the flash memory. - pub async fn write(&mut self, address: usize, data: &[u8]) -> Result<(), Error> { + pub async fn write(&mut self, address: u32, data: &[u8]) -> Result<(), Error> { let ondrop = OnDrop::new(Self::blocking_wait_ready); self.start_write(address, data)?; @@ -406,7 +406,7 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> { } /// Erase a sector on the flash memory. - pub async fn erase(&mut self, address: usize) -> Result<(), Error> { + pub async fn erase(&mut self, address: u32) -> Result<(), Error> { let ondrop = OnDrop::new(Self::blocking_wait_ready); self.start_erase(address)?; @@ -418,28 +418,28 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> { } /// Read data from the flash memory, blocking version. - pub fn blocking_read(&mut self, address: usize, data: &mut [u8]) -> Result<(), Error> { + pub fn blocking_read(&mut self, address: u32, data: &mut [u8]) -> Result<(), Error> { self.start_read(address, data)?; Self::blocking_wait_ready(); Ok(()) } /// Write data to the flash memory, blocking version. - pub fn blocking_write(&mut self, address: usize, data: &[u8]) -> Result<(), Error> { + pub fn blocking_write(&mut self, address: u32, data: &[u8]) -> Result<(), Error> { self.start_write(address, data)?; Self::blocking_wait_ready(); Ok(()) } /// Erase a sector on the flash memory, blocking version. - pub fn blocking_erase(&mut self, address: usize) -> Result<(), Error> { + pub fn blocking_erase(&mut self, address: u32) -> Result<(), Error> { self.start_erase(address)?; Self::blocking_wait_ready(); Ok(()) } } -impl<'d, T: Instance, const FLASH_SIZE: usize> Drop for Qspi<'d, T, FLASH_SIZE> { +impl<'d, T: Instance, const FLASH_SIZE: u32> Drop for Qspi<'d, T, FLASH_SIZE> { fn drop(&mut self) { let r = T::regs(); @@ -486,7 +486,7 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Drop for Qspi<'d, T, FLASH_SIZE> use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash}; -impl<'d, T: Instance, const FLASH_SIZE: usize> ErrorType for Qspi<'d, T, FLASH_SIZE> { +impl<'d, T: Instance, const FLASH_SIZE: u32> ErrorType for Qspi<'d, T, FLASH_SIZE> { type Error = Error; } @@ -496,32 +496,32 @@ impl NorFlashError for Error { } } -impl<'d, T: Instance, const FLASH_SIZE: usize> ReadNorFlash for Qspi<'d, T, FLASH_SIZE> { +impl<'d, T: Instance, const FLASH_SIZE: u32> ReadNorFlash for Qspi<'d, T, FLASH_SIZE> { const READ_SIZE: usize = 4; fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { - self.blocking_read(offset as usize, bytes)?; + self.blocking_read(offset, bytes)?; Ok(()) } fn capacity(&self) -> usize { - FLASH_SIZE + FLASH_SIZE as usize } } -impl<'d, T: Instance, const FLASH_SIZE: usize> NorFlash for Qspi<'d, T, FLASH_SIZE> { +impl<'d, T: Instance, const FLASH_SIZE: u32> NorFlash for Qspi<'d, T, FLASH_SIZE> { const WRITE_SIZE: usize = 4; const ERASE_SIZE: usize = 4096; fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { - for address in (from as usize..to as usize).step_by(::ERASE_SIZE) { + for address in (from..to).step_by(::ERASE_SIZE) { self.blocking_erase(address)?; } Ok(()) } fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { - self.blocking_write(offset as usize, bytes)?; + self.blocking_write(offset, bytes)?; Ok(()) } } @@ -534,19 +534,19 @@ mod _eh1 { use super::*; - impl<'d, T: Instance, const FLASH_SIZE: usize> AsyncNorFlash for Qspi<'d, T, FLASH_SIZE> { + impl<'d, T: Instance, const FLASH_SIZE: u32> AsyncNorFlash for Qspi<'d, T, FLASH_SIZE> { const WRITE_SIZE: usize = ::WRITE_SIZE; const ERASE_SIZE: usize = ::ERASE_SIZE; type WriteFuture<'a> = impl Future> + 'a where Self: 'a; fn write<'a>(&'a mut self, offset: u32, data: &'a [u8]) -> Self::WriteFuture<'a> { - async move { self.write(offset as usize, data).await } + async move { self.write(offset, data).await } } type EraseFuture<'a> = impl Future> + 'a where Self: 'a; fn erase<'a>(&'a mut self, from: u32, to: u32) -> Self::EraseFuture<'a> { async move { - for address in (from as usize..to as usize).step_by(::ERASE_SIZE) { + for address in (from..to).step_by(::ERASE_SIZE) { self.erase(address).await? } Ok(()) @@ -554,15 +554,15 @@ mod _eh1 { } } - impl<'d, T: Instance, const FLASH_SIZE: usize> AsyncReadNorFlash for Qspi<'d, T, FLASH_SIZE> { + impl<'d, T: Instance, const FLASH_SIZE: u32> AsyncReadNorFlash for Qspi<'d, T, FLASH_SIZE> { const READ_SIZE: usize = 4; type ReadFuture<'a> = impl Future> + 'a where Self: 'a; fn read<'a>(&'a mut self, address: u32, data: &'a mut [u8]) -> Self::ReadFuture<'a> { - async move { self.read(address as usize, data).await } + async move { self.read(address, data).await } } fn capacity(&self) -> usize { - FLASH_SIZE + FLASH_SIZE as usize } } } diff --git a/examples/nrf52840/src/bin/qspi.rs b/examples/nrf52840/src/bin/qspi.rs index bdcf710b..bc55f846 100644 --- a/examples/nrf52840/src/bin/qspi.rs +++ b/examples/nrf52840/src/bin/qspi.rs @@ -52,23 +52,23 @@ async fn main(_spawner: Spawner) { for i in 0..8 { info!("page {:?}: erasing... ", i); - unwrap!(q.erase(i * PAGE_SIZE).await); + unwrap!(q.erase(i * PAGE_SIZE as u32).await); for j in 0..PAGE_SIZE { - buf.0[j] = pattern((j + i * PAGE_SIZE) as u32); + buf.0[j] = pattern((j as u32 + i * PAGE_SIZE as u32) as u32); } info!("programming..."); - unwrap!(q.write(i * PAGE_SIZE, &buf.0).await); + unwrap!(q.write(i * PAGE_SIZE as u32, &buf.0).await); } for i in 0..8 { info!("page {:?}: reading... ", i); - unwrap!(q.read(i * PAGE_SIZE, &mut buf.0).await); + unwrap!(q.read(i * PAGE_SIZE as u32, &mut buf.0).await); info!("verifying..."); for j in 0..PAGE_SIZE { - assert_eq!(buf.0[j], pattern((j + i * PAGE_SIZE) as u32)); + assert_eq!(buf.0[j], pattern((j as u32 + i * PAGE_SIZE as u32) as u32)); } } From 8eb8ea617419726915834555266e37568b8504e0 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 5 Mar 2023 02:33:02 +0100 Subject: [PATCH 31/51] nrf/qspi: remove FLASH_SIZE const generic param. --- embassy-nrf/src/qspi.rs | 36 ++++++++++++---------- examples/nrf52840/src/bin/qspi.rs | 2 +- examples/nrf52840/src/bin/qspi_lowpower.rs | 2 +- 3 files changed, 22 insertions(+), 18 deletions(-) diff --git a/embassy-nrf/src/qspi.rs b/embassy-nrf/src/qspi.rs index 404438ea..3f7f464d 100644 --- a/embassy-nrf/src/qspi.rs +++ b/embassy-nrf/src/qspi.rs @@ -8,6 +8,7 @@ use core::task::Poll; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; +use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash}; use crate::gpio::{self, Pin as GpioPin}; use crate::interrupt::{Interrupt, InterruptExt}; @@ -82,6 +83,8 @@ pub struct Config { pub spi_mode: SpiMode, /// Addressing mode (24-bit or 32-bit) pub address_mode: AddressMode, + /// Flash memory capacity in bytes. This is the value reported by the `embedded-storage` traits. + pub capacity: u32, } impl Default for Config { @@ -96,6 +99,7 @@ impl Default for Config { sck_delay: 80, spi_mode: SpiMode::MODE0, address_mode: AddressMode::_24BIT, + capacity: 0, } } } @@ -111,12 +115,13 @@ pub enum Error { } /// QSPI flash driver. -pub struct Qspi<'d, T: Instance, const FLASH_SIZE: u32> { +pub struct Qspi<'d, T: Instance> { irq: PeripheralRef<'d, T::Interrupt>, dpm_enabled: bool, + capacity: u32, } -impl<'d, T: Instance, const FLASH_SIZE: u32> Qspi<'d, T, FLASH_SIZE> { +impl<'d, T: Instance> Qspi<'d, T> { /// Create a new QSPI driver. pub fn new( _qspi: impl Peripheral

+ 'd, @@ -128,7 +133,7 @@ impl<'d, T: Instance, const FLASH_SIZE: u32> Qspi<'d, T, FLASH_SIZE> { io2: impl Peripheral

+ 'd, io3: impl Peripheral

+ 'd, config: Config, - ) -> Qspi<'d, T, FLASH_SIZE> { + ) -> Self { into_ref!(irq, sck, csn, io0, io1, io2, io3); let r = T::regs(); @@ -194,6 +199,7 @@ impl<'d, T: Instance, const FLASH_SIZE: u32> Qspi<'d, T, FLASH_SIZE> { let res = Self { dpm_enabled: config.deep_power_down.is_some(), irq, + capacity: config.capacity, }; r.events_ready.reset(); @@ -326,7 +332,7 @@ impl<'d, T: Instance, const FLASH_SIZE: u32> Qspi<'d, T, FLASH_SIZE> { assert_eq!(data.as_ptr() as u32 % 4, 0); assert_eq!(data.len() as u32 % 4, 0); assert_eq!(address % 4, 0); - if address > FLASH_SIZE { + if address > self.capacity { return Err(Error::OutOfBounds); } @@ -348,7 +354,7 @@ impl<'d, T: Instance, const FLASH_SIZE: u32> Qspi<'d, T, FLASH_SIZE> { assert_eq!(data.len() as u32 % 4, 0); assert_eq!(address % 4, 0); - if address > FLASH_SIZE { + if address > self.capacity { return Err(Error::OutOfBounds); } @@ -366,7 +372,7 @@ impl<'d, T: Instance, const FLASH_SIZE: u32> Qspi<'d, T, FLASH_SIZE> { fn start_erase(&mut self, address: u32) -> Result<(), Error> { assert_eq!(address % 4096, 0); - if address > FLASH_SIZE { + if address > self.capacity { return Err(Error::OutOfBounds); } @@ -439,7 +445,7 @@ impl<'d, T: Instance, const FLASH_SIZE: u32> Qspi<'d, T, FLASH_SIZE> { } } -impl<'d, T: Instance, const FLASH_SIZE: u32> Drop for Qspi<'d, T, FLASH_SIZE> { +impl<'d, T: Instance> Drop for Qspi<'d, T> { fn drop(&mut self) { let r = T::regs(); @@ -484,9 +490,7 @@ impl<'d, T: Instance, const FLASH_SIZE: u32> Drop for Qspi<'d, T, FLASH_SIZE> { } } -use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash}; - -impl<'d, T: Instance, const FLASH_SIZE: u32> ErrorType for Qspi<'d, T, FLASH_SIZE> { +impl<'d, T: Instance> ErrorType for Qspi<'d, T> { type Error = Error; } @@ -496,7 +500,7 @@ impl NorFlashError for Error { } } -impl<'d, T: Instance, const FLASH_SIZE: u32> ReadNorFlash for Qspi<'d, T, FLASH_SIZE> { +impl<'d, T: Instance> ReadNorFlash for Qspi<'d, T> { const READ_SIZE: usize = 4; fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { @@ -505,11 +509,11 @@ impl<'d, T: Instance, const FLASH_SIZE: u32> ReadNorFlash for Qspi<'d, T, FLASH_ } fn capacity(&self) -> usize { - FLASH_SIZE as usize + self.capacity as usize } } -impl<'d, T: Instance, const FLASH_SIZE: u32> NorFlash for Qspi<'d, T, FLASH_SIZE> { +impl<'d, T: Instance> NorFlash for Qspi<'d, T> { const WRITE_SIZE: usize = 4; const ERASE_SIZE: usize = 4096; @@ -534,7 +538,7 @@ mod _eh1 { use super::*; - impl<'d, T: Instance, const FLASH_SIZE: u32> AsyncNorFlash for Qspi<'d, T, FLASH_SIZE> { + impl<'d, T: Instance> AsyncNorFlash for Qspi<'d, T> { const WRITE_SIZE: usize = ::WRITE_SIZE; const ERASE_SIZE: usize = ::ERASE_SIZE; @@ -554,7 +558,7 @@ mod _eh1 { } } - impl<'d, T: Instance, const FLASH_SIZE: u32> AsyncReadNorFlash for Qspi<'d, T, FLASH_SIZE> { + impl<'d, T: Instance> AsyncReadNorFlash for Qspi<'d, T> { const READ_SIZE: usize = 4; type ReadFuture<'a> = impl Future> + 'a where Self: 'a; fn read<'a>(&'a mut self, address: u32, data: &'a mut [u8]) -> Self::ReadFuture<'a> { @@ -562,7 +566,7 @@ mod _eh1 { } fn capacity(&self) -> usize { - FLASH_SIZE as usize + self.capacity as usize } } } diff --git a/examples/nrf52840/src/bin/qspi.rs b/examples/nrf52840/src/bin/qspi.rs index bc55f846..be665149 100644 --- a/examples/nrf52840/src/bin/qspi.rs +++ b/examples/nrf52840/src/bin/qspi.rs @@ -24,7 +24,7 @@ async fn main(_spawner: Spawner) { config.write_page_size = qspi::WritePageSize::_256BYTES; let irq = interrupt::take!(QSPI); - let mut q: qspi::Qspi<_, 67108864> = qspi::Qspi::new( + let mut q = qspi::Qspi::new( p.QSPI, irq, p.P0_19, p.P0_17, p.P0_20, p.P0_21, p.P0_22, p.P0_23, config, ); diff --git a/examples/nrf52840/src/bin/qspi_lowpower.rs b/examples/nrf52840/src/bin/qspi_lowpower.rs index 9341a237..5008481c 100644 --- a/examples/nrf52840/src/bin/qspi_lowpower.rs +++ b/examples/nrf52840/src/bin/qspi_lowpower.rs @@ -31,7 +31,7 @@ async fn main(_p: Spawner) { exit_time: 3, // tRDP = 35uS }); - let mut q: qspi::Qspi<_, 67108864> = qspi::Qspi::new( + let mut q = qspi::Qspi::new( &mut p.QSPI, &mut irq, &mut p.P0_19, From f7dfc49c5c40d70852d6d3c7313973adf97e4716 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 5 Mar 2023 02:55:00 +0100 Subject: [PATCH 32/51] nrf/qspi: add _raw variants of methods that don't do bounds checks. Useful for the nRF7002, which presents as a "fake" QSPI flash, and the "capacity" concept doesn't really apply to it. --- embassy-nrf/src/qspi.rs | 90 +++++++++++++++++----- examples/nrf52840/src/bin/qspi.rs | 3 + examples/nrf52840/src/bin/qspi_lowpower.rs | 3 + 3 files changed, 76 insertions(+), 20 deletions(-) diff --git a/embassy-nrf/src/qspi.rs b/embassy-nrf/src/qspi.rs index 3f7f464d..d514e027 100644 --- a/embassy-nrf/src/qspi.rs +++ b/embassy-nrf/src/qspi.rs @@ -329,12 +329,10 @@ impl<'d, T: Instance> Qspi<'d, T> { } fn start_read(&mut self, address: u32, data: &mut [u8]) -> Result<(), Error> { + // TODO: Return these as errors instead. assert_eq!(data.as_ptr() as u32 % 4, 0); assert_eq!(data.len() as u32 % 4, 0); assert_eq!(address % 4, 0); - if address > self.capacity { - return Err(Error::OutOfBounds); - } let r = T::regs(); @@ -350,14 +348,11 @@ impl<'d, T: Instance> Qspi<'d, T> { } fn start_write(&mut self, address: u32, data: &[u8]) -> Result<(), Error> { + // TODO: Return these as errors instead. assert_eq!(data.as_ptr() as u32 % 4, 0); assert_eq!(data.len() as u32 % 4, 0); assert_eq!(address % 4, 0); - if address > self.capacity { - return Err(Error::OutOfBounds); - } - let r = T::regs(); r.write.src.write(|w| unsafe { w.src().bits(data.as_ptr() as u32) }); r.write.dst.write(|w| unsafe { w.dst().bits(address) }); @@ -371,10 +366,8 @@ impl<'d, T: Instance> Qspi<'d, T> { } fn start_erase(&mut self, address: u32) -> Result<(), Error> { + // TODO: Return these as errors instead. assert_eq!(address % 4096, 0); - if address > self.capacity { - return Err(Error::OutOfBounds); - } let r = T::regs(); r.erase.ptr.write(|w| unsafe { w.ptr().bits(address) }); @@ -387,8 +380,12 @@ impl<'d, T: Instance> Qspi<'d, T> { Ok(()) } - /// Read data from the flash memory. - pub async fn read(&mut self, address: u32, data: &mut [u8]) -> Result<(), Error> { + /// Raw QSPI read. + /// + /// The difference with `read` is that this does not do bounds checks + /// against the flash capacity. It is intended for use when QSPI is used as + /// a raw bus, not with flash memory. + pub async fn read_raw(&mut self, address: u32, data: &mut [u8]) -> Result<(), Error> { let ondrop = OnDrop::new(Self::blocking_wait_ready); self.start_read(address, data)?; @@ -399,8 +396,12 @@ impl<'d, T: Instance> Qspi<'d, T> { Ok(()) } - /// Write data to the flash memory. - pub async fn write(&mut self, address: u32, data: &[u8]) -> Result<(), Error> { + /// Raw QSPI write. + /// + /// The difference with `write` is that this does not do bounds checks + /// against the flash capacity. It is intended for use when QSPI is used as + /// a raw bus, not with flash memory. + pub async fn write_raw(&mut self, address: u32, data: &[u8]) -> Result<(), Error> { let ondrop = OnDrop::new(Self::blocking_wait_ready); self.start_write(address, data)?; @@ -411,8 +412,46 @@ impl<'d, T: Instance> Qspi<'d, T> { Ok(()) } + /// Raw QSPI read, blocking version. + /// + /// The difference with `blocking_read` is that this does not do bounds checks + /// against the flash capacity. It is intended for use when QSPI is used as + /// a raw bus, not with flash memory. + pub fn blocking_read_raw(&mut self, address: u32, data: &mut [u8]) -> Result<(), Error> { + self.start_read(address, data)?; + Self::blocking_wait_ready(); + Ok(()) + } + + /// Raw QSPI write, blocking version. + /// + /// The difference with `blocking_write` is that this does not do bounds checks + /// against the flash capacity. It is intended for use when QSPI is used as + /// a raw bus, not with flash memory. + pub fn blocking_write_raw(&mut self, address: u32, data: &[u8]) -> Result<(), Error> { + self.start_write(address, data)?; + Self::blocking_wait_ready(); + Ok(()) + } + + /// Read data from the flash memory. + pub async fn read(&mut self, address: u32, data: &mut [u8]) -> Result<(), Error> { + self.bounds_check(address, data.len())?; + self.read_raw(address, data).await + } + + /// Write data to the flash memory. + pub async fn write(&mut self, address: u32, data: &[u8]) -> Result<(), Error> { + self.bounds_check(address, data.len())?; + self.write_raw(address, data).await + } + /// Erase a sector on the flash memory. pub async fn erase(&mut self, address: u32) -> Result<(), Error> { + if address >= self.capacity { + return Err(Error::OutOfBounds); + } + let ondrop = OnDrop::new(Self::blocking_wait_ready); self.start_erase(address)?; @@ -425,24 +464,35 @@ impl<'d, T: Instance> Qspi<'d, T> { /// Read data from the flash memory, blocking version. pub fn blocking_read(&mut self, address: u32, data: &mut [u8]) -> Result<(), Error> { - self.start_read(address, data)?; - Self::blocking_wait_ready(); - Ok(()) + self.bounds_check(address, data.len())?; + self.blocking_read_raw(address, data) } /// Write data to the flash memory, blocking version. pub fn blocking_write(&mut self, address: u32, data: &[u8]) -> Result<(), Error> { - self.start_write(address, data)?; - Self::blocking_wait_ready(); - Ok(()) + self.bounds_check(address, data.len())?; + self.blocking_write_raw(address, data) } /// Erase a sector on the flash memory, blocking version. pub fn blocking_erase(&mut self, address: u32) -> Result<(), Error> { + if address >= self.capacity { + return Err(Error::OutOfBounds); + } + self.start_erase(address)?; Self::blocking_wait_ready(); Ok(()) } + + fn bounds_check(&self, address: u32, len: usize) -> Result<(), Error> { + let len_u32: u32 = len.try_into().map_err(|_| Error::OutOfBounds)?; + let end_address = address.checked_add(len_u32).ok_or(Error::OutOfBounds)?; + if end_address > self.capacity { + return Err(Error::OutOfBounds); + } + Ok(()) + } } impl<'d, T: Instance> Drop for Qspi<'d, T> { diff --git a/examples/nrf52840/src/bin/qspi.rs b/examples/nrf52840/src/bin/qspi.rs index be665149..21a10940 100644 --- a/examples/nrf52840/src/bin/qspi.rs +++ b/examples/nrf52840/src/bin/qspi.rs @@ -4,6 +4,7 @@ use defmt::{assert_eq, info, unwrap}; use embassy_executor::Spawner; +use embassy_nrf::qspi::Frequency; use embassy_nrf::{interrupt, qspi}; use {defmt_rtt as _, panic_probe as _}; @@ -19,6 +20,8 @@ async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); // Config for the MX25R64 present in the nRF52840 DK let mut config = qspi::Config::default(); + config.capacity = 8 * 1024 * 1024; // 8 MB + config.frequency = Frequency::M32; config.read_opcode = qspi::ReadOpcode::READ4IO; config.write_opcode = qspi::WriteOpcode::PP4IO; config.write_page_size = qspi::WritePageSize::_256BYTES; diff --git a/examples/nrf52840/src/bin/qspi_lowpower.rs b/examples/nrf52840/src/bin/qspi_lowpower.rs index 5008481c..20c90391 100644 --- a/examples/nrf52840/src/bin/qspi_lowpower.rs +++ b/examples/nrf52840/src/bin/qspi_lowpower.rs @@ -6,6 +6,7 @@ use core::mem; use defmt::{info, unwrap}; use embassy_executor::Spawner; +use embassy_nrf::qspi::Frequency; use embassy_nrf::{interrupt, qspi}; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; @@ -23,6 +24,8 @@ async fn main(_p: Spawner) { loop { // Config for the MX25R64 present in the nRF52840 DK let mut config = qspi::Config::default(); + config.capacity = 8 * 1024 * 1024; // 8 MB + config.frequency = Frequency::M32; config.read_opcode = qspi::ReadOpcode::READ4IO; config.write_opcode = qspi::WriteOpcode::PP4IO; config.write_page_size = qspi::WritePageSize::_256BYTES; From 6dfda69cc45dfc259ee03f25342c729a3f3315c8 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 5 Mar 2023 20:19:18 +0100 Subject: [PATCH 33/51] readme: add embassy-rp --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 6d5e75ea..33f38daf 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ Rust's async/await allows - **Hardware Abstraction Layers** - HALs implement safe, idiomatic Rust APIs to use the hardware capabilities, so raw register manipulation is not needed. The Embassy project maintains HALs for select hardware, but you can still use HALs from other projects with Embassy. - embassy-stm32, for all STM32 microcontroller families. - embassy-nrf, for the Nordic Semiconductor nRF52, nRF53, nRF91 series. + - embassy-rp, for the Raspberry Pi RP2040 microcontroller. - esp-rs, for the Espressif Systems ESP32 series of chips. - Embassy HAL support for Espressif chips is being developed in the [esp-rs/esp-hal](https://github.com/esp-rs/esp-hal) repository. - Async WiFi, Bluetooth and ESP-NOW is being developed in the [esp-rs/esp-wifi](https://github.com/esp-rs/esp-wifi) repository. From c88bbaa5ecc340e14fd540e3025a8635456e5405 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 5 Mar 2023 23:13:22 +0100 Subject: [PATCH 34/51] time/ticker: make sure the future for .next() is Unpin. --- embassy-time/src/timer.rs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/embassy-time/src/timer.rs b/embassy-time/src/timer.rs index 416830a7..52620d23 100644 --- a/embassy-time/src/timer.rs +++ b/embassy-time/src/timer.rs @@ -1,9 +1,9 @@ -use core::future::Future; +use core::future::{poll_fn, Future}; use core::pin::Pin; use core::task::{Context, Poll, Waker}; use futures_util::future::{select, Either}; -use futures_util::{pin_mut, Stream, StreamExt}; +use futures_util::{pin_mut, Stream}; use crate::{Duration, Instant}; @@ -134,8 +134,17 @@ impl Ticker { } /// Waits for the next tick - pub async fn next(&mut self) { - ::next(self).await; + pub fn next(&mut self) -> impl Future + '_ { + poll_fn(|cx| { + if self.expires_at <= Instant::now() { + let dur = self.duration; + self.expires_at += dur; + Poll::Ready(()) + } else { + schedule_wake(self.expires_at, cx.waker()); + Poll::Pending + } + }) } } From a054891263cbd17315cdf85ecdcc6359313063bc Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 21 Feb 2023 22:19:06 +0100 Subject: [PATCH 35/51] cortex-m: rename Handler to DynHandler. I want to use the name Handler for the new interrupt binding macro. --- embassy-cortex-m/src/interrupt.rs | 6 +++--- embassy-macros/src/macros/cortex_m_interrupt_declare.rs | 4 ++-- embassy-macros/src/macros/cortex_m_interrupt_take.rs | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/embassy-cortex-m/src/interrupt.rs b/embassy-cortex-m/src/interrupt.rs index 1df8671b..ead9d52f 100644 --- a/embassy-cortex-m/src/interrupt.rs +++ b/embassy-cortex-m/src/interrupt.rs @@ -15,12 +15,12 @@ pub mod _export { /// Implementation detail, do not use outside embassy crates. #[doc(hidden)] -pub struct Handler { +pub struct DynHandler { pub func: AtomicPtr<()>, pub ctx: AtomicPtr<()>, } -impl Handler { +impl DynHandler { pub const fn new() -> Self { Self { func: AtomicPtr::new(ptr::null_mut()), @@ -51,7 +51,7 @@ pub unsafe trait Interrupt: Peripheral

{ /// Implementation detail, do not use outside embassy crates. #[doc(hidden)] - unsafe fn __handler(&self) -> &'static Handler; + unsafe fn __handler(&self) -> &'static DynHandler; } /// Represents additional behavior for all interrupts. diff --git a/embassy-macros/src/macros/cortex_m_interrupt_declare.rs b/embassy-macros/src/macros/cortex_m_interrupt_declare.rs index ebbb47cd..699883ef 100644 --- a/embassy-macros/src/macros/cortex_m_interrupt_declare.rs +++ b/embassy-macros/src/macros/cortex_m_interrupt_declare.rs @@ -21,9 +21,9 @@ pub fn run(name: syn::Ident) -> Result { unsafe fn steal() -> Self { Self(()) } - unsafe fn __handler(&self) -> &'static ::embassy_cortex_m::interrupt::Handler { + unsafe fn __handler(&self) -> &'static ::embassy_cortex_m::interrupt::DynHandler { #[export_name = #name_handler] - static HANDLER: ::embassy_cortex_m::interrupt::Handler = ::embassy_cortex_m::interrupt::Handler::new(); + static HANDLER: ::embassy_cortex_m::interrupt::DynHandler = ::embassy_cortex_m::interrupt::DynHandler::new(); &HANDLER } } diff --git a/embassy-macros/src/macros/cortex_m_interrupt_take.rs b/embassy-macros/src/macros/cortex_m_interrupt_take.rs index d30189ce..e2ebf98c 100644 --- a/embassy-macros/src/macros/cortex_m_interrupt_take.rs +++ b/embassy-macros/src/macros/cortex_m_interrupt_take.rs @@ -30,7 +30,7 @@ pub fn run(name: syn::Ident) -> Result { pub unsafe extern "C" fn trampoline() { extern "C" { #[link_name = #name_handler] - static HANDLER: interrupt::Handler; + static HANDLER: interrupt::DynHandler; } let func = HANDLER.func.load(interrupt::_export::atomic::Ordering::Relaxed); From 42c13c8c3d9514866c2842009f76e88e8cb01b22 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 26 Feb 2023 22:42:22 +0100 Subject: [PATCH 36/51] nrf: add new interrupt binding traits and macro. --- embassy-cortex-m/src/interrupt.rs | 30 ++++++++++++++++++++++++++++ embassy-nrf/src/lib.rs | 33 +++++++++++++++++++++++++++---- 2 files changed, 59 insertions(+), 4 deletions(-) diff --git a/embassy-cortex-m/src/interrupt.rs b/embassy-cortex-m/src/interrupt.rs index ead9d52f..3a82726d 100644 --- a/embassy-cortex-m/src/interrupt.rs +++ b/embassy-cortex-m/src/interrupt.rs @@ -13,6 +13,36 @@ pub mod _export { pub use embassy_macros::{cortex_m_interrupt as interrupt, cortex_m_interrupt_declare as declare}; } +/// Interrupt handler trait. +/// +/// Drivers that need to handle interrupts implement this trait. +/// The user must ensure `on_interrupt()` is called every time the interrupt fires. +/// Drivers must use use [`Binding`] to assert at compile time that the user has done so. +pub trait Handler { + /// Interrupt handler function. + /// + /// Must be called every time the `I` interrupt fires, synchronously from + /// the interrupt handler context. + /// + /// # Safety + /// + /// This function must ONLY be called from the interrupt handler for `I`. + unsafe fn on_interrupt(); +} + +/// Compile-time assertion that an interrupt has been bound to a handler. +/// +/// For the vast majority of cases, you should use the `bind_interrupts!` +/// macro instead of writing `unsafe impl`s of this trait. +/// +/// # Safety +/// +/// By implementing this trait, you are asserting that you have arranged for `H::on_interrupt()` +/// to be called every time the `I` interrupt fires. +/// +/// This allows drivers to check bindings at compile-time. +pub unsafe trait Binding> {} + /// Implementation detail, do not use outside embassy crates. #[doc(hidden)] pub struct DynHandler { diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index 3c5db5c7..446b1f41 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -95,14 +95,39 @@ pub mod wdt; #[cfg_attr(feature = "_nrf9160", path = "chips/nrf9160.rs")] mod chip; -pub use chip::EASY_DMA_SIZE; - pub mod interrupt { - //! nRF interrupts for cortex-m devices. + //! Interrupt definitions and macros to bind them. pub use cortex_m::interrupt::{CriticalSection, Mutex}; pub use embassy_cortex_m::interrupt::*; pub use crate::chip::irqs::*; + + /// Macro to bind interrupts to handlers. + /// + /// This defines the right interrupt handlers, and creates a unit struct (like `struct Irqs;`) + /// and implements the right [`Binding`]s for it. You can pass this struct to drivers to + /// prove at compile-time that the right interrupts have been bound. + // developer note: this macro can't be in `embassy-cortex-m` due to the use of `$crate`. + #[macro_export] + macro_rules! bind_interrupts { + ($vis:vis struct $name:ident { $($irq:ident => $($handler:ty),*;)* }) => { + $vis struct $name; + + $( + #[allow(non_snake_case)] + #[no_mangle] + unsafe extern "C" fn $irq() { + $( + <$handler as $crate::interrupt::Handler<$crate::interrupt::$irq>>::on_interrupt(); + )* + } + + $( + unsafe impl $crate::interrupt::Binding<$crate::interrupt::$irq, $handler> for $name {} + )* + )* + }; + } } // Reexports @@ -111,7 +136,7 @@ pub mod interrupt { pub use chip::pac; #[cfg(not(feature = "unstable-pac"))] pub(crate) use chip::pac; -pub use chip::{peripherals, Peripherals}; +pub use chip::{peripherals, Peripherals, EASY_DMA_SIZE}; pub use embassy_cortex_m::executor; pub use embassy_cortex_m::interrupt::_export::interrupt; pub use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; From 9cf000ef4edd8f230b348ede8d7ce015045a0035 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 5 Mar 2023 20:17:52 +0100 Subject: [PATCH 37/51] nrf/uart: switch to new interrupt binding. --- embassy-nrf/src/buffered_uarte.rs | 411 +++++++++++---------- embassy-nrf/src/uarte.rs | 85 ++--- examples/nrf52840/src/bin/buffered_uart.rs | 11 +- examples/nrf52840/src/bin/uart.rs | 9 +- examples/nrf52840/src/bin/uart_idle.rs | 10 +- examples/nrf52840/src/bin/uart_split.rs | 9 +- examples/nrf5340/src/bin/uart.rs | 10 +- tests/nrf/src/bin/buffered_uart.rs | 10 +- tests/nrf/src/bin/buffered_uart_spam.rs | 13 +- 9 files changed, 299 insertions(+), 269 deletions(-) diff --git a/embassy-nrf/src/buffered_uarte.rs b/embassy-nrf/src/buffered_uarte.rs index ab639aee..75f93f90 100644 --- a/embassy-nrf/src/buffered_uarte.rs +++ b/embassy-nrf/src/buffered_uarte.rs @@ -10,6 +10,7 @@ use core::cmp::min; use core::future::poll_fn; +use core::marker::PhantomData; use core::slice; use core::sync::atomic::{compiler_fence, AtomicU8, AtomicUsize, Ordering}; use core::task::Poll; @@ -23,7 +24,7 @@ pub use pac::uarte0::{baudrate::BAUDRATE_A as Baudrate, config::PARITY_A as Pari use crate::gpio::sealed::Pin; use crate::gpio::{self, AnyPin, Pin as GpioPin, PselBits}; -use crate::interrupt::InterruptExt; +use crate::interrupt::{self, InterruptExt}; use crate::ppi::{ self, AnyConfigurableChannel, AnyGroup, Channel, ConfigurableChannel, Event, Group, Ppi, PpiGroup, Task, }; @@ -71,211 +72,13 @@ impl State { } } -/// Buffered UARTE driver. -pub struct BufferedUarte<'d, U: UarteInstance, T: TimerInstance> { - _peri: PeripheralRef<'d, U>, - timer: Timer<'d, T>, - _ppi_ch1: Ppi<'d, AnyConfigurableChannel, 1, 1>, - _ppi_ch2: Ppi<'d, AnyConfigurableChannel, 1, 2>, - _ppi_group: PpiGroup<'d, AnyGroup>, +/// Interrupt handler. +pub struct InterruptHandler { + _phantom: PhantomData, } -impl<'d, U: UarteInstance, T: TimerInstance> Unpin for BufferedUarte<'d, U, T> {} - -impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> { - /// Create a new BufferedUarte without hardware flow control. - /// - /// # Panics - /// - /// Panics if `rx_buffer.len()` is odd. - pub fn new( - uarte: impl Peripheral

+ 'd, - timer: impl Peripheral

+ 'd, - ppi_ch1: impl Peripheral

+ 'd, - ppi_ch2: impl Peripheral

+ 'd, - ppi_group: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, - rxd: impl Peripheral

+ 'd, - txd: impl Peripheral

+ 'd, - config: Config, - rx_buffer: &'d mut [u8], - tx_buffer: &'d mut [u8], - ) -> Self { - into_ref!(rxd, txd, ppi_ch1, ppi_ch2, ppi_group); - Self::new_inner( - uarte, - timer, - ppi_ch1.map_into(), - ppi_ch2.map_into(), - ppi_group.map_into(), - irq, - rxd.map_into(), - txd.map_into(), - None, - None, - config, - rx_buffer, - tx_buffer, - ) - } - - /// Create a new BufferedUarte with hardware flow control (RTS/CTS) - /// - /// # Panics - /// - /// Panics if `rx_buffer.len()` is odd. - pub fn new_with_rtscts( - uarte: impl Peripheral

+ 'd, - timer: impl Peripheral

+ 'd, - ppi_ch1: impl Peripheral

+ 'd, - ppi_ch2: impl Peripheral

+ 'd, - ppi_group: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, - rxd: impl Peripheral

+ 'd, - txd: impl Peripheral

+ 'd, - cts: impl Peripheral

+ 'd, - rts: impl Peripheral

+ 'd, - config: Config, - rx_buffer: &'d mut [u8], - tx_buffer: &'d mut [u8], - ) -> Self { - into_ref!(rxd, txd, cts, rts, ppi_ch1, ppi_ch2, ppi_group); - Self::new_inner( - uarte, - timer, - ppi_ch1.map_into(), - ppi_ch2.map_into(), - ppi_group.map_into(), - irq, - rxd.map_into(), - txd.map_into(), - Some(cts.map_into()), - Some(rts.map_into()), - config, - rx_buffer, - tx_buffer, - ) - } - - fn new_inner( - peri: impl Peripheral

+ 'd, - timer: impl Peripheral

+ 'd, - ppi_ch1: PeripheralRef<'d, AnyConfigurableChannel>, - ppi_ch2: PeripheralRef<'d, AnyConfigurableChannel>, - ppi_group: PeripheralRef<'d, AnyGroup>, - irq: impl Peripheral

+ 'd, - rxd: PeripheralRef<'d, AnyPin>, - txd: PeripheralRef<'d, AnyPin>, - cts: Option>, - rts: Option>, - config: Config, - rx_buffer: &'d mut [u8], - tx_buffer: &'d mut [u8], - ) -> Self { - into_ref!(peri, timer, irq); - - assert!(rx_buffer.len() % 2 == 0); - - let r = U::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) = &cts { - pin.conf().write(|w| w.input().connect().drive().h0h1()); - } - r.psel.cts.write(|w| unsafe { w.bits(cts.psel_bits()) }); - - if let Some(pin) = &rts { - pin.set_high(); - pin.conf().write(|w| w.dir().output().drive().h0h1()); - } - r.psel.rts.write(|w| unsafe { w.bits(rts.psel_bits()) }); - - // Initialize state - let s = U::buffered_state(); - s.tx_count.store(0, Ordering::Relaxed); - s.rx_bufs.store(0, Ordering::Relaxed); - let len = tx_buffer.len(); - unsafe { s.tx_buf.init(tx_buffer.as_mut_ptr(), len) }; - let len = rx_buffer.len(); - unsafe { s.rx_buf.init(rx_buffer.as_mut_ptr(), len) }; - - // Configure - r.config.write(|w| { - w.hwfc().bit(false); - w.parity().variant(config.parity); - w - }); - r.baudrate.write(|w| w.baudrate().variant(config.baudrate)); - - // clear errors - let errors = r.errorsrc.read().bits(); - r.errorsrc.write(|w| unsafe { w.bits(errors) }); - - r.events_rxstarted.reset(); - r.events_txstarted.reset(); - r.events_error.reset(); - r.events_endrx.reset(); - r.events_endtx.reset(); - - // Enable interrupts - r.intenclr.write(|w| unsafe { w.bits(!0) }); - r.intenset.write(|w| { - w.endtx().set(); - w.rxstarted().set(); - w.error().set(); - w - }); - - // Enable UARTE instance - apply_workaround_for_enable_anomaly(&r); - r.enable.write(|w| w.enable().enabled()); - - // Configure byte counter. - let mut timer = Timer::new_counter(timer); - timer.cc(1).write(rx_buffer.len() as u32 * 2); - timer.cc(1).short_compare_clear(); - timer.clear(); - timer.start(); - - let mut ppi_ch1 = Ppi::new_one_to_one(ppi_ch1, Event::from_reg(&r.events_rxdrdy), timer.task_count()); - ppi_ch1.enable(); - - s.rx_ppi_ch.store(ppi_ch2.number() as u8, Ordering::Relaxed); - let mut ppi_group = PpiGroup::new(ppi_group); - let mut ppi_ch2 = Ppi::new_one_to_two( - ppi_ch2, - Event::from_reg(&r.events_endrx), - Task::from_reg(&r.tasks_startrx), - ppi_group.task_disable_all(), - ); - ppi_ch2.disable(); - ppi_group.add_channel(&ppi_ch2); - - irq.disable(); - irq.set_handler(Self::on_interrupt); - irq.pend(); - irq.enable(); - - Self { - _peri: peri, - timer, - _ppi_ch1: ppi_ch1, - _ppi_ch2: ppi_ch2, - _ppi_group: ppi_group, - } - } - - fn pend_irq() { - unsafe { ::steal() }.pend() - } - - fn on_interrupt(_: *mut ()) { +impl interrupt::Handler for InterruptHandler { + unsafe fn on_interrupt() { //trace!("irq: start"); let r = U::regs(); let s = U::buffered_state(); @@ -374,6 +177,206 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> { //trace!("irq: end"); } +} + +/// Buffered UARTE driver. +pub struct BufferedUarte<'d, U: UarteInstance, T: TimerInstance> { + _peri: PeripheralRef<'d, U>, + timer: Timer<'d, T>, + _ppi_ch1: Ppi<'d, AnyConfigurableChannel, 1, 1>, + _ppi_ch2: Ppi<'d, AnyConfigurableChannel, 1, 2>, + _ppi_group: PpiGroup<'d, AnyGroup>, +} + +impl<'d, U: UarteInstance, T: TimerInstance> Unpin for BufferedUarte<'d, U, T> {} + +impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> { + /// Create a new BufferedUarte without hardware flow control. + /// + /// # Panics + /// + /// Panics if `rx_buffer.len()` is odd. + pub fn new( + uarte: impl Peripheral

+ 'd, + timer: impl Peripheral

+ 'd, + ppi_ch1: impl Peripheral

+ 'd, + ppi_ch2: impl Peripheral

+ 'd, + ppi_group: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, + rxd: impl Peripheral

+ 'd, + txd: impl Peripheral

+ 'd, + config: Config, + rx_buffer: &'d mut [u8], + tx_buffer: &'d mut [u8], + ) -> Self { + into_ref!(rxd, txd, ppi_ch1, ppi_ch2, ppi_group); + Self::new_inner( + uarte, + timer, + ppi_ch1.map_into(), + ppi_ch2.map_into(), + ppi_group.map_into(), + rxd.map_into(), + txd.map_into(), + None, + None, + config, + rx_buffer, + tx_buffer, + ) + } + + /// Create a new BufferedUarte with hardware flow control (RTS/CTS) + /// + /// # Panics + /// + /// Panics if `rx_buffer.len()` is odd. + pub fn new_with_rtscts( + uarte: impl Peripheral

+ 'd, + timer: impl Peripheral

+ 'd, + ppi_ch1: impl Peripheral

+ 'd, + ppi_ch2: impl Peripheral

+ 'd, + ppi_group: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, + rxd: impl Peripheral

+ 'd, + txd: impl Peripheral

+ 'd, + cts: impl Peripheral

+ 'd, + rts: impl Peripheral

+ 'd, + config: Config, + rx_buffer: &'d mut [u8], + tx_buffer: &'d mut [u8], + ) -> Self { + into_ref!(rxd, txd, cts, rts, ppi_ch1, ppi_ch2, ppi_group); + Self::new_inner( + uarte, + timer, + ppi_ch1.map_into(), + ppi_ch2.map_into(), + ppi_group.map_into(), + rxd.map_into(), + txd.map_into(), + Some(cts.map_into()), + Some(rts.map_into()), + config, + rx_buffer, + tx_buffer, + ) + } + + fn new_inner( + peri: impl Peripheral

+ 'd, + timer: impl Peripheral

+ 'd, + ppi_ch1: PeripheralRef<'d, AnyConfigurableChannel>, + ppi_ch2: PeripheralRef<'d, AnyConfigurableChannel>, + ppi_group: PeripheralRef<'d, AnyGroup>, + rxd: PeripheralRef<'d, AnyPin>, + txd: PeripheralRef<'d, AnyPin>, + cts: Option>, + rts: Option>, + config: Config, + rx_buffer: &'d mut [u8], + tx_buffer: &'d mut [u8], + ) -> Self { + into_ref!(peri, timer); + + assert!(rx_buffer.len() % 2 == 0); + + let r = U::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) = &cts { + pin.conf().write(|w| w.input().connect().drive().h0h1()); + } + r.psel.cts.write(|w| unsafe { w.bits(cts.psel_bits()) }); + + if let Some(pin) = &rts { + pin.set_high(); + pin.conf().write(|w| w.dir().output().drive().h0h1()); + } + r.psel.rts.write(|w| unsafe { w.bits(rts.psel_bits()) }); + + // Initialize state + let s = U::buffered_state(); + s.tx_count.store(0, Ordering::Relaxed); + s.rx_bufs.store(0, Ordering::Relaxed); + let len = tx_buffer.len(); + unsafe { s.tx_buf.init(tx_buffer.as_mut_ptr(), len) }; + let len = rx_buffer.len(); + unsafe { s.rx_buf.init(rx_buffer.as_mut_ptr(), len) }; + + // Configure + r.config.write(|w| { + w.hwfc().bit(false); + w.parity().variant(config.parity); + w + }); + r.baudrate.write(|w| w.baudrate().variant(config.baudrate)); + + // clear errors + let errors = r.errorsrc.read().bits(); + r.errorsrc.write(|w| unsafe { w.bits(errors) }); + + r.events_rxstarted.reset(); + r.events_txstarted.reset(); + r.events_error.reset(); + r.events_endrx.reset(); + r.events_endtx.reset(); + + // Enable interrupts + r.intenclr.write(|w| unsafe { w.bits(!0) }); + r.intenset.write(|w| { + w.endtx().set(); + w.rxstarted().set(); + w.error().set(); + w + }); + + // Enable UARTE instance + apply_workaround_for_enable_anomaly(&r); + r.enable.write(|w| w.enable().enabled()); + + // Configure byte counter. + let mut timer = Timer::new_counter(timer); + timer.cc(1).write(rx_buffer.len() as u32 * 2); + timer.cc(1).short_compare_clear(); + timer.clear(); + timer.start(); + + let mut ppi_ch1 = Ppi::new_one_to_one(ppi_ch1, Event::from_reg(&r.events_rxdrdy), timer.task_count()); + ppi_ch1.enable(); + + s.rx_ppi_ch.store(ppi_ch2.number() as u8, Ordering::Relaxed); + let mut ppi_group = PpiGroup::new(ppi_group); + let mut ppi_ch2 = Ppi::new_one_to_two( + ppi_ch2, + Event::from_reg(&r.events_endrx), + Task::from_reg(&r.tasks_startrx), + ppi_group.task_disable_all(), + ); + ppi_ch2.disable(); + ppi_group.add_channel(&ppi_ch2); + + unsafe { U::Interrupt::steal() }.pend(); + unsafe { U::Interrupt::steal() }.enable(); + + Self { + _peri: peri, + timer, + _ppi_ch1: ppi_ch1, + _ppi_ch2: ppi_ch2, + _ppi_group: ppi_group, + } + } + + fn pend_irq() { + unsafe { ::steal() }.pend() + } /// Adjust the baud rate to the provided value. pub fn set_baudrate(&mut self, baudrate: Baudrate) { diff --git a/embassy-nrf/src/uarte.rs b/embassy-nrf/src/uarte.rs index 00afbd05..3934d1b5 100644 --- a/embassy-nrf/src/uarte.rs +++ b/embassy-nrf/src/uarte.rs @@ -14,6 +14,7 @@ #![macro_use] use core::future::poll_fn; +use core::marker::PhantomData; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; @@ -26,7 +27,7 @@ pub use pac::uarte0::{baudrate::BAUDRATE_A as Baudrate, config::PARITY_A as Pari use crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE}; use crate::gpio::sealed::Pin as _; use crate::gpio::{self, AnyPin, Pin as GpioPin, PselBits}; -use crate::interrupt::{Interrupt, InterruptExt}; +use crate::interrupt::{self, Interrupt, InterruptExt}; use crate::ppi::{AnyConfigurableChannel, ConfigurableChannel, Event, Ppi, Task}; use crate::timer::{Frequency, Instance as TimerInstance, Timer}; use crate::util::slice_in_ram_or; @@ -62,6 +63,27 @@ pub enum Error { BufferNotInRAM, } +/// Interrupt handler. +pub struct InterruptHandler { + _phantom: PhantomData, +} + +impl interrupt::Handler for InterruptHandler { + unsafe fn on_interrupt() { + let r = T::regs(); + let s = T::state(); + + if r.events_endrx.read().bits() != 0 { + s.endrx_waker.wake(); + r.intenclr.write(|w| w.endrx().clear()); + } + if r.events_endtx.read().bits() != 0 { + s.endtx_waker.wake(); + r.intenclr.write(|w| w.endtx().clear()); + } + } +} + /// UARTE driver. pub struct Uarte<'d, T: Instance> { tx: UarteTx<'d, T>, @@ -86,19 +108,19 @@ impl<'d, T: Instance> Uarte<'d, T> { /// Create a new UARTE without hardware flow control pub fn new( uarte: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, rxd: impl Peripheral

+ 'd, txd: impl Peripheral

+ 'd, config: Config, ) -> Self { into_ref!(rxd, txd); - Self::new_inner(uarte, irq, rxd.map_into(), txd.map_into(), None, None, config) + Self::new_inner(uarte, rxd.map_into(), txd.map_into(), None, None, config) } /// Create a new UARTE with hardware flow control (RTS/CTS) pub fn new_with_rtscts( uarte: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, rxd: impl Peripheral

+ 'd, txd: impl Peripheral

+ 'd, cts: impl Peripheral

+ 'd, @@ -108,7 +130,6 @@ impl<'d, T: Instance> Uarte<'d, T> { into_ref!(rxd, txd, cts, rts); Self::new_inner( uarte, - irq, rxd.map_into(), txd.map_into(), Some(cts.map_into()), @@ -119,14 +140,13 @@ impl<'d, T: Instance> Uarte<'d, T> { fn new_inner( uarte: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, rxd: PeripheralRef<'d, AnyPin>, txd: PeripheralRef<'d, AnyPin>, cts: Option>, rts: Option>, config: Config, ) -> Self { - into_ref!(uarte, irq); + into_ref!(uarte); let r = T::regs(); @@ -148,9 +168,8 @@ impl<'d, T: Instance> Uarte<'d, T> { } r.psel.rts.write(|w| unsafe { w.bits(rts.psel_bits()) }); - irq.set_handler(Self::on_interrupt); - irq.unpend(); - irq.enable(); + unsafe { T::Interrupt::steal() }.unpend(); + unsafe { T::Interrupt::steal() }.enable(); let hardware_flow_control = match (rts.is_some(), cts.is_some()) { (false, false) => false, @@ -238,20 +257,6 @@ impl<'d, T: Instance> Uarte<'d, T> { Event::from_reg(&r.events_endtx) } - fn on_interrupt(_: *mut ()) { - let r = T::regs(); - let s = T::state(); - - if r.events_endrx.read().bits() != 0 { - s.endrx_waker.wake(); - r.intenclr.write(|w| w.endrx().clear()); - } - if r.events_endtx.read().bits() != 0 { - s.endtx_waker.wake(); - r.intenclr.write(|w| w.endtx().clear()); - } - } - /// Read bytes until the buffer is filled. pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { self.rx.read(buffer).await @@ -308,34 +313,33 @@ impl<'d, T: Instance> UarteTx<'d, T> { /// Create a new tx-only UARTE without hardware flow control pub fn new( uarte: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, txd: impl Peripheral

+ 'd, config: Config, ) -> Self { into_ref!(txd); - Self::new_inner(uarte, irq, txd.map_into(), None, config) + Self::new_inner(uarte, txd.map_into(), None, config) } /// Create a new tx-only UARTE with hardware flow control (RTS/CTS) pub fn new_with_rtscts( uarte: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, txd: impl Peripheral

+ 'd, cts: impl Peripheral

+ 'd, config: Config, ) -> Self { into_ref!(txd, cts); - Self::new_inner(uarte, irq, txd.map_into(), Some(cts.map_into()), config) + Self::new_inner(uarte, txd.map_into(), Some(cts.map_into()), config) } fn new_inner( uarte: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, txd: PeripheralRef<'d, AnyPin>, cts: Option>, config: Config, ) -> Self { - into_ref!(uarte, irq); + into_ref!(uarte); let r = T::regs(); @@ -354,9 +358,8 @@ impl<'d, T: Instance> UarteTx<'d, T> { let hardware_flow_control = cts.is_some(); configure(r, config, hardware_flow_control); - irq.set_handler(Uarte::::on_interrupt); - irq.unpend(); - irq.enable(); + unsafe { T::Interrupt::steal() }.unpend(); + unsafe { T::Interrupt::steal() }.enable(); let s = T::state(); s.tx_rx_refcount.store(1, Ordering::Relaxed); @@ -506,34 +509,33 @@ impl<'d, T: Instance> UarteRx<'d, T> { /// Create a new rx-only UARTE without hardware flow control pub fn new( uarte: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, rxd: impl Peripheral

+ 'd, config: Config, ) -> Self { into_ref!(rxd); - Self::new_inner(uarte, irq, rxd.map_into(), None, config) + Self::new_inner(uarte, rxd.map_into(), None, config) } /// Create a new rx-only UARTE with hardware flow control (RTS/CTS) pub fn new_with_rtscts( uarte: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, rxd: impl Peripheral

+ 'd, rts: impl Peripheral

+ 'd, config: Config, ) -> Self { into_ref!(rxd, rts); - Self::new_inner(uarte, irq, rxd.map_into(), Some(rts.map_into()), config) + Self::new_inner(uarte, rxd.map_into(), Some(rts.map_into()), config) } fn new_inner( uarte: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, rxd: PeripheralRef<'d, AnyPin>, rts: Option>, config: Config, ) -> Self { - into_ref!(uarte, irq); + into_ref!(uarte); let r = T::regs(); @@ -549,9 +551,8 @@ impl<'d, T: Instance> UarteRx<'d, T> { r.psel.txd.write(|w| w.connect().disconnected()); r.psel.cts.write(|w| w.connect().disconnected()); - irq.set_handler(Uarte::::on_interrupt); - irq.unpend(); - irq.enable(); + unsafe { T::Interrupt::steal() }.unpend(); + unsafe { T::Interrupt::steal() }.enable(); let hardware_flow_control = rts.is_some(); configure(r, config, hardware_flow_control); diff --git a/examples/nrf52840/src/bin/buffered_uart.rs b/examples/nrf52840/src/bin/buffered_uart.rs index 5b934b7d..23869537 100644 --- a/examples/nrf52840/src/bin/buffered_uart.rs +++ b/examples/nrf52840/src/bin/buffered_uart.rs @@ -4,11 +4,15 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_nrf::buffered_uarte::BufferedUarte; -use embassy_nrf::{interrupt, uarte}; +use embassy_nrf::buffered_uarte::{self, BufferedUarte}; +use embassy_nrf::{bind_interrupts, peripherals, uarte}; use embedded_io::asynch::Write; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + UARTE0_UART0 => buffered_uarte::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); @@ -19,14 +23,13 @@ async fn main(_spawner: Spawner) { let mut tx_buffer = [0u8; 4096]; let mut rx_buffer = [0u8; 4096]; - let irq = interrupt::take!(UARTE0_UART0); let mut u = BufferedUarte::new( p.UARTE0, p.TIMER0, p.PPI_CH0, p.PPI_CH1, p.PPI_GROUP0, - irq, + Irqs, p.P0_08, p.P0_06, config, diff --git a/examples/nrf52840/src/bin/uart.rs b/examples/nrf52840/src/bin/uart.rs index 600f7a6e..50d5cab8 100644 --- a/examples/nrf52840/src/bin/uart.rs +++ b/examples/nrf52840/src/bin/uart.rs @@ -4,9 +4,13 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_nrf::{interrupt, uarte}; +use embassy_nrf::{bind_interrupts, peripherals, uarte}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + UARTE0_UART0 => uarte::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); @@ -14,8 +18,7 @@ async fn main(_spawner: Spawner) { config.parity = uarte::Parity::EXCLUDED; config.baudrate = uarte::Baudrate::BAUD115200; - let irq = interrupt::take!(UARTE0_UART0); - let mut uart = uarte::Uarte::new(p.UARTE0, irq, p.P0_08, p.P0_06, config); + let mut uart = uarte::Uarte::new(p.UARTE0, Irqs, p.P0_08, p.P0_06, config); info!("uarte initialized!"); diff --git a/examples/nrf52840/src/bin/uart_idle.rs b/examples/nrf52840/src/bin/uart_idle.rs index 6af4f709..e1f42fa6 100644 --- a/examples/nrf52840/src/bin/uart_idle.rs +++ b/examples/nrf52840/src/bin/uart_idle.rs @@ -4,9 +4,14 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_nrf::{interrupt, uarte}; +use embassy_nrf::peripherals::UARTE0; +use embassy_nrf::{bind_interrupts, uarte}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + UARTE0_UART0 => uarte::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); @@ -14,8 +19,7 @@ async fn main(_spawner: Spawner) { config.parity = uarte::Parity::EXCLUDED; config.baudrate = uarte::Baudrate::BAUD115200; - let irq = interrupt::take!(UARTE0_UART0); - let uart = uarte::Uarte::new(p.UARTE0, irq, p.P0_08, p.P0_06, config); + let uart = uarte::Uarte::new(p.UARTE0, Irqs, p.P0_08, p.P0_06, config); let (mut tx, mut rx) = uart.split_with_idle(p.TIMER0, p.PPI_CH0, p.PPI_CH1); info!("uarte initialized!"); diff --git a/examples/nrf52840/src/bin/uart_split.rs b/examples/nrf52840/src/bin/uart_split.rs index 1adaf53f..9979a1d5 100644 --- a/examples/nrf52840/src/bin/uart_split.rs +++ b/examples/nrf52840/src/bin/uart_split.rs @@ -6,13 +6,17 @@ use defmt::*; use embassy_executor::Spawner; use embassy_nrf::peripherals::UARTE0; use embassy_nrf::uarte::UarteRx; -use embassy_nrf::{interrupt, uarte}; +use embassy_nrf::{bind_interrupts, uarte}; use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; use embassy_sync::channel::Channel; use {defmt_rtt as _, panic_probe as _}; static CHANNEL: Channel = Channel::new(); +bind_interrupts!(struct Irqs { + UARTE0_UART0 => uarte::InterruptHandler; +}); + #[embassy_executor::main] async fn main(spawner: Spawner) { let p = embassy_nrf::init(Default::default()); @@ -20,8 +24,7 @@ async fn main(spawner: Spawner) { config.parity = uarte::Parity::EXCLUDED; config.baudrate = uarte::Baudrate::BAUD115200; - let irq = interrupt::take!(UARTE0_UART0); - let uart = uarte::Uarte::new(p.UARTE0, irq, p.P0_08, p.P0_06, config); + let uart = uarte::Uarte::new(p.UARTE0, Irqs, p.P0_08, p.P0_06, config); let (mut tx, rx) = uart.split(); info!("uarte initialized!"); diff --git a/examples/nrf5340/src/bin/uart.rs b/examples/nrf5340/src/bin/uart.rs index 5f448c2b..d6853970 100644 --- a/examples/nrf5340/src/bin/uart.rs +++ b/examples/nrf5340/src/bin/uart.rs @@ -4,9 +4,14 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_nrf::{interrupt, uarte}; +use embassy_nrf::peripherals::SERIAL0; +use embassy_nrf::{bind_interrupts, uarte}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + SERIAL0 => uarte::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); @@ -14,8 +19,7 @@ async fn main(_spawner: Spawner) { config.parity = uarte::Parity::EXCLUDED; config.baudrate = uarte::Baudrate::BAUD115200; - let irq = interrupt::take!(SERIAL0); - let mut uart = uarte::Uarte::new(p.SERIAL0, irq, p.P1_00, p.P1_01, config); + let mut uart = uarte::Uarte::new(p.SERIAL0, Irqs, p.P1_00, p.P1_01, config); info!("uarte initialized!"); diff --git a/tests/nrf/src/bin/buffered_uart.rs b/tests/nrf/src/bin/buffered_uart.rs index 0550b0bb..e73d4f0b 100644 --- a/tests/nrf/src/bin/buffered_uart.rs +++ b/tests/nrf/src/bin/buffered_uart.rs @@ -5,10 +5,14 @@ use defmt::{assert_eq, *}; use embassy_executor::Spawner; use embassy_futures::join::join; -use embassy_nrf::buffered_uarte::BufferedUarte; -use embassy_nrf::{interrupt, uarte}; +use embassy_nrf::buffered_uarte::{self, BufferedUarte}; +use embassy_nrf::{bind_interrupts, peripherals, uarte}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + UARTE0_UART0 => buffered_uarte::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); @@ -25,7 +29,7 @@ async fn main(_spawner: Spawner) { p.PPI_CH0, p.PPI_CH1, p.PPI_GROUP0, - interrupt::take!(UARTE0_UART0), + Irqs, p.P1_03, p.P1_02, config.clone(), diff --git a/tests/nrf/src/bin/buffered_uart_spam.rs b/tests/nrf/src/bin/buffered_uart_spam.rs index 57aaeca4..74eda6d0 100644 --- a/tests/nrf/src/bin/buffered_uart_spam.rs +++ b/tests/nrf/src/bin/buffered_uart_spam.rs @@ -7,14 +7,19 @@ use core::ptr::NonNull; use defmt::{assert_eq, *}; use embassy_executor::Spawner; -use embassy_nrf::buffered_uarte::BufferedUarte; +use embassy_nrf::buffered_uarte::{self, BufferedUarte}; use embassy_nrf::gpio::{Level, Output, OutputDrive}; use embassy_nrf::ppi::{Event, Ppi, Task}; use embassy_nrf::uarte::Uarte; -use embassy_nrf::{interrupt, pac, uarte}; +use embassy_nrf::{bind_interrupts, pac, peripherals, uarte}; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + UARTE0_UART0 => buffered_uarte::InterruptHandler; + UARTE1 => uarte::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let mut p = embassy_nrf::init(Default::default()); @@ -33,7 +38,7 @@ async fn main(_spawner: Spawner) { p.PPI_CH0, p.PPI_CH1, p.PPI_GROUP0, - interrupt::take!(UARTE0_UART0), + Irqs, p.P1_03, p.P1_04, config.clone(), @@ -49,7 +54,7 @@ async fn main(_spawner: Spawner) { // Tx spam in a loop. const NSPAM: usize = 17; static mut TX_BUF: [u8; NSPAM] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]; - let _spam = Uarte::new(p.UARTE1, interrupt::take!(UARTE1), p.P1_01, p.P1_02, config.clone()); + let _spam = Uarte::new(p.UARTE1, Irqs, p.P1_01, p.P1_02, config.clone()); let spam_peri: pac::UARTE1 = unsafe { mem::transmute(()) }; let event = unsafe { Event::new_unchecked(NonNull::new_unchecked(&spam_peri.events_endtx as *const _ as _)) }; let task = unsafe { Task::new_unchecked(NonNull::new_unchecked(&spam_peri.tasks_starttx as *const _ as _)) }; From 63b75eaf644eee2da2c16f72dbd46bb404f5fdbd Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 5 Mar 2023 20:27:33 +0100 Subject: [PATCH 38/51] nrf/timer: remove awaitable. --- embassy-nrf/src/timer.rs | 134 ++----------------- examples/nrf52840/src/bin/awaitable_timer.rs | 26 ---- 2 files changed, 10 insertions(+), 150 deletions(-) delete mode 100644 examples/nrf52840/src/bin/awaitable_timer.rs diff --git a/embassy-nrf/src/timer.rs b/embassy-nrf/src/timer.rs index 3b0d2f1c..a9487a9f 100644 --- a/embassy-nrf/src/timer.rs +++ b/embassy-nrf/src/timer.rs @@ -6,15 +6,9 @@ #![macro_use] -use core::future::poll_fn; -use core::marker::PhantomData; -use core::task::Poll; - -use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; -use embassy_sync::waitqueue::AtomicWaker; -use crate::interrupt::{Interrupt, InterruptExt}; +use crate::interrupt::Interrupt; use crate::ppi::{Event, Task}; use crate::{pac, Peripheral}; @@ -26,8 +20,6 @@ pub(crate) mod sealed { /// The number of CC registers this instance has. const CCS: usize; fn regs() -> &'static pac::timer0::RegisterBlock; - /// Storage for the waker for CC register `n`. - fn waker(n: usize) -> &'static AtomicWaker; } pub trait ExtendedInstance {} @@ -50,12 +42,6 @@ macro_rules! impl_timer { fn regs() -> &'static pac::timer0::RegisterBlock { unsafe { &*(pac::$pac_type::ptr() as *const pac::timer0::RegisterBlock) } } - fn waker(n: usize) -> &'static ::embassy_sync::waitqueue::AtomicWaker { - use ::embassy_sync::waitqueue::AtomicWaker; - const NEW_AW: AtomicWaker = AtomicWaker::new(); - static WAKERS: [AtomicWaker; $ccs] = [NEW_AW; $ccs]; - &WAKERS[n] - } } impl crate::timer::Instance for peripherals::$type { type Interrupt = crate::interrupt::$irq; @@ -99,59 +85,18 @@ pub enum Frequency { /// nRF Timer driver. /// /// The timer has an internal counter, which is incremented for every tick of the timer. -/// The counter is 32-bit, so it wraps back to 0 at 4294967296. +/// The counter is 32-bit, so it wraps back to 0 when it reaches 2^32. /// /// It has either 4 or 6 Capture/Compare registers, which can be used to capture the current state of the counter /// or trigger an event when the counter reaches a certain value. -pub trait TimerType: sealed::TimerType {} - -/// Marker type indicating the timer driver can await expiration (it owns the timer interrupt). -pub enum Awaitable {} - -/// Marker type indicating the timer driver cannot await expiration (it does not own the timer interrupt). -pub enum NotAwaitable {} - -impl sealed::TimerType for Awaitable {} -impl sealed::TimerType for NotAwaitable {} -impl TimerType for Awaitable {} -impl TimerType for NotAwaitable {} - /// Timer driver. -pub struct Timer<'d, T: Instance, I: TimerType = NotAwaitable> { +pub struct Timer<'d, T: Instance> { _p: PeripheralRef<'d, T>, - _i: PhantomData, } -impl<'d, T: Instance> Timer<'d, T, Awaitable> { - /// Create a new async-capable timer driver. - pub fn new_awaitable(timer: impl Peripheral

+ 'd, irq: impl Peripheral

+ 'd) -> Self { - into_ref!(irq); - - irq.set_handler(Self::on_interrupt); - irq.unpend(); - irq.enable(); - - Self::new_inner(timer, false) - } - - /// Create a new async-capable timer driver in counter mode. - pub fn new_awaitable_counter( - timer: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, - ) -> Self { - into_ref!(irq); - - irq.set_handler(Self::on_interrupt); - irq.unpend(); - irq.enable(); - - Self::new_inner(timer, true) - } -} - -impl<'d, T: Instance> Timer<'d, T, NotAwaitable> { - /// Create a `Timer` driver without an interrupt, meaning `Cc::wait` won't work. +impl<'d, T: Instance> Timer<'d, T> { + /// Create a new `Timer` driver. /// /// This can be useful for triggering tasks via PPI /// `Uarte` uses this internally. @@ -159,28 +104,20 @@ impl<'d, T: Instance> Timer<'d, T, NotAwaitable> { Self::new_inner(timer, false) } - /// Create a `Timer` driver in counter mode without an interrupt, meaning `Cc::wait` won't work. + /// Create a new `Timer` driver in counter mode. /// /// This can be useful for triggering tasks via PPI /// `Uarte` uses this internally. pub fn new_counter(timer: impl Peripheral

+ 'd) -> Self { Self::new_inner(timer, true) } -} -impl<'d, T: Instance, I: TimerType> Timer<'d, T, I> { - /// Create a `Timer` without an interrupt, meaning `Cc::wait` won't work. - /// - /// This is used by the public constructors. fn new_inner(timer: impl Peripheral

+ 'd, is_counter: bool) -> Self { into_ref!(timer); let regs = T::regs(); - let mut this = Self { - _p: timer, - _i: PhantomData, - }; + let mut this = Self { _p: timer }; // Stop the timer before doing anything else, // since changing BITMODE while running can cause 'unpredictable behaviour' according to the specification. @@ -272,31 +209,17 @@ impl<'d, T: Instance, I: TimerType> Timer<'d, T, I> { .write(|w| unsafe { w.prescaler().bits(frequency as u8) }) } - fn on_interrupt(_: *mut ()) { - let regs = T::regs(); - for n in 0..T::CCS { - if regs.events_compare[n].read().bits() != 0 { - // Clear the interrupt, otherwise the interrupt will be repeatedly raised as soon as the interrupt handler exits. - // We can't clear the event, because it's used to poll whether the future is done or still pending. - regs.intenclr - .modify(|r, w| unsafe { w.bits(r.bits() | (1 << (16 + n))) }); - T::waker(n).wake(); - } - } - } - /// Returns this timer's `n`th CC register. /// /// # Panics /// Panics if `n` >= the number of CC registers this timer has (4 for a normal timer, 6 for an extended timer). - pub fn cc(&mut self, n: usize) -> Cc { + pub fn cc(&mut self, n: usize) -> Cc { if n >= T::CCS { panic!("Cannot get CC register {} of timer with {} CC registers.", n, T::CCS); } Cc { n, _p: self._p.reborrow(), - _i: PhantomData, } } } @@ -308,49 +231,12 @@ impl<'d, T: Instance, I: TimerType> Timer<'d, T, I> { /// /// The timer will fire the register's COMPARE event when its counter reaches the value stored in the register. /// When the register's CAPTURE task is triggered, the timer will store the current value of its counter in the register -pub struct Cc<'d, T: Instance, I: TimerType = NotAwaitable> { +pub struct Cc<'d, T: Instance> { n: usize, _p: PeripheralRef<'d, T>, - _i: PhantomData, } -impl<'d, T: Instance> Cc<'d, T, Awaitable> { - /// Wait until the timer's counter reaches the value stored in this register. - /// - /// This requires a mutable reference so that this task's waker cannot be overwritten by a second call to `wait`. - pub async fn wait(&mut self) { - let regs = T::regs(); - - // Enable the interrupt for this CC's COMPARE event. - regs.intenset - .modify(|r, w| unsafe { w.bits(r.bits() | (1 << (16 + self.n))) }); - - // Disable the interrupt if the future is dropped. - let on_drop = OnDrop::new(|| { - regs.intenclr - .modify(|r, w| unsafe { w.bits(r.bits() | (1 << (16 + self.n))) }); - }); - - poll_fn(|cx| { - T::waker(self.n).register(cx.waker()); - - if regs.events_compare[self.n].read().bits() != 0 { - // Reset the register for next time - regs.events_compare[self.n].reset(); - Poll::Ready(()) - } else { - Poll::Pending - } - }) - .await; - - // The interrupt was already disabled in the interrupt handler, so there's no need to disable it again. - on_drop.defuse(); - } -} -impl<'d, T: Instance> Cc<'d, T, NotAwaitable> {} - -impl<'d, T: Instance, I: TimerType> Cc<'d, T, I> { +impl<'d, T: Instance> Cc<'d, T> { /// Get the current value stored in the register. pub fn read(&self) -> u32 { T::regs().cc[self.n].read().cc().bits() diff --git a/examples/nrf52840/src/bin/awaitable_timer.rs b/examples/nrf52840/src/bin/awaitable_timer.rs deleted file mode 100644 index b32af236..00000000 --- a/examples/nrf52840/src/bin/awaitable_timer.rs +++ /dev/null @@ -1,26 +0,0 @@ -#![no_std] -#![no_main] -#![feature(type_alias_impl_trait)] - -use defmt::info; -use embassy_executor::Spawner; -use embassy_nrf::interrupt; -use embassy_nrf::timer::Timer; -use {defmt_rtt as _, panic_probe as _}; - -#[embassy_executor::main] -async fn main(_spawner: Spawner) { - let p = embassy_nrf::init(Default::default()); - let mut t = Timer::new_awaitable(p.TIMER0, interrupt::take!(TIMER0)); - // default frequency is 1MHz, so this triggers every second - t.cc(0).write(1_000_000); - // clear the timer value on cc[0] compare match - t.cc(0).short_compare_clear(); - t.start(); - - loop { - // wait for compare match - t.cc(0).wait().await; - info!("hardware timer tick"); - } -} From 34563b74aa48c53622344541153266b0227fc9bf Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 5 Mar 2023 20:40:13 +0100 Subject: [PATCH 39/51] nrf/i2s: switch to new interrupt binding. --- embassy-nrf/src/i2s.rs | 77 ++++++++++++----------- examples/nrf52840/src/bin/i2s_effect.rs | 15 +++-- examples/nrf52840/src/bin/i2s_monitor.rs | 9 ++- examples/nrf52840/src/bin/i2s_waveform.rs | 9 ++- 4 files changed, 59 insertions(+), 51 deletions(-) diff --git a/embassy-nrf/src/i2s.rs b/embassy-nrf/src/i2s.rs index 770df7c8..8a1188ce 100644 --- a/embassy-nrf/src/i2s.rs +++ b/embassy-nrf/src/i2s.rs @@ -14,7 +14,7 @@ use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; use crate::gpio::{AnyPin, Pin as GpioPin}; -use crate::interrupt::Interrupt; +use crate::interrupt::{self, Interrupt}; use crate::pac::i2s::RegisterBlock; use crate::util::{slice_in_ram_or, slice_ptr_parts}; use crate::{Peripheral, EASY_DMA_SIZE}; @@ -363,10 +363,39 @@ impl From for u8 { } } +/// Interrupt handler. +pub struct InterruptHandler { + _phantom: PhantomData, +} + +impl interrupt::Handler for InterruptHandler { + unsafe fn on_interrupt() { + let device = Device::::new(); + let s = T::state(); + + if device.is_tx_ptr_updated() { + trace!("TX INT"); + s.tx_waker.wake(); + device.disable_tx_ptr_interrupt(); + } + + if device.is_rx_ptr_updated() { + trace!("RX INT"); + s.rx_waker.wake(); + device.disable_rx_ptr_interrupt(); + } + + if device.is_stopped() { + trace!("STOPPED INT"); + s.stop_waker.wake(); + device.disable_stopped_interrupt(); + } + } +} + /// I2S driver. pub struct I2S<'d, T: Instance> { i2s: PeripheralRef<'d, T>, - irq: PeripheralRef<'d, T::Interrupt>, mck: Option>, sck: PeripheralRef<'d, AnyPin>, lrck: PeripheralRef<'d, AnyPin>, @@ -378,19 +407,18 @@ pub struct I2S<'d, T: Instance> { impl<'d, T: Instance> I2S<'d, T> { /// Create a new I2S in master mode - pub fn master( + pub fn new_master( i2s: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, mck: impl Peripheral

+ 'd, sck: impl Peripheral

+ 'd, lrck: impl Peripheral

+ 'd, master_clock: MasterClock, config: Config, ) -> Self { - into_ref!(i2s, irq, mck, sck, lrck); + into_ref!(i2s, mck, sck, lrck); Self { i2s, - irq, mck: Some(mck.map_into()), sck: sck.map_into(), lrck: lrck.map_into(), @@ -402,17 +430,16 @@ impl<'d, T: Instance> I2S<'d, T> { } /// Create a new I2S in slave mode - pub fn slave( + pub fn new_slave( i2s: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, sck: impl Peripheral

+ 'd, lrck: impl Peripheral

+ 'd, config: Config, ) -> Self { - into_ref!(i2s, irq, sck, lrck); + into_ref!(i2s, sck, lrck); Self { i2s, - irq, mck: None, sck: sck.map_into(), lrck: lrck.map_into(), @@ -537,9 +564,8 @@ impl<'d, T: Instance> I2S<'d, T> { } fn setup_interrupt(&self) { - self.irq.set_handler(Self::on_interrupt); - self.irq.unpend(); - self.irq.enable(); + unsafe { T::Interrupt::steal() }.unpend(); + unsafe { T::Interrupt::steal() }.enable(); let device = Device::::new(); device.disable_tx_ptr_interrupt(); @@ -555,29 +581,6 @@ impl<'d, T: Instance> I2S<'d, T> { device.enable_stopped_interrupt(); } - fn on_interrupt(_: *mut ()) { - let device = Device::::new(); - let s = T::state(); - - if device.is_tx_ptr_updated() { - trace!("TX INT"); - s.tx_waker.wake(); - device.disable_tx_ptr_interrupt(); - } - - if device.is_rx_ptr_updated() { - trace!("RX INT"); - s.rx_waker.wake(); - device.disable_rx_ptr_interrupt(); - } - - if device.is_stopped() { - trace!("STOPPED INT"); - s.stop_waker.wake(); - device.disable_stopped_interrupt(); - } - } - async fn stop() { compiler_fence(Ordering::SeqCst); @@ -1168,7 +1171,7 @@ pub(crate) mod sealed { } } -/// I2S peripehral instance. +/// I2S peripheral instance. pub trait Instance: Peripheral

+ sealed::Instance + 'static + Send { /// Interrupt for this peripheral. type Interrupt: Interrupt; diff --git a/examples/nrf52840/src/bin/i2s_effect.rs b/examples/nrf52840/src/bin/i2s_effect.rs index 52d46e4f..391514d9 100644 --- a/examples/nrf52840/src/bin/i2s_effect.rs +++ b/examples/nrf52840/src/bin/i2s_effect.rs @@ -7,7 +7,7 @@ use core::f32::consts::PI; use defmt::{error, info}; use embassy_executor::Spawner; use embassy_nrf::i2s::{self, Channels, Config, MasterClock, MultiBuffering, Sample as _, SampleWidth, I2S}; -use embassy_nrf::interrupt; +use embassy_nrf::{bind_interrupts, peripherals}; use {defmt_rtt as _, panic_probe as _}; type Sample = i16; @@ -15,6 +15,10 @@ type Sample = i16; const NUM_BUFFERS: usize = 2; const NUM_SAMPLES: usize = 4; +bind_interrupts!(struct Irqs { + I2S => i2s::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); @@ -28,15 +32,10 @@ async fn main(_spawner: Spawner) { config.sample_width = SampleWidth::_16bit; config.channels = Channels::MonoLeft; - let irq = interrupt::take!(I2S); let buffers_out = MultiBuffering::::new(); let buffers_in = MultiBuffering::::new(); - let mut full_duplex_stream = I2S::master(p.I2S, irq, p.P0_25, p.P0_26, p.P0_27, master_clock, config).full_duplex( - p.P0_29, - p.P0_28, - buffers_out, - buffers_in, - ); + let mut full_duplex_stream = I2S::new_master(p.I2S, Irqs, p.P0_25, p.P0_26, p.P0_27, master_clock, config) + .full_duplex(p.P0_29, p.P0_28, buffers_out, buffers_in); let mut modulator = SineOsc::new(); modulator.set_frequency(8.0, 1.0 / sample_rate as f32); diff --git a/examples/nrf52840/src/bin/i2s_monitor.rs b/examples/nrf52840/src/bin/i2s_monitor.rs index 5ebfd954..4ed597c0 100644 --- a/examples/nrf52840/src/bin/i2s_monitor.rs +++ b/examples/nrf52840/src/bin/i2s_monitor.rs @@ -5,14 +5,18 @@ use defmt::{debug, error, info}; use embassy_executor::Spawner; use embassy_nrf::i2s::{self, Channels, Config, DoubleBuffering, MasterClock, Sample as _, SampleWidth, I2S}; -use embassy_nrf::interrupt; use embassy_nrf::pwm::{Prescaler, SimplePwm}; +use embassy_nrf::{bind_interrupts, peripherals}; use {defmt_rtt as _, panic_probe as _}; type Sample = i16; const NUM_SAMPLES: usize = 500; +bind_interrupts!(struct Irqs { + I2S => i2s::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); @@ -26,10 +30,9 @@ async fn main(_spawner: Spawner) { config.sample_width = SampleWidth::_16bit; config.channels = Channels::MonoLeft; - let irq = interrupt::take!(I2S); let buffers = DoubleBuffering::::new(); let mut input_stream = - I2S::master(p.I2S, irq, p.P0_25, p.P0_26, p.P0_27, master_clock, config).input(p.P0_29, buffers); + I2S::new_master(p.I2S, Irqs, p.P0_25, p.P0_26, p.P0_27, master_clock, config).input(p.P0_29, buffers); // Configure the PWM to use the pins corresponding to the RGB leds let mut pwm = SimplePwm::new_3ch(p.PWM0, p.P0_23, p.P0_22, p.P0_24); diff --git a/examples/nrf52840/src/bin/i2s_waveform.rs b/examples/nrf52840/src/bin/i2s_waveform.rs index eda93067..f2c1166b 100644 --- a/examples/nrf52840/src/bin/i2s_waveform.rs +++ b/examples/nrf52840/src/bin/i2s_waveform.rs @@ -7,13 +7,17 @@ use core::f32::consts::PI; use defmt::{error, info}; use embassy_executor::Spawner; use embassy_nrf::i2s::{self, Channels, Config, DoubleBuffering, MasterClock, Sample as _, SampleWidth, I2S}; -use embassy_nrf::interrupt; +use embassy_nrf::{bind_interrupts, peripherals}; use {defmt_rtt as _, panic_probe as _}; type Sample = i16; const NUM_SAMPLES: usize = 50; +bind_interrupts!(struct Irqs { + I2S => i2s::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); @@ -27,10 +31,9 @@ async fn main(_spawner: Spawner) { config.sample_width = SampleWidth::_16bit; config.channels = Channels::MonoLeft; - let irq = interrupt::take!(I2S); let buffers = DoubleBuffering::::new(); let mut output_stream = - I2S::master(p.I2S, irq, p.P0_25, p.P0_26, p.P0_27, master_clock, config).output(p.P0_28, buffers); + I2S::new_master(p.I2S, Irqs, p.P0_25, p.P0_26, p.P0_27, master_clock, config).output(p.P0_28, buffers); let mut waveform = Waveform::new(1.0 / sample_rate as f32); From f8f1d3bcf09045616f5b63a08a9623cd14acd045 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 5 Mar 2023 20:50:45 +0100 Subject: [PATCH 40/51] nrf/pdm: make available on all chips, use Instance trait, switch to new interrupt binding. --- embassy-nrf/src/chips/nrf52810.rs | 2 + embassy-nrf/src/chips/nrf52811.rs | 2 + embassy-nrf/src/chips/nrf52832.rs | 5 + embassy-nrf/src/chips/nrf52833.rs | 2 + embassy-nrf/src/chips/nrf52840.rs | 2 + embassy-nrf/src/chips/nrf5340_app.rs | 7 +- embassy-nrf/src/chips/nrf9160.rs | 2 + embassy-nrf/src/lib.rs | 2 + embassy-nrf/src/pdm.rs | 131 ++++++++++++++++++--------- examples/nrf52840/src/bin/pdm.rs | 10 +- 10 files changed, 116 insertions(+), 49 deletions(-) diff --git a/embassy-nrf/src/chips/nrf52810.rs b/embassy-nrf/src/chips/nrf52810.rs index 6b5c134b..c6ccb6a0 100644 --- a/embassy-nrf/src/chips/nrf52810.rs +++ b/embassy-nrf/src/chips/nrf52810.rs @@ -148,6 +148,8 @@ impl_twis!(TWI0, TWIS0, TWIM0_TWIS0_TWI0); impl_pwm!(PWM0, PWM0, PWM0); +impl_pdm!(PDM, PDM, PDM); + impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); diff --git a/embassy-nrf/src/chips/nrf52811.rs b/embassy-nrf/src/chips/nrf52811.rs index c5de9a44..7d1bce1c 100644 --- a/embassy-nrf/src/chips/nrf52811.rs +++ b/embassy-nrf/src/chips/nrf52811.rs @@ -150,6 +150,8 @@ impl_twis!(TWISPI0, TWIS0, TWIM0_TWIS0_TWI0_SPIM0_SPIS0_SPI0); impl_pwm!(PWM0, PWM0, PWM0); +impl_pdm!(PDM, PDM, PDM); + impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); diff --git a/embassy-nrf/src/chips/nrf52832.rs b/embassy-nrf/src/chips/nrf52832.rs index c2b23fc5..ce19a18e 100644 --- a/embassy-nrf/src/chips/nrf52832.rs +++ b/embassy-nrf/src/chips/nrf52832.rs @@ -146,6 +146,9 @@ embassy_hal_common::peripherals! { // I2S I2S, + + // PDM + PDM, } impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); @@ -168,6 +171,8 @@ impl_pwm!(PWM0, PWM0, PWM0); impl_pwm!(PWM1, PWM1, PWM1); impl_pwm!(PWM2, PWM2, PWM2); +impl_pdm!(PDM, PDM, PDM); + impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); diff --git a/embassy-nrf/src/chips/nrf52833.rs b/embassy-nrf/src/chips/nrf52833.rs index 95f71ade..08b82021 100644 --- a/embassy-nrf/src/chips/nrf52833.rs +++ b/embassy-nrf/src/chips/nrf52833.rs @@ -197,6 +197,8 @@ impl_pwm!(PWM1, PWM1, PWM1); impl_pwm!(PWM2, PWM2, PWM2); impl_pwm!(PWM3, PWM3, PWM3); +impl_pdm!(PDM, PDM, PDM); + impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); diff --git a/embassy-nrf/src/chips/nrf52840.rs b/embassy-nrf/src/chips/nrf52840.rs index 5e7479e8..4e8b6d9e 100644 --- a/embassy-nrf/src/chips/nrf52840.rs +++ b/embassy-nrf/src/chips/nrf52840.rs @@ -208,6 +208,8 @@ impl_timer!(TIMER4, TIMER4, TIMER4, extended); impl_qspi!(QSPI, QSPI, QSPI); +impl_pdm!(PDM, PDM, PDM); + impl_pin!(P0_00, 0, 0); impl_pin!(P0_01, 0, 1); impl_pin!(P0_02, 0, 2); diff --git a/embassy-nrf/src/chips/nrf5340_app.rs b/embassy-nrf/src/chips/nrf5340_app.rs index 9c7b738e..050612b1 100644 --- a/embassy-nrf/src/chips/nrf5340_app.rs +++ b/embassy-nrf/src/chips/nrf5340_app.rs @@ -34,7 +34,7 @@ pub mod pac { nvmc_ns as nvmc, oscillators_ns as oscillators, p0_ns as p0, - pdm0_ns as pdm0, + pdm0_ns as pdm, power_ns as power, pwm0_ns as pwm0, qdec0_ns as qdec0, @@ -253,6 +253,9 @@ embassy_hal_common::peripherals! { // QSPI QSPI, + // PDM + PDM0, + // GPIOTE GPIOTE_CH0, GPIOTE_CH1, @@ -398,6 +401,8 @@ impl_timer!(TIMER2, TIMER2, TIMER2); impl_qspi!(QSPI, QSPI, QSPI); +impl_pdm!(PDM0, PDM0, PDM0); + impl_pin!(P0_00, 0, 0); impl_pin!(P0_01, 0, 1); #[cfg(feature = "nfc-pins-as-gpio")] diff --git a/embassy-nrf/src/chips/nrf9160.rs b/embassy-nrf/src/chips/nrf9160.rs index 385bd133..d2b45114 100644 --- a/embassy-nrf/src/chips/nrf9160.rs +++ b/embassy-nrf/src/chips/nrf9160.rs @@ -301,6 +301,8 @@ impl_pwm!(PWM1, PWM1, PWM1); impl_pwm!(PWM2, PWM2, PWM2); impl_pwm!(PWM3, PWM3, PWM3); +impl_pdm!(PDM, PDM, PDM); + impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index 446b1f41..17aa5ad1 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -47,8 +47,10 @@ pub mod nvmc; #[cfg(any( feature = "nrf52810", feature = "nrf52811", + feature = "nrf52832", feature = "nrf52833", feature = "nrf52840", + feature = "_nrf5340-app", feature = "_nrf9160" ))] pub mod pdm; diff --git a/embassy-nrf/src/pdm.rs b/embassy-nrf/src/pdm.rs index 54feca4c..8815bb31 100644 --- a/embassy-nrf/src/pdm.rs +++ b/embassy-nrf/src/pdm.rs @@ -1,25 +1,37 @@ //! Pulse Density Modulation (PDM) mirophone driver. +#![macro_use] + use core::marker::PhantomData; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; +use embassy_cortex_m::interrupt::Interrupt; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; -use embassy_sync::waitqueue::AtomicWaker; use futures::future::poll_fn; use crate::chip::EASY_DMA_SIZE; use crate::gpio::sealed::Pin; use crate::gpio::{AnyPin, Pin as GpioPin}; use crate::interrupt::{self, InterruptExt}; -use crate::peripherals::PDM; -use crate::{pac, Peripheral}; +use crate::Peripheral; + +/// Interrupt handler. +pub struct InterruptHandler { + _phantom: PhantomData, +} + +impl interrupt::Handler for InterruptHandler { + unsafe fn on_interrupt() { + T::regs().intenclr.write(|w| w.end().clear()); + T::state().waker.wake(); + } +} /// PDM microphone interface -pub struct Pdm<'d> { - irq: PeripheralRef<'d, interrupt::PDM>, - phantom: PhantomData<&'d PDM>, +pub struct Pdm<'d, T: Instance> { + _peri: PeripheralRef<'d, T>, } /// PDM error. @@ -35,32 +47,30 @@ pub enum Error { NotRunning, } -static WAKER: AtomicWaker = AtomicWaker::new(); static DUMMY_BUFFER: [i16; 1] = [0; 1]; -impl<'d> Pdm<'d> { +impl<'d, T: Instance> Pdm<'d, T> { /// Create PDM driver pub fn new( - pdm: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + pdm: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, clk: impl Peripheral

+ 'd, din: impl Peripheral

+ 'd, config: Config, ) -> Self { - into_ref!(clk, din); - Self::new_inner(pdm, irq, clk.map_into(), din.map_into(), config) + into_ref!(pdm, clk, din); + Self::new_inner(pdm, clk.map_into(), din.map_into(), config) } fn new_inner( - _pdm: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + pdm: PeripheralRef<'d, T>, clk: PeripheralRef<'d, AnyPin>, din: PeripheralRef<'d, AnyPin>, config: Config, ) -> Self { - into_ref!(irq); + into_ref!(pdm); - let r = Self::regs(); + let r = T::regs(); // setup gpio pins din.conf().write(|w| w.input().set_bit()); @@ -84,26 +94,18 @@ impl<'d> Pdm<'d> { r.gainr.write(|w| w.gainr().default_gain()); // IRQ - irq.disable(); - irq.set_handler(|_| { - let r = Self::regs(); - r.intenclr.write(|w| w.end().clear()); - WAKER.wake(); - }); - irq.enable(); + unsafe { T::Interrupt::steal() }.unpend(); + unsafe { T::Interrupt::steal() }.enable(); r.enable.write(|w| w.enable().set_bit()); - Self { - phantom: PhantomData, - irq, - } + Self { _peri: pdm } } /// Start sampling microphon data into a dummy buffer /// Usefull to start the microphon and keep it active between recording samples pub async fn start(&mut self) { - let r = Self::regs(); + let r = T::regs(); // start dummy sampling because microphon needs some setup time r.sample @@ -113,13 +115,13 @@ impl<'d> Pdm<'d> { .maxcnt .write(|w| unsafe { w.buffsize().bits(DUMMY_BUFFER.len() as _) }); - r.tasks_start.write(|w| w.tasks_start().set_bit()); + r.tasks_start.write(|w| unsafe { w.bits(1) }); } /// Stop sampling microphon data inta a dummy buffer pub async fn stop(&mut self) { - let r = Self::regs(); - r.tasks_stop.write(|w| w.tasks_stop().set_bit()); + let r = T::regs(); + r.tasks_stop.write(|w| unsafe { w.bits(1) }); r.events_started.reset(); } @@ -132,9 +134,9 @@ impl<'d> Pdm<'d> { return Err(Error::BufferTooLong); } - let r = Self::regs(); + let r = T::regs(); - if r.events_started.read().events_started().bit_is_clear() { + if r.events_started.read().bits() == 0 { return Err(Error::NotRunning); } @@ -179,7 +181,7 @@ impl<'d> Pdm<'d> { } async fn wait_for_sample() { - let r = Self::regs(); + let r = T::regs(); r.events_end.reset(); r.intenset.write(|w| w.end().set()); @@ -187,8 +189,8 @@ impl<'d> Pdm<'d> { compiler_fence(Ordering::SeqCst); poll_fn(|cx| { - WAKER.register(cx.waker()); - if r.events_end.read().events_end().bit_is_set() { + T::state().waker.register(cx.waker()); + if r.events_end.read().bits() != 0 { return Poll::Ready(()); } Poll::Pending @@ -197,10 +199,6 @@ impl<'d> Pdm<'d> { compiler_fence(Ordering::SeqCst); } - - fn regs() -> &'static pac::pdm::RegisterBlock { - unsafe { &*pac::PDM::ptr() } - } } /// PDM microphone driver Config @@ -238,13 +236,11 @@ pub enum Edge { LeftFalling, } -impl<'d> Drop for Pdm<'d> { +impl<'d, T: Instance> Drop for Pdm<'d, T> { fn drop(&mut self) { - let r = Self::regs(); + let r = T::regs(); - r.tasks_stop.write(|w| w.tasks_stop().set_bit()); - - self.irq.disable(); + r.tasks_stop.write(|w| unsafe { w.bits(1) }); r.enable.write(|w| w.enable().disabled()); @@ -252,3 +248,48 @@ impl<'d> Drop for Pdm<'d> { r.psel.clk.reset(); } } + +pub(crate) mod sealed { + use embassy_sync::waitqueue::AtomicWaker; + + /// Peripheral static state + pub struct State { + pub waker: AtomicWaker, + } + + impl State { + pub const fn new() -> Self { + Self { + waker: AtomicWaker::new(), + } + } + } + + pub trait Instance { + fn regs() -> &'static crate::pac::pdm::RegisterBlock; + fn state() -> &'static State; + } +} + +/// PDM peripheral instance. +pub trait Instance: Peripheral

+ sealed::Instance + 'static + Send { + /// Interrupt for this peripheral. + type Interrupt: Interrupt; +} + +macro_rules! impl_pdm { + ($type:ident, $pac_type:ident, $irq:ident) => { + impl crate::pdm::sealed::Instance for peripherals::$type { + fn regs() -> &'static crate::pac::pdm::RegisterBlock { + unsafe { &*pac::$pac_type::ptr() } + } + fn state() -> &'static crate::pdm::sealed::State { + static STATE: crate::pdm::sealed::State = crate::pdm::sealed::State::new(); + &STATE + } + } + impl crate::pdm::Instance for peripherals::$type { + type Interrupt = crate::interrupt::$irq; + } + }; +} diff --git a/examples/nrf52840/src/bin/pdm.rs b/examples/nrf52840/src/bin/pdm.rs index 7388580f..6b41320c 100644 --- a/examples/nrf52840/src/bin/pdm.rs +++ b/examples/nrf52840/src/bin/pdm.rs @@ -4,16 +4,20 @@ use defmt::info; use embassy_executor::Spawner; -use embassy_nrf::interrupt; -use embassy_nrf::pdm::{Config, Pdm}; +use embassy_nrf::pdm::{self, Config, Pdm}; +use embassy_nrf::{bind_interrupts, peripherals}; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + PDM => pdm::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_p: Spawner) { let p = embassy_nrf::init(Default::default()); let config = Config::default(); - let mut pdm = Pdm::new(p.PDM, interrupt::take!(PDM), p.P0_01, p.P0_00, config); + let mut pdm = Pdm::new(p.PDM, Irqs, p.P0_01, p.P0_00, config); loop { pdm.start().await; From c66b28e759dc42c5f802336385a66eb8a82dab9a Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 5 Mar 2023 21:28:13 +0100 Subject: [PATCH 41/51] nrf/qdec: make available on all chips, use Instance trait, switch to new interrupt binding. --- embassy-nrf/src/chips/nrf52805.rs | 2 + embassy-nrf/src/chips/nrf52810.rs | 2 + embassy-nrf/src/chips/nrf52811.rs | 2 + embassy-nrf/src/chips/nrf52820.rs | 2 + embassy-nrf/src/chips/nrf52832.rs | 2 + embassy-nrf/src/chips/nrf52833.rs | 2 + embassy-nrf/src/chips/nrf52840.rs | 2 + embassy-nrf/src/chips/nrf5340_app.rs | 9 ++- embassy-nrf/src/lib.rs | 2 +- embassy-nrf/src/qdec.rs | 112 +++++++++++++++++++-------- examples/nrf52840/src/bin/qdec.rs | 9 ++- 11 files changed, 108 insertions(+), 38 deletions(-) diff --git a/embassy-nrf/src/chips/nrf52805.rs b/embassy-nrf/src/chips/nrf52805.rs index 3c74a2a6..185cda43 100644 --- a/embassy-nrf/src/chips/nrf52805.rs +++ b/embassy-nrf/src/chips/nrf52805.rs @@ -140,6 +140,8 @@ impl_twim!(TWI0, TWIM0, TWIM0_TWIS0_TWI0); impl_twis!(TWI0, TWIS0, TWIM0_TWIS0_TWI0); +impl_qdec!(QDEC, QDEC, QDEC); + impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); diff --git a/embassy-nrf/src/chips/nrf52810.rs b/embassy-nrf/src/chips/nrf52810.rs index c6ccb6a0..1e3c054a 100644 --- a/embassy-nrf/src/chips/nrf52810.rs +++ b/embassy-nrf/src/chips/nrf52810.rs @@ -150,6 +150,8 @@ impl_pwm!(PWM0, PWM0, PWM0); impl_pdm!(PDM, PDM, PDM); +impl_qdec!(QDEC, QDEC, QDEC); + impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); diff --git a/embassy-nrf/src/chips/nrf52811.rs b/embassy-nrf/src/chips/nrf52811.rs index 7d1bce1c..3bb44171 100644 --- a/embassy-nrf/src/chips/nrf52811.rs +++ b/embassy-nrf/src/chips/nrf52811.rs @@ -152,6 +152,8 @@ impl_pwm!(PWM0, PWM0, PWM0); impl_pdm!(PDM, PDM, PDM); +impl_qdec!(QDEC, QDEC, QDEC); + impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); diff --git a/embassy-nrf/src/chips/nrf52820.rs b/embassy-nrf/src/chips/nrf52820.rs index 81b07f32..b28778f3 100644 --- a/embassy-nrf/src/chips/nrf52820.rs +++ b/embassy-nrf/src/chips/nrf52820.rs @@ -153,6 +153,8 @@ impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); impl_timer!(TIMER3, TIMER3, TIMER3, extended); +impl_qdec!(QDEC, QDEC, QDEC); + impl_pin!(P0_00, 0, 0); impl_pin!(P0_01, 0, 1); impl_pin!(P0_02, 0, 2); diff --git a/embassy-nrf/src/chips/nrf52832.rs b/embassy-nrf/src/chips/nrf52832.rs index ce19a18e..00dc9fd8 100644 --- a/embassy-nrf/src/chips/nrf52832.rs +++ b/embassy-nrf/src/chips/nrf52832.rs @@ -173,6 +173,8 @@ impl_pwm!(PWM2, PWM2, PWM2); impl_pdm!(PDM, PDM, PDM); +impl_qdec!(QDEC, QDEC, QDEC); + impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); diff --git a/embassy-nrf/src/chips/nrf52833.rs b/embassy-nrf/src/chips/nrf52833.rs index 08b82021..345608c9 100644 --- a/embassy-nrf/src/chips/nrf52833.rs +++ b/embassy-nrf/src/chips/nrf52833.rs @@ -199,6 +199,8 @@ impl_pwm!(PWM3, PWM3, PWM3); impl_pdm!(PDM, PDM, PDM); +impl_qdec!(QDEC, QDEC, QDEC); + impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); diff --git a/embassy-nrf/src/chips/nrf52840.rs b/embassy-nrf/src/chips/nrf52840.rs index 4e8b6d9e..630f01aa 100644 --- a/embassy-nrf/src/chips/nrf52840.rs +++ b/embassy-nrf/src/chips/nrf52840.rs @@ -210,6 +210,8 @@ impl_qspi!(QSPI, QSPI, QSPI); impl_pdm!(PDM, PDM, PDM); +impl_qdec!(QDEC, QDEC, QDEC); + impl_pin!(P0_00, 0, 0); impl_pin!(P0_01, 0, 1); impl_pin!(P0_02, 0, 2); diff --git a/embassy-nrf/src/chips/nrf5340_app.rs b/embassy-nrf/src/chips/nrf5340_app.rs index 050612b1..34f96800 100644 --- a/embassy-nrf/src/chips/nrf5340_app.rs +++ b/embassy-nrf/src/chips/nrf5340_app.rs @@ -37,7 +37,7 @@ pub mod pac { pdm0_ns as pdm, power_ns as power, pwm0_ns as pwm0, - qdec0_ns as qdec0, + qdec0_ns as qdec, qspi_ns as qspi, regulators_ns as regulators, reset_ns as reset, @@ -256,6 +256,10 @@ embassy_hal_common::peripherals! { // PDM PDM0, + // QDEC + QDEC0, + QDEC1, + // GPIOTE GPIOTE_CH0, GPIOTE_CH1, @@ -403,6 +407,9 @@ impl_qspi!(QSPI, QSPI, QSPI); impl_pdm!(PDM0, PDM0, PDM0); +impl_qdec!(QDEC0, QDEC0, QDEC0); +impl_qdec!(QDEC1, QDEC1, QDEC1); + impl_pin!(P0_00, 0, 0); impl_pin!(P0_01, 0, 1); #[cfg(feature = "nfc-pins-as-gpio")] diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index 17aa5ad1..feefa248 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -57,7 +57,7 @@ pub mod pdm; pub mod ppi; #[cfg(not(any(feature = "nrf52805", feature = "nrf52820", feature = "_nrf5340-net")))] pub mod pwm; -#[cfg(not(any(feature = "nrf51", feature = "_nrf9160", feature = "_nrf5340")))] +#[cfg(not(any(feature = "nrf51", feature = "_nrf9160", feature = "_nrf5340-net")))] pub mod qdec; #[cfg(any(feature = "nrf52840", feature = "_nrf5340-app"))] pub mod qspi; diff --git a/embassy-nrf/src/qdec.rs b/embassy-nrf/src/qdec.rs index c01babca..4d2a0919 100644 --- a/embassy-nrf/src/qdec.rs +++ b/embassy-nrf/src/qdec.rs @@ -1,20 +1,22 @@ //! Quadrature decoder (QDEC) driver. +#![macro_use] + use core::future::poll_fn; +use core::marker::PhantomData; use core::task::Poll; +use embassy_cortex_m::interrupt::Interrupt; use embassy_hal_common::{into_ref, PeripheralRef}; -use embassy_sync::waitqueue::AtomicWaker; use crate::gpio::sealed::Pin as _; use crate::gpio::{AnyPin, Pin as GpioPin}; use crate::interrupt::InterruptExt; -use crate::peripherals::QDEC; -use crate::{interrupt, pac, Peripheral}; +use crate::{interrupt, Peripheral}; /// Quadrature decoder driver. -pub struct Qdec<'d> { - _p: PeripheralRef<'d, QDEC>, +pub struct Qdec<'d, T: Instance> { + _p: PeripheralRef<'d, T>, } /// QDEC config @@ -44,44 +46,52 @@ impl Default for Config { } } -static WAKER: AtomicWaker = AtomicWaker::new(); +/// Interrupt handler. +pub struct InterruptHandler { + _phantom: PhantomData, +} -impl<'d> Qdec<'d> { +impl interrupt::Handler for InterruptHandler { + unsafe fn on_interrupt() { + T::regs().intenclr.write(|w| w.reportrdy().clear()); + T::state().waker.wake(); + } +} + +impl<'d, T: Instance> Qdec<'d, T> { /// Create a new QDEC. pub fn new( - qdec: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + qdec: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, a: impl Peripheral

+ 'd, b: impl Peripheral

+ 'd, config: Config, ) -> Self { - into_ref!(a, b); - Self::new_inner(qdec, irq, a.map_into(), b.map_into(), None, config) + into_ref!(qdec, a, b); + Self::new_inner(qdec, a.map_into(), b.map_into(), None, config) } /// Create a new QDEC, with a pin for LED output. pub fn new_with_led( - qdec: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + qdec: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, a: impl Peripheral

+ 'd, b: impl Peripheral

+ 'd, led: impl Peripheral

+ 'd, config: Config, ) -> Self { - into_ref!(a, b, led); - Self::new_inner(qdec, irq, a.map_into(), b.map_into(), Some(led.map_into()), config) + into_ref!(qdec, a, b, led); + Self::new_inner(qdec, a.map_into(), b.map_into(), Some(led.map_into()), config) } fn new_inner( - p: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + p: PeripheralRef<'d, T>, a: PeripheralRef<'d, AnyPin>, b: PeripheralRef<'d, AnyPin>, led: Option>, config: Config, ) -> Self { - into_ref!(p, irq); - let r = Self::regs(); + let r = T::regs(); // Select pins. a.conf().write(|w| w.input().connect().pull().pullup()); @@ -124,20 +134,15 @@ impl<'d> Qdec<'d> { SamplePeriod::_131ms => w.sampleper()._131ms(), }); + unsafe { T::Interrupt::steal() }.unpend(); + unsafe { T::Interrupt::steal() }.enable(); + // Enable peripheral r.enable.write(|w| w.enable().set_bit()); // Start sampling unsafe { r.tasks_start.write(|w| w.bits(1)) }; - irq.disable(); - irq.set_handler(|_| { - let r = Self::regs(); - r.intenclr.write(|w| w.reportrdy().clear()); - WAKER.wake(); - }); - irq.enable(); - Self { _p: p } } @@ -155,12 +160,12 @@ impl<'d> Qdec<'d> { /// let delta = q.read().await; /// ``` pub async fn read(&mut self) -> i16 { - let t = Self::regs(); + let t = T::regs(); t.intenset.write(|w| w.reportrdy().set()); unsafe { t.tasks_readclracc.write(|w| w.bits(1)) }; let value = poll_fn(|cx| { - WAKER.register(cx.waker()); + T::state().waker.register(cx.waker()); if t.events_reportrdy.read().bits() == 0 { return Poll::Pending; } else { @@ -172,10 +177,6 @@ impl<'d> Qdec<'d> { .await; value } - - fn regs() -> &'static pac::qdec::RegisterBlock { - unsafe { &*pac::QDEC::ptr() } - } } /// Sample period @@ -236,3 +237,48 @@ pub enum LedPolarity { /// Active low (a low output turns on the LED). ActiveLow, } + +pub(crate) mod sealed { + use embassy_sync::waitqueue::AtomicWaker; + + /// Peripheral static state + pub struct State { + pub waker: AtomicWaker, + } + + impl State { + pub const fn new() -> Self { + Self { + waker: AtomicWaker::new(), + } + } + } + + pub trait Instance { + fn regs() -> &'static crate::pac::qdec::RegisterBlock; + fn state() -> &'static State; + } +} + +/// qdec peripheral instance. +pub trait Instance: Peripheral

+ sealed::Instance + 'static + Send { + /// Interrupt for this peripheral. + type Interrupt: Interrupt; +} + +macro_rules! impl_qdec { + ($type:ident, $pac_type:ident, $irq:ident) => { + impl crate::qdec::sealed::Instance for peripherals::$type { + fn regs() -> &'static crate::pac::qdec::RegisterBlock { + unsafe { &*pac::$pac_type::ptr() } + } + fn state() -> &'static crate::qdec::sealed::State { + static STATE: crate::qdec::sealed::State = crate::qdec::sealed::State::new(); + &STATE + } + } + impl crate::qdec::Instance for peripherals::$type { + type Interrupt = crate::interrupt::$irq; + } + }; +} diff --git a/examples/nrf52840/src/bin/qdec.rs b/examples/nrf52840/src/bin/qdec.rs index 600bba07..59783d31 100644 --- a/examples/nrf52840/src/bin/qdec.rs +++ b/examples/nrf52840/src/bin/qdec.rs @@ -4,16 +4,19 @@ use defmt::info; use embassy_executor::Spawner; -use embassy_nrf::interrupt; use embassy_nrf::qdec::{self, Qdec}; +use embassy_nrf::{bind_interrupts, peripherals}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + QDEC => qdec::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); - let irq = interrupt::take!(QDEC); let config = qdec::Config::default(); - let mut rotary_enc = Qdec::new(p.QDEC, irq, p.P0_31, p.P0_30, config); + let mut rotary_enc = Qdec::new(p.QDEC, Irqs, p.P0_31, p.P0_30, config); info!("Turn rotary encoder!"); let mut value = 0; From 96788ac93a1e98ef8d9d5e8d80d5102aef34d45d Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 5 Mar 2023 21:37:21 +0100 Subject: [PATCH 42/51] nrf/qspi: switch to new interrupt binding. --- embassy-nrf/src/qspi.rs | 65 ++++++++++++---------- examples/nrf52840/src/bin/qspi.rs | 9 ++- examples/nrf52840/src/bin/qspi_lowpower.rs | 9 ++- 3 files changed, 47 insertions(+), 36 deletions(-) diff --git a/embassy-nrf/src/qspi.rs b/embassy-nrf/src/qspi.rs index d514e027..7f004b9f 100644 --- a/embassy-nrf/src/qspi.rs +++ b/embassy-nrf/src/qspi.rs @@ -3,6 +3,7 @@ #![macro_use] use core::future::poll_fn; +use core::marker::PhantomData; use core::ptr; use core::task::Poll; @@ -11,12 +12,12 @@ use embassy_hal_common::{into_ref, PeripheralRef}; use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash}; use crate::gpio::{self, Pin as GpioPin}; -use crate::interrupt::{Interrupt, InterruptExt}; +use crate::interrupt::{self, Interrupt, InterruptExt}; pub use crate::pac::qspi::ifconfig0::{ ADDRMODE_A as AddressMode, PPSIZE_A as WritePageSize, READOC_A as ReadOpcode, WRITEOC_A as WriteOpcode, }; pub use crate::pac::qspi::ifconfig1::SPIMODE_A as SpiMode; -use crate::{pac, Peripheral}; +use crate::Peripheral; /// Deep power-down config. pub struct DeepPowerDownConfig { @@ -114,9 +115,26 @@ pub enum Error { // TODO add "not in data memory" error and check for it } +/// Interrupt handler. +pub struct InterruptHandler { + _phantom: PhantomData, +} + +impl interrupt::Handler for InterruptHandler { + unsafe fn on_interrupt() { + let r = T::regs(); + let s = T::state(); + + if r.events_ready.read().bits() != 0 { + s.waker.wake(); + r.intenclr.write(|w| w.ready().clear()); + } + } +} + /// QSPI flash driver. pub struct Qspi<'d, T: Instance> { - irq: PeripheralRef<'d, T::Interrupt>, + _peri: PeripheralRef<'d, T>, dpm_enabled: bool, capacity: u32, } @@ -124,8 +142,8 @@ pub struct Qspi<'d, T: Instance> { impl<'d, T: Instance> Qspi<'d, T> { /// Create a new QSPI driver. pub fn new( - _qspi: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + qspi: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, sck: impl Peripheral

+ 'd, csn: impl Peripheral

+ 'd, io0: impl Peripheral

+ 'd, @@ -134,7 +152,7 @@ impl<'d, T: Instance> Qspi<'d, T> { io3: impl Peripheral

+ 'd, config: Config, ) -> Self { - into_ref!(irq, sck, csn, io0, io1, io2, io3); + into_ref!(qspi, sck, csn, io0, io1, io2, io3); let r = T::regs(); @@ -189,16 +207,15 @@ impl<'d, T: Instance> Qspi<'d, T> { w }); - irq.set_handler(Self::on_interrupt); - irq.unpend(); - irq.enable(); + unsafe { T::Interrupt::steal() }.unpend(); + unsafe { T::Interrupt::steal() }.enable(); // Enable it r.enable.write(|w| w.enable().enabled()); let res = Self { + _peri: qspi, dpm_enabled: config.deep_power_down.is_some(), - irq, capacity: config.capacity, }; @@ -212,16 +229,6 @@ impl<'d, T: Instance> Qspi<'d, T> { res } - fn on_interrupt(_: *mut ()) { - let r = T::regs(); - let s = T::state(); - - if r.events_ready.read().bits() != 0 { - s.ready_waker.wake(); - r.intenclr.write(|w| w.ready().clear()); - } - } - /// Do a custom QSPI instruction. pub async fn custom_instruction(&mut self, opcode: u8, req: &[u8], resp: &mut [u8]) -> Result<(), Error> { let ondrop = OnDrop::new(Self::blocking_wait_ready); @@ -310,7 +317,7 @@ impl<'d, T: Instance> Qspi<'d, T> { poll_fn(move |cx| { let r = T::regs(); let s = T::state(); - s.ready_waker.register(cx.waker()); + s.waker.register(cx.waker()); if r.events_ready.read().bits() != 0 { return Poll::Ready(()); } @@ -525,8 +532,6 @@ impl<'d, T: Instance> Drop for Qspi<'d, T> { r.enable.write(|w| w.enable().disabled()); - self.irq.disable(); - // Note: we do NOT deconfigure CSN here. If DPM is in use and we disconnect CSN, // leaving it floating, the flash chip might read it as zero which would cause it to // spuriously exit DPM. @@ -624,27 +629,27 @@ mod _eh1 { pub(crate) mod sealed { use embassy_sync::waitqueue::AtomicWaker; - use super::*; - + /// Peripheral static state pub struct State { - pub ready_waker: AtomicWaker, + pub waker: AtomicWaker, } + impl State { pub const fn new() -> Self { Self { - ready_waker: AtomicWaker::new(), + waker: AtomicWaker::new(), } } } pub trait Instance { - fn regs() -> &'static pac::qspi::RegisterBlock; + fn regs() -> &'static crate::pac::qspi::RegisterBlock; fn state() -> &'static State; } } /// QSPI peripheral instance. -pub trait Instance: Peripheral

+ sealed::Instance + 'static { +pub trait Instance: Peripheral

+ sealed::Instance + 'static + Send { /// Interrupt for this peripheral. type Interrupt: Interrupt; } @@ -652,7 +657,7 @@ pub trait Instance: Peripheral

+ sealed::Instance + 'static { macro_rules! impl_qspi { ($type:ident, $pac_type:ident, $irq:ident) => { impl crate::qspi::sealed::Instance for peripherals::$type { - fn regs() -> &'static pac::qspi::RegisterBlock { + fn regs() -> &'static crate::pac::qspi::RegisterBlock { unsafe { &*pac::$pac_type::ptr() } } fn state() -> &'static crate::qspi::sealed::State { diff --git a/examples/nrf52840/src/bin/qspi.rs b/examples/nrf52840/src/bin/qspi.rs index 21a10940..9e8a01f4 100644 --- a/examples/nrf52840/src/bin/qspi.rs +++ b/examples/nrf52840/src/bin/qspi.rs @@ -5,7 +5,7 @@ use defmt::{assert_eq, info, unwrap}; use embassy_executor::Spawner; use embassy_nrf::qspi::Frequency; -use embassy_nrf::{interrupt, qspi}; +use embassy_nrf::{bind_interrupts, peripherals, qspi}; use {defmt_rtt as _, panic_probe as _}; const PAGE_SIZE: usize = 4096; @@ -15,6 +15,10 @@ const PAGE_SIZE: usize = 4096; #[repr(C, align(4))] struct AlignedBuf([u8; 4096]); +bind_interrupts!(struct Irqs { + QSPI => qspi::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); @@ -26,9 +30,8 @@ async fn main(_spawner: Spawner) { config.write_opcode = qspi::WriteOpcode::PP4IO; config.write_page_size = qspi::WritePageSize::_256BYTES; - let irq = interrupt::take!(QSPI); let mut q = qspi::Qspi::new( - p.QSPI, irq, p.P0_19, p.P0_17, p.P0_20, p.P0_21, p.P0_22, p.P0_23, config, + p.QSPI, Irqs, p.P0_19, p.P0_17, p.P0_20, p.P0_21, p.P0_22, p.P0_23, config, ); let mut id = [1; 3]; diff --git a/examples/nrf52840/src/bin/qspi_lowpower.rs b/examples/nrf52840/src/bin/qspi_lowpower.rs index 20c90391..22a5c0c6 100644 --- a/examples/nrf52840/src/bin/qspi_lowpower.rs +++ b/examples/nrf52840/src/bin/qspi_lowpower.rs @@ -7,7 +7,7 @@ use core::mem; use defmt::{info, unwrap}; use embassy_executor::Spawner; use embassy_nrf::qspi::Frequency; -use embassy_nrf::{interrupt, qspi}; +use embassy_nrf::{bind_interrupts, peripherals, qspi}; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; @@ -16,10 +16,13 @@ use {defmt_rtt as _, panic_probe as _}; #[repr(C, align(4))] struct AlignedBuf([u8; 64]); +bind_interrupts!(struct Irqs { + QSPI => qspi::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_p: Spawner) { let mut p = embassy_nrf::init(Default::default()); - let mut irq = interrupt::take!(QSPI); loop { // Config for the MX25R64 present in the nRF52840 DK @@ -36,7 +39,7 @@ async fn main(_p: Spawner) { let mut q = qspi::Qspi::new( &mut p.QSPI, - &mut irq, + Irqs, &mut p.P0_19, &mut p.P0_17, &mut p.P0_20, From d113fcfe326bd338df2db7733fcf0ae9f230c594 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 5 Mar 2023 21:50:15 +0100 Subject: [PATCH 43/51] nrf/rng: make available on all chips, use Instance trait, switch to new interrupt binding. --- embassy-nrf/src/chips/nrf52805.rs | 2 + embassy-nrf/src/chips/nrf52810.rs | 2 + embassy-nrf/src/chips/nrf52811.rs | 2 + embassy-nrf/src/chips/nrf52820.rs | 2 + embassy-nrf/src/chips/nrf52832.rs | 2 + embassy-nrf/src/chips/nrf52833.rs | 2 + embassy-nrf/src/chips/nrf52840.rs | 2 + embassy-nrf/src/chips/nrf5340_net.rs | 5 + embassy-nrf/src/lib.rs | 2 +- embassy-nrf/src/rng.rs | 193 ++++++++++++++-------- examples/nrf52840/src/bin/rng.rs | 8 +- examples/nrf52840/src/bin/usb_ethernet.rs | 8 +- 12 files changed, 154 insertions(+), 76 deletions(-) diff --git a/embassy-nrf/src/chips/nrf52805.rs b/embassy-nrf/src/chips/nrf52805.rs index 185cda43..e406c081 100644 --- a/embassy-nrf/src/chips/nrf52805.rs +++ b/embassy-nrf/src/chips/nrf52805.rs @@ -142,6 +142,8 @@ impl_twis!(TWI0, TWIS0, TWIM0_TWIS0_TWI0); impl_qdec!(QDEC, QDEC, QDEC); +impl_rng!(RNG, RNG, RNG); + impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); diff --git a/embassy-nrf/src/chips/nrf52810.rs b/embassy-nrf/src/chips/nrf52810.rs index 1e3c054a..153795e5 100644 --- a/embassy-nrf/src/chips/nrf52810.rs +++ b/embassy-nrf/src/chips/nrf52810.rs @@ -152,6 +152,8 @@ impl_pdm!(PDM, PDM, PDM); impl_qdec!(QDEC, QDEC, QDEC); +impl_rng!(RNG, RNG, RNG); + impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); diff --git a/embassy-nrf/src/chips/nrf52811.rs b/embassy-nrf/src/chips/nrf52811.rs index 3bb44171..a7a7cf58 100644 --- a/embassy-nrf/src/chips/nrf52811.rs +++ b/embassy-nrf/src/chips/nrf52811.rs @@ -154,6 +154,8 @@ impl_pdm!(PDM, PDM, PDM); impl_qdec!(QDEC, QDEC, QDEC); +impl_rng!(RNG, RNG, RNG); + impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); diff --git a/embassy-nrf/src/chips/nrf52820.rs b/embassy-nrf/src/chips/nrf52820.rs index b28778f3..14a1b8cc 100644 --- a/embassy-nrf/src/chips/nrf52820.rs +++ b/embassy-nrf/src/chips/nrf52820.rs @@ -155,6 +155,8 @@ impl_timer!(TIMER3, TIMER3, TIMER3, extended); impl_qdec!(QDEC, QDEC, QDEC); +impl_rng!(RNG, RNG, RNG); + impl_pin!(P0_00, 0, 0); impl_pin!(P0_01, 0, 1); impl_pin!(P0_02, 0, 2); diff --git a/embassy-nrf/src/chips/nrf52832.rs b/embassy-nrf/src/chips/nrf52832.rs index 00dc9fd8..83ecd0de 100644 --- a/embassy-nrf/src/chips/nrf52832.rs +++ b/embassy-nrf/src/chips/nrf52832.rs @@ -175,6 +175,8 @@ impl_pdm!(PDM, PDM, PDM); impl_qdec!(QDEC, QDEC, QDEC); +impl_rng!(RNG, RNG, RNG); + impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); diff --git a/embassy-nrf/src/chips/nrf52833.rs b/embassy-nrf/src/chips/nrf52833.rs index 345608c9..5e5db04d 100644 --- a/embassy-nrf/src/chips/nrf52833.rs +++ b/embassy-nrf/src/chips/nrf52833.rs @@ -201,6 +201,8 @@ impl_pdm!(PDM, PDM, PDM); impl_qdec!(QDEC, QDEC, QDEC); +impl_rng!(RNG, RNG, RNG); + impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); diff --git a/embassy-nrf/src/chips/nrf52840.rs b/embassy-nrf/src/chips/nrf52840.rs index 630f01aa..f6d33f85 100644 --- a/embassy-nrf/src/chips/nrf52840.rs +++ b/embassy-nrf/src/chips/nrf52840.rs @@ -212,6 +212,8 @@ impl_pdm!(PDM, PDM, PDM); impl_qdec!(QDEC, QDEC, QDEC); +impl_rng!(RNG, RNG, RNG); + impl_pin!(P0_00, 0, 0); impl_pin!(P0_01, 0, 1); impl_pin!(P0_02, 0, 2); diff --git a/embassy-nrf/src/chips/nrf5340_net.rs b/embassy-nrf/src/chips/nrf5340_net.rs index d7ba6c16..1e59528c 100644 --- a/embassy-nrf/src/chips/nrf5340_net.rs +++ b/embassy-nrf/src/chips/nrf5340_net.rs @@ -127,6 +127,9 @@ embassy_hal_common::peripherals! { // SAADC SAADC, + // RNG + RNG, + // PWM PWM0, PWM1, @@ -252,6 +255,8 @@ impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); +impl_rng!(RNG, RNG, RNG); + impl_pin!(P0_00, 0, 0); impl_pin!(P0_01, 0, 1); impl_pin!(P0_02, 0, 2); diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index feefa248..ee29ce20 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -61,7 +61,7 @@ pub mod pwm; pub mod qdec; #[cfg(any(feature = "nrf52840", feature = "_nrf5340-app"))] pub mod qspi; -#[cfg(not(any(feature = "_nrf5340", feature = "_nrf9160")))] +#[cfg(not(any(feature = "_nrf5340-app", feature = "_nrf9160")))] pub mod rng; #[cfg(not(any(feature = "nrf52820", feature = "_nrf5340-net")))] pub mod saadc; diff --git a/embassy-nrf/src/rng.rs b/embassy-nrf/src/rng.rs index b0b3a8eb..a5602248 100644 --- a/embassy-nrf/src/rng.rs +++ b/embassy-nrf/src/rng.rs @@ -1,83 +1,48 @@ //! Random Number Generator (RNG) driver. +#![macro_use] + use core::future::poll_fn; +use core::marker::PhantomData; use core::ptr; use core::sync::atomic::{AtomicPtr, Ordering}; use core::task::Poll; +use embassy_cortex_m::interrupt::Interrupt; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use crate::interrupt::InterruptExt; -use crate::peripherals::RNG; -use crate::{interrupt, pac, Peripheral}; +use crate::{interrupt, Peripheral}; -impl RNG { - fn regs() -> &'static pac::rng::RegisterBlock { - unsafe { &*pac::RNG::ptr() } - } +/// Interrupt handler. +pub struct InterruptHandler { + _phantom: PhantomData, } -static STATE: State = State { - ptr: AtomicPtr::new(ptr::null_mut()), - end: AtomicPtr::new(ptr::null_mut()), - waker: AtomicWaker::new(), -}; +impl interrupt::Handler for InterruptHandler { + unsafe fn on_interrupt() { + let s = T::state(); + let r = T::regs(); -struct State { - ptr: AtomicPtr, - end: AtomicPtr, - waker: AtomicWaker, -} - -/// A wrapper around an nRF RNG peripheral. -/// -/// It has a non-blocking API, and a blocking api through `rand`. -pub struct Rng<'d> { - irq: PeripheralRef<'d, interrupt::RNG>, -} - -impl<'d> Rng<'d> { - /// Creates a new RNG driver from the `RNG` peripheral and interrupt. - /// - /// SAFETY: The future returned from `fill_bytes` must not have its lifetime end without running its destructor, - /// e.g. using `mem::forget`. - /// - /// The synchronous API is safe. - pub fn new(_rng: impl Peripheral

+ 'd, irq: impl Peripheral

+ 'd) -> Self { - into_ref!(irq); - - let this = Self { irq }; - - this.stop(); - this.disable_irq(); - - this.irq.set_handler(Self::on_interrupt); - this.irq.unpend(); - this.irq.enable(); - - this - } - - fn on_interrupt(_: *mut ()) { // Clear the event. - RNG::regs().events_valrdy.reset(); + r.events_valrdy.reset(); // Mutate the slice within a critical section, // so that the future isn't dropped in between us loading the pointer and actually dereferencing it. let (ptr, end) = critical_section::with(|_| { - let ptr = STATE.ptr.load(Ordering::Relaxed); + let ptr = s.ptr.load(Ordering::Relaxed); // We need to make sure we haven't already filled the whole slice, // in case the interrupt fired again before the executor got back to the future. - let end = STATE.end.load(Ordering::Relaxed); + let end = s.end.load(Ordering::Relaxed); if !ptr.is_null() && ptr != end { // If the future was dropped, the pointer would have been set to null, // so we're still good to mutate the slice. // The safety contract of `Rng::new` means that the future can't have been dropped // without calling its destructor. unsafe { - *ptr = RNG::regs().value.read().value().bits(); + *ptr = r.value.read().value().bits(); } } (ptr, end) @@ -90,15 +55,15 @@ impl<'d> Rng<'d> { } let new_ptr = unsafe { ptr.add(1) }; - match STATE + match s .ptr .compare_exchange(ptr, new_ptr, Ordering::Relaxed, Ordering::Relaxed) { Ok(_) => { - let end = STATE.end.load(Ordering::Relaxed); + let end = s.end.load(Ordering::Relaxed); // It doesn't matter if `end` was changed under our feet, because then this will just be false. if new_ptr == end { - STATE.waker.wake(); + s.waker.wake(); } } Err(_) => { @@ -107,21 +72,53 @@ impl<'d> Rng<'d> { } } } +} + +/// A wrapper around an nRF RNG peripheral. +/// +/// It has a non-blocking API, and a blocking api through `rand`. +pub struct Rng<'d, T: Instance> { + _peri: PeripheralRef<'d, T>, +} + +impl<'d, T: Instance> Rng<'d, T> { + /// Creates a new RNG driver from the `RNG` peripheral and interrupt. + /// + /// SAFETY: The future returned from `fill_bytes` must not have its lifetime end without running its destructor, + /// e.g. using `mem::forget`. + /// + /// The synchronous API is safe. + pub fn new( + rng: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, + ) -> Self { + into_ref!(rng); + + let this = Self { _peri: rng }; + + this.stop(); + this.disable_irq(); + + unsafe { T::Interrupt::steal() }.unpend(); + unsafe { T::Interrupt::steal() }.enable(); + + this + } fn stop(&self) { - RNG::regs().tasks_stop.write(|w| unsafe { w.bits(1) }) + T::regs().tasks_stop.write(|w| unsafe { w.bits(1) }) } fn start(&self) { - RNG::regs().tasks_start.write(|w| unsafe { w.bits(1) }) + T::regs().tasks_start.write(|w| unsafe { w.bits(1) }) } fn enable_irq(&self) { - RNG::regs().intenset.write(|w| w.valrdy().set()); + T::regs().intenset.write(|w| w.valrdy().set()); } fn disable_irq(&self) { - RNG::regs().intenclr.write(|w| w.valrdy().clear()); + T::regs().intenclr.write(|w| w.valrdy().clear()); } /// Enable or disable the RNG's bias correction. @@ -131,7 +128,7 @@ impl<'d> Rng<'d> { /// /// Defaults to disabled. pub fn set_bias_correction(&self, enable: bool) { - RNG::regs().config.write(|w| w.dercen().bit(enable)) + T::regs().config.write(|w| w.dercen().bit(enable)) } /// Fill the buffer with random bytes. @@ -140,11 +137,13 @@ impl<'d> Rng<'d> { return; // Nothing to fill } + let s = T::state(); + let range = dest.as_mut_ptr_range(); // Even if we've preempted the interrupt, it can't preempt us again, // so we don't need to worry about the order we write these in. - STATE.ptr.store(range.start, Ordering::Relaxed); - STATE.end.store(range.end, Ordering::Relaxed); + s.ptr.store(range.start, Ordering::Relaxed); + s.end.store(range.end, Ordering::Relaxed); self.enable_irq(); self.start(); @@ -154,16 +153,16 @@ impl<'d> Rng<'d> { self.disable_irq(); // The interrupt is now disabled and can't preempt us anymore, so the order doesn't matter here. - STATE.ptr.store(ptr::null_mut(), Ordering::Relaxed); - STATE.end.store(ptr::null_mut(), Ordering::Relaxed); + s.ptr.store(ptr::null_mut(), Ordering::Relaxed); + s.end.store(ptr::null_mut(), Ordering::Relaxed); }); poll_fn(|cx| { - STATE.waker.register(cx.waker()); + s.waker.register(cx.waker()); // The interrupt will never modify `end`, so load it first and then get the most up-to-date `ptr`. - let end = STATE.end.load(Ordering::Relaxed); - let ptr = STATE.ptr.load(Ordering::Relaxed); + let end = s.end.load(Ordering::Relaxed); + let ptr = s.ptr.load(Ordering::Relaxed); if ptr == end { // We're done. @@ -183,7 +182,7 @@ impl<'d> Rng<'d> { self.start(); for byte in dest.iter_mut() { - let regs = RNG::regs(); + let regs = T::regs(); while regs.events_valrdy.read().bits() == 0 {} regs.events_valrdy.reset(); *byte = regs.value.read().value().bits(); @@ -193,13 +192,16 @@ impl<'d> Rng<'d> { } } -impl<'d> Drop for Rng<'d> { +impl<'d, T: Instance> Drop for Rng<'d, T> { fn drop(&mut self) { - self.irq.disable() + self.stop(); + let s = T::state(); + s.ptr.store(ptr::null_mut(), Ordering::Relaxed); + s.end.store(ptr::null_mut(), Ordering::Relaxed); } } -impl<'d> rand_core::RngCore for Rng<'d> { +impl<'d, T: Instance> rand_core::RngCore for Rng<'d, T> { fn fill_bytes(&mut self, dest: &mut [u8]) { self.blocking_fill_bytes(dest); } @@ -223,4 +225,53 @@ impl<'d> rand_core::RngCore for Rng<'d> { } } -impl<'d> rand_core::CryptoRng for Rng<'d> {} +impl<'d, T: Instance> rand_core::CryptoRng for Rng<'d, T> {} + +pub(crate) mod sealed { + use super::*; + + /// Peripheral static state + pub struct State { + pub ptr: AtomicPtr, + pub end: AtomicPtr, + pub waker: AtomicWaker, + } + + impl State { + pub const fn new() -> Self { + Self { + ptr: AtomicPtr::new(ptr::null_mut()), + end: AtomicPtr::new(ptr::null_mut()), + waker: AtomicWaker::new(), + } + } + } + + pub trait Instance { + fn regs() -> &'static crate::pac::rng::RegisterBlock; + fn state() -> &'static State; + } +} + +/// RNG peripheral instance. +pub trait Instance: Peripheral

+ sealed::Instance + 'static + Send { + /// Interrupt for this peripheral. + type Interrupt: Interrupt; +} + +macro_rules! impl_rng { + ($type:ident, $pac_type:ident, $irq:ident) => { + impl crate::rng::sealed::Instance for peripherals::$type { + fn regs() -> &'static crate::pac::rng::RegisterBlock { + unsafe { &*pac::$pac_type::ptr() } + } + fn state() -> &'static crate::rng::sealed::State { + static STATE: crate::rng::sealed::State = crate::rng::sealed::State::new(); + &STATE + } + } + impl crate::rng::Instance for peripherals::$type { + type Interrupt = crate::interrupt::$irq; + } + }; +} diff --git a/examples/nrf52840/src/bin/rng.rs b/examples/nrf52840/src/bin/rng.rs index 64707394..855743f5 100644 --- a/examples/nrf52840/src/bin/rng.rs +++ b/examples/nrf52840/src/bin/rng.rs @@ -3,15 +3,19 @@ #![feature(type_alias_impl_trait)] use embassy_executor::Spawner; -use embassy_nrf::interrupt; use embassy_nrf::rng::Rng; +use embassy_nrf::{bind_interrupts, peripherals, rng}; use rand::Rng as _; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + RNG => rng::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); - let mut rng = Rng::new(p.RNG, interrupt::take!(RNG)); + let mut rng = Rng::new(p.RNG, Irqs); // Async API let mut bytes = [0; 4]; diff --git a/examples/nrf52840/src/bin/usb_ethernet.rs b/examples/nrf52840/src/bin/usb_ethernet.rs index 97978089..083a1cbb 100644 --- a/examples/nrf52840/src/bin/usb_ethernet.rs +++ b/examples/nrf52840/src/bin/usb_ethernet.rs @@ -10,7 +10,7 @@ use embassy_net::tcp::TcpSocket; use embassy_net::{Stack, StackResources}; use embassy_nrf::rng::Rng; use embassy_nrf::usb::{Driver, HardwareVbusDetect}; -use embassy_nrf::{interrupt, pac, peripherals}; +use embassy_nrf::{bind_interrupts, interrupt, pac, peripherals, rng}; use embassy_usb::class::cdc_ncm::embassy_net::{Device, Runner, State as NetState}; use embassy_usb::class::cdc_ncm::{CdcNcmClass, State}; use embassy_usb::{Builder, Config, UsbDevice}; @@ -18,6 +18,10 @@ use embedded_io::asynch::Write; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + RNG => rng::InterruptHandler; +}); + type MyDriver = Driver<'static, peripherals::USBD, HardwareVbusDetect>; macro_rules! singleton { @@ -108,7 +112,7 @@ async fn main(spawner: Spawner) { //}); // Generate random seed - let mut rng = Rng::new(p.RNG, interrupt::take!(RNG)); + let mut rng = Rng::new(p.RNG, Irqs); let mut seed = [0; 8]; rng.blocking_fill_bytes(&mut seed); let seed = u64::from_le_bytes(seed); From 2dc56082033f650083355464c3106ccb57302338 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 5 Mar 2023 21:56:22 +0100 Subject: [PATCH 44/51] nrf/saadc: switch to new interrupt binding. --- embassy-nrf/src/saadc.rs | 62 ++++++++++--------- examples/nrf52840/src/bin/saadc.rs | 8 ++- examples/nrf52840/src/bin/saadc_continuous.rs | 8 ++- 3 files changed, 46 insertions(+), 32 deletions(-) diff --git a/embassy-nrf/src/saadc.rs b/embassy-nrf/src/saadc.rs index 2d01a3dd..af952f03 100644 --- a/embassy-nrf/src/saadc.rs +++ b/embassy-nrf/src/saadc.rs @@ -6,6 +6,7 @@ use core::future::poll_fn; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; +use embassy_cortex_m::interrupt::{Interrupt, InterruptExt}; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{impl_peripheral, into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; @@ -17,7 +18,6 @@ use saadc::oversample::OVERSAMPLE_A; use saadc::resolution::VAL_A; use self::sealed::Input as _; -use crate::interrupt::InterruptExt; use crate::ppi::{ConfigurableChannel, Event, Ppi, Task}; use crate::timer::{Frequency, Instance as TimerInstance, Timer}; use crate::{interrupt, pac, peripherals, Peripheral}; @@ -28,9 +28,30 @@ use crate::{interrupt, pac, peripherals, Peripheral}; #[non_exhaustive] pub enum Error {} -/// One-shot and continuous SAADC. -pub struct Saadc<'d, const N: usize> { - _p: PeripheralRef<'d, peripherals::SAADC>, +/// Interrupt handler. +pub struct InterruptHandler { + _private: (), +} + +impl interrupt::Handler for InterruptHandler { + unsafe fn on_interrupt() { + let r = unsafe { &*SAADC::ptr() }; + + if r.events_calibratedone.read().bits() != 0 { + r.intenclr.write(|w| w.calibratedone().clear()); + WAKER.wake(); + } + + if r.events_end.read().bits() != 0 { + r.intenclr.write(|w| w.end().clear()); + WAKER.wake(); + } + + if r.events_started.read().bits() != 0 { + r.intenclr.write(|w| w.started().clear()); + WAKER.wake(); + } + } } static WAKER: AtomicWaker = AtomicWaker::new(); @@ -114,15 +135,20 @@ pub enum CallbackResult { Stop, } +/// One-shot and continuous SAADC. +pub struct Saadc<'d, const N: usize> { + _p: PeripheralRef<'d, peripherals::SAADC>, +} + impl<'d, const N: usize> Saadc<'d, N> { /// Create a new SAADC driver. pub fn new( saadc: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding + 'd, config: Config, channel_configs: [ChannelConfig; N], ) -> Self { - into_ref!(saadc, irq); + into_ref!(saadc); let r = unsafe { &*SAADC::ptr() }; @@ -163,32 +189,12 @@ impl<'d, const N: usize> Saadc<'d, N> { // Disable all events interrupts r.intenclr.write(|w| unsafe { w.bits(0x003F_FFFF) }); - irq.set_handler(Self::on_interrupt); - irq.unpend(); - irq.enable(); + unsafe { interrupt::SAADC::steal() }.unpend(); + unsafe { interrupt::SAADC::steal() }.enable(); Self { _p: saadc } } - fn on_interrupt(_ctx: *mut ()) { - let r = Self::regs(); - - if r.events_calibratedone.read().bits() != 0 { - r.intenclr.write(|w| w.calibratedone().clear()); - WAKER.wake(); - } - - if r.events_end.read().bits() != 0 { - r.intenclr.write(|w| w.end().clear()); - WAKER.wake(); - } - - if r.events_started.read().bits() != 0 { - r.intenclr.write(|w| w.started().clear()); - WAKER.wake(); - } - } - fn regs() -> &'static saadc::RegisterBlock { unsafe { &*SAADC::ptr() } } diff --git a/examples/nrf52840/src/bin/saadc.rs b/examples/nrf52840/src/bin/saadc.rs index 7cf58809..ffd9a7f4 100644 --- a/examples/nrf52840/src/bin/saadc.rs +++ b/examples/nrf52840/src/bin/saadc.rs @@ -4,17 +4,21 @@ use defmt::info; use embassy_executor::Spawner; -use embassy_nrf::interrupt; use embassy_nrf::saadc::{ChannelConfig, Config, Saadc}; +use embassy_nrf::{bind_interrupts, saadc}; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + SAADC => saadc::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_p: Spawner) { let mut p = embassy_nrf::init(Default::default()); let config = Config::default(); let channel_config = ChannelConfig::single_ended(&mut p.P0_02); - let mut saadc = Saadc::new(p.SAADC, interrupt::take!(SAADC), config, [channel_config]); + let mut saadc = Saadc::new(p.SAADC, Irqs, config, [channel_config]); loop { let mut buf = [0; 1]; diff --git a/examples/nrf52840/src/bin/saadc_continuous.rs b/examples/nrf52840/src/bin/saadc_continuous.rs index 2551d15f..a25e1746 100644 --- a/examples/nrf52840/src/bin/saadc_continuous.rs +++ b/examples/nrf52840/src/bin/saadc_continuous.rs @@ -4,14 +4,18 @@ use defmt::info; use embassy_executor::Spawner; -use embassy_nrf::interrupt; use embassy_nrf::saadc::{CallbackResult, ChannelConfig, Config, Saadc}; use embassy_nrf::timer::Frequency; +use embassy_nrf::{bind_interrupts, saadc}; use embassy_time::Duration; use {defmt_rtt as _, panic_probe as _}; // Demonstrates both continuous sampling and scanning multiple channels driven by a PPI linked timer +bind_interrupts!(struct Irqs { + SAADC => saadc::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_p: Spawner) { let mut p = embassy_nrf::init(Default::default()); @@ -21,7 +25,7 @@ async fn main(_p: Spawner) { let channel_3_config = ChannelConfig::single_ended(&mut p.P0_04); let mut saadc = Saadc::new( p.SAADC, - interrupt::take!(SAADC), + Irqs, config, [channel_1_config, channel_2_config, channel_3_config], ); From a32e82029a8b6944ef3e9861b09095bae01b37a3 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 5 Mar 2023 22:00:52 +0100 Subject: [PATCH 45/51] nrf/spim: switch to new interrupt binding. --- embassy-nrf/src/spim.rs | 59 +++++++++++--------- examples/nrf52840/src/bin/lora_p2p_report.rs | 9 ++- examples/nrf52840/src/bin/lora_p2p_sense.rs | 9 ++- examples/nrf52840/src/bin/spim.rs | 9 ++- 4 files changed, 50 insertions(+), 36 deletions(-) diff --git a/embassy-nrf/src/spim.rs b/embassy-nrf/src/spim.rs index 17e43578..89cbdfee 100644 --- a/embassy-nrf/src/spim.rs +++ b/embassy-nrf/src/spim.rs @@ -3,6 +3,7 @@ #![macro_use] use core::future::poll_fn; +use core::marker::PhantomData; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; @@ -14,7 +15,7 @@ pub use pac::spim0::frequency::FREQUENCY_A as Frequency; use crate::chip::FORCE_COPY_BUFFER_SIZE; use crate::gpio::sealed::Pin as _; use crate::gpio::{self, AnyPin, Pin as GpioPin, PselBits}; -use crate::interrupt::{Interrupt, InterruptExt}; +use crate::interrupt::{self, Interrupt, InterruptExt}; use crate::util::{slice_in_ram_or, slice_ptr_parts, slice_ptr_parts_mut}; use crate::{pac, Peripheral}; @@ -31,11 +32,6 @@ pub enum Error { BufferNotInRAM, } -/// SPIM driver. -pub struct Spim<'d, T: Instance> { - _p: PeripheralRef<'d, T>, -} - /// SPIM configuration. #[non_exhaustive] pub struct Config { @@ -62,11 +58,33 @@ impl Default for Config { } } +/// Interrupt handler. +pub struct InterruptHandler { + _phantom: PhantomData, +} + +impl interrupt::Handler for InterruptHandler { + unsafe fn on_interrupt() { + let r = T::regs(); + let s = T::state(); + + if r.events_end.read().bits() != 0 { + s.end_waker.wake(); + r.intenclr.write(|w| w.end().clear()); + } + } +} + +/// SPIM driver. +pub struct Spim<'d, T: Instance> { + _p: PeripheralRef<'d, T>, +} + impl<'d, T: Instance> Spim<'d, T> { /// Create a new SPIM driver. pub fn new( spim: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, sck: impl Peripheral

+ 'd, miso: impl Peripheral

+ 'd, mosi: impl Peripheral

+ 'd, @@ -75,7 +93,6 @@ impl<'d, T: Instance> Spim<'d, T> { into_ref!(sck, miso, mosi); Self::new_inner( spim, - irq, sck.map_into(), Some(miso.map_into()), Some(mosi.map_into()), @@ -86,36 +103,35 @@ impl<'d, T: Instance> Spim<'d, T> { /// Create a new SPIM driver, capable of TX only (MOSI only). pub fn new_txonly( spim: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, sck: impl Peripheral

+ 'd, mosi: impl Peripheral

+ 'd, config: Config, ) -> Self { into_ref!(sck, mosi); - Self::new_inner(spim, irq, sck.map_into(), None, Some(mosi.map_into()), config) + Self::new_inner(spim, sck.map_into(), None, Some(mosi.map_into()), config) } /// Create a new SPIM driver, capable of RX only (MISO only). pub fn new_rxonly( spim: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, sck: impl Peripheral

+ 'd, miso: impl Peripheral

+ 'd, config: Config, ) -> Self { into_ref!(sck, miso); - Self::new_inner(spim, irq, sck.map_into(), Some(miso.map_into()), None, config) + Self::new_inner(spim, sck.map_into(), Some(miso.map_into()), None, config) } fn new_inner( spim: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, sck: PeripheralRef<'d, AnyPin>, miso: Option>, mosi: Option>, config: Config, ) -> Self { - into_ref!(spim, irq); + into_ref!(spim); let r = T::regs(); @@ -191,23 +207,12 @@ impl<'d, T: Instance> Spim<'d, T> { // Disable all events interrupts r.intenclr.write(|w| unsafe { w.bits(0xFFFF_FFFF) }); - irq.set_handler(Self::on_interrupt); - irq.unpend(); - irq.enable(); + unsafe { T::Interrupt::steal() }.unpend(); + unsafe { T::Interrupt::steal() }.enable(); Self { _p: spim } } - fn on_interrupt(_: *mut ()) { - let r = T::regs(); - let s = T::state(); - - if r.events_end.read().bits() != 0 { - s.end_waker.wake(); - r.intenclr.write(|w| w.end().clear()); - } - } - fn prepare(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> { slice_in_ram_or(tx, Error::BufferNotInRAM)?; // NOTE: RAM slice check for rx is not necessary, as a mutable diff --git a/examples/nrf52840/src/bin/lora_p2p_report.rs b/examples/nrf52840/src/bin/lora_p2p_report.rs index d512b83f..e24f0db0 100644 --- a/examples/nrf52840/src/bin/lora_p2p_report.rs +++ b/examples/nrf52840/src/bin/lora_p2p_report.rs @@ -11,11 +11,15 @@ use defmt::*; use embassy_executor::Spawner; use embassy_lora::sx126x::*; use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pin as _, Pull}; -use embassy_nrf::{interrupt, spim}; +use embassy_nrf::{bind_interrupts, peripherals, spim}; use embassy_time::{Duration, Timer}; use lorawan_device::async_device::radio::{Bandwidth, CodingRate, PhyRxTx, RfConfig, SpreadingFactor}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1 => spim::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); @@ -23,8 +27,7 @@ async fn main(_spawner: Spawner) { spi_config.frequency = spim::Frequency::M16; let mut radio = { - let irq = interrupt::take!(SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); - let spim = spim::Spim::new(p.TWISPI1, irq, p.P1_11, p.P1_13, p.P1_12, spi_config); + let spim = spim::Spim::new(p.TWISPI1, Irqs, p.P1_11, p.P1_13, p.P1_12, spi_config); let cs = Output::new(p.P1_10.degrade(), Level::High, OutputDrive::Standard); let reset = Output::new(p.P1_06.degrade(), Level::High, OutputDrive::Standard); diff --git a/examples/nrf52840/src/bin/lora_p2p_sense.rs b/examples/nrf52840/src/bin/lora_p2p_sense.rs index b9768874..b6f41ffc 100644 --- a/examples/nrf52840/src/bin/lora_p2p_sense.rs +++ b/examples/nrf52840/src/bin/lora_p2p_sense.rs @@ -12,13 +12,17 @@ use defmt::*; use embassy_executor::Spawner; use embassy_lora::sx126x::*; use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pin as _, Pull}; -use embassy_nrf::{interrupt, spim}; +use embassy_nrf::{bind_interrupts, peripherals, spim}; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::pubsub::{PubSubChannel, Publisher}; use embassy_time::{Duration, Timer}; use lorawan_device::async_device::radio::{Bandwidth, CodingRate, PhyRxTx, RfConfig, SpreadingFactor, TxConfig}; use {defmt_rtt as _, panic_probe as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1 => spim::InterruptHandler; +}); + // Message bus: queue of 2, 1 subscriber (Lora P2P), 2 publishers (temperature, motion detection) static MESSAGE_BUS: PubSubChannel = PubSubChannel::new(); @@ -58,8 +62,7 @@ async fn main(spawner: Spawner) { spi_config.frequency = spim::Frequency::M16; let mut radio = { - let irq = interrupt::take!(SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); - let spim = spim::Spim::new(p.TWISPI1, irq, p.P1_11, p.P1_13, p.P1_12, spi_config); + let spim = spim::Spim::new(p.TWISPI1, Irqs, p.P1_11, p.P1_13, p.P1_12, spi_config); let cs = Output::new(p.P1_10.degrade(), Level::High, OutputDrive::Standard); let reset = Output::new(p.P1_06.degrade(), Level::High, OutputDrive::Standard); diff --git a/examples/nrf52840/src/bin/spim.rs b/examples/nrf52840/src/bin/spim.rs index 132e0166..9d1843a8 100644 --- a/examples/nrf52840/src/bin/spim.rs +++ b/examples/nrf52840/src/bin/spim.rs @@ -5,9 +5,13 @@ use defmt::{info, unwrap}; use embassy_executor::Spawner; use embassy_nrf::gpio::{Level, Output, OutputDrive}; -use embassy_nrf::{interrupt, spim}; +use embassy_nrf::{bind_interrupts, peripherals, spim}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + SPIM3 => spim::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); @@ -16,8 +20,7 @@ async fn main(_spawner: Spawner) { let mut config = spim::Config::default(); config.frequency = spim::Frequency::M16; - let irq = interrupt::take!(SPIM3); - let mut spim = spim::Spim::new(p.SPI3, irq, p.P0_29, p.P0_28, p.P0_30, config); + let mut spim = spim::Spim::new(p.SPI3, Irqs, p.P0_29, p.P0_28, p.P0_30, config); let mut ncs = Output::new(p.P0_31, Level::High, OutputDrive::Standard); From 9f5762d3654464011b5ddda771d4866c547106a0 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 5 Mar 2023 22:05:02 +0100 Subject: [PATCH 46/51] nrf/spis: switch to new interrupt binding. --- embassy-nrf/src/spis.rs | 85 ++++++++++++++----------------- examples/nrf52840/src/bin/spis.rs | 9 ++-- 2 files changed, 43 insertions(+), 51 deletions(-) diff --git a/embassy-nrf/src/spis.rs b/embassy-nrf/src/spis.rs index 1b743647..55b5e060 100644 --- a/embassy-nrf/src/spis.rs +++ b/embassy-nrf/src/spis.rs @@ -2,6 +2,7 @@ #![macro_use] use core::future::poll_fn; +use core::marker::PhantomData; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; @@ -12,7 +13,7 @@ pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MO use crate::chip::FORCE_COPY_BUFFER_SIZE; use crate::gpio::sealed::Pin as _; use crate::gpio::{self, AnyPin, Pin as GpioPin}; -use crate::interrupt::{Interrupt, InterruptExt}; +use crate::interrupt::{self, Interrupt, InterruptExt}; use crate::util::{slice_in_ram_or, slice_ptr_parts, slice_ptr_parts_mut}; use crate::{pac, Peripheral}; @@ -29,11 +30,6 @@ pub enum Error { BufferNotInRAM, } -/// SPIS driver. -pub struct Spis<'d, T: Instance> { - _p: PeripheralRef<'d, T>, -} - /// SPIS configuration. #[non_exhaustive] pub struct Config { @@ -67,11 +63,38 @@ impl Default for Config { } } +/// Interrupt handler. +pub struct InterruptHandler { + _phantom: PhantomData, +} + +impl interrupt::Handler for InterruptHandler { + unsafe fn on_interrupt() { + let r = T::regs(); + let s = T::state(); + + if r.events_end.read().bits() != 0 { + s.waker.wake(); + r.intenclr.write(|w| w.end().clear()); + } + + if r.events_acquired.read().bits() != 0 { + s.waker.wake(); + r.intenclr.write(|w| w.acquired().clear()); + } + } +} + +/// SPIS driver. +pub struct Spis<'d, T: Instance> { + _p: PeripheralRef<'d, T>, +} + impl<'d, T: Instance> Spis<'d, T> { /// Create a new SPIS driver. pub fn new( spis: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, cs: impl Peripheral

+ 'd, sck: impl Peripheral

+ 'd, miso: impl Peripheral

+ 'd, @@ -81,7 +104,6 @@ impl<'d, T: Instance> Spis<'d, T> { into_ref!(cs, sck, miso, mosi); Self::new_inner( spis, - irq, cs.map_into(), sck.map_into(), Some(miso.map_into()), @@ -93,48 +115,31 @@ impl<'d, T: Instance> Spis<'d, T> { /// Create a new SPIS driver, capable of TX only (MISO only). pub fn new_txonly( spis: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, cs: impl Peripheral

+ 'd, sck: impl Peripheral

+ 'd, miso: impl Peripheral

+ 'd, config: Config, ) -> Self { into_ref!(cs, sck, miso); - Self::new_inner( - spis, - irq, - cs.map_into(), - sck.map_into(), - Some(miso.map_into()), - None, - config, - ) + Self::new_inner(spis, cs.map_into(), sck.map_into(), Some(miso.map_into()), None, config) } /// Create a new SPIS driver, capable of RX only (MOSI only). pub fn new_rxonly( spis: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, cs: impl Peripheral

+ 'd, sck: impl Peripheral

+ 'd, mosi: impl Peripheral

+ 'd, config: Config, ) -> Self { into_ref!(cs, sck, mosi); - Self::new_inner( - spis, - irq, - cs.map_into(), - sck.map_into(), - None, - Some(mosi.map_into()), - config, - ) + Self::new_inner(spis, cs.map_into(), sck.map_into(), None, Some(mosi.map_into()), config) } fn new_inner( spis: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, cs: PeripheralRef<'d, AnyPin>, sck: PeripheralRef<'d, AnyPin>, miso: Option>, @@ -143,7 +148,7 @@ impl<'d, T: Instance> Spis<'d, T> { ) -> Self { compiler_fence(Ordering::SeqCst); - into_ref!(spis, irq, cs, sck); + into_ref!(spis, cs, sck); let r = T::regs(); @@ -209,28 +214,12 @@ impl<'d, T: Instance> Spis<'d, T> { // Disable all events interrupts. r.intenclr.write(|w| unsafe { w.bits(0xFFFF_FFFF) }); - irq.set_handler(Self::on_interrupt); - irq.unpend(); - irq.enable(); + unsafe { T::Interrupt::steal() }.unpend(); + unsafe { T::Interrupt::steal() }.enable(); Self { _p: spis } } - fn on_interrupt(_: *mut ()) { - let r = T::regs(); - let s = T::state(); - - if r.events_end.read().bits() != 0 { - s.waker.wake(); - r.intenclr.write(|w| w.end().clear()); - } - - if r.events_acquired.read().bits() != 0 { - s.waker.wake(); - r.intenclr.write(|w| w.acquired().clear()); - } - } - fn prepare(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> { slice_in_ram_or(tx, Error::BufferNotInRAM)?; // NOTE: RAM slice check for rx is not necessary, as a mutable diff --git a/examples/nrf52840/src/bin/spis.rs b/examples/nrf52840/src/bin/spis.rs index fe3b0c53..77b6e8b6 100644 --- a/examples/nrf52840/src/bin/spis.rs +++ b/examples/nrf52840/src/bin/spis.rs @@ -4,17 +4,20 @@ use defmt::info; use embassy_executor::Spawner; -use embassy_nrf::interrupt; use embassy_nrf::spis::{Config, Spis}; +use embassy_nrf::{bind_interrupts, peripherals, spis}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + SPIM2_SPIS2_SPI2 => spis::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); info!("Running!"); - let irq = interrupt::take!(SPIM2_SPIS2_SPI2); - let mut spis = Spis::new(p.SPI2, irq, p.P0_31, p.P0_29, p.P0_28, p.P0_30, Config::default()); + let mut spis = Spis::new(p.SPI2, Irqs, p.P0_31, p.P0_29, p.P0_28, p.P0_30, Config::default()); loop { let mut rx_buf = [0_u8; 64]; From 9e58d9274c63ebe48217e94162e47bea3211ff1c Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 5 Mar 2023 22:12:34 +0100 Subject: [PATCH 47/51] nrf/twim: switch to new interrupt binding. --- embassy-nrf/src/twim.rs | 47 +++++++++++++--------- examples/nrf52840/src/bin/twim.rs | 9 +++-- examples/nrf52840/src/bin/twim_lowpower.rs | 9 +++-- 3 files changed, 39 insertions(+), 26 deletions(-) diff --git a/embassy-nrf/src/twim.rs b/embassy-nrf/src/twim.rs index 0dcb2b0d..ef4c929a 100644 --- a/embassy-nrf/src/twim.rs +++ b/embassy-nrf/src/twim.rs @@ -3,6 +3,7 @@ #![macro_use] use core::future::{poll_fn, Future}; +use core::marker::PhantomData; use core::sync::atomic::compiler_fence; use core::sync::atomic::Ordering::SeqCst; use core::task::Poll; @@ -15,7 +16,7 @@ use embassy_time::{Duration, Instant}; use crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE}; use crate::gpio::Pin as GpioPin; -use crate::interrupt::{Interrupt, InterruptExt}; +use crate::interrupt::{self, Interrupt, InterruptExt}; use crate::util::{slice_in_ram, slice_in_ram_or}; use crate::{gpio, pac, Peripheral}; @@ -92,6 +93,27 @@ pub enum Error { Timeout, } +/// Interrupt handler. +pub struct InterruptHandler { + _phantom: PhantomData, +} + +impl interrupt::Handler for InterruptHandler { + unsafe fn on_interrupt() { + let r = T::regs(); + let s = T::state(); + + if r.events_stopped.read().bits() != 0 { + s.end_waker.wake(); + r.intenclr.write(|w| w.stopped().clear()); + } + if r.events_error.read().bits() != 0 { + s.end_waker.wake(); + r.intenclr.write(|w| w.error().clear()); + } + } +} + /// TWI driver. pub struct Twim<'d, T: Instance> { _p: PeripheralRef<'d, T>, @@ -101,12 +123,12 @@ impl<'d, T: Instance> Twim<'d, T> { /// Create a new TWI driver. pub fn new( twim: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, sda: impl Peripheral

+ 'd, scl: impl Peripheral

+ 'd, config: Config, ) -> Self { - into_ref!(twim, irq, sda, scl); + into_ref!(twim, sda, scl); let r = T::regs(); @@ -152,27 +174,12 @@ impl<'d, T: Instance> Twim<'d, T> { // Disable all events interrupts r.intenclr.write(|w| unsafe { w.bits(0xFFFF_FFFF) }); - irq.set_handler(Self::on_interrupt); - irq.unpend(); - irq.enable(); + unsafe { T::Interrupt::steal() }.unpend(); + unsafe { T::Interrupt::steal() }.enable(); Self { _p: twim } } - fn on_interrupt(_: *mut ()) { - let r = T::regs(); - let s = T::state(); - - if r.events_stopped.read().bits() != 0 { - s.end_waker.wake(); - r.intenclr.write(|w| w.stopped().clear()); - } - if r.events_error.read().bits() != 0 { - s.end_waker.wake(); - r.intenclr.write(|w| w.error().clear()); - } - } - /// Set TX buffer, checking that it is in RAM and has suitable length. unsafe fn set_tx_buffer(&mut self, buffer: &[u8]) -> Result<(), Error> { slice_in_ram_or(buffer, Error::BufferNotInRAM)?; diff --git a/examples/nrf52840/src/bin/twim.rs b/examples/nrf52840/src/bin/twim.rs index a027cc1e..959e3a4b 100644 --- a/examples/nrf52840/src/bin/twim.rs +++ b/examples/nrf52840/src/bin/twim.rs @@ -8,19 +8,22 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_nrf::interrupt; use embassy_nrf::twim::{self, Twim}; +use embassy_nrf::{bind_interrupts, peripherals}; use {defmt_rtt as _, panic_probe as _}; const ADDRESS: u8 = 0x50; +bind_interrupts!(struct Irqs { + SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0 => twim::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); info!("Initializing TWI..."); let config = twim::Config::default(); - let irq = interrupt::take!(SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); - let mut twi = Twim::new(p.TWISPI0, irq, p.P0_03, p.P0_04, config); + let mut twi = Twim::new(p.TWISPI0, Irqs, p.P0_03, p.P0_04, config); info!("Reading..."); diff --git a/examples/nrf52840/src/bin/twim_lowpower.rs b/examples/nrf52840/src/bin/twim_lowpower.rs index e30cc968..0970d3c3 100644 --- a/examples/nrf52840/src/bin/twim_lowpower.rs +++ b/examples/nrf52840/src/bin/twim_lowpower.rs @@ -12,25 +12,28 @@ use core::mem; use defmt::*; use embassy_executor::Spawner; -use embassy_nrf::interrupt; use embassy_nrf::twim::{self, Twim}; +use embassy_nrf::{bind_interrupts, peripherals}; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; const ADDRESS: u8 = 0x50; +bind_interrupts!(struct Irqs { + SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0 => twim::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_p: Spawner) { let mut p = embassy_nrf::init(Default::default()); info!("Started!"); - let mut irq = interrupt::take!(SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); loop { info!("Initializing TWI..."); let config = twim::Config::default(); // Create the TWIM instance with borrowed singletons, so they're not consumed. - let mut twi = Twim::new(&mut p.TWISPI0, &mut irq, &mut p.P0_03, &mut p.P0_04, config); + let mut twi = Twim::new(&mut p.TWISPI0, Irqs, &mut p.P0_03, &mut p.P0_04, config); info!("Reading..."); From 36319fc121f19f86dded45b6fb93aed7c3f4ae33 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 5 Mar 2023 22:09:54 +0100 Subject: [PATCH 48/51] nrf/temp: switch to new interrupt binding. --- embassy-nrf/src/temp.rs | 35 +++++++++++++++++++++---------- examples/nrf52840/src/bin/temp.rs | 9 +++++--- 2 files changed, 30 insertions(+), 14 deletions(-) diff --git a/embassy-nrf/src/temp.rs b/embassy-nrf/src/temp.rs index 5298faab..3a75ec62 100644 --- a/embassy-nrf/src/temp.rs +++ b/embassy-nrf/src/temp.rs @@ -3,6 +3,7 @@ use core::future::poll_fn; use core::task::Poll; +use embassy_cortex_m::interrupt::Interrupt; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; @@ -12,27 +13,39 @@ use crate::interrupt::InterruptExt; use crate::peripherals::TEMP; use crate::{interrupt, pac, Peripheral}; +/// Interrupt handler. +pub struct InterruptHandler { + _private: (), +} + +impl interrupt::Handler for InterruptHandler { + unsafe fn on_interrupt() { + let r = unsafe { &*pac::TEMP::PTR }; + r.intenclr.write(|w| w.datardy().clear()); + WAKER.wake(); + } +} + /// Builtin temperature sensor driver. pub struct Temp<'d> { - _irq: PeripheralRef<'d, interrupt::TEMP>, + _peri: PeripheralRef<'d, TEMP>, } static WAKER: AtomicWaker = AtomicWaker::new(); impl<'d> Temp<'d> { /// Create a new temperature sensor driver. - pub fn new(_t: impl Peripheral

+ 'd, irq: impl Peripheral

+ 'd) -> Self { - into_ref!(_t, irq); + pub fn new( + _peri: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding + 'd, + ) -> Self { + into_ref!(_peri); // Enable interrupt that signals temperature values - irq.disable(); - irq.set_handler(|_| { - let t = Self::regs(); - t.intenclr.write(|w| w.datardy().clear()); - WAKER.wake(); - }); - irq.enable(); - Self { _irq: irq } + unsafe { interrupt::TEMP::steal() }.unpend(); + unsafe { interrupt::TEMP::steal() }.enable(); + + Self { _peri } } /// Perform an asynchronous temperature measurement. The returned future diff --git a/examples/nrf52840/src/bin/temp.rs b/examples/nrf52840/src/bin/temp.rs index b06ac709..70957548 100644 --- a/examples/nrf52840/src/bin/temp.rs +++ b/examples/nrf52840/src/bin/temp.rs @@ -4,16 +4,19 @@ use defmt::info; use embassy_executor::Spawner; -use embassy_nrf::interrupt; use embassy_nrf::temp::Temp; +use embassy_nrf::{bind_interrupts, temp}; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + TEMP => temp::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); - let irq = interrupt::take!(TEMP); - let mut temp = Temp::new(p.TEMP, irq); + let mut temp = Temp::new(p.TEMP, Irqs); loop { let value = temp.read().await; From 5913553cb1e95431665d3370dce8154a6869e434 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 5 Mar 2023 22:14:59 +0100 Subject: [PATCH 49/51] nrf/twis: switch to new interrupt binding. --- embassy-nrf/src/twis.rs | 55 +++++++++++++++++-------------- examples/nrf52840/src/bin/twis.rs | 12 ++++--- 2 files changed, 38 insertions(+), 29 deletions(-) diff --git a/embassy-nrf/src/twis.rs b/embassy-nrf/src/twis.rs index c514d9f2..bfa30b04 100644 --- a/embassy-nrf/src/twis.rs +++ b/embassy-nrf/src/twis.rs @@ -3,6 +3,7 @@ #![macro_use] use core::future::{poll_fn, Future}; +use core::marker::PhantomData; use core::sync::atomic::compiler_fence; use core::sync::atomic::Ordering::SeqCst; use core::task::Poll; @@ -14,7 +15,7 @@ use embassy_time::{Duration, Instant}; use crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE}; use crate::gpio::Pin as GpioPin; -use crate::interrupt::{Interrupt, InterruptExt}; +use crate::interrupt::{self, Interrupt, InterruptExt}; use crate::util::slice_in_ram_or; use crate::{gpio, pac, Peripheral}; @@ -108,6 +109,31 @@ pub enum Command { Write(usize), } +/// Interrupt handler. +pub struct InterruptHandler { + _phantom: PhantomData, +} + +impl interrupt::Handler for InterruptHandler { + unsafe fn on_interrupt() { + let r = T::regs(); + let s = T::state(); + + if r.events_read.read().bits() != 0 || r.events_write.read().bits() != 0 { + s.waker.wake(); + r.intenclr.modify(|_r, w| w.read().clear().write().clear()); + } + if r.events_stopped.read().bits() != 0 { + s.waker.wake(); + r.intenclr.modify(|_r, w| w.stopped().clear()); + } + if r.events_error.read().bits() != 0 { + s.waker.wake(); + r.intenclr.modify(|_r, w| w.error().clear()); + } + } +} + /// TWIS driver. pub struct Twis<'d, T: Instance> { _p: PeripheralRef<'d, T>, @@ -117,12 +143,12 @@ impl<'d, T: Instance> Twis<'d, T> { /// Create a new TWIS driver. pub fn new( twis: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, sda: impl Peripheral

+ 'd, scl: impl Peripheral

+ 'd, config: Config, ) -> Self { - into_ref!(twis, irq, sda, scl); + into_ref!(twis, sda, scl); let r = T::regs(); @@ -178,31 +204,12 @@ impl<'d, T: Instance> Twis<'d, T> { // Generate suspend on read event r.shorts.write(|w| w.read_suspend().enabled()); - irq.set_handler(Self::on_interrupt); - irq.unpend(); - irq.enable(); + unsafe { T::Interrupt::steal() }.unpend(); + unsafe { T::Interrupt::steal() }.enable(); Self { _p: twis } } - fn on_interrupt(_: *mut ()) { - let r = T::regs(); - let s = T::state(); - - if r.events_read.read().bits() != 0 || r.events_write.read().bits() != 0 { - s.waker.wake(); - r.intenclr.modify(|_r, w| w.read().clear().write().clear()); - } - if r.events_stopped.read().bits() != 0 { - s.waker.wake(); - r.intenclr.modify(|_r, w| w.stopped().clear()); - } - if r.events_error.read().bits() != 0 { - s.waker.wake(); - r.intenclr.modify(|_r, w| w.error().clear()); - } - } - /// Set TX buffer, checking that it is in RAM and has suitable length. unsafe fn set_tx_buffer(&mut self, buffer: &[u8]) -> Result<(), Error> { slice_in_ram_or(buffer, Error::BufferNotInRAM)?; diff --git a/examples/nrf52840/src/bin/twis.rs b/examples/nrf52840/src/bin/twis.rs index 54cba949..aa42b679 100644 --- a/examples/nrf52840/src/bin/twis.rs +++ b/examples/nrf52840/src/bin/twis.rs @@ -6,19 +6,21 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_nrf::interrupt; use embassy_nrf::twis::{self, Command, Twis}; +use embassy_nrf::{bind_interrupts, peripherals}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0 => twis::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); - let irq = interrupt::take!(SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); let mut config = twis::Config::default(); - // Set i2c address - config.address0 = 0x55; - let mut i2c = Twis::new(p.TWISPI0, irq, p.P0_03, p.P0_04, config); + config.address0 = 0x55; // Set i2c address + let mut i2c = Twis::new(p.TWISPI0, Irqs, p.P0_03, p.P0_04, config); info!("Listening..."); loop { From 5249996d282e1ae08cea1593193e2fe6ca20880a Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 5 Mar 2023 22:36:53 +0100 Subject: [PATCH 50/51] nrf/usb: switch to new interrupt binding, fix vbus detect on nrf53. --- embassy-nrf/src/{usb.rs => usb/mod.rs} | 234 ++++-------------- embassy-nrf/src/usb/vbus_detect.rs | 177 +++++++++++++ examples/nrf52840/Cargo.toml | 7 +- examples/nrf52840/src/bin/usb_ethernet.rs | 12 +- examples/nrf52840/src/bin/usb_hid_keyboard.rs | 16 +- examples/nrf52840/src/bin/usb_hid_mouse.rs | 16 +- examples/nrf52840/src/bin/usb_serial.rs | 16 +- .../nrf52840/src/bin/usb_serial_multitask.rs | 51 ++-- .../nrf52840/src/bin/usb_serial_winusb.rs | 14 +- 9 files changed, 300 insertions(+), 243 deletions(-) rename embassy-nrf/src/{usb.rs => usb/mod.rs} (81%) create mode 100644 embassy-nrf/src/usb/vbus_detect.rs diff --git a/embassy-nrf/src/usb.rs b/embassy-nrf/src/usb/mod.rs similarity index 81% rename from embassy-nrf/src/usb.rs rename to embassy-nrf/src/usb/mod.rs index cd142f00..56de511d 100644 --- a/embassy-nrf/src/usb.rs +++ b/embassy-nrf/src/usb/mod.rs @@ -2,10 +2,12 @@ #![macro_use] +pub mod vbus_detect; + use core::future::poll_fn; use core::marker::PhantomData; use core::mem::MaybeUninit; -use core::sync::atomic::{compiler_fence, AtomicBool, AtomicU32, Ordering}; +use core::sync::atomic::{compiler_fence, AtomicU32, Ordering}; use core::task::Poll; use cortex_m::peripheral::NVIC; @@ -15,7 +17,8 @@ use embassy_usb_driver as driver; use embassy_usb_driver::{Direction, EndpointAddress, EndpointError, EndpointInfo, EndpointType, Event, Unsupported}; use pac::usbd::RegisterBlock; -use crate::interrupt::{Interrupt, InterruptExt}; +use self::vbus_detect::VbusDetect; +use crate::interrupt::{self, Interrupt, InterruptExt}; use crate::util::slice_in_ram; use crate::{pac, Peripheral}; @@ -26,185 +29,13 @@ static EP_IN_WAKERS: [AtomicWaker; 8] = [NEW_AW; 8]; static EP_OUT_WAKERS: [AtomicWaker; 8] = [NEW_AW; 8]; static READY_ENDPOINTS: AtomicU32 = AtomicU32::new(0); -/// Trait for detecting USB VBUS power. -/// -/// There are multiple ways to detect USB power. The behavior -/// here provides a hook into determining whether it is. -pub trait VbusDetect { - /// Report whether power is detected. - /// - /// This is indicated by the `USBREGSTATUS.VBUSDETECT` register, or the - /// `USBDETECTED`, `USBREMOVED` events from the `POWER` peripheral. - fn is_usb_detected(&self) -> bool; - - /// Wait until USB power is ready. - /// - /// USB power ready is indicated by the `USBREGSTATUS.OUTPUTRDY` register, or the - /// `USBPWRRDY` event from the `POWER` peripheral. - async fn wait_power_ready(&mut self) -> Result<(), ()>; +/// Interrupt handler. +pub struct InterruptHandler { + _phantom: PhantomData, } -/// [`VbusDetect`] implementation using the native hardware POWER peripheral. -/// -/// Unsuitable for usage with the nRF softdevice, since it reserves exclusive acces -/// to POWER. In that case, use [`VbusDetectSignal`]. -#[cfg(not(feature = "_nrf5340-app"))] -pub struct HardwareVbusDetect { - _private: (), -} - -static POWER_WAKER: AtomicWaker = NEW_AW; - -#[cfg(not(feature = "_nrf5340-app"))] -impl HardwareVbusDetect { - /// Create a new `VbusDetectNative`. - pub fn new(power_irq: impl Interrupt) -> Self { - let regs = unsafe { &*pac::POWER::ptr() }; - - power_irq.set_handler(Self::on_interrupt); - power_irq.unpend(); - power_irq.enable(); - - regs.intenset - .write(|w| w.usbdetected().set().usbremoved().set().usbpwrrdy().set()); - - Self { _private: () } - } - - #[cfg(not(feature = "_nrf5340-app"))] - fn on_interrupt(_: *mut ()) { - let regs = unsafe { &*pac::POWER::ptr() }; - - if regs.events_usbdetected.read().bits() != 0 { - regs.events_usbdetected.reset(); - BUS_WAKER.wake(); - } - - if regs.events_usbremoved.read().bits() != 0 { - regs.events_usbremoved.reset(); - BUS_WAKER.wake(); - POWER_WAKER.wake(); - } - - if regs.events_usbpwrrdy.read().bits() != 0 { - regs.events_usbpwrrdy.reset(); - POWER_WAKER.wake(); - } - } -} - -#[cfg(not(feature = "_nrf5340-app"))] -impl VbusDetect for HardwareVbusDetect { - fn is_usb_detected(&self) -> bool { - let regs = unsafe { &*pac::POWER::ptr() }; - regs.usbregstatus.read().vbusdetect().is_vbus_present() - } - - async fn wait_power_ready(&mut self) -> Result<(), ()> { - poll_fn(move |cx| { - POWER_WAKER.register(cx.waker()); - let regs = unsafe { &*pac::POWER::ptr() }; - - if regs.usbregstatus.read().outputrdy().is_ready() { - Poll::Ready(Ok(())) - } else if !self.is_usb_detected() { - Poll::Ready(Err(())) - } else { - Poll::Pending - } - }) - .await - } -} - -/// Software-backed [`VbusDetect`] implementation. -/// -/// This implementation does not interact with the hardware, it allows user code -/// to notify the power events by calling functions instead. -/// -/// This is suitable for use with the nRF softdevice, by calling the functions -/// when the softdevice reports power-related events. -pub struct SoftwareVbusDetect { - usb_detected: AtomicBool, - power_ready: AtomicBool, -} - -impl SoftwareVbusDetect { - /// Create a new `SoftwareVbusDetect`. - pub fn new(usb_detected: bool, power_ready: bool) -> Self { - BUS_WAKER.wake(); - - Self { - usb_detected: AtomicBool::new(usb_detected), - power_ready: AtomicBool::new(power_ready), - } - } - - /// Report whether power was detected. - /// - /// Equivalent to the `USBDETECTED`, `USBREMOVED` events from the `POWER` peripheral. - pub fn detected(&self, detected: bool) { - self.usb_detected.store(detected, Ordering::Relaxed); - self.power_ready.store(false, Ordering::Relaxed); - BUS_WAKER.wake(); - POWER_WAKER.wake(); - } - - /// Report when USB power is ready. - /// - /// Equivalent to the `USBPWRRDY` event from the `POWER` peripheral. - pub fn ready(&self) { - self.power_ready.store(true, Ordering::Relaxed); - POWER_WAKER.wake(); - } -} - -impl VbusDetect for &SoftwareVbusDetect { - fn is_usb_detected(&self) -> bool { - self.usb_detected.load(Ordering::Relaxed) - } - - async fn wait_power_ready(&mut self) -> Result<(), ()> { - poll_fn(move |cx| { - POWER_WAKER.register(cx.waker()); - - if self.power_ready.load(Ordering::Relaxed) { - Poll::Ready(Ok(())) - } else if !self.usb_detected.load(Ordering::Relaxed) { - Poll::Ready(Err(())) - } else { - Poll::Pending - } - }) - .await - } -} - -/// USB driver. -pub struct Driver<'d, T: Instance, P: VbusDetect> { - _p: PeripheralRef<'d, T>, - alloc_in: Allocator, - alloc_out: Allocator, - usb_supply: P, -} - -impl<'d, T: Instance, P: VbusDetect> Driver<'d, T, P> { - /// Create a new USB driver. - pub fn new(usb: impl Peripheral

+ 'd, irq: impl Peripheral

+ 'd, usb_supply: P) -> Self { - into_ref!(usb, irq); - irq.set_handler(Self::on_interrupt); - irq.unpend(); - irq.enable(); - - Self { - _p: usb, - alloc_in: Allocator::new(), - alloc_out: Allocator::new(), - usb_supply, - } - } - - fn on_interrupt(_: *mut ()) { +impl interrupt::Handler for InterruptHandler { + unsafe fn on_interrupt() { let regs = T::regs(); if regs.events_usbreset.read().bits() != 0 { @@ -255,11 +86,40 @@ impl<'d, T: Instance, P: VbusDetect> Driver<'d, T, P> { } } -impl<'d, T: Instance, P: VbusDetect + 'd> driver::Driver<'d> for Driver<'d, T, P> { +/// USB driver. +pub struct Driver<'d, T: Instance, V: VbusDetect> { + _p: PeripheralRef<'d, T>, + alloc_in: Allocator, + alloc_out: Allocator, + vbus_detect: V, +} + +impl<'d, T: Instance, V: VbusDetect> Driver<'d, T, V> { + /// Create a new USB driver. + pub fn new( + usb: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, + vbus_detect: V, + ) -> Self { + into_ref!(usb); + + unsafe { T::Interrupt::steal() }.unpend(); + unsafe { T::Interrupt::steal() }.enable(); + + Self { + _p: usb, + alloc_in: Allocator::new(), + alloc_out: Allocator::new(), + vbus_detect, + } + } +} + +impl<'d, T: Instance, V: VbusDetect + 'd> driver::Driver<'d> for Driver<'d, T, V> { type EndpointOut = Endpoint<'d, T, Out>; type EndpointIn = Endpoint<'d, T, In>; type ControlPipe = ControlPipe<'d, T>; - type Bus = Bus<'d, T, P>; + type Bus = Bus<'d, T, V>; fn alloc_endpoint_in( &mut self, @@ -298,7 +158,7 @@ impl<'d, T: Instance, P: VbusDetect + 'd> driver::Driver<'d> for Driver<'d, T, P Bus { _p: unsafe { self._p.clone_unchecked() }, power_available: false, - usb_supply: self.usb_supply, + vbus_detect: self.vbus_detect, }, ControlPipe { _p: self._p, @@ -309,13 +169,13 @@ impl<'d, T: Instance, P: VbusDetect + 'd> driver::Driver<'d> for Driver<'d, T, P } /// USB bus. -pub struct Bus<'d, T: Instance, P: VbusDetect> { +pub struct Bus<'d, T: Instance, V: VbusDetect> { _p: PeripheralRef<'d, T>, power_available: bool, - usb_supply: P, + vbus_detect: V, } -impl<'d, T: Instance, P: VbusDetect> driver::Bus for Bus<'d, T, P> { +impl<'d, T: Instance, V: VbusDetect> driver::Bus for Bus<'d, T, V> { async fn enable(&mut self) { let regs = T::regs(); @@ -347,7 +207,7 @@ impl<'d, T: Instance, P: VbusDetect> driver::Bus for Bus<'d, T, P> { w }); - if self.usb_supply.wait_power_ready().await.is_ok() { + if self.vbus_detect.wait_power_ready().await.is_ok() { // Enable the USB pullup, allowing enumeration. regs.usbpullup.write(|w| w.connect().enabled()); trace!("enabled"); @@ -406,7 +266,7 @@ impl<'d, T: Instance, P: VbusDetect> driver::Bus for Bus<'d, T, P> { trace!("USB event: ready"); } - if self.usb_supply.is_usb_detected() != self.power_available { + if self.vbus_detect.is_usb_detected() != self.power_available { self.power_available = !self.power_available; if self.power_available { trace!("Power event: available"); diff --git a/embassy-nrf/src/usb/vbus_detect.rs b/embassy-nrf/src/usb/vbus_detect.rs new file mode 100644 index 00000000..cecd4c59 --- /dev/null +++ b/embassy-nrf/src/usb/vbus_detect.rs @@ -0,0 +1,177 @@ +//! Trait and implementations for performing VBUS detection. + +use core::future::poll_fn; +use core::sync::atomic::{AtomicBool, Ordering}; +use core::task::Poll; + +use embassy_sync::waitqueue::AtomicWaker; + +use super::BUS_WAKER; +use crate::interrupt::{self, Interrupt, InterruptExt}; +use crate::pac; + +/// Trait for detecting USB VBUS power. +/// +/// There are multiple ways to detect USB power. The behavior +/// here provides a hook into determining whether it is. +pub trait VbusDetect { + /// Report whether power is detected. + /// + /// This is indicated by the `USBREGSTATUS.VBUSDETECT` register, or the + /// `USBDETECTED`, `USBREMOVED` events from the `POWER` peripheral. + fn is_usb_detected(&self) -> bool; + + /// Wait until USB power is ready. + /// + /// USB power ready is indicated by the `USBREGSTATUS.OUTPUTRDY` register, or the + /// `USBPWRRDY` event from the `POWER` peripheral. + async fn wait_power_ready(&mut self) -> Result<(), ()>; +} + +#[cfg(not(feature = "_nrf5340"))] +type UsbRegIrq = interrupt::POWER_CLOCK; +#[cfg(feature = "_nrf5340")] +type UsbRegIrq = interrupt::USBREGULATOR; + +#[cfg(not(feature = "_nrf5340"))] +type UsbRegPeri = pac::POWER; +#[cfg(feature = "_nrf5340")] +type UsbRegPeri = pac::USBREGULATOR; + +/// Interrupt handler. +pub struct InterruptHandler { + _private: (), +} + +impl interrupt::Handler for InterruptHandler { + unsafe fn on_interrupt() { + let regs = unsafe { &*UsbRegPeri::ptr() }; + + if regs.events_usbdetected.read().bits() != 0 { + regs.events_usbdetected.reset(); + BUS_WAKER.wake(); + } + + if regs.events_usbremoved.read().bits() != 0 { + regs.events_usbremoved.reset(); + BUS_WAKER.wake(); + POWER_WAKER.wake(); + } + + if regs.events_usbpwrrdy.read().bits() != 0 { + regs.events_usbpwrrdy.reset(); + POWER_WAKER.wake(); + } + } +} + +/// [`VbusDetect`] implementation using the native hardware POWER peripheral. +/// +/// Unsuitable for usage with the nRF softdevice, since it reserves exclusive acces +/// to POWER. In that case, use [`VbusDetectSignal`]. +pub struct HardwareVbusDetect { + _private: (), +} + +static POWER_WAKER: AtomicWaker = AtomicWaker::new(); + +impl HardwareVbusDetect { + /// Create a new `VbusDetectNative`. + pub fn new(_irq: impl interrupt::Binding + 'static) -> Self { + let regs = unsafe { &*UsbRegPeri::ptr() }; + + unsafe { UsbRegIrq::steal() }.unpend(); + unsafe { UsbRegIrq::steal() }.enable(); + + regs.intenset + .write(|w| w.usbdetected().set().usbremoved().set().usbpwrrdy().set()); + + Self { _private: () } + } +} + +impl VbusDetect for HardwareVbusDetect { + fn is_usb_detected(&self) -> bool { + let regs = unsafe { &*UsbRegPeri::ptr() }; + regs.usbregstatus.read().vbusdetect().is_vbus_present() + } + + async fn wait_power_ready(&mut self) -> Result<(), ()> { + poll_fn(move |cx| { + POWER_WAKER.register(cx.waker()); + let regs = unsafe { &*UsbRegPeri::ptr() }; + + if regs.usbregstatus.read().outputrdy().is_ready() { + Poll::Ready(Ok(())) + } else if !self.is_usb_detected() { + Poll::Ready(Err(())) + } else { + Poll::Pending + } + }) + .await + } +} + +/// Software-backed [`VbusDetect`] implementation. +/// +/// This implementation does not interact with the hardware, it allows user code +/// to notify the power events by calling functions instead. +/// +/// This is suitable for use with the nRF softdevice, by calling the functions +/// when the softdevice reports power-related events. +pub struct SoftwareVbusDetect { + usb_detected: AtomicBool, + power_ready: AtomicBool, +} + +impl SoftwareVbusDetect { + /// Create a new `SoftwareVbusDetect`. + pub fn new(usb_detected: bool, power_ready: bool) -> Self { + BUS_WAKER.wake(); + + Self { + usb_detected: AtomicBool::new(usb_detected), + power_ready: AtomicBool::new(power_ready), + } + } + + /// Report whether power was detected. + /// + /// Equivalent to the `USBDETECTED`, `USBREMOVED` events from the `POWER` peripheral. + pub fn detected(&self, detected: bool) { + self.usb_detected.store(detected, Ordering::Relaxed); + self.power_ready.store(false, Ordering::Relaxed); + BUS_WAKER.wake(); + POWER_WAKER.wake(); + } + + /// Report when USB power is ready. + /// + /// Equivalent to the `USBPWRRDY` event from the `POWER` peripheral. + pub fn ready(&self) { + self.power_ready.store(true, Ordering::Relaxed); + POWER_WAKER.wake(); + } +} + +impl VbusDetect for &SoftwareVbusDetect { + fn is_usb_detected(&self) -> bool { + self.usb_detected.load(Ordering::Relaxed) + } + + async fn wait_power_ready(&mut self) -> Result<(), ()> { + poll_fn(move |cx| { + POWER_WAKER.register(cx.waker()); + + if self.power_ready.load(Ordering::Relaxed) { + Poll::Ready(Ok(())) + } else if !self.usb_detected.load(Ordering::Relaxed) { + Poll::Ready(Err(())) + } else { + Poll::Pending + } + }) + .await + } +} diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index cfdda076..cc88d92c 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml @@ -6,7 +6,6 @@ license = "MIT OR Apache-2.0" [features] default = ["nightly"] -msos-descriptor = ["embassy-usb/msos-descriptor"] nightly = ["embassy-executor/nightly", "embassy-nrf/nightly", "embassy-net/nightly", "embassy-nrf/unstable-traits", "embassy-usb", "embedded-io/async", "embassy-net", "embassy-lora", "lorawan-device", "lorawan"] @@ -17,7 +16,7 @@ embassy-executor = { version = "0.1.0", path = "../../embassy-executor", feature embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"], optional = true } -embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"], optional = true } +embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt", "msos-descriptor",], optional = true } embedded-io = "0.4.0" embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx126x", "time", "defmt"], optional = true } @@ -36,7 +35,3 @@ rand = { version = "0.8.4", default-features = false } embedded-storage = "0.3.0" usbd-hid = "0.6.0" serde = { version = "1.0.136", default-features = false } - -[[bin]] -name = "usb_serial_winusb" -required-features = ["msos-descriptor"] diff --git a/examples/nrf52840/src/bin/usb_ethernet.rs b/examples/nrf52840/src/bin/usb_ethernet.rs index 083a1cbb..b8a72313 100644 --- a/examples/nrf52840/src/bin/usb_ethernet.rs +++ b/examples/nrf52840/src/bin/usb_ethernet.rs @@ -9,8 +9,9 @@ use embassy_executor::Spawner; use embassy_net::tcp::TcpSocket; use embassy_net::{Stack, StackResources}; use embassy_nrf::rng::Rng; -use embassy_nrf::usb::{Driver, HardwareVbusDetect}; -use embassy_nrf::{bind_interrupts, interrupt, pac, peripherals, rng}; +use embassy_nrf::usb::vbus_detect::HardwareVbusDetect; +use embassy_nrf::usb::Driver; +use embassy_nrf::{bind_interrupts, pac, peripherals, rng, usb}; use embassy_usb::class::cdc_ncm::embassy_net::{Device, Runner, State as NetState}; use embassy_usb::class::cdc_ncm::{CdcNcmClass, State}; use embassy_usb::{Builder, Config, UsbDevice}; @@ -19,6 +20,8 @@ use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { + USBD => usb::InterruptHandler; + POWER_CLOCK => usb::vbus_detect::InterruptHandler; RNG => rng::InterruptHandler; }); @@ -60,9 +63,7 @@ async fn main(spawner: Spawner) { while clock.events_hfclkstarted.read().bits() != 1 {} // Create the driver, from the HAL. - let irq = interrupt::take!(USBD); - let power_irq = interrupt::take!(POWER_CLOCK); - let driver = Driver::new(p.USBD, irq, HardwareVbusDetect::new(power_irq)); + let driver = Driver::new(p.USBD, Irqs, HardwareVbusDetect::new(Irqs)); // Create embassy-usb Config let mut config = Config::new(0xc0de, 0xcafe); @@ -86,6 +87,7 @@ async fn main(spawner: Spawner) { &mut singleton!([0; 256])[..], &mut singleton!([0; 256])[..], &mut singleton!([0; 128])[..], + &mut singleton!([0; 128])[..], ); // Our MAC addr. diff --git a/examples/nrf52840/src/bin/usb_hid_keyboard.rs b/examples/nrf52840/src/bin/usb_hid_keyboard.rs index 3d8a114c..7ccd2946 100644 --- a/examples/nrf52840/src/bin/usb_hid_keyboard.rs +++ b/examples/nrf52840/src/bin/usb_hid_keyboard.rs @@ -10,8 +10,9 @@ use embassy_executor::Spawner; use embassy_futures::join::join; use embassy_futures::select::{select, Either}; use embassy_nrf::gpio::{Input, Pin, Pull}; -use embassy_nrf::usb::{Driver, HardwareVbusDetect}; -use embassy_nrf::{interrupt, pac}; +use embassy_nrf::usb::vbus_detect::HardwareVbusDetect; +use embassy_nrf::usb::Driver; +use embassy_nrf::{bind_interrupts, pac, peripherals, usb}; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::signal::Signal; use embassy_usb::class::hid::{HidReaderWriter, ReportId, RequestHandler, State}; @@ -20,6 +21,11 @@ use embassy_usb::{Builder, Config, Handler}; use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + USBD => usb::InterruptHandler; + POWER_CLOCK => usb::vbus_detect::InterruptHandler; +}); + static SUSPENDED: AtomicBool = AtomicBool::new(false); #[embassy_executor::main] @@ -32,9 +38,7 @@ async fn main(_spawner: Spawner) { while clock.events_hfclkstarted.read().bits() != 1 {} // Create the driver, from the HAL. - let irq = interrupt::take!(USBD); - let power_irq = interrupt::take!(POWER_CLOCK); - let driver = Driver::new(p.USBD, irq, HardwareVbusDetect::new(power_irq)); + let driver = Driver::new(p.USBD, Irqs, HardwareVbusDetect::new(Irqs)); // Create embassy-usb Config let mut config = Config::new(0xc0de, 0xcafe); @@ -50,6 +54,7 @@ async fn main(_spawner: Spawner) { let mut device_descriptor = [0; 256]; let mut config_descriptor = [0; 256]; let mut bos_descriptor = [0; 256]; + let mut msos_descriptor = [0; 256]; let mut control_buf = [0; 64]; let request_handler = MyRequestHandler {}; let mut device_handler = MyDeviceHandler::new(); @@ -62,6 +67,7 @@ async fn main(_spawner: Spawner) { &mut device_descriptor, &mut config_descriptor, &mut bos_descriptor, + &mut msos_descriptor, &mut control_buf, ); diff --git a/examples/nrf52840/src/bin/usb_hid_mouse.rs b/examples/nrf52840/src/bin/usb_hid_mouse.rs index d7c9d55b..edf634a5 100644 --- a/examples/nrf52840/src/bin/usb_hid_mouse.rs +++ b/examples/nrf52840/src/bin/usb_hid_mouse.rs @@ -7,8 +7,9 @@ use core::mem; use defmt::*; use embassy_executor::Spawner; use embassy_futures::join::join; -use embassy_nrf::usb::{Driver, HardwareVbusDetect}; -use embassy_nrf::{interrupt, pac}; +use embassy_nrf::usb::vbus_detect::HardwareVbusDetect; +use embassy_nrf::usb::Driver; +use embassy_nrf::{bind_interrupts, pac, peripherals, usb}; use embassy_time::{Duration, Timer}; use embassy_usb::class::hid::{HidWriter, ReportId, RequestHandler, State}; use embassy_usb::control::OutResponse; @@ -16,6 +17,11 @@ use embassy_usb::{Builder, Config}; use usbd_hid::descriptor::{MouseReport, SerializedDescriptor}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + USBD => usb::InterruptHandler; + POWER_CLOCK => usb::vbus_detect::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); @@ -26,9 +32,7 @@ async fn main(_spawner: Spawner) { while clock.events_hfclkstarted.read().bits() != 1 {} // Create the driver, from the HAL. - let irq = interrupt::take!(USBD); - let power_irq = interrupt::take!(POWER_CLOCK); - let driver = Driver::new(p.USBD, irq, HardwareVbusDetect::new(power_irq)); + let driver = Driver::new(p.USBD, Irqs, HardwareVbusDetect::new(Irqs)); // Create embassy-usb Config let mut config = Config::new(0xc0de, 0xcafe); @@ -43,6 +47,7 @@ async fn main(_spawner: Spawner) { let mut device_descriptor = [0; 256]; let mut config_descriptor = [0; 256]; let mut bos_descriptor = [0; 256]; + let mut msos_descriptor = [0; 256]; let mut control_buf = [0; 64]; let request_handler = MyRequestHandler {}; @@ -54,6 +59,7 @@ async fn main(_spawner: Spawner) { &mut device_descriptor, &mut config_descriptor, &mut bos_descriptor, + &mut msos_descriptor, &mut control_buf, ); diff --git a/examples/nrf52840/src/bin/usb_serial.rs b/examples/nrf52840/src/bin/usb_serial.rs index 102d7ea6..9727a4f5 100644 --- a/examples/nrf52840/src/bin/usb_serial.rs +++ b/examples/nrf52840/src/bin/usb_serial.rs @@ -7,13 +7,19 @@ use core::mem; use defmt::{info, panic}; use embassy_executor::Spawner; use embassy_futures::join::join; -use embassy_nrf::usb::{Driver, HardwareVbusDetect, Instance, VbusDetect}; -use embassy_nrf::{interrupt, pac}; +use embassy_nrf::usb::vbus_detect::{HardwareVbusDetect, VbusDetect}; +use embassy_nrf::usb::{Driver, Instance}; +use embassy_nrf::{bind_interrupts, pac, peripherals, usb}; use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; use embassy_usb::driver::EndpointError; use embassy_usb::{Builder, Config}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + USBD => usb::InterruptHandler; + POWER_CLOCK => usb::vbus_detect::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); @@ -24,9 +30,7 @@ async fn main(_spawner: Spawner) { while clock.events_hfclkstarted.read().bits() != 1 {} // Create the driver, from the HAL. - let irq = interrupt::take!(USBD); - let power_irq = interrupt::take!(POWER_CLOCK); - let driver = Driver::new(p.USBD, irq, HardwareVbusDetect::new(power_irq)); + let driver = Driver::new(p.USBD, Irqs, HardwareVbusDetect::new(Irqs)); // Create embassy-usb Config let mut config = Config::new(0xc0de, 0xcafe); @@ -48,6 +52,7 @@ async fn main(_spawner: Spawner) { let mut device_descriptor = [0; 256]; let mut config_descriptor = [0; 256]; let mut bos_descriptor = [0; 256]; + let mut msos_descriptor = [0; 256]; let mut control_buf = [0; 64]; let mut state = State::new(); @@ -58,6 +63,7 @@ async fn main(_spawner: Spawner) { &mut device_descriptor, &mut config_descriptor, &mut bos_descriptor, + &mut msos_descriptor, &mut control_buf, ); diff --git a/examples/nrf52840/src/bin/usb_serial_multitask.rs b/examples/nrf52840/src/bin/usb_serial_multitask.rs index 558d4ba6..6da2c2a2 100644 --- a/examples/nrf52840/src/bin/usb_serial_multitask.rs +++ b/examples/nrf52840/src/bin/usb_serial_multitask.rs @@ -6,14 +6,29 @@ use core::mem; use defmt::{info, panic, unwrap}; use embassy_executor::Spawner; -use embassy_nrf::usb::{Driver, HardwareVbusDetect}; -use embassy_nrf::{interrupt, pac, peripherals}; +use embassy_nrf::usb::vbus_detect::HardwareVbusDetect; +use embassy_nrf::usb::Driver; +use embassy_nrf::{bind_interrupts, pac, peripherals, usb}; use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; use embassy_usb::driver::EndpointError; use embassy_usb::{Builder, Config, UsbDevice}; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + USBD => usb::InterruptHandler; + POWER_CLOCK => usb::vbus_detect::InterruptHandler; +}); + +macro_rules! singleton { + ($val:expr) => {{ + type T = impl Sized; + static STATIC_CELL: StaticCell = StaticCell::new(); + let (x,) = STATIC_CELL.init(($val,)); + x + }}; +} + type MyDriver = Driver<'static, peripherals::USBD, HardwareVbusDetect>; #[embassy_executor::task] @@ -39,10 +54,9 @@ async fn main(spawner: Spawner) { info!("Enabling ext hfosc..."); clock.tasks_hfclkstart.write(|w| unsafe { w.bits(1) }); while clock.events_hfclkstarted.read().bits() != 1 {} + // Create the driver, from the HAL. - let irq = interrupt::take!(USBD); - let power_irq = interrupt::take!(POWER_CLOCK); - let driver = Driver::new(p.USBD, irq, HardwareVbusDetect::new(power_irq)); + let driver = Driver::new(p.USBD, Irqs, HardwareVbusDetect::new(Irqs)); // Create embassy-usb Config let mut config = Config::new(0xc0de, 0xcafe); @@ -59,34 +73,21 @@ async fn main(spawner: Spawner) { config.device_protocol = 0x01; config.composite_with_iads = true; - struct Resources { - device_descriptor: [u8; 256], - config_descriptor: [u8; 256], - bos_descriptor: [u8; 256], - control_buf: [u8; 64], - serial_state: State<'static>, - } - static RESOURCES: StaticCell = StaticCell::new(); - let res = RESOURCES.init(Resources { - device_descriptor: [0; 256], - config_descriptor: [0; 256], - bos_descriptor: [0; 256], - control_buf: [0; 64], - serial_state: State::new(), - }); + let state = singleton!(State::new()); // Create embassy-usb DeviceBuilder using the driver and config. let mut builder = Builder::new( driver, config, - &mut res.device_descriptor, - &mut res.config_descriptor, - &mut res.bos_descriptor, - &mut res.control_buf, + &mut singleton!([0; 256])[..], + &mut singleton!([0; 256])[..], + &mut singleton!([0; 256])[..], + &mut singleton!([0; 128])[..], + &mut singleton!([0; 128])[..], ); // Create classes on the builder. - let class = CdcAcmClass::new(&mut builder, &mut res.serial_state, 64); + let class = CdcAcmClass::new(&mut builder, state, 64); // Build the builder. let usb = builder.build(); diff --git a/examples/nrf52840/src/bin/usb_serial_winusb.rs b/examples/nrf52840/src/bin/usb_serial_winusb.rs index 6561fc3b..6e4f71a4 100644 --- a/examples/nrf52840/src/bin/usb_serial_winusb.rs +++ b/examples/nrf52840/src/bin/usb_serial_winusb.rs @@ -7,8 +7,9 @@ use core::mem; use defmt::{info, panic}; use embassy_executor::Spawner; use embassy_futures::join::join; -use embassy_nrf::usb::{Driver, HardwareVbusDetect, Instance, VbusDetect}; -use embassy_nrf::{interrupt, pac}; +use embassy_nrf::usb::vbus_detect::{HardwareVbusDetect, VbusDetect}; +use embassy_nrf::usb::{Driver, Instance}; +use embassy_nrf::{bind_interrupts, pac, peripherals, usb}; use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; use embassy_usb::driver::EndpointError; use embassy_usb::msos::{self, windows_version}; @@ -16,6 +17,11 @@ use embassy_usb::types::InterfaceNumber; use embassy_usb::{Builder, Config}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + USBD => usb::InterruptHandler; + POWER_CLOCK => usb::vbus_detect::InterruptHandler; +}); + // This is a randomly generated GUID to allow clients on Windows to find our device const DEVICE_INTERFACE_GUIDS: &[&str] = &["{EAA9A5DC-30BA-44BC-9232-606CDC875321}"]; @@ -29,9 +35,7 @@ async fn main(_spawner: Spawner) { while clock.events_hfclkstarted.read().bits() != 1 {} // Create the driver, from the HAL. - let irq = interrupt::take!(USBD); - let power_irq = interrupt::take!(POWER_CLOCK); - let driver = Driver::new(p.USBD, irq, HardwareVbusDetect::new(power_irq)); + let driver = Driver::new(p.USBD, Irqs, HardwareVbusDetect::new(Irqs)); // Create embassy-usb Config let mut config = Config::new(0xc0de, 0xcafe); From f5e09a8f4a6d7b1bd6723c60915cb9f29cf352f7 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 5 Mar 2023 22:40:53 +0100 Subject: [PATCH 51/51] nrf/interrupt: do not reexport `take!` macro. --- embassy-nrf/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index ee29ce20..cb629902 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -100,7 +100,7 @@ mod chip; pub mod interrupt { //! Interrupt definitions and macros to bind them. pub use cortex_m::interrupt::{CriticalSection, Mutex}; - pub use embassy_cortex_m::interrupt::*; + pub use embassy_cortex_m::interrupt::{Binding, Handler, Interrupt, InterruptExt, Priority}; pub use crate::chip::irqs::*;