diff options
| author | Dario Nieuwenhuis <[email protected]> | 2021-05-10 23:11:02 +0200 |
|---|---|---|
| committer | GitHub <[email protected]> | 2021-05-10 23:11:02 +0200 |
| commit | f817f374b6f787338aac17ee5f655a5f2a722a80 (patch) | |
| tree | 40d2d7ab6828477c54c5fa97ab91541ea754cf10 | |
| parent | 1703700970a716bf9b0044ee3d8cd3e15a50fcc2 (diff) | |
| parent | 95439b493f4325c38e62e669fec81bb4892f3dcd (diff) | |
Merge pull request #169 from lulf/nrf-uart-read-until-idle
Add implementation of ReadUntilIdle for nRF UART
| -rw-r--r-- | .vscode/settings.json | 4 | ||||
| -rw-r--r-- | embassy-nrf-examples/src/bin/uart_idle.rs | 49 | ||||
| -rw-r--r-- | embassy-nrf/src/uarte.rs | 166 | ||||
| -rw-r--r-- | embassy-stm32-examples/.cargo/config.toml | 2 |
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"] | ||
| 9 | mod example_common; | ||
| 10 | use embassy_traits::uart::ReadUntilIdle; | ||
| 11 | use example_common::*; | ||
| 12 | |||
| 13 | use defmt::panic; | ||
| 14 | use embassy::executor::Spawner; | ||
| 15 | use embassy::traits::uart::{Read, Write}; | ||
| 16 | use embassy::util::Steal; | ||
| 17 | use embassy_nrf::gpio::NoPin; | ||
| 18 | use embassy_nrf::{interrupt, uarte, Peripherals}; | ||
| 19 | |||
| 20 | #[embassy::main] | ||
| 21 | async 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; | |||
| 5 | use core::sync::atomic::{compiler_fence, Ordering}; | 5 | use core::sync::atomic::{compiler_fence, Ordering}; |
| 6 | use core::task::Poll; | 6 | use core::task::Poll; |
| 7 | use embassy::interrupt::InterruptExt; | 7 | use embassy::interrupt::InterruptExt; |
| 8 | use embassy::traits::uart::{Error, Read, Write}; | 8 | use embassy::traits::uart::{Error, Read, ReadUntilIdle, Write}; |
| 9 | use embassy::util::{AtomicWaker, OnDrop, Unborrow}; | 9 | use embassy::util::{AtomicWaker, OnDrop, Unborrow}; |
| 10 | use embassy_extras::unborrow; | 10 | use embassy_extras::unborrow; |
| 11 | use futures::future::poll_fn; | 11 | use futures::future::poll_fn; |
| @@ -17,7 +17,9 @@ use crate::interrupt; | |||
| 17 | use crate::interrupt::Interrupt; | 17 | use crate::interrupt::Interrupt; |
| 18 | use crate::pac; | 18 | use crate::pac; |
| 19 | use crate::peripherals; | 19 | use crate::peripherals; |
| 20 | use crate::ppi::{AnyConfigurableChannel, ConfigurableChannel, Event, Ppi, Task}; | ||
| 20 | use crate::target_constants::EASY_DMA_SIZE; | 21 | use crate::target_constants::EASY_DMA_SIZE; |
| 22 | use 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. |
| 23 | pub use pac::uarte0::{baudrate::BAUDRATE_A as Baudrate, config::PARITY_A as Parity}; | 25 | pub 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. | ||
| 288 | pub 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 | |||
| 295 | impl<'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 | |||
| 364 | impl<'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 | |||
| 426 | impl<'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 | |||
| 439 | impl<'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 | |||
| 284 | mod sealed { | 448 | mod 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] |
| 28 | target = "thumbv7em-none-eabihf" | 28 | target = "thumbv7em-none-eabi" |
