diff --git a/boards/rp-pico/examples/pico_i2c_pio.rs b/boards/rp-pico/examples/pico_i2c_pio.rs index 6d350ee..b6cb1d0 100644 --- a/boards/rp-pico/examples/pico_i2c_pio.rs +++ b/boards/rp-pico/examples/pico_i2c_pio.rs @@ -88,18 +88,20 @@ fn main() -> ! { &mut pac.RESETS, ); - let mut uart = hal::uart::UartPeripheral::<_, _>::new(pac.UART0, &mut pac.RESETS) + let uart_pins = ( + // UART TX (characters sent from RP2040) on pin 1 (GPIO0) + pins.gpio0.into_mode::(), + // UART RX (characters reveived by RP2040) on pin 2 (GPIO1) + pins.gpio1.into_mode::(), + ); + + let mut uart = hal::uart::UartPeripheral::new(pac.UART0, uart_pins, &mut pac.RESETS) .enable( hal::uart::common_configs::_115200_8_N_1, clocks.peripheral_clock.into(), ) .unwrap(); - // UART TX (characters sent from RP2040) on pin 1 (GPIO0) - let _tx_pin = pins.gpio0.into_mode::(); - // UART RX (characters reveived by RP2040) on pin 2 (GPIO1) - let _rx_pin = pins.gpio1.into_mode::(); - let (mut pio, sm0, _, _, _) = pac.PIO0.split(&mut pac.RESETS); let mut i2c_pio = i2c_pio::I2C::new( diff --git a/rp2040-hal/examples/adc.rs b/rp2040-hal/examples/adc.rs index 950ab7c..c848d93 100644 --- a/rp2040-hal/examples/adc.rs +++ b/rp2040-hal/examples/adc.rs @@ -84,18 +84,20 @@ fn main() -> ! { &mut pac.RESETS, ); + // UART TX (characters sent from pico) on pin 1 (GPIO0) and RX (on pin 2 (GPIO1) + let uart_pins = ( + pins.gpio0.into_mode::(), + pins.gpio1.into_mode::(), + ); + // Create a UART driver - let mut uart = hal::uart::UartPeripheral::<_, _>::new(pac.UART0, &mut pac.RESETS) + let mut uart = hal::uart::UartPeripheral::new(pac.UART0, uart_pins, &mut pac.RESETS) .enable( hal::uart::common_configs::_9600_8_N_1, clocks.peripheral_clock.into(), ) .unwrap(); - // UART TX (characters sent from pico) on pin 1 (GPIO0) and RX (on pin 2 (GPIO1) - let _tx_pin = pins.gpio0.into_mode::(); - let _rx_pin = pins.gpio1.into_mode::(); - // Write to the UART uart.write_full_blocking(b"ADC example\r\n"); diff --git a/rp2040-hal/examples/rom_funcs.rs b/rp2040-hal/examples/rom_funcs.rs index 4a89dd6..e4a349a 100644 --- a/rp2040-hal/examples/rom_funcs.rs +++ b/rp2040-hal/examples/rom_funcs.rs @@ -80,18 +80,19 @@ fn main() -> ! { &mut pac.RESETS, ); - let mut uart = hal::uart::UartPeripheral::<_, _>::new(pac.UART0, &mut pac.RESETS) + let uart_pins = ( + // UART TX (characters sent from RP2040) on pin 1 (GPIO0) + pins.gpio0.into_mode::(), + // UART RX (characters reveived by RP2040) on pin 2 (GPIO1) + pins.gpio1.into_mode::(), + ); + let mut uart = hal::uart::UartPeripheral::new(pac.UART0, uart_pins, &mut pac.RESETS) .enable( hal::uart::common_configs::_9600_8_N_1, clocks.peripheral_clock.into(), ) .unwrap(); - // UART TX (characters sent from RP2040) on pin 1 (GPIO0) - let _tx_pin = pins.gpio0.into_mode::(); - // UART RX (characters reveived by RP2040) on pin 2 (GPIO1) - let _rx_pin = pins.gpio1.into_mode::(); - writeln!(uart, "ROM Copyright: {}", hal::rom_data::copyright_string()).unwrap(); writeln!( uart, diff --git a/rp2040-hal/examples/uart.rs b/rp2040-hal/examples/uart.rs index 0705599..c4e122e 100644 --- a/rp2040-hal/examples/uart.rs +++ b/rp2040-hal/examples/uart.rs @@ -82,18 +82,19 @@ fn main() -> ! { &mut pac.RESETS, ); - let mut uart = hal::uart::UartPeripheral::<_, _>::new(pac.UART0, &mut pac.RESETS) + let uart_pins = ( + // UART TX (characters sent from RP2040) on pin 1 (GPIO0) + pins.gpio0.into_mode::(), + // UART RX (characters reveived by RP2040) on pin 2 (GPIO1) + pins.gpio1.into_mode::(), + ); + let mut uart = hal::uart::UartPeripheral::new(pac.UART0, uart_pins, &mut pac.RESETS) .enable( hal::uart::common_configs::_9600_8_N_1, clocks.peripheral_clock.into(), ) .unwrap(); - // UART TX (characters sent from RP2040) on pin 1 (GPIO0) - let _tx_pin = pins.gpio0.into_mode::(); - // UART RX (characters reveived by RP2040) on pin 2 (GPIO1) - let _rx_pin = pins.gpio1.into_mode::(); - uart.write_full_blocking(b"UART example\r\n"); let mut value = 0u32; diff --git a/rp2040-hal/src/uart/common_configs.rs b/rp2040-hal/src/uart/common_configs.rs new file mode 100644 index 0000000..d23869f --- /dev/null +++ b/rp2040-hal/src/uart/common_configs.rs @@ -0,0 +1,42 @@ +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, +}; diff --git a/rp2040-hal/src/uart/mod.rs b/rp2040-hal/src/uart/mod.rs new file mode 100644 index 0000000..5c0f727 --- /dev/null +++ b/rp2040-hal/src/uart/mod.rs @@ -0,0 +1,47 @@ +//! Universal Asynchronous Receiver Transmitter (UART) +//! +//! See [Chapter 4 Section 2](https://datasheets.raspberrypi.org/rp2040/rp2040_datasheet.pdf) of the datasheet for more details +//! +//! ## Usage +//! +//! See [examples/uart.rs](https://github.com/rp-rs/rp-hal/tree/main/rp2040-hal/examples/uart.rs) for a more complete example +//! ```no_run +//! use rp2040_hal::{clocks::init_clocks_and_plls, gpio::{Pins, FunctionUart}, pac, sio::Sio, uart::{self, UartPeripheral}, watchdog::Watchdog}; +//! +//! const XOSC_CRYSTAL_FREQ: u32 = 12_000_000; // Typically found in BSP crates +//! +//! let mut peripherals = pac::Peripherals::take().unwrap(); +//! let sio = Sio::new(peripherals.SIO); +//! let pins = Pins::new(peripherals.IO_BANK0, peripherals.PADS_BANK0, sio.gpio_bank0, &mut peripherals.RESETS); +//! let mut watchdog = Watchdog::new(peripherals.WATCHDOG); +//! let mut clocks = init_clocks_and_plls(XOSC_CRYSTAL_FREQ, peripherals.XOSC, peripherals.CLOCKS, peripherals.PLL_SYS, peripherals.PLL_USB, &mut peripherals.RESETS, &mut watchdog).ok().unwrap(); +//! +//! // Set up UART on GP0 and GP1 (Pico pins 1 and 2) +//! let pins = ( +//! pins.gpio0.into_mode::(), +//! pins.gpio1.into_mode::(), +//! ); +//! // Need to perform clock init before using UART or it will freeze. +//! let uart = UartPeripheral::new(peripherals.UART0, pins, &mut peripherals.RESETS) +//! .enable( +//! uart::common_configs::_9600_8_N_1, +//! clocks.peripheral_clock.into(), +//! ).unwrap(); +//! +//! uart.write_full_blocking(b"Hello World!\r\n"); +//! ``` + +mod peripheral; +mod pins; +mod reader; +mod utils; +mod writer; + +pub use self::peripheral::UartPeripheral; +pub use self::pins::*; +pub use self::reader::{ReadError, ReadErrorType, Reader}; +pub use self::utils::*; +pub use self::writer::Writer; + +/// Common configurations for UART. +pub mod common_configs; diff --git a/rp2040-hal/src/uart.rs b/rp2040-hal/src/uart/peripheral.rs similarity index 50% rename from rp2040-hal/src/uart.rs rename to rp2040-hal/src/uart/peripheral.rs index 49fced3..0cc18df 100644 --- a/rp2040-hal/src/uart.rs +++ b/rp2040-hal/src/uart/peripheral.rs @@ -16,226 +16,74 @@ //! let mut watchdog = Watchdog::new(peripherals.WATCHDOG); //! let mut clocks = init_clocks_and_plls(XOSC_CRYSTAL_FREQ, peripherals.XOSC, peripherals.CLOCKS, peripherals.PLL_SYS, peripherals.PLL_USB, &mut peripherals.RESETS, &mut watchdog).ok().unwrap(); //! +//! // Set up UART on GP0 and GP1 (Pico pins 1 and 2) +//! let pins = ( +//! pins.gpio0.into_mode::(), +//! pins.gpio1.into_mode::(), +//! ); //! // Need to perform clock init before using UART or it will freeze. -//! let uart = UartPeripheral::<_, _>::new(peripherals.UART0, &mut peripherals.RESETS) +//! let uart = UartPeripheral::new(peripherals.UART0, pins, &mut peripherals.RESETS) //! .enable( //! uart::common_configs::_9600_8_N_1, //! clocks.peripheral_clock.into(), //! ) //! .unwrap(); //! -//! // Set up UART on GP0 and GP1 (Pico pins 1 and 2) -//! let _tx_pin = pins.gpio0.into_mode::(); -//! let _rx_pin = pins.gpio1.into_mode::(); //! uart.write_full_blocking(b"Hello World!\r\n"); //! ``` +use super::*; +use crate::pac::uart0::uartlcr_h::W as UART_LCR_H_Writer; use core::convert::Infallible; use core::fmt; -use core::ops::Deref; +use embedded_hal::serial::{Read, Write}; use embedded_time::fixed_point::FixedPoint; use embedded_time::rate::Baud; use embedded_time::rate::Hertz; +use nb::Error::{Other, WouldBlock}; +use rp2040_pac::{UART0, UART1}; #[cfg(feature = "eh1_0_alpha")] use eh1_0_alpha::serial::nb as eh1; -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, -}; - -use crate::resets::SubsystemReset; - -/// 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" -#[cfg_attr(feature = "eh1_0_alpha", derive(Debug))] -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, -} - -#[cfg(feature = "eh1_0_alpha")] -impl eh1_0_alpha::serial::Error for ReadErrorType { - fn kind(&self) -> eh1_0_alpha::serial::ErrorKind { - match self { - ReadErrorType::Overrun => eh1_0_alpha::serial::ErrorKind::Overrun, - ReadErrorType::Break => eh1_0_alpha::serial::ErrorKind::Other, - ReadErrorType::Parity => eh1_0_alpha::serial::ErrorKind::Parity, - ReadErrorType::Framing => eh1_0_alpha::serial::ErrorKind::FrameFormat, - } - } -} - -/// State of the UART Peripheral. -pub trait State {} - -/// Trait to handle both underlying devices (UART0 & UART1) -pub trait UartDevice: Deref + SubsystemReset {} - -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 { - /// The desired baud rate for the peripheral - pub baudrate: Baud, - /// Number of data bits per character (5, 6, 7 or 8) - pub data_bits: DataBits, - /// Number of stop bits after each character - pub stop_bits: StopBits, - /// Parity Bit: None, Some(Even), Some(Odd) - pub 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 { +pub struct UartPeripheral> { device: D, _state: S, + pins: P, config: UartConfig, effective_baudrate: Baud, } -impl UartPeripheral { - fn transition(self, state: To) -> UartPeripheral { +impl> UartPeripheral { + fn transition(self, state: To) -> UartPeripheral { UartPeripheral { device: self.device, + pins: self.pins, config: self.config, effective_baudrate: self.effective_baudrate, _state: state, } } - /// Releases the underlying device. - pub fn free(self) -> D { - self.device + /// Releases the underlying device and pins. + pub fn free(self) -> (D, P) { + (self.device, self.pins) } } -impl UartPeripheral { +impl> UartPeripheral { /// Creates an UartPeripheral in Disabled state. - pub fn new(device: D, resets: &mut pac::RESETS) -> UartPeripheral { + pub fn new(device: D, pins: P, resets: &mut pac::RESETS) -> UartPeripheral { device.reset_bring_down(resets); device.reset_bring_up(resets); UartPeripheral { device, + _state: Disabled, + pins, config: common_configs::_9600_8_N_1, // placeholder effective_baudrate: Baud(0), - _state: Disabled, } } @@ -244,8 +92,8 @@ impl UartPeripheral { self, config: UartConfig, frequency: Hertz, - ) -> Result, Error> { - let mut device = self.free(); + ) -> Result, Error> { + let (mut device, pins) = self.free(); let effective_baudrate = configure_baudrate(&mut device, &config.baudrate, &frequency)?; device.uartlcr_h.write(|w| { @@ -254,11 +102,14 @@ impl UartPeripheral { w }); - // Enable the UART, both TX and RX + // Enable the UART, and the TX,RC,CTS and RTS based on the pins device.uartcr.write(|w| { w.uarten().set_bit(); - w.txe().set_bit(); - w.rxe().set_bit(); + w.txe().bit(P::TX_ENABLED); + w.rxe().bit(P::RX_ENABLED); + w.ctsen().bit(P::CTS_ENABLED); + w.rtsen().bit(P::RTS_ENABLED); + w }); @@ -271,67 +122,36 @@ impl UartPeripheral { Ok(UartPeripheral { device, config, + pins, effective_baudrate, _state: Enabled, }) } } -impl UartPeripheral { +impl> UartPeripheral { /// Disable this UART Peripheral, falling back to the Disabled state. - pub fn disable(self) -> UartPeripheral { + 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.ctsen().clear_bit(); + w.rtsen().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..]) + super::writer::write_raw(&self.device, data) } /// Reads bytes from the UART. @@ -340,83 +160,75 @@ impl UartPeripheral { /// - some bytes were read, it is deemed to be a success /// Upon success, it will return how many bytes were read. pub fn read_raw<'b>(&self, buffer: &'b mut [u8]) -> nb::Result> { - let mut bytes_read = 0; - - Ok(loop { - if !self.uart_is_readable() { - if bytes_read == 0 { - return Err(WouldBlock); - } else { - break bytes_read; - } - } - - if bytes_read < buffer.len() { - let mut error: Option = None; - - let read = self.device.uartdr.read(); - - if read.oe().bit_is_set() { - error = Some(ReadErrorType::Overrun); - } - - if read.be().bit_is_set() { - error = Some(ReadErrorType::Break); - } - - if read.pe().bit_is_set() { - error = Some(ReadErrorType::Parity); - } - - if 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] = read.data().bits(); - bytes_read += 1; - } else { - break bytes_read; - } - }) + super::reader::read_raw(&self.device, buffer) } /// 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!(), - } - } + super::writer::write_full_blocking(&self.device, data); } /// 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; + super::reader::read_full_blocking(&self.device, buffer) + } - while offset != buffer.len() { - offset += match self.read_raw(&mut buffer[offset..]) { - Ok(bytes_read) => bytes_read, - Err(e) => match e { - Other(inner) => return Err(inner.err_type), - WouldBlock => continue, - }, - } + /// Join the reader and writer halves together back into the original Uart peripheral. + /// + /// A reader/writer pair can be obtained by calling [`split`]. + /// + /// [`split`]: #method.split + pub fn join(reader: Reader, writer: Writer) -> Self { + let _ = writer; + Self { + device: reader.device, + _state: Enabled, + pins: reader.pins, + config: reader.config, + effective_baudrate: reader.effective_baudrate, } + } +} - Ok(()) +impl> UartPeripheral { + /// Split this peripheral into a separate reader and writer. + pub fn split(self) -> (Reader, Writer) { + let reader = Reader { + device: self.device, + pins: self.pins, + config: self.config, + effective_baudrate: self.effective_baudrate, + }; + // Safety: reader and writer will never write to the same address + let device_copy = unsafe { &*UART0::ptr() }; + let writer = Writer { + device: device_copy, + device_marker: core::marker::PhantomData, + pins: core::marker::PhantomData, + }; + (reader, writer) + } +} + +impl> UartPeripheral { + /// Split this peripheral into a separate reader and writer. + pub fn split(self) -> (Reader, Writer) { + let reader = Reader { + device: self.device, + pins: self.pins, + config: self.config, + effective_baudrate: self.effective_baudrate, + }; + // Safety: reader and writer will never write to the same address + let device_copy = unsafe { &*UART1::ptr() }; + let writer = Writer { + device: device_copy, + device_marker: core::marker::PhantomData, + pins: core::marker::PhantomData, + }; + (reader, writer) } } @@ -510,7 +322,7 @@ fn set_format<'w>( w } -impl Read for UartPeripheral { +impl> Read for UartPeripheral { type Error = ReadErrorType; fn read(&mut self) -> nb::Result { @@ -527,7 +339,7 @@ impl Read for UartPeripheral { } #[cfg(feature = "eh1_0_alpha")] -impl eh1::Read for UartPeripheral { +impl> eh1::Read for UartPeripheral { type Error = ReadErrorType; fn read(&mut self) -> nb::Result { @@ -542,31 +354,7 @@ impl eh1::Read for UartPeripheral { } } } - -/// Same as core::convert::Infallible, but implementing spi::Error -/// -/// For eh 1.0.0-alpha.6, Infallible doesn't implement spi::Error, -/// so use a locally defined type instead. -/// This should be removed with the next release of e-h. -/// (https://github.com/rust-embedded/embedded-hal/pull/328) -#[cfg(feature = "eh1_0_alpha")] -pub enum SerialInfallible {} - -#[cfg(feature = "eh1_0_alpha")] -impl core::fmt::Debug for SerialInfallible { - fn fmt(&self, _f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - match *self {} - } -} - -#[cfg(feature = "eh1_0_alpha")] -impl eh1_0_alpha::serial::Error for SerialInfallible { - fn kind(&self) -> eh1_0_alpha::serial::ErrorKind { - match *self {} - } -} - -impl Write for UartPeripheral { +impl> Write for UartPeripheral { type Error = Infallible; fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> { @@ -578,12 +366,12 @@ impl Write for UartPeripheral { } fn flush(&mut self) -> nb::Result<(), Self::Error> { - self.transmit_flushed() + super::writer::transmit_flushed(&self.device) } } #[cfg(feature = "eh1_0_alpha")] -impl eh1::Write for UartPeripheral { +impl> eh1::Write for UartPeripheral { type Error = SerialInfallible; fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> { @@ -595,14 +383,14 @@ impl eh1::Write for UartPeripheral { } fn flush(&mut self) -> nb::Result<(), Self::Error> { - self.transmit_flushed().map_err(|e| match e { + super::writer::transmit_flushed(&self.device).map_err(|e| match e { WouldBlock => WouldBlock, Other(v) => match v {}, }) } } -impl fmt::Write for UartPeripheral { +impl> fmt::Write for UartPeripheral { fn write_str(&mut self, s: &str) -> fmt::Result { s.bytes() .try_for_each(|c| nb::block!(self.write(c))) diff --git a/rp2040-hal/src/uart/pins.rs b/rp2040-hal/src/uart/pins.rs new file mode 100644 index 0000000..8de6687 --- /dev/null +++ b/rp2040-hal/src/uart/pins.rs @@ -0,0 +1,222 @@ +use crate::gpio::{bank0, FunctionUart, Pin}; +use crate::pac::{UART0, UART1}; + +use super::UartDevice; + +/// Declares a valid UART pinout. +pub trait ValidUartPinout { + /// Indicates TX should be enabled for this pinout + const TX_ENABLED: bool; + /// Indicates RX should be enabled for this pinout + const RX_ENABLED: bool; + /// Indicates CTS should be enabled for this pinout + const CTS_ENABLED: bool; + /// Indicates RTS should be enabled for this pinout + const RTS_ENABLED: bool; +} + +impl ValidUartPinout for Pins +where + UART: UartDevice, + TX: Tx, + RX: Rx, + CTS: Cts, + RTS: Rts, +{ + const TX_ENABLED: bool = TX::ENABLED; + const RX_ENABLED: bool = RX::ENABLED; + const CTS_ENABLED: bool = CTS::ENABLED; + const RTS_ENABLED: bool = RTS::ENABLED; +} + +impl ValidUartPinout for (TX, RX) +where + UART: UartDevice, + TX: Tx, + RX: Rx, +{ + const TX_ENABLED: bool = TX::ENABLED; + const RX_ENABLED: bool = RX::ENABLED; + const CTS_ENABLED: bool = false; + const RTS_ENABLED: bool = false; +} + +impl ValidUartPinout for (TX, RX, CTS, RTS) +where + UART: UartDevice, + TX: Tx, + RX: Rx, + CTS: Cts, + RTS: Rts, +{ + const TX_ENABLED: bool = TX::ENABLED; + const RX_ENABLED: bool = RX::ENABLED; + const CTS_ENABLED: bool = CTS::ENABLED; + const RTS_ENABLED: bool = RTS::ENABLED; +} + +/// Customizable Uart pinout, allowing you to set the pins individually. +/// +/// The following pins are valid UART pins: +/// +/// |UART | TX | RX | CTS | RTS | +/// |-----|-------------|-------------|-------------|-------------| +/// |UART0|0, 12, 16, 28|1, 13, 17, 29|2, 14, 18 |3, 15, 19 | +/// |UART1|4, 8, 20, 24 |5, 9, 21, 25 |6, 10, 22, 26|7, 11, 23, 27| +/// +/// Every field can be set to `()` to not configure them. +/// +/// Note that you can also use tuples `(RX, TX)` or `(RX, TX, CTS, RTS)` instead of this type. +/// +/// This struct can either be filled manually or with a builder pattern: +/// +/// ```no_run +/// # use rp2040_hal::uart::{Pins, ValidUartPinout}; +/// # use rp2040_hal::pac::UART0; +/// # let gpio_pins: rp2040_hal::gpio::Pins = unsafe { core::mem::zeroed() }; +/// let pins = Pins::default() +/// .tx(gpio_pins.gpio0.into_mode()) +/// .rx(gpio_pins.gpio1.into_mode()); +/// +/// fn assert_is_valid_uart0>(_: T) {} +/// +/// assert_is_valid_uart0(pins); +/// ``` +#[allow(missing_docs)] +pub struct Pins { + pub tx: TX, + pub rx: RX, + pub rts: RTS, + pub cts: CTS, +} + +impl Default for Pins<(), (), (), ()> { + fn default() -> Self { + Self { + tx: (), + rx: (), + rts: (), + cts: (), + } + } +} + +impl Pins { + /// Set the TX pin + pub fn tx(self, tx: NTX) -> Pins { + Pins { + tx, + rx: self.rx, + rts: self.rts, + cts: self.cts, + } + } + /// Set the RX pin + pub fn rx(self, rx: NRX) -> Pins { + Pins { + tx: self.tx, + rx, + rts: self.rts, + cts: self.cts, + } + } + /// Set the CTS pin + pub fn cts(self, cts: NCTS) -> Pins { + Pins { + tx: self.tx, + rx: self.rx, + rts: self.rts, + cts, + } + } + /// Set the RTS pin + pub fn rts(self, rts: NRTS) -> Pins { + Pins { + tx: self.tx, + rx: self.rx, + rts, + cts: self.cts, + } + } +} + +/// Indicates a valid TX pin for UART0 or UART1 +pub trait Tx { + #[allow(missing_docs)] + const ENABLED: bool; +} +/// Indicates a valid RX pin for UART0 or UART1 +pub trait Rx { + #[allow(missing_docs)] + const ENABLED: bool; +} +/// Indicates a valid CTS pin for UART0 or UART1 +pub trait Cts { + #[allow(missing_docs)] + const ENABLED: bool; +} +/// Indicates a valid RTS pin for UART0 or UART1 +pub trait Rts { + #[allow(missing_docs)] + const ENABLED: bool; +} + +impl Tx for () { + const ENABLED: bool = false; +} +impl Rx for () { + const ENABLED: bool = false; +} +impl Cts for () { + const ENABLED: bool = false; +} +impl Rts for () { + const ENABLED: bool = false; +} + +macro_rules! impl_valid_uart { + ($($uart:ident: { + tx: [$($tx:ident),*], + rx: [$($rx:ident),*], + cts: [$($cts:ident),*], + rts: [$($rts:ident),*], + }),*) => { + $( + $( + impl Tx<$uart> for Pin { + const ENABLED: bool = true; + } + )* + $( + impl Rx<$uart> for Pin { + const ENABLED: bool = true; + } + )* + $( + impl Cts<$uart> for Pin { + const ENABLED: bool = true; + } + )* + $( + impl Rts<$uart> for Pin { + const ENABLED: bool = true; + } + )* + )* + }; +} + +impl_valid_uart!( + UART0: { + tx: [Gpio0, Gpio12, Gpio16, Gpio28], + rx: [Gpio1, Gpio13, Gpio17, Gpio29], + cts: [Gpio2, Gpio14, Gpio18], + rts: [Gpio3, Gpio15, Gpio19], + }, + UART1: { + tx: [Gpio4, Gpio8, Gpio20, Gpio24], + rx: [Gpio5, Gpio9, Gpio21, Gpio25], + cts: [Gpio6, Gpio10, Gpio22, Gpio26], + rts: [Gpio7, Gpio11, Gpio23, Gpio27], + } +); diff --git a/rp2040-hal/src/uart/reader.rs b/rp2040-hal/src/uart/reader.rs new file mode 100644 index 0000000..bb0f478 --- /dev/null +++ b/rp2040-hal/src/uart/reader.rs @@ -0,0 +1,179 @@ +use super::{UartConfig, UartDevice, ValidUartPinout}; +use embedded_hal::serial::Read; +use embedded_time::rate::Baud; +use nb::Error::*; + +#[cfg(feature = "eh1_0_alpha")] +use eh1_0_alpha::serial::nb as eh1; + +/// 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" +#[cfg_attr(feature = "eh1_0_alpha", derive(Debug))] +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, +} + +#[cfg(feature = "eh1_0_alpha")] +impl eh1_0_alpha::serial::Error for ReadErrorType { + fn kind(&self) -> eh1_0_alpha::serial::ErrorKind { + match self { + ReadErrorType::Overrun => eh1_0_alpha::serial::ErrorKind::Overrun, + ReadErrorType::Break => eh1_0_alpha::serial::ErrorKind::Other, + ReadErrorType::Parity => eh1_0_alpha::serial::ErrorKind::Parity, + ReadErrorType::Framing => eh1_0_alpha::serial::ErrorKind::FrameFormat, + } + } +} + +pub(crate) fn is_readable(device: &D) -> bool { + device.uartfr.read().rxfe().bit_is_clear() +} + +pub(crate) fn read_raw<'b, D: UartDevice>( + device: &D, + buffer: &'b mut [u8], +) -> nb::Result> { + let mut bytes_read = 0; + + Ok(loop { + if !is_readable(device) { + if bytes_read == 0 { + return Err(WouldBlock); + } else { + break bytes_read; + } + } + + if bytes_read < buffer.len() { + let mut error: Option = None; + + let read = device.uartdr.read(); + + if read.oe().bit_is_set() { + error = Some(ReadErrorType::Overrun); + } + + if read.be().bit_is_set() { + error = Some(ReadErrorType::Break); + } + + if read.pe().bit_is_set() { + error = Some(ReadErrorType::Parity); + } + + if 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] = read.data().bits(); + bytes_read += 1; + } else { + break bytes_read; + } + }) +} + +pub(crate) fn read_full_blocking( + device: &D, + buffer: &mut [u8], +) -> Result<(), ReadErrorType> { + let mut offset = 0; + + while offset != buffer.len() { + offset += match read_raw(device, &mut buffer[offset..]) { + Ok(bytes_read) => bytes_read, + Err(e) => match e { + Other(inner) => return Err(inner.err_type), + WouldBlock => continue, + }, + } + } + + Ok(()) +} + +/// Half of an [`UartPeripheral`] that is only capable of reading. Obtained by calling [`UartPeripheral::split()`] +/// +/// [`UartPeripheral`]: struct.UartPeripheral.html +/// [`UartPeripheral::split()`]: struct.UartPeripheral.html#method.split +pub struct Reader> { + pub(super) device: D, + pub(super) pins: P, + pub(super) config: UartConfig, + pub(super) effective_baudrate: Baud, +} + +impl> Reader { + /// 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, it will return how many bytes were read. + pub fn read_raw<'b>(&self, buffer: &'b mut [u8]) -> nb::Result> { + read_raw(&self.device, buffer) + } + + /// 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> { + read_full_blocking(&self.device, buffer) + } +} + +impl> Read for Reader { + 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), + }, + } + } +} + +#[cfg(feature = "eh1_0_alpha")] +impl> eh1::Read for Reader { + 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), + }, + } + } +} diff --git a/rp2040-hal/src/uart/utils.rs b/rp2040-hal/src/uart/utils.rs new file mode 100644 index 0000000..9d9e540 --- /dev/null +++ b/rp2040-hal/src/uart/utils.rs @@ -0,0 +1,98 @@ +use crate::pac::{uart0::RegisterBlock, UART0, UART1}; +use crate::resets::SubsystemReset; +use core::ops::Deref; +use embedded_time::rate::Baud; + +/// Error type for UART operations. +#[derive(Debug)] +pub enum Error { + /// Bad argument : when things overflow, ... + BadArgument, +} +/// State of the UART Peripheral. +pub trait State {} + +/// Trait to handle both underlying devices (UART0 & UART1) +pub trait UartDevice: Deref + SubsystemReset + 'static {} + +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. +#[non_exhaustive] +pub struct UartConfig { + /// The baudrate the uart will run at. + pub baudrate: Baud, + + /// The amount of data bits the uart should be configured to. + pub data_bits: DataBits, + + /// The amount of stop bits the uart should be configured to. + pub stop_bits: StopBits, + + /// The parity that this uart should have + pub parity: Option, +} + +/// Same as core::convert::Infallible, but implementing spi::Error +/// +/// For eh 1.0.0-alpha.6, Infallible doesn't implement spi::Error, +/// so use a locally defined type instead. +/// This should be removed with the next release of e-h. +/// (https://github.com/rust-embedded/embedded-hal/pull/328) +#[cfg(feature = "eh1_0_alpha")] +pub enum SerialInfallible {} + +#[cfg(feature = "eh1_0_alpha")] +impl core::fmt::Debug for SerialInfallible { + fn fmt(&self, _f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match *self {} + } +} + +#[cfg(feature = "eh1_0_alpha")] +impl eh1_0_alpha::serial::Error for SerialInfallible { + fn kind(&self) -> eh1_0_alpha::serial::ErrorKind { + match *self {} + } +} diff --git a/rp2040-hal/src/uart/writer.rs b/rp2040-hal/src/uart/writer.rs new file mode 100644 index 0000000..3944ed0 --- /dev/null +++ b/rp2040-hal/src/uart/writer.rs @@ -0,0 +1,129 @@ +use super::{UartDevice, ValidUartPinout}; +use core::fmt; +use core::{convert::Infallible, marker::PhantomData}; +use embedded_hal::serial::Write; +use nb::Error::*; +use rp2040_pac::uart0::RegisterBlock; + +#[cfg(feature = "eh1_0_alpha")] +use eh1_0_alpha::serial::nb as eh1; + +pub(crate) fn transmit_flushed(rb: &RegisterBlock) -> nb::Result<(), Infallible> { + if rb.uartfr.read().txfe().bit_is_set() { + Ok(()) + } else { + Err(WouldBlock) + } +} + +fn uart_is_writable(rb: &RegisterBlock) -> bool { + rb.uartfr.read().txff().bit_is_clear() +} + +pub(crate) fn write_raw<'d>( + rb: &RegisterBlock, + data: &'d [u8], +) -> nb::Result<&'d [u8], Infallible> { + let mut bytes_written = 0; + + for c in data { + if !uart_is_writable(rb) { + if bytes_written == 0 { + return Err(WouldBlock); + } else { + return Ok(&data[bytes_written..]); + } + } + + rb.uartdr.write(|w| unsafe { + w.data().bits(*c); + w + }); + + bytes_written += 1; + } + Ok(&data[bytes_written..]) +} + +pub(crate) fn write_full_blocking(rb: &RegisterBlock, data: &[u8]) { + let mut temp = data; + + while !temp.is_empty() { + temp = match write_raw(rb, temp) { + Ok(remaining) => remaining, + Err(WouldBlock) => continue, + Err(_) => unreachable!(), + } + } +} + +/// Half of an [`UartPeripheral`] that is only capable of writing. Obtained by calling [`UartPeripheral::split()`] +/// +/// [`UartPeripheral`]: struct.UartPeripheral.html +/// [`UartPeripheral::split()`]: struct.UartPeripheral.html#method.split +pub struct Writer> { + pub(super) device: &'static RegisterBlock, + pub(super) device_marker: PhantomData, + pub(super) pins: PhantomData

, +} + +impl> Writer { + /// 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> { + write_raw(self.device, data) + } + + /// Writes bytes to the UART. + /// This function blocks until the full buffer has been sent. + pub fn write_full_blocking(&self, data: &[u8]) { + super::writer::write_full_blocking(self.device, data); + } +} + +impl> Write for Writer { + 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> { + super::writer::transmit_flushed(self.device) + } +} + +#[cfg(feature = "eh1_0_alpha")] +impl> eh1::Write for Writer { + type Error = super::utils::SerialInfallible; + + 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> { + transmit_flushed(&self.device).map_err(|e| match e { + WouldBlock => WouldBlock, + Other(v) => match v {}, + }) + } +} + +impl> fmt::Write for Writer { + fn write_str(&mut self, s: &str) -> fmt::Result { + s.bytes() + .try_for_each(|c| nb::block!(self.write(c))) + .map_err(|_| fmt::Error) + } +}