use super::{FifoWatermark, UartDevice, ValidUartPinout};
use crate::dma::{EndlessWriteTarget, WriteTarget};
use core::fmt;
use core::{convert::Infallible, marker::PhantomData};
#[cfg(feature = "eh1_0_alpha")]
use eh1_0_alpha::serial as eh1;
#[cfg(feature = "eh1_0_alpha")]
use eh_nb_1_0_alpha::serial as eh1nb;
use embedded_hal::serial::Write;
use nb::Error::*;
use rp2040_pac::uart0::RegisterBlock;
pub fn set_tx_watermark(rb: &RegisterBlock, watermark: FifoWatermark) {
let wm = match watermark {
FifoWatermark::Bytes4 => 4,
FifoWatermark::Bytes8 => 3,
FifoWatermark::Bytes16 => 2,
FifoWatermark::Bytes24 => 1,
FifoWatermark::Bytes28 => 0,
};
rb.uartifls.modify(|_r, w| unsafe { w.txiflsel().bits(wm) });
}
pub(crate) fn transmit_flushed(rb: &RegisterBlock) -> nb::Result<(), Infallible> {
if rb.uartfr.read().txfe().bit_is_set() {
Ok(())
} else {
Err(WouldBlock)
}
}
pub(crate) fn uart_is_writable(rb: &RegisterBlock) -> bool {
rb.uartfr.read().txff().bit_is_clear()
}
pub(crate) fn write_raw<'d>(
rb: &RegisterBlock,
data: &'d [u8],
) -> nb::Result<&'d [u8], Infallible> {
let mut bytes_written = 0;
for c in data {
if !uart_is_writable(rb) {
if bytes_written == 0 {
return Err(WouldBlock);
} else {
return Ok(&data[bytes_written..]);
}
}
rb.uartdr.write(|w| unsafe {
w.data().bits(*c);
w
});
bytes_written += 1;
}
Ok(&data[bytes_written..])
}
pub(crate) fn write_full_blocking(rb: &RegisterBlock, data: &[u8]) {
let mut temp = data;
while !temp.is_empty() {
temp = match write_raw(rb, temp) {
Ok(remaining) => remaining,
Err(WouldBlock) => continue,
Err(_) => unreachable!(),
}
}
}
pub(crate) fn enable_tx_interrupt(rb: &RegisterBlock) {
rb.uartifls.modify(|_r, w| unsafe { w.txiflsel().bits(2) });
rb.uartimsc.modify(|_r, w| {
w.txim().set_bit();
w
});
}
pub(crate) fn disable_tx_interrupt(rb: &RegisterBlock) {
rb.uartimsc.modify(|_r, w| {
w.txim().clear_bit();
w
});
}
pub struct Writer<D: UartDevice, P: ValidUartPinout<D>> {
pub(super) device: D,
pub(super) device_marker: PhantomData<D>,
pub(super) pins: PhantomData<P>,
}
impl<D: UartDevice, P: ValidUartPinout<D>> Writer<D, P> {
pub fn write_raw<'d>(&self, data: &'d [u8]) -> nb::Result<&'d [u8], Infallible> {
write_raw(&self.device, data)
}
pub fn write_full_blocking(&self, data: &[u8]) {
write_full_blocking(&self.device, data);
}
pub fn enable_tx_interrupt(&mut self) {
enable_tx_interrupt(&self.device)
}
pub fn disable_tx_interrupt(&mut self) {
disable_tx_interrupt(&self.device)
}
}
impl<D: UartDevice, P: ValidUartPinout<D>> Write<u8> for Writer<D, P> {
type Error = Infallible;
fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> {
if self.write_raw(&[word]).is_err() {
Err(WouldBlock)
} else {
Ok(())
}
}
fn flush(&mut self) -> nb::Result<(), Self::Error> {
transmit_flushed(&self.device)
}
}
impl<D: UartDevice, P: ValidUartPinout<D>> WriteTarget for Writer<D, P> {
type TransmittedWord = u8;
fn tx_treq() -> Option<u8> {
Some(D::tx_dreq())
}
fn tx_address_count(&mut self) -> (u32, u32) {
(&self.device.uartdr as *const _ as u32, u32::MAX)
}
fn tx_increment(&self) -> bool {
false
}
}
impl<D: UartDevice, P: ValidUartPinout<D>> EndlessWriteTarget for Writer<D, P> {}
#[cfg(feature = "eh1_0_alpha")]
impl<D: UartDevice, P: ValidUartPinout<D>> eh1::ErrorType for Writer<D, P> {
type Error = core::convert::Infallible;
}
#[cfg(feature = "eh1_0_alpha")]
impl<D: UartDevice, P: ValidUartPinout<D>> eh1nb::Write<u8> for Writer<D, P> {
fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> {
if self.write_raw(&[word]).is_err() {
Err(WouldBlock)
} else {
Ok(())
}
}
fn flush(&mut self) -> nb::Result<(), Self::Error> {
transmit_flushed(&self.device).map_err(|e| match e {
WouldBlock => WouldBlock,
Other(v) => match v {},
})
}
}
impl<D: UartDevice, P: ValidUartPinout<D>> fmt::Write for Writer<D, P> {
fn write_str(&mut self, s: &str) -> fmt::Result {
s.bytes()
.try_for_each(|c| nb::block!(self.write(c)))
.map_err(|_| fmt::Error)
}
}