use core::cell::UnsafeCell;
use super::UART;
#[allow(unused)]
use crate::gpio::{Function, Pin};
use crate::{
ccu::{self, ClockGate, Clocks},
time::Bps,
CCU,
};
use base_address::{BaseAddress, Dynamic, Static};
use uart16550::{CharLen, Register, Uart16550, PARITY};
#[repr(C)]
pub struct RegisterBlock {
uart16550: Uart16550<u32>,
_reserved0: [u32; 24],
usr: USR<u32>, }
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct Config {
pub baudrate: Bps,
pub wordlength: WordLength,
pub parity: Parity,
pub stopbits: StopBits,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum WordLength {
Five,
Six,
Seven,
Eight,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum Parity {
None,
Odd,
Even,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum StopBits {
One,
Two,
}
impl<const B: usize> UART<Static<B>> {
#[inline]
pub const unsafe fn steal_static() -> UART<Static<B>> {
UART { base: Static::<B> }
}
}
impl UART<Dynamic> {
#[inline]
pub unsafe fn steal_dynamic(base: *const ()) -> UART<Dynamic> {
UART {
base: Dynamic::new(base as usize),
}
}
}
impl core::ops::Deref for RegisterBlock {
type Target = Uart16550<u32>;
fn deref(&self) -> &Self::Target {
&self.uart16550
}
}
pub struct Serial<A: BaseAddress, const I: usize, PINS: Pins<I>> {
uart: UART<A>,
pins: PINS,
}
impl<A: BaseAddress, const I: usize, PINS: Pins<I>> Serial<A, I, PINS> {
#[inline]
pub fn new<A1: BaseAddress>(
uart: UART<A>,
pins: PINS,
config: impl Into<Config>,
clocks: &Clocks,
ccu: &CCU<A1>,
) -> Self {
let Config {
baudrate,
wordlength,
parity,
stopbits,
} = config.into();
let bps = baudrate.0;
unsafe { PINS::ClockGate::reset(&ccu) };
let interrupt_types = uart.ier().read();
uart.ier().write(
interrupt_types
.disable_ms()
.disable_rda()
.disable_rls()
.disable_thre(),
);
let uart_clk = (clocks.apb1.0 + 8 * bps) / (16 * bps);
uart.write_divisor(uart_clk as u16);
let char_len = match wordlength {
WordLength::Five => CharLen::FIVE,
WordLength::Six => CharLen::SIX,
WordLength::Seven => CharLen::SEVEN,
WordLength::Eight => CharLen::EIGHT,
};
let one_stop_bit = matches!(stopbits, StopBits::One);
let parity = match parity {
Parity::None => PARITY::NONE,
Parity::Odd => PARITY::ODD,
Parity::Even => PARITY::EVEN,
};
let lcr = uart.lcr().read();
uart.lcr().write(
lcr.set_char_len(char_len)
.set_one_stop_bit(one_stop_bit)
.set_parity(parity),
);
Serial { uart, pins }
}
#[inline]
pub fn free<A1: BaseAddress>(self, ccu: &CCU<A1>) -> (UART<A>, PINS) {
unsafe { PINS::ClockGate::free(&ccu) };
(self.uart, self.pins)
}
}
pub trait Pins<const I: usize> {
type ClockGate: ccu::ClockGate;
}
pub trait Transmit<const I: usize> {}
pub trait Receive<const I: usize> {}
impl<const I: usize, T, R> Pins<I> for (T, R)
where
T: Transmit<I>,
R: Receive<I>,
{
type ClockGate = ccu::UART<I>;
}
impl<A: BaseAddress, const I: usize, PINS: Pins<I>> embedded_hal::serial::ErrorType
for Serial<A, I, PINS>
{
type Error = embedded_hal::serial::ErrorKind;
}
impl<A: BaseAddress, const I: usize, PINS: Pins<I>> embedded_hal::serial::Write
for Serial<A, I, PINS>
{
#[inline]
fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> {
for c in buffer {
while self.uart.usr.read().busy() {
core::hint::spin_loop()
}
self.uart.rbr_thr().tx_data(*c);
}
Ok(())
}
#[inline]
fn flush(&mut self) -> Result<(), Self::Error> {
while !self.uart.usr.read().transmit_fifo_empty() {
core::hint::spin_loop()
}
Ok(())
}
}
impl<A: BaseAddress, const I: usize, PINS: Pins<I>> embedded_hal_nb::serial::Write<u8>
for Serial<A, I, PINS>
{
#[inline]
fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> {
if self.uart.usr.read().busy() {
return Err(nb::Error::WouldBlock);
}
self.uart.rbr_thr().tx_data(word);
Ok(())
}
#[inline]
fn flush(&mut self) -> nb::Result<(), Self::Error> {
if !self.uart.usr.read().transmit_fifo_empty() {
return Err(nb::Error::WouldBlock);
}
Ok(())
}
}
#[repr(transparent)]
pub struct USR<R: Register>(UnsafeCell<R>);
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[repr(transparent)]
pub struct UartStatus(u8);
impl<R: uart16550::Register> USR<R> {
#[inline]
pub fn write(&self, val: UartStatus) {
unsafe { self.0.get().write_volatile(R::from(val.0)) }
}
#[inline]
pub fn read(&self) -> UartStatus {
UartStatus(unsafe { self.0.get().read_volatile() }.val())
}
}
impl UartStatus {
const RFF: u8 = 1 << 4;
const RFNE: u8 = 1 << 3;
const TFE: u8 = 1 << 2;
const TFNF: u8 = 1 << 1;
const BUSY: u8 = 1 << 0;
#[inline]
pub const fn receive_fifo_full(self) -> bool {
self.0 & Self::RFF != 0
}
#[inline]
pub const fn receive_fifo_not_empty(self) -> bool {
self.0 & Self::RFNE != 0
}
#[inline]
pub const fn transmit_fifo_empty(self) -> bool {
self.0 & Self::TFE != 0
}
#[inline]
pub const fn transmit_fifo_not_full(self) -> bool {
self.0 & Self::TFNF != 0
}
#[inline]
pub const fn busy(self) -> bool {
self.0 & Self::BUSY != 0
}
}
#[cfg(test)]
mod tests {
use super::RegisterBlock;
use memoffset::offset_of;
#[test]
fn offset_uart() {
assert_eq!(offset_of!(RegisterBlock, usr), 0x7c);
}
}