diff options
Diffstat (limited to 'embassy-nrf/src')
| -rw-r--r-- | embassy-nrf/src/uarte.rs | 159 |
1 files changed, 158 insertions, 1 deletions
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; | |||
| 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,161 @@ impl<'d, T: Instance> Write for Uarte<'d, T> { | |||
| 281 | } | 283 | } |
| 282 | } | 284 | } |
| 283 | 285 | ||
| 286 | /// Interface to an UARTE peripheral that uses timers and PPI to emulate | ||
| 287 | /// ReadUntilIdle. | ||
| 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_rxstarted)); | ||
| 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 | let n: usize = poll_fn(|cx| { | ||
| 404 | s.endrx_waker.register(cx.waker()); | ||
| 405 | if r.events_endrx.read().bits() != 0 { | ||
| 406 | let n: usize = r.rxd.amount.read().amount().bits() as usize; | ||
| 407 | return Poll::Ready(n); | ||
| 408 | } | ||
| 409 | Poll::Pending | ||
| 410 | }) | ||
| 411 | .await; | ||
| 412 | |||
| 413 | compiler_fence(Ordering::SeqCst); | ||
| 414 | r.events_rxstarted.reset(); | ||
| 415 | // Stop timer | ||
| 416 | rt.tasks_stop.write(|w| unsafe { w.bits(1) }); | ||
| 417 | drop.defuse(); | ||
| 418 | |||
| 419 | Ok(n) | ||
| 420 | } | ||
| 421 | } | ||
| 422 | } | ||
| 423 | |||
| 424 | impl<'d, U: Instance, T: TimerInstance> Read for UarteWithIdle<'d, U, T> { | ||
| 425 | #[rustfmt::skip] | ||
| 426 | type ReadFuture<'a> where Self: 'a = impl Future<Output = Result<(), Error>> + 'a; | ||
| 427 | fn read<'a>(&'a mut self, rx_buffer: &'a mut [u8]) -> Self::ReadFuture<'a> { | ||
| 428 | self.uarte.read(rx_buffer) | ||
| 429 | } | ||
| 430 | } | ||
| 431 | |||
| 432 | impl<'d, U: Instance, T: TimerInstance> Write for UarteWithIdle<'d, U, T> { | ||
| 433 | #[rustfmt::skip] | ||
| 434 | type WriteFuture<'a> where Self: 'a = impl Future<Output = Result<(), Error>> + 'a; | ||
| 435 | |||
| 436 | fn write<'a>(&'a mut self, tx_buffer: &'a [u8]) -> Self::WriteFuture<'a> { | ||
| 437 | self.uarte.write(tx_buffer) | ||
| 438 | } | ||
| 439 | } | ||
| 440 | |||
| 284 | mod sealed { | 441 | mod sealed { |
| 285 | use super::*; | 442 | use super::*; |
| 286 | 443 | ||
