Skip to main content

ariel_os_rp/
uart.rs

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