ariel_os_rp/
uart.rs

1//! UART configuration.
2
3#![expect(unsafe_code)]
4
5use ariel_os_embassy_common::{impl_async_uart_for_driver_enum, uart::ConfigError};
6
7use embassy_rp::{
8    Peri, bind_interrupts, peripherals,
9    uart::{BufferedInterruptHandler, BufferedUart, RxPin, TxPin},
10};
11
12/// UART interface configuration.
13#[derive(Debug, Copy, Clone, PartialEq, Eq)]
14#[cfg_attr(feature = "defmt", derive(defmt::Format))]
15#[non_exhaustive]
16pub struct Config {
17    /// The baud rate at which UART should operate.
18    pub baudrate: ariel_os_embassy_common::uart::Baudrate<Baudrate>,
19    /// Number of data bits.
20    pub data_bits: DataBits,
21    /// Number of stop bits.
22    pub stop_bits: StopBits,
23    /// Parity mode used for the interface.
24    pub parity: Parity,
25}
26
27impl Default for Config {
28    fn default() -> Self {
29        Self {
30            baudrate: ariel_os_embassy_common::uart::Baudrate::_115200,
31            data_bits: DataBits::Data8,
32            stop_bits: StopBits::Stop1,
33            parity: Parity::None,
34        }
35    }
36}
37
38/// UART baud rate.
39#[derive(Debug, Copy, Clone, PartialEq, Eq)]
40#[cfg_attr(feature = "defmt", derive(defmt::Format))]
41pub struct Baudrate {
42    /// The baud rate at which UART should operate.
43    baud: u32,
44}
45
46impl From<Baudrate> for u32 {
47    fn from(baud: Baudrate) -> u32 {
48        baud.baud
49    }
50}
51
52impl From<u32> for Baudrate {
53    fn from(baudrate: u32) -> Baudrate {
54        Baudrate { baud: baudrate }
55    }
56}
57
58impl From<ariel_os_embassy_common::uart::Baudrate<Self>> for Baudrate {
59    fn from(baud: ariel_os_embassy_common::uart::Baudrate<Self>) -> Baudrate {
60        match baud {
61            ariel_os_embassy_common::uart::Baudrate::Hal(baud) => baud,
62            ariel_os_embassy_common::uart::Baudrate::_2400 => Baudrate { baud: 2400 },
63            ariel_os_embassy_common::uart::Baudrate::_4800 => Baudrate { baud: 4800 },
64            ariel_os_embassy_common::uart::Baudrate::_9600 => Baudrate { baud: 9600 },
65            ariel_os_embassy_common::uart::Baudrate::_19200 => Baudrate { baud: 19_200 },
66            ariel_os_embassy_common::uart::Baudrate::_38400 => Baudrate { baud: 38_400 },
67            ariel_os_embassy_common::uart::Baudrate::_57600 => Baudrate { baud: 57_600 },
68            ariel_os_embassy_common::uart::Baudrate::_115200 => Baudrate { baud: 115_200 },
69        }
70    }
71}
72
73/// UART number of data bits.
74#[derive(Debug, Copy, Clone, PartialEq, Eq)]
75#[cfg_attr(feature = "defmt", derive(defmt::Format))]
76pub enum DataBits {
77    /// 5 bits per character.
78    Data5,
79    /// 6 bits per character.
80    Data6,
81    /// 7 bits per character.
82    Data7,
83    /// 8 bits per character.
84    Data8,
85}
86
87fn from_data_bits(databits: DataBits) -> embassy_rp::uart::DataBits {
88    match databits {
89        DataBits::Data5 => embassy_rp::uart::DataBits::DataBits5,
90        DataBits::Data6 => embassy_rp::uart::DataBits::DataBits6,
91        DataBits::Data7 => embassy_rp::uart::DataBits::DataBits7,
92        DataBits::Data8 => embassy_rp::uart::DataBits::DataBits8,
93    }
94}
95
96impl From<ariel_os_embassy_common::uart::DataBits<Self>> for DataBits {
97    fn from(databits: ariel_os_embassy_common::uart::DataBits<Self>) -> DataBits {
98        match databits {
99            ariel_os_embassy_common::uart::DataBits::Hal(bits) => bits,
100            ariel_os_embassy_common::uart::DataBits::Data8 => DataBits::Data8,
101        }
102    }
103}
104
105/// Parity bit.
106#[derive(Debug, Copy, Clone, PartialEq, Eq)]
107#[cfg_attr(feature = "defmt", derive(defmt::Format))]
108pub enum Parity {
109    /// No parity bit.
110    None,
111    /// Even parity bit.
112    Even,
113    /// Odd parity bit.
114    Odd,
115}
116
117fn from_parity(parity: Parity) -> embassy_rp::uart::Parity {
118    match parity {
119        Parity::None => embassy_rp::uart::Parity::ParityNone,
120        Parity::Even => embassy_rp::uart::Parity::ParityEven,
121        Parity::Odd => embassy_rp::uart::Parity::ParityOdd,
122    }
123}
124
125impl From<ariel_os_embassy_common::uart::Parity<Self>> for Parity {
126    fn from(parity: ariel_os_embassy_common::uart::Parity<Self>) -> Self {
127        match parity {
128            ariel_os_embassy_common::uart::Parity::Hal(parity) => parity,
129            ariel_os_embassy_common::uart::Parity::None => Self::None,
130            ariel_os_embassy_common::uart::Parity::Even => Self::Even,
131        }
132    }
133}
134
135/// UART number of stop bits.
136#[derive(Debug, Copy, Clone, PartialEq, Eq)]
137#[cfg_attr(feature = "defmt", derive(defmt::Format))]
138pub enum StopBits {
139    /// One stop bit.
140    Stop1,
141    /// Two stop bits.
142    Stop2,
143}
144
145fn from_stop_bits(stop_bits: StopBits) -> embassy_rp::uart::StopBits {
146    match stop_bits {
147        StopBits::Stop1 => embassy_rp::uart::StopBits::STOP1,
148        StopBits::Stop2 => embassy_rp::uart::StopBits::STOP2,
149    }
150}
151
152impl From<ariel_os_embassy_common::uart::StopBits<Self>> for StopBits {
153    fn from(stopbits: ariel_os_embassy_common::uart::StopBits<Self>) -> Self {
154        match stopbits {
155            ariel_os_embassy_common::uart::StopBits::Hal(stopbits) => stopbits,
156            ariel_os_embassy_common::uart::StopBits::Stop1 => StopBits::Stop1,
157        }
158    }
159}
160
161macro_rules! define_uart_drivers {
162    ($( $interrupt:ident => $peripheral:ident ),* $(,)?) => {
163        $(
164            /// Peripheral-specific UART driver.
165            pub struct $peripheral<'d> {
166                uart: BufferedUart,
167                // This field is necessary as embassy_rp's `BufferedUart` does not
168                // actually have a lifetime, but `impl_async_uart_for_driver_enum!()`
169                // expects one on the `Uart` enum.
170                _phantom: core::marker::PhantomData<&'d ()>
171            }
172
173            // Make this struct a compile-time-enforced singleton: having multiple statics
174            // defined with the same name would result in a compile-time error.
175            paste::paste! {
176                #[allow(dead_code)]
177                static [<PREVENT_MULTIPLE_ $peripheral>]: () = ();
178            }
179
180            impl<'d> $peripheral<'d> {
181                /// Returns a driver implementing embedded-io traits for this Uart
182                /// peripheral.
183                ///
184                /// # Errors
185                ///
186                /// This never returns an error.
187                #[expect(clippy::new_ret_no_self)]
188                pub fn new(
189                    rx_pin: Peri<'d, impl RxPin<peripherals::$peripheral>>,
190                    tx_pin: Peri<'d, impl TxPin<peripherals::$peripheral>>,
191                    rx_buf: &mut [u8],
192                    tx_buf: &mut [u8],
193                    config: Config,
194                ) -> Result<Uart<'d>, ConfigError> {
195                    let mut uart_config = embassy_rp::uart::Config::default();
196                    uart_config.baudrate = Baudrate::from(config.baudrate).into();
197                    uart_config.data_bits = from_data_bits(config.data_bits);
198                    uart_config.stop_bits = from_stop_bits(config.stop_bits);
199                    uart_config.parity = from_parity(config.parity);
200                    bind_interrupts!(struct Irqs {
201                        $interrupt => BufferedInterruptHandler<peripherals::$peripheral>;
202                    });
203
204                    // FIXME(safety): enforce that the init code indeed has run
205                    // SAFETY: this struct being a singleton prevents us from stealing the
206                    // peripheral multiple times.
207                    let uart_peripheral = unsafe { peripherals::$peripheral::steal() };
208
209                    let uart = BufferedUart::new(
210                        uart_peripheral,
211                        // Order of TX / RX is swapped compared to other platforms
212                        tx_pin,
213                        rx_pin,
214                        Irqs,
215                        tx_buf,
216                        rx_buf,
217                        uart_config,
218                    );
219
220                    Ok(Uart::$peripheral(Self { uart, _phantom: core::marker::PhantomData }))
221                }
222            }
223        )*
224
225        /// Peripheral-agnostic UART driver.
226        pub enum Uart<'d> {
227            $(
228                #[doc = concat!(stringify!($peripheral), " peripheral.")]
229                $peripheral($peripheral<'d>)
230            ),*
231        }
232
233        impl embedded_io_async::ErrorType for Uart<'_> {
234            type Error = embassy_rp::uart::Error;
235        }
236
237        impl_async_uart_for_driver_enum!(Uart, $( $peripheral ),*);
238    }
239}
240
241define_uart_drivers!(
242   UART0_IRQ => UART0,
243   UART1_IRQ => UART1,
244);
245
246#[doc(hidden)]
247pub fn init(peripherals: &mut crate::OptionalPeripherals) {
248    // Take all UART peripherals and do nothing with them.
249    cfg_if::cfg_if! {
250        if #[cfg(any(context = "rp2040", context = "rp235xa"))] {
251            let _ = peripherals.UART0.take().unwrap();
252            let _ = peripherals.UART1.take().unwrap();
253        } else {
254            compile_error!("this RP chip is not supported");
255        }
256    }
257}