Files
novaOS/src/peripherals/uart.rs

147 lines
3.2 KiB
Rust

use core::{
arch::asm,
fmt::{self, Write},
};
use crate::{read_address, write_address};
const BAUD: u32 = 115200;
const UART_CLK: u32 = 48_000_000;
const UART0_DR: u32 = 0x3F20_1000;
const UART0_FR: u32 = 0x3F20_1018;
const UART0_FR_TXFF: u32 = 1 << 5;
const UART0_IBRD: u32 = 0x3F20_1024;
const UART0_FBRD: u32 = 0x3F20_1028;
const UART0_CR: u32 = 0x3F20_1030;
const UART0_CR_UARTEN: u32 = 1 << 0;
const UART0_CR_TXE: u32 = 1 << 8;
const UART0_CR_RXE: u32 = 1 << 9;
const UART0_LCRH: u32 = 0x3F20_102C;
const UART0_LCRH_FEN: u32 = 1 << 4;
const UART0_IMSC: u32 = 0x3F20_1038;
const UART0_IMSC_RXIM: u32 = 1 << 4;
const UART0_ICR: u32 = 0x3F20_1044;
pub struct Uart;
impl Write for Uart {
fn write_str(&mut self, s: &str) -> core::fmt::Result {
for byte in s.bytes() {
while (unsafe { read_address(UART0_FR) } & UART0_FR_TXFF) != 0 {
unsafe { asm!("nop") }
}
unsafe { write_address(UART0_DR, byte as u32) };
}
// wait till uart is not busy anymore
while ((unsafe { read_address(UART0_FR) } >> 3) & 0b1) != 0 {}
Ok(())
}
}
#[macro_export]
macro_rules! print {
() => {};
($($arg:tt)*) => {
$crate::peripherals::uart::_print(format_args!($($arg)*))
};
}
#[macro_export]
macro_rules! println {
() => {};
($($arg:tt)*) => {
$crate::print!($($arg)*);
$crate::print!("\r\n");
};
}
pub fn _print(args: fmt::Arguments) {
let _ = Uart.write_fmt(args);
}
/// Initialize UART peripheral
pub fn uart_init() {
let baud_div_times_64 = (UART_CLK * 4) / BAUD;
let ibrd = baud_div_times_64 / 64;
let fbrd = baud_div_times_64 % 64;
uart_enable(false);
uart_fifo_enable(true);
unsafe {
write_address(UART0_IBRD, ibrd);
write_address(UART0_FBRD, fbrd);
}
uart_enable_rx_interrupt();
uart_set_lcrh(0b11, true);
// Enable transmit, receive and uart
let mut cr = unsafe { read_address(UART0_CR) };
cr |= UART0_CR_UARTEN | UART0_CR_TXE | UART0_CR_RXE;
unsafe { write_address(UART0_CR, cr) };
}
/// Enable UARTEN
fn uart_enable(enable: bool) {
let mut cr = unsafe { read_address(UART0_CR) };
if enable {
cr |= UART0_CR_UARTEN;
} else {
cr &= !UART0_CR_UARTEN;
}
unsafe { write_address(UART0_CR, cr) };
}
/// Enable UART FIFO
fn uart_fifo_enable(enable: bool) {
let mut lcrh = unsafe { read_address(UART0_LCRH) };
if enable {
lcrh |= UART0_LCRH_FEN;
} else {
lcrh &= !UART0_LCRH_FEN;
}
unsafe { write_address(UART0_LCRH, lcrh) };
}
#[inline(always)]
fn uart_enable_rx_interrupt() {
unsafe { write_address(UART0_IMSC, UART0_IMSC_RXIM) };
}
/// Set UART word length and set FIFO status
#[inline(always)]
fn uart_set_lcrh(wlen: u32, enable_fifo: bool) {
let mut value = (wlen & 0b11) << 5;
if enable_fifo {
value |= UART0_LCRH_FEN;
}
unsafe { write_address(UART0_LCRH, value) };
}
#[inline(always)]
pub fn read_uart_data() -> char {
(unsafe { read_address(UART0_DR) } & 0xFF) as u8 as char
}
#[inline(always)]
pub fn clear_uart_interrupt_state() {
unsafe {
write_address(UART0_ICR, 1 << 4);
}
}