From bdfb4d82c9c9d1d2f9f23c45f4d03132e7f2b962 Mon Sep 17 00:00:00 2001 From: Victor Koenders Date: Thu, 25 Nov 2021 11:02:26 +0100 Subject: [PATCH 1/7] Split uart in separate files, introduced a typesystem constraint for valid UART pin configurations --- rp2040-hal/src/uart/common_configs.rs | 42 ++++ rp2040-hal/src/uart/mod.rs | 42 ++++ .../src/{uart.rs => uart/peripheral.rs} | 155 +------------- rp2040-hal/src/uart/pins.rs | 195 ++++++++++++++++++ rp2040-hal/src/uart/utils.rs | 101 +++++++++ 5 files changed, 383 insertions(+), 152 deletions(-) create mode 100644 rp2040-hal/src/uart/common_configs.rs create mode 100644 rp2040-hal/src/uart/mod.rs rename rp2040-hal/src/{uart.rs => uart/peripheral.rs} (79%) create mode 100644 rp2040-hal/src/uart/pins.rs create mode 100644 rp2040-hal/src/uart/utils.rs 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..780c1c8 --- /dev/null +++ b/rp2040-hal/src/uart/mod.rs @@ -0,0 +1,42 @@ +//! 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(); +//! +//! // Need to perform clock init before using UART or it will freeze. +//! let uart = UartPeripheral::<_, _>::enable( +//! peripherals.UART0, +//! &mut peripherals.RESETS, +//! 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"); +//! ``` + +mod peripheral; +mod pins; +mod utils; + +pub use self::peripheral::UartPeripheral; +pub use self::pins::*; +pub use self::utils::*; + +/// 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 79% rename from rp2040-hal/src/uart.rs rename to rp2040-hal/src/uart/peripheral.rs index 49fced3..3136b08 100644 --- a/rp2040-hal/src/uart.rs +++ b/rp2040-hal/src/uart/peripheral.rs @@ -30,58 +30,16 @@ //! 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; - -#[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 { @@ -94,113 +52,6 @@ impl eh1_0_alpha::serial::Error for ReadErrorType { } } -/// 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 { device: D, diff --git a/rp2040-hal/src/uart/pins.rs b/rp2040-hal/src/uart/pins.rs new file mode 100644 index 0000000..2d49991 --- /dev/null +++ b/rp2040-hal/src/uart/pins.rs @@ -0,0 +1,195 @@ +use crate::gpio::{bank0, FunctionUart, Pin}; +use crate::pac::{UART0, UART1}; + +/// Declares a valid UART pinout. +pub trait ValidUartPinout {} + +impl ValidUartPinout for Pins +where + TX: Tx, + RX: Rx, + CTS: Cts, + RTS: Rts, +{ +} + +impl ValidUartPinout for (TX, RX) +where + TX: Tx, + RX: Rx, +{ +} + +impl ValidUartPinout for (TX, RX, CTS, RTS) +where + TX: Tx, + RX: Rx, + CTS: Cts, + RTS: Rts, +{ +} + +/// 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. +#[allow(missing_docs)] +pub struct Pins { + pub tx: TX, + pub rx: RX, + pub rts: RTS, + pub cts: CTS, +} + +impl Pins<(), (), (), ()> { + /// Create a new pinout. This can be used as 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::new() + /// .tx(gpio_pins.gpio0.into_mode()) + /// .rx(gpio_pins.gpio1.into_mode()); + /// + /// fn assert_is_valid_uart0>(_: T) {} + /// + /// assert_is_valid_uart0(pins); + /// ``` + pub fn new() -> 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 IS_SET: bool; +} +/// Indicates a valid RX pin for UART0 or UART1 +pub trait Rx { + #[allow(missing_docs)] + const IS_SET: bool; +} +/// Indicates a valid CTS pin for UART0 or UART1 +pub trait Cts { + #[allow(missing_docs)] + const IS_SET: bool; +} +/// Indicates a valid RTS pin for UART0 or UART1 +pub trait Rts { + #[allow(missing_docs)] + const IS_SET: bool; +} + +impl Tx for () { + const IS_SET: bool = false; +} +impl Rx for () { + const IS_SET: bool = false; +} +impl Cts for () { + const IS_SET: bool = false; +} +impl Rts for () { + const IS_SET: 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 IS_SET: bool = true; + } + )* + $( + impl Rx<$uart> for Pin { + const IS_SET: bool = true; + } + )* + $( + impl Cts<$uart> for Pin { + const IS_SET: bool = true; + } + )* + $( + impl Rts<$uart> for Pin { + const IS_SET: 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/utils.rs b/rp2040-hal/src/uart/utils.rs new file mode 100644 index 0000000..36b125f --- /dev/null +++ b/rp2040-hal/src/uart/utils.rs @@ -0,0 +1,101 @@ +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, +} + +/// 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, +} + +/// 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. +#[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, +} From a4a0bcf9876fcf85d57ab95c1c891fed4cbb5653 Mon Sep 17 00:00:00 2001 From: Victor Koenders Date: Thu, 25 Nov 2021 13:55:14 +0100 Subject: [PATCH 2/7] Added pins to the uart constructor functions --- rp2040-hal/src/uart/mod.rs | 11 +++-- rp2040-hal/src/uart/peripheral.rs | 51 ++++++++++++-------- rp2040-hal/src/uart/pins.rs | 80 ++++++++++++++++++++----------- 3 files changed, 88 insertions(+), 54 deletions(-) diff --git a/rp2040-hal/src/uart/mod.rs b/rp2040-hal/src/uart/mod.rs index 780c1c8..d980e93 100644 --- a/rp2040-hal/src/uart/mod.rs +++ b/rp2040-hal/src/uart/mod.rs @@ -16,17 +16,20 @@ //! 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::<_, _>::enable( +//! let uart = UartPeripheral::enable( //! peripherals.UART0, +//! pins, //! &mut peripherals.RESETS, //! 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"); //! ``` diff --git a/rp2040-hal/src/uart/peripheral.rs b/rp2040-hal/src/uart/peripheral.rs index 3136b08..2845cb7 100644 --- a/rp2040-hal/src/uart/peripheral.rs +++ b/rp2040-hal/src/uart/peripheral.rs @@ -53,40 +53,43 @@ impl eh1_0_alpha::serial::Error for ReadErrorType { } /// 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, } } @@ -95,8 +98,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| { @@ -105,11 +108,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 }); @@ -122,20 +128,23 @@ 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 }); @@ -361,7 +370,7 @@ fn set_format<'w>( w } -impl Read for UartPeripheral { +impl> Read for UartPeripheral { type Error = ReadErrorType; fn read(&mut self) -> nb::Result { @@ -378,7 +387,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 { @@ -417,7 +426,7 @@ impl eh1_0_alpha::serial::Error for SerialInfallible { } } -impl Write for UartPeripheral { +impl> Write for UartPeripheral { type Error = Infallible; fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> { @@ -434,7 +443,7 @@ impl Write for UartPeripheral { } #[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> { @@ -453,7 +462,7 @@ impl eh1::Write for UartPeripheral { } } -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 index 2d49991..0cbfb03 100644 --- a/rp2040-hal/src/uart/pins.rs +++ b/rp2040-hal/src/uart/pins.rs @@ -2,7 +2,16 @@ use crate::gpio::{bank0, FunctionUart, Pin}; use crate::pac::{UART0, UART1}; /// Declares a valid UART pinout. -pub trait ValidUartPinout {} +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 @@ -11,6 +20,10 @@ where 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) @@ -18,6 +31,10 @@ where 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) @@ -27,6 +44,10 @@ where 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. @@ -41,6 +62,21 @@ where /// 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, @@ -49,22 +85,8 @@ pub struct Pins { pub cts: CTS, } -impl Pins<(), (), (), ()> { - /// Create a new pinout. This can be used as 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::new() - /// .tx(gpio_pins.gpio0.into_mode()) - /// .rx(gpio_pins.gpio1.into_mode()); - /// - /// fn assert_is_valid_uart0>(_: T) {} - /// - /// assert_is_valid_uart0(pins); - /// ``` - pub fn new() -> Self { +impl Default for Pins<(), (), (), ()> { + fn default() -> Self { Self { tx: (), rx: (), @@ -116,35 +138,35 @@ impl Pins { /// Indicates a valid TX pin for UART0 or UART1 pub trait Tx { #[allow(missing_docs)] - const IS_SET: bool; + const ENABLED: bool; } /// Indicates a valid RX pin for UART0 or UART1 pub trait Rx { #[allow(missing_docs)] - const IS_SET: bool; + const ENABLED: bool; } /// Indicates a valid CTS pin for UART0 or UART1 pub trait Cts { #[allow(missing_docs)] - const IS_SET: bool; + const ENABLED: bool; } /// Indicates a valid RTS pin for UART0 or UART1 pub trait Rts { #[allow(missing_docs)] - const IS_SET: bool; + const ENABLED: bool; } impl Tx for () { - const IS_SET: bool = false; + const ENABLED: bool = false; } impl Rx for () { - const IS_SET: bool = false; + const ENABLED: bool = false; } impl Cts for () { - const IS_SET: bool = false; + const ENABLED: bool = false; } impl Rts for () { - const IS_SET: bool = false; + const ENABLED: bool = false; } macro_rules! impl_valid_uart { @@ -157,22 +179,22 @@ macro_rules! impl_valid_uart { $( $( impl Tx<$uart> for Pin { - const IS_SET: bool = true; + const ENABLED: bool = true; } )* $( impl Rx<$uart> for Pin { - const IS_SET: bool = true; + const ENABLED: bool = true; } )* $( impl Cts<$uart> for Pin { - const IS_SET: bool = true; + const ENABLED: bool = true; } )* $( impl Rts<$uart> for Pin { - const IS_SET: bool = true; + const ENABLED: bool = true; } )* )* From a8a27672b8d0d4092fa559612008069a30b1f69c Mon Sep 17 00:00:00 2001 From: Victor Koenders Date: Thu, 25 Nov 2021 14:03:27 +0100 Subject: [PATCH 3/7] Constraint the uart::pin:: traits to UartDevice --- rp2040-hal/src/uart/pins.rs | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/rp2040-hal/src/uart/pins.rs b/rp2040-hal/src/uart/pins.rs index 0cbfb03..8de6687 100644 --- a/rp2040-hal/src/uart/pins.rs +++ b/rp2040-hal/src/uart/pins.rs @@ -1,8 +1,10 @@ use crate::gpio::{bank0, FunctionUart, Pin}; use crate::pac::{UART0, UART1}; +use super::UartDevice; + /// Declares a valid UART pinout. -pub trait ValidUartPinout { +pub trait ValidUartPinout { /// Indicates TX should be enabled for this pinout const TX_ENABLED: bool; /// Indicates RX should be enabled for this pinout @@ -15,6 +17,7 @@ pub trait ValidUartPinout { impl ValidUartPinout for Pins where + UART: UartDevice, TX: Tx, RX: Rx, CTS: Cts, @@ -28,6 +31,7 @@ where impl ValidUartPinout for (TX, RX) where + UART: UartDevice, TX: Tx, RX: Rx, { @@ -39,6 +43,7 @@ where impl ValidUartPinout for (TX, RX, CTS, RTS) where + UART: UartDevice, TX: Tx, RX: Rx, CTS: Cts, @@ -136,36 +141,36 @@ impl Pins { } /// Indicates a valid TX pin for UART0 or UART1 -pub trait Tx { +pub trait Tx { #[allow(missing_docs)] const ENABLED: bool; } /// Indicates a valid RX pin for UART0 or UART1 -pub trait Rx { +pub trait Rx { #[allow(missing_docs)] const ENABLED: bool; } /// Indicates a valid CTS pin for UART0 or UART1 -pub trait Cts { +pub trait Cts { #[allow(missing_docs)] const ENABLED: bool; } /// Indicates a valid RTS pin for UART0 or UART1 -pub trait Rts { +pub trait Rts { #[allow(missing_docs)] const ENABLED: bool; } -impl Tx for () { +impl Tx for () { const ENABLED: bool = false; } -impl Rx for () { +impl Rx for () { const ENABLED: bool = false; } -impl Cts for () { +impl Cts for () { const ENABLED: bool = false; } -impl Rts for () { +impl Rts for () { const ENABLED: bool = false; } From 84b8fb05b032df985194f346ab7e60f5e7d05cf7 Mon Sep 17 00:00:00 2001 From: Victor Koenders Date: Sat, 27 Nov 2021 13:09:11 +0100 Subject: [PATCH 4/7] Fixed compile errors on eh1_0_alpha feature --- rp2040-hal/src/uart/peripheral.rs | 12 ------------ rp2040-hal/src/uart/utils.rs | 12 ++++++++++++ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/rp2040-hal/src/uart/peripheral.rs b/rp2040-hal/src/uart/peripheral.rs index 2845cb7..385342c 100644 --- a/rp2040-hal/src/uart/peripheral.rs +++ b/rp2040-hal/src/uart/peripheral.rs @@ -40,18 +40,6 @@ use embedded_time::rate::Baud; use embedded_time::rate::Hertz; use nb::Error::{Other, WouldBlock}; -#[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, - } - } -} - /// An UART Peripheral based on an underlying UART device. pub struct UartPeripheral> { device: D, diff --git a/rp2040-hal/src/uart/utils.rs b/rp2040-hal/src/uart/utils.rs index 36b125f..8997b7c 100644 --- a/rp2040-hal/src/uart/utils.rs +++ b/rp2040-hal/src/uart/utils.rs @@ -35,6 +35,18 @@ pub enum ReadErrorType { 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 {} From c41c27313193295506966a60c0fb12513f8ceed1 Mon Sep 17 00:00:00 2001 From: Victor Koenders Date: Sat, 27 Nov 2021 13:40:57 +0100 Subject: [PATCH 5/7] Added reader/writer split to UartPeripheral --- rp2040-hal/src/uart/mod.rs | 4 + rp2040-hal/src/uart/peripheral.rs | 183 ++++++++++-------------------- rp2040-hal/src/uart/reader.rs | 176 ++++++++++++++++++++++++++++ rp2040-hal/src/uart/utils.rs | 63 ++++------ rp2040-hal/src/uart/writer.rs | 126 ++++++++++++++++++++ 5 files changed, 388 insertions(+), 164 deletions(-) create mode 100644 rp2040-hal/src/uart/reader.rs create mode 100644 rp2040-hal/src/uart/writer.rs diff --git a/rp2040-hal/src/uart/mod.rs b/rp2040-hal/src/uart/mod.rs index d980e93..729f865 100644 --- a/rp2040-hal/src/uart/mod.rs +++ b/rp2040-hal/src/uart/mod.rs @@ -35,11 +35,15 @@ 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/peripheral.rs b/rp2040-hal/src/uart/peripheral.rs index 385342c..cb6e83e 100644 --- a/rp2040-hal/src/uart/peripheral.rs +++ b/rp2040-hal/src/uart/peripheral.rs @@ -39,6 +39,7 @@ 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}; /// An UART Peripheral based on an underlying UART device. pub struct UartPeripheral> { @@ -139,47 +140,13 @@ impl> UartPeripheral { 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. @@ -188,83 +155,73 @@ 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`]. + 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) } } @@ -390,30 +347,6 @@ impl> eh1::Read for UartPeripheral) -> 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 { type Error = Infallible; @@ -426,7 +359,7 @@ impl> Write for UartPeripheral nb::Result<(), Self::Error> { - self.transmit_flushed() + super::writer::transmit_flushed(&self.device) } } @@ -443,7 +376,7 @@ impl> eh1::Write for UartPeripheral 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 {}, }) diff --git a/rp2040-hal/src/uart/reader.rs b/rp2040-hal/src/uart/reader.rs new file mode 100644 index 0000000..b6e126d --- /dev/null +++ b/rp2040-hal/src/uart/reader.rs @@ -0,0 +1,176 @@ +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()`] +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 index 8997b7c..9d9e540 100644 --- a/rp2040-hal/src/uart/utils.rs +++ b/rp2040-hal/src/uart/utils.rs @@ -9,49 +9,11 @@ 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 {} +pub trait UartDevice: Deref + SubsystemReset + 'static {} impl UartDevice for UART0 {} impl UartDevice for UART1 {} @@ -111,3 +73,26 @@ pub struct UartConfig { /// 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..7b81808 --- /dev/null +++ b/rp2040-hal/src/uart/writer.rs @@ -0,0 +1,126 @@ +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()`] +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) + } +} From 86444448a4422101c0e0bd3c4fbd5bbd0dc42d14 Mon Sep 17 00:00:00 2001 From: Victor Koenders Date: Sat, 27 Nov 2021 13:46:29 +0100 Subject: [PATCH 6/7] Migrated examples to the new uart system --- boards/rp-pico/examples/pico_i2c_pio.rs | 14 ++++++++------ rp2040-hal/examples/adc.rs | 12 +++++++----- rp2040-hal/examples/rom_funcs.rs | 13 +++++++------ rp2040-hal/examples/uart.rs | 13 +++++++------ rp2040-hal/src/uart/peripheral.rs | 2 ++ rp2040-hal/src/uart/reader.rs | 3 +++ rp2040-hal/src/uart/writer.rs | 3 +++ 7 files changed, 37 insertions(+), 23 deletions(-) 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/peripheral.rs b/rp2040-hal/src/uart/peripheral.rs index cb6e83e..65bd199 100644 --- a/rp2040-hal/src/uart/peripheral.rs +++ b/rp2040-hal/src/uart/peripheral.rs @@ -173,6 +173,8 @@ impl> UartPeripheral { /// 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 { diff --git a/rp2040-hal/src/uart/reader.rs b/rp2040-hal/src/uart/reader.rs index b6e126d..bb0f478 100644 --- a/rp2040-hal/src/uart/reader.rs +++ b/rp2040-hal/src/uart/reader.rs @@ -118,6 +118,9 @@ pub(crate) fn read_full_blocking( } /// 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, diff --git a/rp2040-hal/src/uart/writer.rs b/rp2040-hal/src/uart/writer.rs index 7b81808..3944ed0 100644 --- a/rp2040-hal/src/uart/writer.rs +++ b/rp2040-hal/src/uart/writer.rs @@ -58,6 +58,9 @@ pub(crate) fn write_full_blocking(rb: &RegisterBlock, data: &[u8]) { } /// 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, From 8f63be0f22275e12e15bcf483a661aa300d1418b Mon Sep 17 00:00:00 2001 From: Victor Koenders Date: Sun, 12 Dec 2021 10:32:07 +0100 Subject: [PATCH 7/7] Fixed failing doctest --- rp2040-hal/src/uart/mod.rs | 6 ++---- rp2040-hal/src/uart/peripheral.rs | 13 +++++++++---- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/rp2040-hal/src/uart/mod.rs b/rp2040-hal/src/uart/mod.rs index 729f865..5c0f727 100644 --- a/rp2040-hal/src/uart/mod.rs +++ b/rp2040-hal/src/uart/mod.rs @@ -22,10 +22,8 @@ //! pins.gpio1.into_mode::(), //! ); //! // Need to perform clock init before using UART or it will freeze. -//! let uart = UartPeripheral::enable( -//! peripherals.UART0, -//! pins, -//! &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(); diff --git a/rp2040-hal/src/uart/peripheral.rs b/rp2040-hal/src/uart/peripheral.rs index 65bd199..0cc18df 100644 --- a/rp2040-hal/src/uart/peripheral.rs +++ b/rp2040-hal/src/uart/peripheral.rs @@ -16,17 +16,19 @@ //! 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"); //! ``` @@ -41,6 +43,9 @@ 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; + /// An UART Peripheral based on an underlying UART device. pub struct UartPeripheral> { device: D,