From 040fffd667f8bdc089df828783aa0a76a06d4b5a Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 10 May 2021 17:15:41 +0200 Subject: [PATCH 1/7] Don't use -eabihf --- .vscode/settings.json | 4 ++-- embassy-stm32-examples/.cargo/config.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index dc200f79e..8a292c0be 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -4,8 +4,8 @@ "rust-analyzer.cargo.allFeatures": false, "rust-analyzer.checkOnSave.allFeatures": false, "rust-analyzer.checkOnSave.allTargets": false, - "rust-analyzer.cargo.target": "thumbv7em-none-eabihf", - "rust-analyzer.checkOnSave.target": "thumbv7em-none-eabihf", + "rust-analyzer.cargo.target": "thumbv7em-none-eabi", + "rust-analyzer.checkOnSave.target": "thumbv7em-none-eabi", "rust-analyzer.procMacro.enable": true, "rust-analyzer.cargo.loadOutDirsFromCheck": true, "files.watcherExclude": { diff --git a/embassy-stm32-examples/.cargo/config.toml b/embassy-stm32-examples/.cargo/config.toml index 7c1d4dfb6..3ccca879d 100644 --- a/embassy-stm32-examples/.cargo/config.toml +++ b/embassy-stm32-examples/.cargo/config.toml @@ -25,4 +25,4 @@ rustflags = [ ] [build] -target = "thumbv7em-none-eabihf" +target = "thumbv7em-none-eabi" From dda338cedb6b82a8d7cd75d18a8ebc6dfd46f53a Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Mon, 10 May 2021 15:45:40 +0200 Subject: [PATCH 2/7] Add implementation of ReadUntilIdle for nRF UART Add type UarteWithIdle that implements Read, Write and ReadUntilIdle traits. The type uses a timer + 2 PPI channels internally, triggered on RTXSTARTED event. --- embassy-nrf/src/uarte.rs | 159 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 158 insertions(+), 1 deletion(-) diff --git a/embassy-nrf/src/uarte.rs b/embassy-nrf/src/uarte.rs index a02f7c347..0f574ba36 100644 --- a/embassy-nrf/src/uarte.rs +++ b/embassy-nrf/src/uarte.rs @@ -5,7 +5,7 @@ use core::marker::PhantomData; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; use embassy::interrupt::InterruptExt; -use embassy::traits::uart::{Error, Read, Write}; +use embassy::traits::uart::{Error, Read, ReadUntilIdle, Write}; use embassy::util::{AtomicWaker, OnDrop, Unborrow}; use embassy_extras::unborrow; use futures::future::poll_fn; @@ -17,7 +17,9 @@ use crate::interrupt; use crate::interrupt::Interrupt; use crate::pac; use crate::peripherals; +use crate::ppi::{AnyConfigurableChannel, ConfigurableChannel, Event, Ppi, Task}; use crate::target_constants::EASY_DMA_SIZE; +use crate::timer::Instance as TimerInstance; // 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}; @@ -281,6 +283,161 @@ impl<'d, T: Instance> Write for Uarte<'d, T> { } } +/// Interface to an UARTE peripheral that uses timers and PPI to emulate +/// ReadUntilIdle. +pub struct UarteWithIdle<'d, U: Instance, T: TimerInstance> { + uarte: Uarte<'d, U>, + timer: T, + _ppi_ch1: Ppi<'d, AnyConfigurableChannel>, + _ppi_ch2: Ppi<'d, AnyConfigurableChannel>, +} + +impl<'d, U: Instance, T: TimerInstance> UarteWithIdle<'d, U, T> { + /// Creates the interface to a UARTE instance. + /// Sets the baud rate, parity and assigns the pins to the UARTE peripheral. + /// + /// # Safety + /// + /// The returned API is safe unless you use `mem::forget` (or similar safe mechanisms) + /// on stack allocated buffers which which have been passed to [`send()`](Uarte::send) + /// or [`receive`](Uarte::receive). + #[allow(unused_unsafe)] + pub unsafe fn new( + uarte: impl Unborrow + 'd, + timer: impl Unborrow + 'd, + ppi_ch1: impl Unborrow + 'd, + ppi_ch2: impl Unborrow + 'd, + irq: impl Unborrow + 'd, + rxd: impl Unborrow + 'd, + txd: impl Unborrow + 'd, + cts: impl Unborrow + 'd, + rts: impl Unborrow + 'd, + config: Config, + ) -> Self { + let baudrate = config.baudrate; + let uarte = Uarte::new(uarte, irq, rxd, txd, cts, rts, config); + + unborrow!(timer, ppi_ch1, ppi_ch2); + + let r = U::regs(); + let rt = timer.regs(); + + // BAUDRATE register values are `baudrate * 2^32 / 16000000` + // source: https://devzone.nordicsemi.com/f/nordic-q-a/391/uart-baudrate-register-values + // + // We want to stop RX if line is idle for 2 bytes worth of time + // That is 20 bits (each byte is 1 start bit + 8 data bits + 1 stop bit) + // This gives us the amount of 16M ticks for 20 bits. + let timeout = 0x8000_0000 / (baudrate as u32 / 40); + + rt.tasks_stop.write(|w| unsafe { w.bits(1) }); + rt.bitmode.write(|w| w.bitmode()._32bit()); + rt.prescaler.write(|w| unsafe { w.prescaler().bits(0) }); + rt.cc[0].write(|w| unsafe { w.bits(timeout) }); + rt.mode.write(|w| w.mode().timer()); + rt.shorts.write(|w| { + w.compare0_clear().set_bit(); + w.compare0_stop().set_bit(); + w + }); + + let mut ppi_ch1 = Ppi::new(ppi_ch1.degrade_configurable()); + ppi_ch1.set_event(Event::from_reg(&r.events_rxstarted)); + ppi_ch1.set_task(Task::from_reg(&rt.tasks_clear)); + ppi_ch1.set_fork_task(Task::from_reg(&rt.tasks_start)); + ppi_ch1.enable(); + + let mut ppi_ch2 = Ppi::new(ppi_ch2.degrade_configurable()); + ppi_ch2.set_event(Event::from_reg(&rt.events_compare[0])); + ppi_ch2.set_task(Task::from_reg(&r.tasks_stoprx)); + ppi_ch2.enable(); + + Self { + uarte, + timer, + _ppi_ch1: ppi_ch1, + _ppi_ch2: ppi_ch2, + } + } +} + +impl<'d, U: Instance, T: TimerInstance> ReadUntilIdle for UarteWithIdle<'d, U, T> { + #[rustfmt::skip] + type ReadUntilIdleFuture<'a> where Self: 'a = impl Future> + 'a; + fn read_until_idle<'a>(&'a mut self, rx_buffer: &'a mut [u8]) -> Self::ReadUntilIdleFuture<'a> { + async move { + let ptr = rx_buffer.as_ptr(); + let len = rx_buffer.len(); + assert!(len <= EASY_DMA_SIZE); + + let r = U::regs(); + let s = U::state(); + + let rt = self.timer.regs(); + + let drop = OnDrop::new(move || { + info!("read drop: stopping"); + + rt.tasks_stop.write(|w| unsafe { w.bits(1) }); + + r.intenclr.write(|w| w.endrx().clear()); + r.events_rxto.reset(); + r.tasks_stoprx.write(|w| unsafe { w.bits(1) }); + + while r.events_endrx.read().bits() == 0 {} + + info!("read drop: stopped"); + }); + + r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) }); + r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) }); + + r.events_endrx.reset(); + r.intenset.write(|w| w.endrx().set()); + + compiler_fence(Ordering::SeqCst); + + trace!("startrx"); + r.tasks_startrx.write(|w| unsafe { w.bits(1) }); + + let n: usize = poll_fn(|cx| { + s.endrx_waker.register(cx.waker()); + if r.events_endrx.read().bits() != 0 { + let n: usize = r.rxd.amount.read().amount().bits() as usize; + return Poll::Ready(n); + } + Poll::Pending + }) + .await; + + compiler_fence(Ordering::SeqCst); + r.events_rxstarted.reset(); + // Stop timer + rt.tasks_stop.write(|w| unsafe { w.bits(1) }); + drop.defuse(); + + Ok(n) + } + } +} + +impl<'d, U: Instance, T: TimerInstance> Read for UarteWithIdle<'d, U, T> { + #[rustfmt::skip] + type ReadFuture<'a> where Self: 'a = impl Future> + 'a; + fn read<'a>(&'a mut self, rx_buffer: &'a mut [u8]) -> Self::ReadFuture<'a> { + self.uarte.read(rx_buffer) + } +} + +impl<'d, U: Instance, T: TimerInstance> Write for UarteWithIdle<'d, U, T> { + #[rustfmt::skip] + type WriteFuture<'a> where Self: 'a = impl Future> + 'a; + + fn write<'a>(&'a mut self, tx_buffer: &'a [u8]) -> Self::WriteFuture<'a> { + self.uarte.write(tx_buffer) + } +} + mod sealed { use super::*; From 5afe01617a8fe9e46b4bde47a3dfa24e737ae9fa Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Mon, 10 May 2021 20:13:08 +0200 Subject: [PATCH 3/7] Use rxdrdy as trigger for timer --- embassy-nrf/src/uarte.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-nrf/src/uarte.rs b/embassy-nrf/src/uarte.rs index 0f574ba36..7172f5033 100644 --- a/embassy-nrf/src/uarte.rs +++ b/embassy-nrf/src/uarte.rs @@ -342,7 +342,7 @@ impl<'d, U: Instance, T: TimerInstance> UarteWithIdle<'d, U, T> { }); let mut ppi_ch1 = Ppi::new(ppi_ch1.degrade_configurable()); - ppi_ch1.set_event(Event::from_reg(&r.events_rxstarted)); + ppi_ch1.set_event(Event::from_reg(&r.events_rxdrdy)); ppi_ch1.set_task(Task::from_reg(&rt.tasks_clear)); ppi_ch1.set_fork_task(Task::from_reg(&rt.tasks_start)); ppi_ch1.enable(); From 687dda106f5090a3c056f503827d0fec3b8b2fa5 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Mon, 10 May 2021 20:16:13 +0200 Subject: [PATCH 4/7] Read rx amount outside of poll_fn --- embassy-nrf/src/uarte.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/embassy-nrf/src/uarte.rs b/embassy-nrf/src/uarte.rs index 7172f5033..36c9e6b23 100644 --- a/embassy-nrf/src/uarte.rs +++ b/embassy-nrf/src/uarte.rs @@ -400,11 +400,10 @@ impl<'d, U: Instance, T: TimerInstance> ReadUntilIdle for UarteWithIdle<'d, U, T trace!("startrx"); r.tasks_startrx.write(|w| unsafe { w.bits(1) }); - let n: usize = poll_fn(|cx| { + poll_fn(|cx| { s.endrx_waker.register(cx.waker()); if r.events_endrx.read().bits() != 0 { - let n: usize = r.rxd.amount.read().amount().bits() as usize; - return Poll::Ready(n); + return Poll::Ready(()); } Poll::Pending }) @@ -412,6 +411,8 @@ impl<'d, U: Instance, T: TimerInstance> ReadUntilIdle for UarteWithIdle<'d, U, T compiler_fence(Ordering::SeqCst); r.events_rxstarted.reset(); + let n = r.rxd.amount.read().amount().bits() as usize; + // Stop timer rt.tasks_stop.write(|w| unsafe { w.bits(1) }); drop.defuse(); From e9eb3ce6c1908710513e8876b42c28024287ce11 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Mon, 10 May 2021 20:16:52 +0200 Subject: [PATCH 5/7] Disable PPI trigger during normal read --- embassy-nrf/src/uarte.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/embassy-nrf/src/uarte.rs b/embassy-nrf/src/uarte.rs index 36c9e6b23..b814b9ae9 100644 --- a/embassy-nrf/src/uarte.rs +++ b/embassy-nrf/src/uarte.rs @@ -288,7 +288,7 @@ impl<'d, T: Instance> Write for Uarte<'d, T> { pub struct UarteWithIdle<'d, U: Instance, T: TimerInstance> { uarte: Uarte<'d, U>, timer: T, - _ppi_ch1: Ppi<'d, AnyConfigurableChannel>, + ppi_ch1: Ppi<'d, AnyConfigurableChannel>, _ppi_ch2: Ppi<'d, AnyConfigurableChannel>, } @@ -355,7 +355,7 @@ impl<'d, U: Instance, T: TimerInstance> UarteWithIdle<'d, U, T> { Self { uarte, timer, - _ppi_ch1: ppi_ch1, + ppi_ch1: ppi_ch1, _ppi_ch2: ppi_ch2, } } @@ -410,11 +410,12 @@ impl<'d, U: Instance, T: TimerInstance> ReadUntilIdle for UarteWithIdle<'d, U, T .await; compiler_fence(Ordering::SeqCst); - r.events_rxstarted.reset(); let n = r.rxd.amount.read().amount().bits() as usize; // Stop timer rt.tasks_stop.write(|w| unsafe { w.bits(1) }); + r.events_rxstarted.reset(); + drop.defuse(); Ok(n) @@ -426,7 +427,12 @@ impl<'d, U: Instance, T: TimerInstance> Read for UarteWithIdle<'d, U, T> { #[rustfmt::skip] type ReadFuture<'a> where Self: 'a = impl Future> + 'a; fn read<'a>(&'a mut self, rx_buffer: &'a mut [u8]) -> Self::ReadFuture<'a> { - self.uarte.read(rx_buffer) + async move { + self.ppi_ch1.disable(); + let result = self.uarte.read(rx_buffer).await; + self.ppi_ch1.enable(); + result + } } } From 0a3c2365107999b785eb74c9a7d2ff4da2377bb9 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Mon, 10 May 2021 20:20:35 +0200 Subject: [PATCH 6/7] Improve comment --- embassy-nrf/src/uarte.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-nrf/src/uarte.rs b/embassy-nrf/src/uarte.rs index b814b9ae9..04907fb56 100644 --- a/embassy-nrf/src/uarte.rs +++ b/embassy-nrf/src/uarte.rs @@ -283,8 +283,8 @@ impl<'d, T: Instance> Write for Uarte<'d, T> { } } -/// Interface to an UARTE peripheral that uses timers and PPI to emulate -/// ReadUntilIdle. +/// Interface to an UARTE peripheral that uses an additional timer and two PPI channels, +/// allowing it to implement the ReadUntilIdle trait. pub struct UarteWithIdle<'d, U: Instance, T: TimerInstance> { uarte: Uarte<'d, U>, timer: T, From 95439b493f4325c38e62e669fec81bb4892f3dcd Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 10 May 2021 23:04:37 +0200 Subject: [PATCH 7/7] Add uart_idle example. --- embassy-nrf-examples/src/bin/uart_idle.rs | 49 +++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 embassy-nrf-examples/src/bin/uart_idle.rs diff --git a/embassy-nrf-examples/src/bin/uart_idle.rs b/embassy-nrf-examples/src/bin/uart_idle.rs new file mode 100644 index 000000000..54d524ae5 --- /dev/null +++ b/embassy-nrf-examples/src/bin/uart_idle.rs @@ -0,0 +1,49 @@ +#![no_std] +#![no_main] +#![feature(min_type_alias_impl_trait)] +#![feature(impl_trait_in_bindings)] +#![feature(type_alias_impl_trait)] +#![allow(incomplete_features)] + +#[path = "../example_common.rs"] +mod example_common; +use embassy_traits::uart::ReadUntilIdle; +use example_common::*; + +use defmt::panic; +use embassy::executor::Spawner; +use embassy::traits::uart::{Read, Write}; +use embassy::util::Steal; +use embassy_nrf::gpio::NoPin; +use embassy_nrf::{interrupt, uarte, Peripherals}; + +#[embassy::main] +async fn main(spawner: Spawner) { + let p = unsafe { Peripherals::steal() }; + + let mut config = uarte::Config::default(); + config.parity = uarte::Parity::EXCLUDED; + config.baudrate = uarte::Baudrate::BAUD115200; + + let irq = interrupt::take!(UARTE0_UART0); + let mut uart = unsafe { + uarte::UarteWithIdle::new( + p.UARTE0, p.TIMER0, p.PPI_CH0, p.PPI_CH1, irq, p.P0_08, p.P0_06, NoPin, NoPin, config, + ) + }; + + info!("uarte initialized!"); + + // Message must be in SRAM + let mut buf = [0; 8]; + buf.copy_from_slice(b"Hello!\r\n"); + + unwrap!(uart.write(&buf).await); + info!("wrote hello in uart!"); + + loop { + info!("reading..."); + let n = unwrap!(uart.read_until_idle(&mut buf).await); + info!("got {} bytes", n); + } +}