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.
This commit is contained in:
pennae 2023-04-30 08:04:21 +02:00
parent 7336b8cd88
commit 1d2f6667df
2 changed files with 95 additions and 1 deletions

View file

@ -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> {

View file

@ -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>) {