//! Driver for the UART. use core::arch::asm; use core::fmt; use crate::traits::console::{Write, Console}; use crate::traits::synchronization::{DummyMutex, Mutex}; use super::memory_map::uart as mm; pub struct Uart<'a> { inner: DummyMutex>, } pub struct UartInner<'a> { initialized: bool, memory_map: &'a mm::UartMemoryMap, } impl<'a> UartInner<'a> { /// Constructor for [`Uart`]. const fn new(memory_map: &'a mm::UartMemoryMap) -> Self { Self { initialized: false, memory_map } } /// Initialise the UART. fn init(&mut self) { // TODO: Recover from possible previous test. self.flush(); // Stop UART self.memory_map.cr_uarten.read_and_write(0); // Flush the FIFOs self.memory_map.lcrh_fen.read_and_write(0); // Clear all interrupt self.memory_map.icr.write_without_read(0); // Config UART // 8N1 115_200 bauds. // divbaud = freq/16/baudrate = 48_000_000 / 16 / 115_200 = 26.041666666666668 // => IBRD = 26 // => FBRD = round(0.041666666666668 * 64) = 3 // TODO: why 64? self.memory_map.ibrd.write_without_read(26); self.memory_map.fbrd.write_without_read(3); // Set word len to 8 let lcrh_val = self.memory_map.lcrh_wlen.read_and_write_to_u32(0b11, 0); // Reenable the FIFOs let lcrh_val = self.memory_map.lcrh_fen.read_and_write_to_u32(1, lcrh_val); self.memory_map.lcrh.write_without_read(lcrh_val); let cr_val = self.memory_map.cr_txe.read_and_write_to_u32(1, 0); // TODO: let cr_val = self.memory_map.cr_rxe.read_and_write_to_u32(1, 0); to enable read self.memory_map.cr.write_without_read(cr_val); // Start the UART self.memory_map.cr_uarten.read_and_write(1); self.initialized = true; } /// Test if the UART is busy. fn is_busy(&self) -> bool { self.memory_map.fr_busy.read() != 0 } /// Test if the the TX FIFO is full. fn is_tx_full(&self) -> bool { self.memory_map.fr_txff.read() != 0 } /// Write a character to the Uart fn write_char(&self, c: char) { if !self.initialized { panic!("Cannot write to a non initialized UART"); } while self.is_tx_full() { use super::gpio; let _ = gpio::set_pin_output_state(20, gpio::PinOutputState::High); unsafe { asm!("nop") }; } self.memory_map.dr_data.write_without_read(c as u32); } /// Flush the uart fn flush(&self) { while self.is_busy() { unsafe { asm!("nop") }; } } } // Allow the use of uart for fmt::Write::write_fmt. impl fmt::Write for UartInner<'_> { fn write_str(&mut self, s: &str) -> fmt::Result { for c in s.chars() { if c == '\n' { UartInner::write_char(self, '\r'); } UartInner::write_char(self, c); } Ok(()) } } impl Write for Uart<'_> { fn write_char(&self, c: char) { self.inner.lock(|uart| uart.write_char(c)) } fn write_fmt(&self, args: fmt::Arguments) -> fmt::Result { self.inner.lock(|uart| fmt::Write::write_fmt(uart, args)) } fn flush(&self) { self.inner.lock(|uart| uart.flush()) } } impl<'a> Uart<'a> { // TODO: not sure this should be public? public in bsp only? /// Create a new UART object const fn new(memory_map: &'a mm::UartMemoryMap) -> Self { Self { inner: DummyMutex::new( UartInner::new(memory_map) ) } } /// Initialize the UART. fn init(&self) { self.inner.lock(|uart| uart.init()) } } impl Console for Uart<'_> {} /// The UART object static UART: Uart = Uart::new(&mm::UART); pub fn init() { UART.init(); } // TODO: move? /// Return a reference to the Uart Output. pub fn console() -> &'static dyn Console { &UART }