diff --git a/rp2040-hal/Cargo.toml b/rp2040-hal/Cargo.toml index 192cdbf..9fbe9cb 100644 --- a/rp2040-hal/Cargo.toml +++ b/rp2040-hal/Cargo.toml @@ -19,4 +19,4 @@ rp2040-pac = { git = "https://github.com/rp-rs/rp2040-pac", branch="main" } [dev-dependencies] cortex-m-rt = "0.6.13" panic-halt = "0.2.0" -rp2040-boot2 = { git = "https://github.com/rp-rs/rp2040-boot2-rs", branch="main" } \ No newline at end of file +rp2040-boot2 = { git = "https://github.com/rp-rs/rp2040-boot2-rs", branch="main" } diff --git a/rp2040-hal/src/uart.rs b/rp2040-hal/src/uart.rs index c4e1f76..868db57 100644 --- a/rp2040-hal/src/uart.rs +++ b/rp2040-hal/src/uart.rs @@ -1,3 +1,477 @@ -//! Universal asynchronous receiver-transmitter (UART) +//! Universal Asynchronous Receiver Transmitter (UART) // See [Chapter 4 Section 2](https://datasheets.raspberrypi.org/rp2040/rp2040_datasheet.pdf) for more details -// TODO + +use core::convert::Infallible; +use core::ops::Deref; +use embedded_time::fixed_point::FixedPoint; +use embedded_time::rate::Baud; +use embedded_time::rate::Hertz; + +use embedded_hal::serial::{Read, Write}; + +use nb::Error::{Other, WouldBlock}; + +use crate::pac::{ + uart0::{uartlcr_h::W as UART_LCR_H_Writer, RegisterBlock}, + UART0, UART1, +}; + +/// Error type for UART operations. +#[derive(Debug)] +pub enum Error { + /// Bad argument : when things overflow, ... + BadArgument, +} + +/// When there's a read error. +pub struct ReadError<'err> { + /// The type of error + pub err_type: ReadErrorType, + + /// Reference to the data that was read but eventually discared because of the error. + pub discared: &'err [u8], +} + +/// Possible types of read errors. See Chapter 4, Section 2 §8 - Table 436: "UARTDR Register" +pub enum ReadErrorType { + /// Triggered when the FIFO (or shift-register) is overflowed. + Overrun, + + /// Triggered when a break is received + Break, + + /// Triggered when there is a parity mismatch between what's received and our settings. + Parity, + + /// Triggered when the received character didn't have a valid stop bit. + Framing, +} + +/// State of the UART Peripheral. +pub trait State {} + +/// Trait to handle both underlying devices (UART0 & UART1) +pub trait UartDevice: Deref {} + +impl UartDevice for UART0 {} +impl UartDevice for UART1 {} + +/// UART is enabled. +pub struct Enabled; + +/// UART is disabled. +pub struct Disabled; + +impl State for Enabled {} +impl State for Disabled {} + +/// Data bits +pub enum DataBits { + /// 5 bits + Five, + /// 6 bits + Six, + /// 7 bits + Seven, + /// 8 bits + Eight, +} + +/// Stop bits +pub enum StopBits { + /// 1 bit + One, + + /// 2 bits + Two, +} + +/// Parity +/// The "none" state of parity is represented with the Option type (None). +pub enum Parity { + /// Odd parity + Odd, + + /// Even parity + Even, +} + +/// A struct holding the configuration for an UART device. +pub struct UartConfig { + baudrate: Baud, + data_bits: DataBits, + stop_bits: StopBits, + parity: Option, +} + +/// Common configurations for UART. +pub mod common_configs { + use super::{DataBits, StopBits, UartConfig}; + use embedded_time::rate::Baud; + + /// 9600 baud, 8 data bits, no parity, 1 stop bit + pub const _9600_8_N_1: UartConfig = UartConfig { + baudrate: Baud(9600), + data_bits: DataBits::Eight, + stop_bits: StopBits::One, + parity: None, + }; + + /// 19200 baud, 8 data bits, no parity, 1 stop bit + pub const _19200_8_N_1: UartConfig = UartConfig { + baudrate: Baud(19200), + data_bits: DataBits::Eight, + stop_bits: StopBits::One, + parity: None, + }; + + /// 38400 baud, 8 data bits, no parity, 1 stop bit + pub const _38400_8_N_1: UartConfig = UartConfig { + baudrate: Baud(38400), + data_bits: DataBits::Eight, + stop_bits: StopBits::One, + parity: None, + }; + + /// 57600 baud, 8 data bits, no parity, 1 stop bit + pub const _57600_8_N_1: UartConfig = UartConfig { + baudrate: Baud(57600), + data_bits: DataBits::Eight, + stop_bits: StopBits::One, + parity: None, + }; + + /// 115200 baud, 8 data bits, no parity, 1 stop bit + pub const _115200_8_N_1: UartConfig = UartConfig { + baudrate: Baud(115200), + data_bits: DataBits::Eight, + stop_bits: StopBits::One, + parity: None, + }; +} + +/// An UART Peripheral based on an underlying UART device. +pub struct UartPeripheral { + device: D, + _state: S, + config: UartConfig, + effective_baudrate: Baud, +} + +impl UartPeripheral { + fn transition(self, state: To) -> UartPeripheral { + UartPeripheral { + device: self.device, + config: self.config, + effective_baudrate: self.effective_baudrate, + _state: state, + } + } + + /// Releases the underlying device. + pub fn free(self) -> D { + self.device + } +} + +impl UartPeripheral { + /// Enables the provided UART device with the given configuration. + pub fn enable( + mut device: D, + config: UartConfig, + frequency: Hertz, + ) -> Result, Error> { + let effective_baudrate = configure_baudrate(&mut device, &config.baudrate, &frequency)?; + + // Enable the UART, both TX and RX + device.uartcr.write(|w| { + w.uarten().set_bit(); + w.txe().set_bit(); + w.rxe().set_bit(); + w + }); + + device.uartlcr_h.write(|w| { + w.fen().set_bit(); + + set_format(w, &config.data_bits, &config.stop_bits, &config.parity); + w + }); + + device.uartdmacr.write(|w| { + w.txdmae().set_bit(); + w.rxdmae().set_bit(); + w + }); + + Ok(UartPeripheral { + device, + config, + effective_baudrate, + _state: Enabled, + }) + } +} + +impl UartPeripheral { + /// Disable this UART Peripheral, falling back to the Disabled state. + pub fn disable(self) -> UartPeripheral { + // Disable the UART, both TX and RX + self.device.uartcr.write(|w| { + w.uarten().clear_bit(); + w.txe().clear_bit(); + w.rxe().clear_bit(); + w + }); + + self.transition(Disabled) + } + + pub(crate) fn transmit_flushed(&self) -> nb::Result<(), Infallible> { + if self.device.uartfr.read().txfe().bit_is_set() { + Ok(()) + } else { + Err(WouldBlock) + } + } + + fn uart_is_writable(&self) -> bool { + self.device.uartfr.read().txff().bit_is_clear() + } + + fn uart_is_readable(&self) -> bool { + self.device.uartfr.read().rxfe().bit_is_clear() + } + + /// Writes bytes to the UART. + /// This function writes as long as it can. As soon that the FIFO is full, if : + /// - 0 bytes were written, a WouldBlock Error is returned + /// - some bytes were written, it is deemed to be a success + /// Upon success, the remaining slice is returned. + pub fn write_raw<'d>(&self, data: &'d [u8]) -> nb::Result<&'d [u8], Infallible> { + let mut bytes_written = 0; + + for c in data { + if !self.uart_is_writable() { + if bytes_written == 0 { + return Err(WouldBlock); + } else { + return Ok(&data[bytes_written..]); + } + } + + self.device.uartdr.write(|w| unsafe { + w.data().bits(*c); + w + }); + + bytes_written += 1; + } + Ok(&data[bytes_written..]) + } + + /// Reads bytes from the UART. + /// This function reads as long as it can. As soon that the FIFO is empty, if : + /// - 0 bytes were read, a WouldBlock Error is returned + /// - some bytes were read, it is deemed to be a success + /// Upon success, the remaining slice is returned. + pub fn read_raw<'b>(&self, buffer: &'b mut [u8]) -> nb::Result<&'b mut [u8], ReadError<'b>> { + let mut bytes_read = 0; + + Ok(loop { + if !self.uart_is_readable() { + if bytes_read == 0 { + return Err(WouldBlock); + } else { + break &mut buffer[bytes_read..]; + } + } + + if bytes_read < buffer.len() { + let mut error: Option = None; + + if self.device.uartdr.read().oe().bit_is_set() { + error = Some(ReadErrorType::Overrun); + } + + if self.device.uartdr.read().be().bit_is_set() { + error = Some(ReadErrorType::Break); + } + + if self.device.uartdr.read().pe().bit_is_set() { + error = Some(ReadErrorType::Parity); + } + + if self.device.uartdr.read().fe().bit_is_set() { + error = Some(ReadErrorType::Framing); + } + + if let Some(err_type) = error { + return Err(Other(ReadError { + err_type, + discared: buffer, + })); + } + + buffer[bytes_read] = self.device.uartdr.read().data().bits(); + bytes_read += 1; + } else { + break &mut buffer[bytes_read..]; + } + }) + } + + /// Writes bytes to the UART. + /// This function blocks until the full buffer has been sent. + pub fn write_full_blocking(&self, data: &[u8]) { + let mut temp = data; + + while !temp.is_empty() { + temp = match self.write_raw(temp) { + Ok(remaining) => remaining, + Err(WouldBlock) => continue, + Err(_) => unreachable!(), + } + } + } + + /// Reads bytes from the UART. + /// This function blocks until the full buffer has been received. + pub fn read_full_blocking(&self, buffer: &mut [u8]) -> Result<(), ReadErrorType> { + let mut offset = 0; + + while offset != buffer.len() { + offset += match self.read_raw(&mut buffer[offset..]) { + Ok(remaining) => remaining.len(), + Err(e) => match e { + Other(inner) => return Err(inner.err_type), + WouldBlock => continue, + }, + } + } + + Ok(()) + } +} + +/// The PL011 (PrimeCell UART) supports a fractional baud rate divider +/// From the wanted baudrate, we calculate the divider's two parts: integer and fractional parts. +/// Code inspired from the C SDK. +fn calculate_baudrate_dividers( + wanted_baudrate: &Baud, + frequency: &Hertz, +) -> Result<(u16, u16), Error> { + // See Chapter 4, Section 2 §7.1 from the datasheet for an explanation of how baudrate is + // calculated + let baudrate_div = frequency + .integer() + .checked_mul(8) + .and_then(|r| r.checked_div(*wanted_baudrate.integer())) + .ok_or(Error::BadArgument)?; + + Ok(match (baudrate_div >> 7, ((baudrate_div & 0x7F) + 1) / 2) { + (0, _) => (1, 0), + + (int_part, _) if int_part >= 65535 => (65535, 0), + + (int_part, frac_part) => (int_part as u16, frac_part as u16), + }) +} + +/// Baudrate configuration. Code loosely inspired from the C SDK. +fn configure_baudrate( + device: &mut dyn UartDevice, + wanted_baudrate: &Baud, + frequency: &Hertz, +) -> Result { + let (baud_div_int, baud_div_frac) = calculate_baudrate_dividers(wanted_baudrate, frequency)?; + + // First we load the integer part of the divider. + device.uartibrd.write(|w| unsafe { + w.baud_divint().bits(baud_div_int as u16); + w + }); + + // Then we load the fractional part of the divider. + device.uartfbrd.write(|w| unsafe { + w.baud_divfrac().bits(baud_div_frac as u8); + w + }); + + // PL011 needs a (dummy) line control register write to latch in the + // divisors. We don't want to actually change LCR contents here. + device.uartlcr_h.modify(|_, w| w); + + Ok(Baud( + (4 * *frequency.integer()) / (64 * baud_div_int + baud_div_frac) as u32, + )) +} + +/// Format configuration. Code loosely inspired from the C SDK. +fn set_format<'w>( + w: &'w mut UART_LCR_H_Writer, + data_bits: &DataBits, + stop_bits: &StopBits, + parity: &Option, +) -> &'w mut UART_LCR_H_Writer { + match parity { + Some(p) => { + w.pen().set_bit(); + match p { + Parity::Odd => w.eps().clear_bit(), + Parity::Even => w.eps().set_bit(), + }; + } + None => { + w.pen().bit(false); + } + }; + + unsafe { + w.wlen().bits(match data_bits { + DataBits::Five => 0b00, + DataBits::Six => 0b01, + DataBits::Seven => 0b10, + DataBits::Eight => 0b11, + }) + }; + + match stop_bits { + StopBits::One => w.stp2().clear_bit(), + StopBits::Two => w.stp2().set_bit(), + }; + + w +} + +impl Read for UartPeripheral { + type Error = ReadErrorType; + + fn read(&mut self) -> nb::Result { + let byte: &mut [u8] = &mut [0; 1]; + + match self.read_raw(byte) { + Ok(_) => Ok(byte[0]), + Err(e) => match e { + Other(inner) => Err(Other(inner.err_type)), + WouldBlock => Err(WouldBlock), + }, + } + } +} + +impl Write for UartPeripheral { + type Error = Infallible; + + fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> { + if self.write_raw(&[word]).is_err() { + Err(WouldBlock) + } else { + Ok(()) + } + } + + fn flush(&mut self) -> nb::Result<(), Self::Error> { + self.transmit_flushed() + } +}