From 1d2f6667df1bb0299c4e9b4e1660ee729ce2b463 Mon Sep 17 00:00:00 2001 From: pennae Date: Sun, 30 Apr 2023 08:04:21 +0200 Subject: [PATCH] rp/uart: add set-break functions sending break conditions is necessary to implement some protocols, and the hardware supports this natively. we do have to make sure that we don't assert a break condition while the uart is busy though, otherwise the break may be inserted before the last character in the tx fifo. --- embassy-rp/src/uart/buffered.rs | 47 +++++++++++++++++++++++++++++++ embassy-rp/src/uart/mod.rs | 49 ++++++++++++++++++++++++++++++++- 2 files changed, 95 insertions(+), 1 deletion(-) diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs index cb046193..b87345ee 100644 --- a/embassy-rp/src/uart/buffered.rs +++ b/embassy-rp/src/uart/buffered.rs @@ -5,8 +5,10 @@ use core::task::Poll; use embassy_cortex_m::interrupt::{Interrupt, InterruptExt}; use embassy_hal_common::atomic_ring_buffer::RingBuffer; use embassy_sync::waitqueue::AtomicWaker; +use embassy_time::{Duration, Timer}; use super::*; +use crate::clocks::clk_peri_freq; use crate::RegExt; pub struct State { @@ -136,6 +138,14 @@ impl<'d, T: Instance> BufferedUart<'d, T> { self.rx.blocking_read(buffer) } + pub fn busy(&self) -> bool { + self.tx.busy() + } + + pub async fn send_break(&mut self, bits: u32) { + self.tx.send_break(bits).await + } + pub fn split(self) -> (BufferedUartRx<'d, T>, BufferedUartTx<'d, T>) { (self.rx, self.tx) } @@ -371,6 +381,43 @@ impl<'d, T: Instance> BufferedUartTx<'d, T> { } } } + + pub fn busy(&self) -> bool { + unsafe { T::regs().uartfr().read().busy() } + } + + /// Assert a break condition after waiting for the transmit buffers to empty, + /// for the specified number of bit times. This condition must be asserted + /// for at least two frame times to be effective, `bits` will adjusted + /// according to frame size, parity, and stop bit settings to ensure this. + /// + /// This method may block for a long amount of time since it has to wait + /// for the transmit fifo to empty, which may take a while on slow links. + pub async fn send_break(&mut self, bits: u32) { + let regs = T::regs(); + let bits = bits.max(unsafe { + let lcr = regs.uartlcr_h().read(); + let width = lcr.wlen() as u32 + 5; + let parity = lcr.pen() as u32; + let stops = 1 + lcr.stp2() as u32; + 2 * (1 + width + parity + stops) + }); + let divx64 = unsafe { + ((regs.uartibrd().read().baud_divint() as u32) << 6) + regs.uartfbrd().read().baud_divfrac() as u32 + } as u64; + let div_clk = clk_peri_freq() as u64 * 64; + let wait_usecs = (1_000_000 * bits as u64 * divx64 * 16 + div_clk - 1) / div_clk; + + Self::flush().await.unwrap(); + while self.busy() {} + unsafe { + regs.uartlcr_h().write_set(|w| w.set_brk(true)); + } + Timer::after(Duration::from_micros(wait_usecs)).await; + unsafe { + regs.uartlcr_h().write_clear(|w| w.set_brk(true)); + } + } } impl<'d, T: Instance> Drop for BufferedUartRx<'d, T> { diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index f4a93f63..dd4a1cce 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -1,12 +1,14 @@ use core::marker::PhantomData; use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_time::{Duration, Timer}; +use crate::clocks::clk_peri_freq; use crate::dma::{AnyChannel, Channel}; use crate::gpio::sealed::Pin; use crate::gpio::AnyPin; use crate::pac::io::vals::{Inover, Outover}; -use crate::{pac, peripherals, Peripheral}; +use crate::{pac, peripherals, Peripheral, RegExt}; #[cfg(feature = "nightly")] mod buffered; @@ -146,6 +148,43 @@ impl<'d, T: Instance, M: Mode> UartTx<'d, T, M> { unsafe { while !r.uartfr().read().txfe() {} } Ok(()) } + + pub fn busy(&self) -> bool { + unsafe { T::regs().uartfr().read().busy() } + } + + /// Assert a break condition after waiting for the transmit buffers to empty, + /// for the specified number of bit times. This condition must be asserted + /// for at least two frame times to be effective, `bits` will adjusted + /// according to frame size, parity, and stop bit settings to ensure this. + /// + /// This method may block for a long amount of time since it has to wait + /// for the transmit fifo to empty, which may take a while on slow links. + pub async fn send_break(&mut self, bits: u32) { + let regs = T::regs(); + let bits = bits.max(unsafe { + let lcr = regs.uartlcr_h().read(); + let width = lcr.wlen() as u32 + 5; + let parity = lcr.pen() as u32; + let stops = 1 + lcr.stp2() as u32; + 2 * (1 + width + parity + stops) + }); + let divx64 = unsafe { + ((regs.uartibrd().read().baud_divint() as u32) << 6) + regs.uartfbrd().read().baud_divfrac() as u32 + } as u64; + let div_clk = clk_peri_freq() as u64 * 64; + let wait_usecs = (1_000_000 * bits as u64 * divx64 * 16 + div_clk - 1) / div_clk; + + self.blocking_flush().unwrap(); + while self.busy() {} + unsafe { + regs.uartlcr_h().write_set(|w| w.set_brk(true)); + } + Timer::after(Duration::from_micros(wait_usecs)).await; + unsafe { + regs.uartlcr_h().write_clear(|w| w.set_brk(true)); + } + } } impl<'d, T: Instance> UartTx<'d, T, Blocking> { @@ -526,6 +565,14 @@ impl<'d, T: Instance, M: Mode> Uart<'d, T, M> { self.rx.blocking_read(buffer) } + pub fn busy(&self) -> bool { + self.tx.busy() + } + + pub async fn send_break(&mut self, bits: u32) { + self.tx.send_break(bits).await + } + /// Split the Uart into a transmitter and receiver, which is particuarly /// useful when having two tasks correlating to transmitting and receiving. pub fn split(self) -> (UartTx<'d, T, M>, UartRx<'d, T, M>) {