//! Driver for the UART. use core::arch::asm; use core::fmt; use core::ptr::{read_volatile, write_volatile}; use crate::utils::field::Field; use crate::traits::console::{Write, Console}; use super::memory_map::uart::*; pub struct Uart { initialized: bool, } const TMP_UARTEN: Field = Field::new(CR, 0, 1); const TMP_TXE: Field = Field::new(CR, 8, 1); const TMP_BUSY: Field = Field::new(FR, 3, 1); const TMP_TXFF: Field = Field::new(FR, 5, 1); const TMP_FEN: Field = Field::new(LCRH, 4, 1); const TMP_WLEN: Field = Field::new(LCRH, 5, 2); const TMP_IBRD: Field = Field::new(IBRD, 0, 16); const TMP_FBRD: Field = Field::new(FBRD, 0, 6); const TMP_DATA: Field = Field::new(DR, 0, 8); impl Uart { // TODO: not sure this should be public outside of bsp. /// Constructor for [`Uart`]. pub const fn new() -> Self { Uart { initialized: false } } /// Initialise the UART. pub fn init(&mut self) { // TODO: Recover from possible previous test. self.flush(); // Stop UART let mut cr_val = unsafe { read_volatile(TMP_UARTEN.get_address() as *mut u32) }; cr_val &= !TMP_UARTEN.get_mask(); unsafe { write_volatile(TMP_UARTEN.get_address() as *mut u32, cr_val); } // Flush the FIFOs let mut lcrh_val = unsafe { read_volatile(TMP_FEN.get_address() as *mut u32) }; lcrh_val &= !TMP_FEN.get_mask(); unsafe { write_volatile(TMP_FEN.get_address() as *mut u32, lcrh_val); } // Clear all interrupt unsafe { write_volatile(ICR as *mut u32, 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? unsafe { write_volatile(TMP_IBRD.get_address() as *mut u32, 26 & TMP_IBRD.get_mask()); } unsafe { write_volatile(TMP_FBRD.get_address() as *mut u32, 3 & TMP_FBRD.get_mask()); } lcrh_val = 0; // Set word len to 8 lcrh_val |= 0b11 << TMP_WLEN.get_offset(); // Reenable the FIFOs lcrh_val |= TMP_FEN.get_mask(); unsafe { write_volatile(LCRH as *mut u32, lcrh_val); } let mut cr_val = 0; cr_val |= TMP_TXE.get_mask(); // enable TX unsafe { write_volatile(TMP_TXE.get_address() as *mut u32, cr_val); } // Start the UART cr_val |= TMP_UARTEN.get_mask(); unsafe { write_volatile(TMP_UARTEN.get_address() as *mut u32, cr_val); } self.initialized = true; } /// Test if the UART is busy. fn is_busy(&self) -> bool { let fr_val = unsafe { read_volatile(TMP_BUSY.get_address() as *mut u32) }; (fr_val & TMP_BUSY.get_mask()) != 0 } /// Test if the the TX FIFO is full. fn is_tx_full(&self) -> bool { let fr_val = unsafe { read_volatile(TMP_TXFF.get_address() as *mut u32) }; (fr_val & TMP_TXFF.get_mask()) != 0 } } // Allow the use of uart for fmt::Write::write_fmt. impl fmt::Write for Uart { fn write_str(&mut self, s: &str) -> fmt::Result { for c in s.chars() { if c == '\n' { Write::write_char(self, '\r'); } Write::write_char(self, c); } Ok(()) } } // TODO: add sync impl Write for 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") }; } unsafe { write_volatile(TMP_DATA.get_address() as *mut u32, c as u32) }; } fn write_fmt(&self, args: fmt::Arguments) -> fmt::Result { // TODO: use syncronisation here let mut new_uart = Uart { initialized: self.initialized }; fmt::Write::write_fmt(&mut new_uart, args) } fn flush(&self) { while self.is_busy() { unsafe { asm!("nop") }; } } } impl Console for Uart {} static UART: Uart = Uart { initialized: true }; // TODO: use sync pub fn init() { let mut uart = Uart::new(); uart.init(); } // TODO: move? /// Return a reference to the Uart Output. pub fn console() -> &'static dyn Console { &UART }