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:
parent
7336b8cd88
commit
1d2f6667df
@ -5,8 +5,10 @@ use core::task::Poll;
|
|||||||
use embassy_cortex_m::interrupt::{Interrupt, InterruptExt};
|
use embassy_cortex_m::interrupt::{Interrupt, InterruptExt};
|
||||||
use embassy_hal_common::atomic_ring_buffer::RingBuffer;
|
use embassy_hal_common::atomic_ring_buffer::RingBuffer;
|
||||||
use embassy_sync::waitqueue::AtomicWaker;
|
use embassy_sync::waitqueue::AtomicWaker;
|
||||||
|
use embassy_time::{Duration, Timer};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::clocks::clk_peri_freq;
|
||||||
use crate::RegExt;
|
use crate::RegExt;
|
||||||
|
|
||||||
pub struct State {
|
pub struct State {
|
||||||
@ -136,6 +138,14 @@ impl<'d, T: Instance> BufferedUart<'d, T> {
|
|||||||
self.rx.blocking_read(buffer)
|
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>) {
|
pub fn split(self) -> (BufferedUartRx<'d, T>, BufferedUartTx<'d, T>) {
|
||||||
(self.rx, self.tx)
|
(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> {
|
impl<'d, T: Instance> Drop for BufferedUartRx<'d, T> {
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
|
|
||||||
use embassy_hal_common::{into_ref, PeripheralRef};
|
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::dma::{AnyChannel, Channel};
|
||||||
use crate::gpio::sealed::Pin;
|
use crate::gpio::sealed::Pin;
|
||||||
use crate::gpio::AnyPin;
|
use crate::gpio::AnyPin;
|
||||||
use crate::pac::io::vals::{Inover, Outover};
|
use crate::pac::io::vals::{Inover, Outover};
|
||||||
use crate::{pac, peripherals, Peripheral};
|
use crate::{pac, peripherals, Peripheral, RegExt};
|
||||||
|
|
||||||
#[cfg(feature = "nightly")]
|
#[cfg(feature = "nightly")]
|
||||||
mod buffered;
|
mod buffered;
|
||||||
@ -146,6 +148,43 @@ impl<'d, T: Instance, M: Mode> UartTx<'d, T, M> {
|
|||||||
unsafe { while !r.uartfr().read().txfe() {} }
|
unsafe { while !r.uartfr().read().txfe() {} }
|
||||||
Ok(())
|
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> {
|
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)
|
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
|
/// Split the Uart into a transmitter and receiver, which is particuarly
|
||||||
/// useful when having two tasks correlating to transmitting and receiving.
|
/// useful when having two tasks correlating to transmitting and receiving.
|
||||||
pub fn split(self) -> (UartTx<'d, T, M>, UartRx<'d, T, M>) {
|
pub fn split(self) -> (UartTx<'d, T, M>, UartRx<'d, T, M>) {
|
||||||
|
Loading…
Reference in New Issue
Block a user