You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
147 lines
4.3 KiB
Rust
147 lines
4.3 KiB
Rust
//! 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
|
|
}
|