From bdfb4d82c9c9d1d2f9f23c45f4d03132e7f2b962 Mon Sep 17 00:00:00 2001 From: Victor Koenders Date: Thu, 25 Nov 2021 11:02:26 +0100 Subject: [PATCH] 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, +}