aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDario Nieuwenhuis <[email protected]>2021-05-10 23:11:02 +0200
committerGitHub <[email protected]>2021-05-10 23:11:02 +0200
commitf817f374b6f787338aac17ee5f655a5f2a722a80 (patch)
tree40d2d7ab6828477c54c5fa97ab91541ea754cf10
parent1703700970a716bf9b0044ee3d8cd3e15a50fcc2 (diff)
parent95439b493f4325c38e62e669fec81bb4892f3dcd (diff)
Merge pull request #169 from lulf/nrf-uart-read-until-idle
Add implementation of ReadUntilIdle for nRF UART
-rw-r--r--.vscode/settings.json4
-rw-r--r--embassy-nrf-examples/src/bin/uart_idle.rs49
-rw-r--r--embassy-nrf/src/uarte.rs166
-rw-r--r--embassy-stm32-examples/.cargo/config.toml2
4 files changed, 217 insertions, 4 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 @@
4 "rust-analyzer.cargo.allFeatures": false, 4 "rust-analyzer.cargo.allFeatures": false,
5 "rust-analyzer.checkOnSave.allFeatures": false, 5 "rust-analyzer.checkOnSave.allFeatures": false,
6 "rust-analyzer.checkOnSave.allTargets": false, 6 "rust-analyzer.checkOnSave.allTargets": false,
7 "rust-analyzer.cargo.target": "thumbv7em-none-eabihf", 7 "rust-analyzer.cargo.target": "thumbv7em-none-eabi",
8 "rust-analyzer.checkOnSave.target": "thumbv7em-none-eabihf", 8 "rust-analyzer.checkOnSave.target": "thumbv7em-none-eabi",
9 "rust-analyzer.procMacro.enable": true, 9 "rust-analyzer.procMacro.enable": true,
10 "rust-analyzer.cargo.loadOutDirsFromCheck": true, 10 "rust-analyzer.cargo.loadOutDirsFromCheck": true,
11 "files.watcherExclude": { 11 "files.watcherExclude": {
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 @@
1#![no_std]
2#![no_main]
3#![feature(min_type_alias_impl_trait)]
4#![feature(impl_trait_in_bindings)]
5#![feature(type_alias_impl_trait)]
6#![allow(incomplete_features)]
7
8#[path = "../example_common.rs"]
9mod example_common;
10use embassy_traits::uart::ReadUntilIdle;
11use example_common::*;
12
13use defmt::panic;
14use embassy::executor::Spawner;
15use embassy::traits::uart::{Read, Write};
16use embassy::util::Steal;
17use embassy_nrf::gpio::NoPin;
18use embassy_nrf::{interrupt, uarte, Peripherals};
19
20#[embassy::main]
21async fn main(spawner: Spawner) {
22 let p = unsafe { Peripherals::steal() };
23
24 let mut config = uarte::Config::default();
25 config.parity = uarte::Parity::EXCLUDED;
26 config.baudrate = uarte::Baudrate::BAUD115200;
27
28 let irq = interrupt::take!(UARTE0_UART0);
29 let mut uart = unsafe {
30 uarte::UarteWithIdle::new(
31 p.UARTE0, p.TIMER0, p.PPI_CH0, p.PPI_CH1, irq, p.P0_08, p.P0_06, NoPin, NoPin, config,
32 )
33 };
34
35 info!("uarte initialized!");
36
37 // Message must be in SRAM
38 let mut buf = [0; 8];
39 buf.copy_from_slice(b"Hello!\r\n");
40
41 unwrap!(uart.write(&buf).await);
42 info!("wrote hello in uart!");
43
44 loop {
45 info!("reading...");
46 let n = unwrap!(uart.read_until_idle(&mut buf).await);
47 info!("got {} bytes", n);
48 }
49}
diff --git a/embassy-nrf/src/uarte.rs b/embassy-nrf/src/uarte.rs
index a02f7c347..04907fb56 100644
--- a/embassy-nrf/src/uarte.rs
+++ b/embassy-nrf/src/uarte.rs
@@ -5,7 +5,7 @@ use core::marker::PhantomData;
5use core::sync::atomic::{compiler_fence, Ordering}; 5use core::sync::atomic::{compiler_fence, Ordering};
6use core::task::Poll; 6use core::task::Poll;
7use embassy::interrupt::InterruptExt; 7use embassy::interrupt::InterruptExt;
8use embassy::traits::uart::{Error, Read, Write}; 8use embassy::traits::uart::{Error, Read, ReadUntilIdle, Write};
9use embassy::util::{AtomicWaker, OnDrop, Unborrow}; 9use embassy::util::{AtomicWaker, OnDrop, Unborrow};
10use embassy_extras::unborrow; 10use embassy_extras::unborrow;
11use futures::future::poll_fn; 11use futures::future::poll_fn;
@@ -17,7 +17,9 @@ use crate::interrupt;
17use crate::interrupt::Interrupt; 17use crate::interrupt::Interrupt;
18use crate::pac; 18use crate::pac;
19use crate::peripherals; 19use crate::peripherals;
20use crate::ppi::{AnyConfigurableChannel, ConfigurableChannel, Event, Ppi, Task};
20use crate::target_constants::EASY_DMA_SIZE; 21use crate::target_constants::EASY_DMA_SIZE;
22use crate::timer::Instance as TimerInstance;
21 23
22// Re-export SVD variants to allow user to directly set values. 24// Re-export SVD variants to allow user to directly set values.
23pub use pac::uarte0::{baudrate::BAUDRATE_A as Baudrate, config::PARITY_A as Parity}; 25pub use pac::uarte0::{baudrate::BAUDRATE_A as Baudrate, config::PARITY_A as Parity};
@@ -281,6 +283,168 @@ impl<'d, T: Instance> Write for Uarte<'d, T> {
281 } 283 }
282} 284}
283 285
286/// Interface to an UARTE peripheral that uses an additional timer and two PPI channels,
287/// allowing it to implement the ReadUntilIdle trait.
288pub struct UarteWithIdle<'d, U: Instance, T: TimerInstance> {
289 uarte: Uarte<'d, U>,
290 timer: T,
291 ppi_ch1: Ppi<'d, AnyConfigurableChannel>,
292 _ppi_ch2: Ppi<'d, AnyConfigurableChannel>,
293}
294
295impl<'d, U: Instance, T: TimerInstance> UarteWithIdle<'d, U, T> {
296 /// Creates the interface to a UARTE instance.
297 /// Sets the baud rate, parity and assigns the pins to the UARTE peripheral.
298 ///
299 /// # Safety
300 ///
301 /// The returned API is safe unless you use `mem::forget` (or similar safe mechanisms)
302 /// on stack allocated buffers which which have been passed to [`send()`](Uarte::send)
303 /// or [`receive`](Uarte::receive).
304 #[allow(unused_unsafe)]
305 pub unsafe fn new(
306 uarte: impl Unborrow<Target = U> + 'd,
307 timer: impl Unborrow<Target = T> + 'd,
308 ppi_ch1: impl Unborrow<Target = impl ConfigurableChannel> + 'd,
309 ppi_ch2: impl Unborrow<Target = impl ConfigurableChannel> + 'd,
310 irq: impl Unborrow<Target = U::Interrupt> + 'd,
311 rxd: impl Unborrow<Target = impl GpioPin> + 'd,
312 txd: impl Unborrow<Target = impl GpioPin> + 'd,
313 cts: impl Unborrow<Target = impl GpioOptionalPin> + 'd,
314 rts: impl Unborrow<Target = impl GpioOptionalPin> + 'd,
315 config: Config,
316 ) -> Self {
317 let baudrate = config.baudrate;
318 let uarte = Uarte::new(uarte, irq, rxd, txd, cts, rts, config);
319
320 unborrow!(timer, ppi_ch1, ppi_ch2);
321
322 let r = U::regs();
323 let rt = timer.regs();
324
325 // BAUDRATE register values are `baudrate * 2^32 / 16000000`
326 // source: https://devzone.nordicsemi.com/f/nordic-q-a/391/uart-baudrate-register-values
327 //
328 // We want to stop RX if line is idle for 2 bytes worth of time
329 // That is 20 bits (each byte is 1 start bit + 8 data bits + 1 stop bit)
330 // This gives us the amount of 16M ticks for 20 bits.
331 let timeout = 0x8000_0000 / (baudrate as u32 / 40);
332
333 rt.tasks_stop.write(|w| unsafe { w.bits(1) });
334 rt.bitmode.write(|w| w.bitmode()._32bit());
335 rt.prescaler.write(|w| unsafe { w.prescaler().bits(0) });
336 rt.cc[0].write(|w| unsafe { w.bits(timeout) });
337 rt.mode.write(|w| w.mode().timer());
338 rt.shorts.write(|w| {
339 w.compare0_clear().set_bit();
340 w.compare0_stop().set_bit();
341 w
342 });
343
344 let mut ppi_ch1 = Ppi::new(ppi_ch1.degrade_configurable());
345 ppi_ch1.set_event(Event::from_reg(&r.events_rxdrdy));
346 ppi_ch1.set_task(Task::from_reg(&rt.tasks_clear));
347 ppi_ch1.set_fork_task(Task::from_reg(&rt.tasks_start));
348 ppi_ch1.enable();
349
350 let mut ppi_ch2 = Ppi::new(ppi_ch2.degrade_configurable());
351 ppi_ch2.set_event(Event::from_reg(&rt.events_compare[0]));
352 ppi_ch2.set_task(Task::from_reg(&r.tasks_stoprx));
353 ppi_ch2.enable();
354
355 Self {
356 uarte,
357 timer,
358 ppi_ch1: ppi_ch1,
359 _ppi_ch2: ppi_ch2,
360 }
361 }
362}
363
364impl<'d, U: Instance, T: TimerInstance> ReadUntilIdle for UarteWithIdle<'d, U, T> {
365 #[rustfmt::skip]
366 type ReadUntilIdleFuture<'a> where Self: 'a = impl Future<Output = Result<usize, Error>> + 'a;
367 fn read_until_idle<'a>(&'a mut self, rx_buffer: &'a mut [u8]) -> Self::ReadUntilIdleFuture<'a> {
368 async move {
369 let ptr = rx_buffer.as_ptr();
370 let len = rx_buffer.len();
371 assert!(len <= EASY_DMA_SIZE);
372
373 let r = U::regs();
374 let s = U::state();
375
376 let rt = self.timer.regs();
377
378 let drop = OnDrop::new(move || {
379 info!("read drop: stopping");
380
381 rt.tasks_stop.write(|w| unsafe { w.bits(1) });
382
383 r.intenclr.write(|w| w.endrx().clear());
384 r.events_rxto.reset();
385 r.tasks_stoprx.write(|w| unsafe { w.bits(1) });
386
387 while r.events_endrx.read().bits() == 0 {}
388
389 info!("read drop: stopped");
390 });
391
392 r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) });
393 r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) });
394
395 r.events_endrx.reset();
396 r.intenset.write(|w| w.endrx().set());
397
398 compiler_fence(Ordering::SeqCst);
399
400 trace!("startrx");
401 r.tasks_startrx.write(|w| unsafe { w.bits(1) });
402
403 poll_fn(|cx| {
404 s.endrx_waker.register(cx.waker());
405 if r.events_endrx.read().bits() != 0 {
406 return Poll::Ready(());
407 }
408 Poll::Pending
409 })
410 .await;
411
412 compiler_fence(Ordering::SeqCst);
413 let n = r.rxd.amount.read().amount().bits() as usize;
414
415 // Stop timer
416 rt.tasks_stop.write(|w| unsafe { w.bits(1) });
417 r.events_rxstarted.reset();
418
419 drop.defuse();
420
421 Ok(n)
422 }
423 }
424}
425
426impl<'d, U: Instance, T: TimerInstance> Read for UarteWithIdle<'d, U, T> {
427 #[rustfmt::skip]
428 type ReadFuture<'a> where Self: 'a = impl Future<Output = Result<(), Error>> + 'a;
429 fn read<'a>(&'a mut self, rx_buffer: &'a mut [u8]) -> Self::ReadFuture<'a> {
430 async move {
431 self.ppi_ch1.disable();
432 let result = self.uarte.read(rx_buffer).await;
433 self.ppi_ch1.enable();
434 result
435 }
436 }
437}
438
439impl<'d, U: Instance, T: TimerInstance> Write for UarteWithIdle<'d, U, T> {
440 #[rustfmt::skip]
441 type WriteFuture<'a> where Self: 'a = impl Future<Output = Result<(), Error>> + 'a;
442
443 fn write<'a>(&'a mut self, tx_buffer: &'a [u8]) -> Self::WriteFuture<'a> {
444 self.uarte.write(tx_buffer)
445 }
446}
447
284mod sealed { 448mod sealed {
285 use super::*; 449 use super::*;
286 450
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 = [
25] 25]
26 26
27[build] 27[build]
28target = "thumbv7em-none-eabihf" 28target = "thumbv7em-none-eabi"