use crate::iomuxc;
use crate::ral::{self, lpuart::Instance};
pub struct Pins<TX, RX>
where
TX: iomuxc::lpuart::Pin<Direction = iomuxc::lpuart::Tx>,
RX: iomuxc::lpuart::Pin<Module = TX::Module, Direction = iomuxc::lpuart::Rx>,
{
pub tx: TX,
pub rx: RX,
}
pub struct Lpuart<P, const N: u8> {
pins: P,
pub(crate) lpuart: Instance<N>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Direction {
Tx,
Rx,
}
impl<TX, RX, const N: u8> Lpuart<Pins<TX, RX>, N>
where
TX: iomuxc::lpuart::Pin<Module = iomuxc::consts::Const<N>, Direction = iomuxc::lpuart::Tx>,
RX: iomuxc::lpuart::Pin<Module = iomuxc::consts::Const<N>, Direction = iomuxc::lpuart::Rx>,
{
pub fn new(lpuart: Instance<N>, mut pins: Pins<TX, RX>) -> Self {
iomuxc::lpuart::prepare(&mut pins.tx);
iomuxc::lpuart::prepare(&mut pins.rx);
Self::init(lpuart, pins)
}
}
impl<const N: u8> Lpuart<(), N> {
pub fn without_pins(lpuart: Instance<N>) -> Self {
Self::init(lpuart, ())
}
}
impl<P, const N: u8> Lpuart<P, N> {
pub const N: u8 = N;
fn init(lpuart: Instance<N>, pins: P) -> Self {
ral::write_reg!(ral::lpuart, lpuart, GLOBAL, RST: 1);
ral::write_reg!(ral::lpuart, lpuart, GLOBAL, RST: 0);
ral::modify_reg!(ral::lpuart, lpuart, CTRL, TE: TE_1, RE: RE_1);
Self { pins, lpuart }
}
pub fn is_enabled(&self, direction: Direction) -> bool {
match direction {
Direction::Rx => ral::read_reg!(ral::lpuart, self.lpuart, CTRL, RE == RE_1),
Direction::Tx => ral::read_reg!(ral::lpuart, self.lpuart, CTRL, TE == TE_1),
}
}
pub fn set_enable(&mut self, direction: Direction, enable: bool) {
match direction {
Direction::Rx => ral::modify_reg!(ral::lpuart, self.lpuart, CTRL, RE: enable as u32),
Direction::Tx => ral::modify_reg!(ral::lpuart, self.lpuart, CTRL, TE: enable as u32),
}
}
pub fn reset(&mut self) {
ral::write_reg!(ral::lpuart, self.lpuart, GLOBAL, RST: 1);
ral::write_reg!(ral::lpuart, self.lpuart, GLOBAL, RST: 0);
}
pub fn release(self) -> (Instance<N>, P) {
(self.lpuart, self.pins)
}
pub fn pins(&self) -> &P {
&self.pins
}
pub fn pins_mut(&mut self) -> &mut P {
&mut self.pins
}
pub fn disable<R>(&mut self, func: impl FnOnce(&mut Disabled<N>) -> R) -> R {
let mut disabled = Disabled::new(&self.lpuart);
func(&mut disabled)
}
pub fn baud(&self) -> Baud {
let (osr, sbr, bothedge) =
ral::read_reg!(ral::lpuart, self.lpuart, BAUD, OSR, SBR, BOTHEDGE);
Baud {
osr: osr + 1,
sbr,
bothedge: bothedge != 0,
}
}
pub fn parity(&self) -> Option<Parity> {
let (pe, pt) = ral::read_reg!(ral::lpuart, self.lpuart, CTRL, PE, PT);
const PARITY_ODD: u32 = Parity::Odd as u32;
const PARITY_EVEN: u32 = Parity::Even as u32;
match pt {
PARITY_ODD if pe != 0 => Parity::ODD,
PARITY_EVEN if pe != 0 => Parity::EVEN,
_ => Parity::NONE,
}
}
#[inline]
pub fn is_inverted(&self, direction: Direction) -> bool {
match direction {
Direction::Rx => ral::read_reg!(ral::lpuart, self.lpuart, STAT, RXINV == 1),
Direction::Tx => ral::read_reg!(ral::lpuart, self.lpuart, CTRL, TXINV == 1),
}
}
#[inline]
pub fn is_fifo_enabled(&self, direction: Direction) -> bool {
match direction {
Direction::Rx => ral::read_reg!(ral::lpuart, self.lpuart, FIFO, RXFE == 1),
Direction::Tx => ral::read_reg!(ral::lpuart, self.lpuart, FIFO, TXFE == 1),
}
}
#[inline]
pub fn fifo_watermark(&self, direction: Direction) -> u32 {
match direction {
Direction::Rx => ral::read_reg!(ral::lpuart, self.lpuart, WATER, RXWATER),
Direction::Tx => ral::read_reg!(ral::lpuart, self.lpuart, WATER, TXWATER),
}
}
pub fn read_data(&self) -> ReadData {
ReadData(ral::read_reg!(ral::lpuart, self.lpuart, DATA))
}
pub fn write_byte(&self, byte: u8) {
ral::write_reg!(ral::lpuart, self.lpuart, DATA, byte as u32);
}
pub fn status(&self) -> Status {
let stat = ral::read_reg!(ral::lpuart, self.lpuart, STAT);
let fifo = ral::read_reg!(ral::lpuart, self.lpuart, FIFO);
Status::from_registers(stat, fifo)
}
#[inline]
pub fn clear_status(&mut self, status: Status) {
let stat_flags = status & Status::W1C & Status::stat_mask();
let fifo_flags = status & Status::W1C & Status::fifo_mask();
ral::modify_reg!(ral::lpuart, self.lpuart, STAT, |stat| {
let stat = stat & !Status::stat_mask().stat_bits();
stat | stat_flags.stat_bits()
});
ral::modify_reg!(ral::lpuart, self.lpuart, FIFO, |fifo| {
let fifo = fifo & !Status::fifo_mask().fifo_bits();
fifo | fifo_flags.fifo_bits()
});
}
#[inline]
pub fn flush_fifo(&mut self, direction: Direction) {
flush_fifo(&self.lpuart, direction);
}
pub fn interrupts(&self) -> Interrupts {
let ctrl = ral::read_reg!(ral::lpuart, self.lpuart, CTRL);
let fifo = ral::read_reg!(ral::lpuart, self.lpuart, FIFO);
Interrupts::from_bits_truncate(ctrl | fifo)
}
pub fn enable_dma_transmit(&mut self) {
ral::modify_reg!(ral::lpuart, self.lpuart, BAUD, TDMAE: 1);
}
pub fn disable_dma_transmit(&mut self) {
while ral::read_reg!(ral::lpuart, self.lpuart, BAUD, TDMAE == 1) {
ral::modify_reg!(ral::lpuart, self.lpuart, BAUD, TDMAE: 0);
}
}
pub fn data(&self) -> *const ral::RWRegister<u32> {
core::ptr::addr_of!(self.lpuart.DATA)
}
pub fn enable_dma_receive(&mut self) {
self.clear_status(Status::W1C);
ral::modify_reg!(ral::lpuart, self.lpuart, BAUD, RDMAE: 1);
}
pub fn disable_dma_receive(&mut self) {
while ral::read_reg!(ral::lpuart, self.lpuart, BAUD, RDMAE == 1) {
ral::modify_reg!(ral::lpuart, self.lpuart, BAUD, RDMAE: 0);
}
}
pub fn try_write(&mut self, byte: u8) -> bool {
ral::modify_reg!(ral::lpuart, self.lpuart, FIFO, TXOF: TXOF_1);
self.write_byte(byte);
ral::read_reg!(ral::lpuart, self.lpuart, FIFO, TXOF == TXOF_0)
}
pub fn try_read(&mut self) -> Result<Option<u8>, ReadFlags> {
let data = self.read_data();
if data.flags().contains(ReadFlags::RXEMPT) {
Ok(None)
} else if data
.flags()
.intersects(ReadFlags::PARITY_ERROR | ReadFlags::FRAME_ERROR | ReadFlags::NOISY)
{
Err(data.flags())
} else {
Ok(Some(data.into()))
}
}
}
fn flush_fifo<const N: u8>(lpuart: &Instance<N>, direction: Direction) {
match direction {
Direction::Rx => ral::modify_reg!(ral::lpuart, lpuart, FIFO, RXFLUSH: RXFLUSH_1),
Direction::Tx => ral::modify_reg!(ral::lpuart, lpuart, FIFO, TXFLUSH: TXFLUSH_1),
}
}
pub struct Disabled<'a, const N: u8> {
lpuart: &'a Instance<N>,
te: bool,
re: bool,
}
impl<const N: u8> Drop for Disabled<'_, N> {
fn drop(&mut self) {
ral::modify_reg!(ral::lpuart, self.lpuart, CTRL, TE: self.te as u32, RE: self.re as u32);
}
}
impl<'a, const N: u8> Disabled<'a, N> {
fn new(lpuart: &'a Instance<N>) -> Self {
let (te, re) = ral::read_reg!(ral::lpuart, lpuart, CTRL, TE, RE);
ral::modify_reg!(ral::lpuart, lpuart, CTRL, TE: TE_0, RE: RE_0);
for direction in [Direction::Rx, Direction::Tx] {
flush_fifo(lpuart, direction);
}
Self {
lpuart,
te: te != 0,
re: re != 0,
}
}
pub fn set_baud(&mut self, baud: &Baud) {
ral::modify_reg!(ral::lpuart, self.lpuart,
BAUD,
OSR: baud.osr.clamp(4, 32) - 1,
SBR: baud.sbr.min((1 << 13) - 1),
BOTHEDGE: baud.bothedge as u32)
}
pub fn set_parity(&mut self, parity: Option<Parity>) {
ral::modify_reg!(
ral::lpuart,
self.lpuart,
CTRL,
PE: parity.is_some() as u32,
M: parity.is_some() as u32,
PT: parity.map(|p| p as u32).unwrap_or(0u32)
);
}
#[inline]
pub fn set_inversion(&mut self, direction: Direction, inverted: bool) {
match direction {
Direction::Rx => {
ral::modify_reg!(ral::lpuart, self.lpuart, STAT, RXINV: inverted as u32)
}
Direction::Tx => {
ral::modify_reg!(ral::lpuart, self.lpuart, CTRL, TXINV: inverted as u32)
}
}
}
#[inline]
pub fn disable_fifo(&mut self, direction: Direction) {
match direction {
Direction::Rx => ral::modify_reg!(ral::lpuart, self.lpuart, FIFO, RXFE: RXFE_0),
Direction::Tx => ral::modify_reg!(ral::lpuart, self.lpuart, FIFO, TXFE: TXFE_0),
}
}
#[inline]
pub fn enable_fifo(&mut self, watermark: Watermark) -> u32 {
let size = match watermark.direction {
Direction::Rx => 1 << ral::read_reg!(ral::lpuart, self.lpuart, PARAM, RXFIFO),
Direction::Tx => 1 << ral::read_reg!(ral::lpuart, self.lpuart, PARAM, TXFIFO),
};
let size = watermark.size.min(size - 1);
match watermark.direction {
Direction::Rx => {
ral::modify_reg!(ral::lpuart, self.lpuart, WATER, RXWATER: size);
ral::modify_reg!(ral::lpuart, self.lpuart, FIFO, RXFE: RXFE_1);
}
Direction::Tx => {
ral::modify_reg!(ral::lpuart, self.lpuart, WATER, TXWATER: size);
ral::modify_reg!(ral::lpuart, self.lpuart, FIFO, TXFE: TXFE_1);
}
};
size
}
pub fn set_interrupts(&mut self, interrupts: Interrupts) {
let ctrl_flags = interrupts & Interrupts::ctrl_mask();
let fifo_flags = interrupts & Interrupts::fifo_mask();
ral::modify_reg!(ral::lpuart, self.lpuart, CTRL, |ctrl| {
let ctrl = ctrl & !Interrupts::ctrl_mask().bits();
ctrl | ctrl_flags.bits()
});
ral::modify_reg!(ral::lpuart, self.lpuart, FIFO, |fifo| {
let fifo = fifo & !Interrupts::fifo_mask().bits();
fifo | fifo_flags.bits()
});
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Baud {
pub osr: u32,
pub sbr: u32,
pub bothedge: bool,
}
impl Baud {
pub const fn value(self, source_clock_hz: u32) -> u32 {
source_clock_hz / (self.sbr * self.osr)
}
pub const fn compute(source_clock_hz: u32, baud: u32) -> Baud {
const fn max(left: u32, right: u32) -> u32 {
if left > right {
left
} else {
right
}
}
const fn min(left: u32, right: u32) -> u32 {
if left < right {
left
} else {
right
}
}
let mut err = u32::MAX;
let mut best_osr = 0;
let mut best_sbr = 0;
let mut osr = if baud > 3_000_000 { 4 } else { 8 };
while osr <= 32 {
let mut sbr = 1;
while sbr < 8192 {
let b = source_clock_hz / (sbr * osr);
let e = max(baud, b) - min(baud, b);
if e < err {
err = e;
best_osr = osr;
best_sbr = sbr;
}
sbr += 1;
}
osr += 1;
}
Baud {
osr: best_osr,
sbr: best_sbr,
bothedge: 4 <= best_osr && best_osr <= 7,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u32)]
pub enum Parity {
Even = 0,
Odd = 1,
}
impl Parity {
pub const NONE: Option<Parity> = None;
pub const EVEN: Option<Parity> = Some(Parity::Even);
pub const ODD: Option<Parity> = Some(Parity::Odd);
}
bitflags::bitflags! {
pub struct ReadFlags : u32 {
const NOISY = 1 << 15;
const PARITY_ERROR = 1 << 14;
const FRAME_ERROR = 1 << 13;
const RXEMPT = 1 << 12;
const IDLINE = 1 << 11;
}
}
bitflags::bitflags! {
pub struct Interrupts : u32 {
const OVERRUN = 1 << 27;
const NOISE_ERROR = 1 << 26;
const FRAMING_ERROR = 1 << 25;
const PARITY_ERROR = 1 << 24;
const TRANSMIT_EMPTY = 1 << 23;
const TRANSMIT_COMPLETE = 1 << 22;
const RECEIVE_FULL = 1 << 21;
const TRANSMIT_OVERFLOW = 1 << 9;
const RECEIVE_UNDERFLOW = 1 << 8;
}
}
impl Interrupts {
const fn fifo_mask() -> Self {
Self::from_bits_truncate(
Interrupts::TRANSMIT_OVERFLOW.bits() | Interrupts::RECEIVE_UNDERFLOW.bits(),
)
}
const fn ctrl_mask() -> Self {
Self::from_bits_truncate(Self::all().bits() & !Self::fifo_mask().bits())
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(transparent)]
pub struct ReadData(u32);
impl ReadData {
#[inline]
pub fn flags(self) -> ReadFlags {
ReadFlags::from_bits_truncate(self.0)
}
#[inline]
pub fn raw(self) -> u32 {
self.0
}
}
impl From<ReadData> for u8 {
#[inline]
fn from(read_data: ReadData) -> u8 {
read_data.0 as u8
}
}
bitflags::bitflags! {
pub struct Status : u32 {
const RECEIVE_ACTIVE = 1 << 24;
const TRANSMIT_EMPTY = 1 << 23;
const TRANSMIT_COMPLETE = 1 << 22;
const RECEIVE_FULL = 1 << 21;
const IDLE = 1 << 20;
const OVERRUN = 1 << 19;
const NOISY = 1 << 18;
const FRAME_ERROR = 1 << 17;
const PARITY_ERROR = 1 << 16;
const TRANSMIT_OVERFLOW = 1 << 13;
const RECEIVE_UNDERFLOW = 1 << 12;
}
}
impl Status {
const FIFO_SHIFT: u32 = 4;
pub const W1C: Status =
Self::from_bits_truncate(Self::all().bits() & !Self::read_only_mask().bits());
const fn read_only_mask() -> Self {
Self::from_bits_truncate(
Self::RECEIVE_ACTIVE.bits()
| Self::TRANSMIT_EMPTY.bits()
| Self::TRANSMIT_COMPLETE.bits()
| Self::RECEIVE_FULL.bits(),
)
}
const fn fifo_mask() -> Self {
Self::from_bits_truncate(Self::TRANSMIT_OVERFLOW.bits() | Self::RECEIVE_UNDERFLOW.bits())
}
const fn stat_mask() -> Self {
Self::from_bits_truncate(Self::all().bits() & !Self::fifo_mask().bits())
}
const fn fifo_bits(self) -> u32 {
(self.bits & Self::fifo_mask().bits()) << Self::FIFO_SHIFT
}
const fn stat_bits(self) -> u32 {
self.bits & Self::stat_mask().bits()
}
const fn from_registers(stat: u32, fifo: u32) -> Self {
Self::from_bits_truncate(stat | (fifo >> Self::FIFO_SHIFT))
}
}
#[derive(Debug, Clone, Copy)]
pub struct Watermark {
direction: Direction,
size: u32,
}
impl Watermark {
#[inline]
pub const fn tx(size: u32) -> Self {
Watermark {
direction: Direction::Tx,
size,
}
}
#[inline]
pub const fn rx(size: core::num::NonZeroU32) -> Self {
Watermark {
direction: Direction::Rx,
size: size.get(),
}
}
}
impl<P, const N: u8> eh02::serial::Write<u8> for Lpuart<P, N> {
type Error = core::convert::Infallible;
fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> {
self.flush()?;
self.write_byte(word);
Ok(())
}
fn flush(&mut self) -> nb::Result<(), Self::Error> {
if !self.status().contains(Status::TRANSMIT_EMPTY) {
Err(nb::Error::WouldBlock)
} else {
Ok(())
}
}
}
impl<P, const N: u8> eh02::serial::Read<u8> for Lpuart<P, N> {
type Error = ReadFlags;
fn read(&mut self) -> nb::Result<u8, Self::Error> {
let data = self.read_data();
self.clear_status(Status::W1C);
if data.flags().contains(ReadFlags::RXEMPT) {
Err(nb::Error::WouldBlock)
} else if data
.flags()
.intersects(ReadFlags::PARITY_ERROR | ReadFlags::FRAME_ERROR | ReadFlags::NOISY)
{
Err(nb::Error::Other(data.flags()))
} else {
Ok(data.into())
}
}
}
impl<P, const N: u8> eh02::blocking::serial::Write<u8> for Lpuart<P, N> {
type Error = core::convert::Infallible;
fn bwrite_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> {
for word in buffer {
nb::block!(eh02::serial::Write::write(self, *word))?;
}
Ok(())
}
fn bflush(&mut self) -> Result<(), Self::Error> {
nb::block!(eh02::serial::Write::flush(self))?;
Ok(())
}
}
impl eio06::Error for ReadFlags {
fn kind(&self) -> eio06::ErrorKind {
eio06::ErrorKind::Other
}
}
impl<P, const N: u8> eio06::ErrorType for Lpuart<P, N> {
type Error = ReadFlags;
}
impl<P, const N: u8> eio06::WriteReady for Lpuart<P, N> {
fn write_ready(&mut self) -> Result<bool, Self::Error> {
Ok(self.status().contains(Status::TRANSMIT_EMPTY))
}
}
impl<P, const N: u8> eio06::ReadReady for Lpuart<P, N> {
fn read_ready(&mut self) -> Result<bool, Self::Error> {
Ok(self.status().contains(Status::RECEIVE_FULL))
}
}
impl<P, const N: u8> eio06::Write for Lpuart<P, N> {
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
let mut num_written = 0;
for word in buf {
if num_written == 0 {
while !self.try_write(*word) {}
} else {
if !self.try_write(*word) {
break;
}
}
num_written += 1;
}
Ok(num_written)
}
fn flush(&mut self) -> Result<(), Self::Error> {
while !self.status().contains(Status::TRANSMIT_COMPLETE) {}
Ok(())
}
}
impl<P, const N: u8> eio06::Read for Lpuart<P, N> {
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
let mut num_read = 0;
for word in buf {
let data = if num_read == 0 {
loop {
if let Some(data) = self.try_read()? {
break data;
}
}
} else {
if let Some(data) = self.try_read()? {
data
} else {
break;
}
};
*word = data;
num_read += 1;
}
Ok(num_read)
}
}
#[cfg(test)]
mod tests {
use super::{Baud, ReadData, ReadFlags, Status};
#[test]
fn approximate_baud() {
const UART_CLOCK_HZ: u32 = 24_000_000;
const EXPECTED_BAUD: u32 = 115384;
const TARGET_BAUD: u32 = 115200;
const BAUD: Baud = Baud::compute(UART_CLOCK_HZ, TARGET_BAUD);
assert_eq!(BAUD.value(UART_CLOCK_HZ), EXPECTED_BAUD);
assert!(BAUD.sbr == 8 || BAUD.sbr == 26, "SBR: {}", BAUD.sbr);
if BAUD.sbr == 8 {
assert_eq!(BAUD.osr, 26);
} else {
assert_eq!(BAUD.osr, 8);
}
assert!(!BAUD.bothedge);
}
#[test]
fn non_default_sbr_baud() {
const UART_CLOCK_HZ: u32 = 24_000_000;
const EXPECTED_BAUD: u32 = 9600;
const TARGET_BAUD: u32 = 9600;
const BAUD: Baud = Baud::compute(UART_CLOCK_HZ, TARGET_BAUD);
assert_eq!(BAUD.value(UART_CLOCK_HZ), EXPECTED_BAUD);
assert_eq!(BAUD.osr, 10, "OSR: {}", BAUD.osr);
assert_eq!(BAUD.sbr, 250, "SBR: {}", BAUD.sbr);
assert!(!BAUD.bothedge);
}
#[test]
fn max_baud() {
const UART_CLOCK_HZ: u32 = 24_000_000;
const EXPECTED_BAUD: u32 = 6_000_000;
const TARGET_BAUD: u32 = 6_000_000;
const BAUD: Baud = Baud::compute(UART_CLOCK_HZ, TARGET_BAUD);
assert_eq!(BAUD.value(UART_CLOCK_HZ), EXPECTED_BAUD);
assert_eq!(BAUD.osr, 4, "OSR: {}", BAUD.osr);
assert_eq!(BAUD.sbr, 1, "SBR: {}", BAUD.sbr);
assert!(BAUD.bothedge);
}
#[test]
fn read_data_flags() {
let read_data = ReadData(1 << 15 | 1 << 13);
let flags = read_data.flags();
assert!(flags.contains(ReadFlags::NOISY));
assert!(!flags.contains(ReadFlags::PARITY_ERROR));
assert!(flags.contains(ReadFlags::FRAME_ERROR));
assert!(!flags.contains(ReadFlags::RXEMPT));
assert!(!flags.contains(ReadFlags::IDLINE));
assert!(flags.intersects(ReadFlags::NOISY | ReadFlags::PARITY_ERROR));
assert!(!flags.intersects(ReadFlags::RXEMPT | ReadFlags::PARITY_ERROR));
}
#[test]
fn status_flags() {
assert_eq!(Status::fifo_mask().bits(), (1 << 13) | (1 << 12));
assert_eq!(Status::fifo_mask().fifo_bits(), (1 << 17) | (1 << 16));
assert_eq!(Status::stat_mask().bits(), 0x01FF_0000);
assert_eq!(Status::stat_mask().stat_bits(), 0x01FF_0000);
assert_eq!(Status::W1C.bits(), 0x001F_3000);
assert!(Status::from_registers(0, (1 << 17) | (1 << 16))
.contains(Status::TRANSMIT_OVERFLOW | Status::RECEIVE_UNDERFLOW));
assert!(Status::from_registers(u32::MAX, 0).contains(Status::stat_mask()));
assert!(Status::all().contains(Status::TRANSMIT_EMPTY));
}
}