use crate::IO_BASE; use crate::gpio::{Function, Gpio}; use volatile::{ReadOnly, Volatile}; /// The `AUXENB` register from page 9 of the BCM2837 documentation. const AUX_ENABLES: *mut Volatile = (IO_BASE + 0x215004) as *mut Volatile; /// The base address for the `MU` registers. const MU_REG_BASE: usize = IO_BASE + 0x215040; /// Enum representing bit fields of the `AUX_MU_IIR_REG` register. #[repr(u8)] pub enum MiniUartInterruptId { Transmit = 0b010, Recive = 0b100, } /// Enum representing bit fields of the `AUX_MU_LSR_REG` register. #[repr(u8)] enum LsrStatus { DataReady = 1, TxAvailable = 1 << 5, } /// MU registers starting from `MU_REG_BASE` (ref: peripherals 2.1, page 8) #[repr(C)] #[allow(non_snake_case)] struct Registers { AUX_MU_IO_REG: Volatile, __r0: [u8; 3], AUX_MU_IER_REG: Volatile, __r1: [u8; 3], AUX_MU_IIR_REG: Volatile, __r2: [u8; 3], AUX_MU_LCR_REG: Volatile, __r3: [u8; 3], AUX_MU_MCR_REG: Volatile, __r4: [u8; 3], AUX_MU_LSR_REG: ReadOnly, __r5: [u8; 3], AUX_MU_MSR_REG: ReadOnly, __r6: [u8; 3], AUX_MU_SCRATCH: Volatile, __r7: [u8; 3], AUX_MU_CNTL_REG: Volatile, __r8: [u8; 3], AUX_MU_STAT_REG: ReadOnly, AUX_MU_BAUD_REG: Volatile, } /// The Raspberry Pi's "mini UART". pub struct MiniUart { registers: &'static mut Registers, timeout: Option, } impl MiniUart { /// Returns a new instance of `MiniUart`. #[inline] pub fn new() -> MiniUart { let registers = unsafe { &mut *(MU_REG_BASE as *mut Registers) }; MiniUart { registers: registers, timeout: None, } } /// Initializes the mini UART by enabling it as an auxiliary peripheral, /// setting the data size to 8 bits, setting the BAUD rate to ~115200 (baud /// divider of 270), setting GPIO pins 14 and 15 to alternative function 5 /// (TXD1/RDXD1), and finally enabling the UART transmitter and receiver. /// /// By default, reads will never time out. To set a read timeout, use /// `set_read_timeout()`. pub fn init(&mut self) { // Enable the mini UART as an auxiliary device. unsafe { (*AUX_ENABLES).write(1) } Gpio::new(14).into_alt(Function::Alt5).set_gpio_pd(0); Gpio::new(15).into_alt(Function::Alt5).set_gpio_pd(0); self.registers.AUX_MU_CNTL_REG.write(0); // Disable auto flow control and disable receiver and transmitter (for now) self.registers.AUX_MU_IER_REG.write(1); // Enable receive interrupts and disable transmit interrupts self.registers.AUX_MU_LCR_REG.write(3); // Enable 8 bit mode self.registers.AUX_MU_MCR_REG.write(0); // Set RTS line to be always high self.registers.AUX_MU_BAUD_REG.write(270); // Set baud rate to 115200 self.registers.AUX_MU_CNTL_REG.write(3); // Finally, enable transmitter and receiver } /// Set the read timeout to `milliseconds` milliseconds. pub fn set_read_timeout(&mut self, milliseconds: u32) { self.timeout = Some(milliseconds) } /// Write the byte `byte`. This method blocks until there is space available /// in the output FIFO. pub fn write_byte(&mut self, byte: u8) { while self.registers.AUX_MU_LSR_REG.read() & (LsrStatus::TxAvailable as u8) == 0 {} self.registers.AUX_MU_IO_REG.write(byte); } /// Returns `true` if there is at least one byte ready to be read. If this /// method returns `true`, a subsequent call to `read_byte` is guaranteed to /// return immediately. This method does not block. pub fn has_byte(&self) -> bool { self.registers.AUX_MU_LSR_REG.read() & (LsrStatus::DataReady as u8) != 0 } /// Blocks until there is a byte ready to read. If a read timeout is set, /// this method blocks for at most that amount of time. Otherwise, this /// method blocks indefinitely until there is a byte to read. /// /// Returns `Ok(())` if a byte is ready to read. Returns `Err(())` if the /// timeout expired while waiting for a byte to be ready. If this method /// returns `Ok(())`, a subsequent call to `read_byte` is guaranteed to /// return immediately. pub fn wait_for_byte(&self) -> Result<(), ()> { unimplemented!() } /// Reads a byte. Blocks indefinitely until a byte is ready to be read. pub fn read_byte(&self) -> u8 { while !self.has_byte() {} self.registers.AUX_MU_IO_REG.read() } // Read `AUX_MU_IIR_REG` and determine if the interrupt `id` is pending. pub fn interrupt_is_pending(&self, id: MiniUartInterruptId) -> bool { self.registers.AUX_MU_IIR_REG.read() & 0b110 == id as u8 } }