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    Peripheral, 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<'d, peripherals::$peripheral>,
167            }
168
169            // Make this struct a compile-time-enforced singleton: having multiple statics
170            // defined with the same name would result in a compile-time error.
171            paste::paste! {
172                #[allow(dead_code)]
173                static [<PREVENT_MULTIPLE_ $peripheral>]: () = ();
174            }
175
176            impl<'d> $peripheral<'d> {
177                /// Returns a driver implementing embedded-io traits for this Uart
178                /// peripheral.
179                ///
180                /// # Errors
181                ///
182                /// This never returns an error.
183                #[expect(clippy::new_ret_no_self)]
184                pub fn new(
185                    rx_pin: impl Peripheral<P: RxPin<peripherals::$peripheral>> + 'd,
186                    tx_pin: impl Peripheral<P: TxPin<peripherals::$peripheral>> + 'd,
187                    rx_buf: &'d mut [u8],
188                    tx_buf: &'d mut [u8],
189                    config: Config,
190                ) -> Result<Uart<'d>, ConfigError> {
191                    let mut uart_config = embassy_rp::uart::Config::default();
192                    uart_config.baudrate = Baudrate::from(config.baudrate).into();
193                    uart_config.data_bits = from_data_bits(config.data_bits);
194                    uart_config.stop_bits = from_stop_bits(config.stop_bits);
195                    uart_config.parity = from_parity(config.parity);
196                    bind_interrupts!(struct Irqs {
197                        $interrupt => BufferedInterruptHandler<peripherals::$peripheral>;
198                    });
199
200                    // FIXME(safety): enforce that the init code indeed has run
201                    // SAFETY: this struct being a singleton prevents us from stealing the
202                    // peripheral multiple times.
203                    let uart_peripheral = unsafe { peripherals::$peripheral::steal() };
204
205                    let uart = BufferedUart::new(
206                        uart_peripheral,
207                        Irqs,
208                        // Order of TX / RX is swapped compared to other platforms
209                        tx_pin,
210                        rx_pin,
211                        tx_buf,
212                        rx_buf,
213                        uart_config,
214                    );
215
216                    Ok(Uart::$peripheral(Self { uart }))
217                }
218            }
219        )*
220
221        /// Peripheral-agnostic UART driver.
222        pub enum Uart<'d> {
223            $(
224                #[doc = concat!(stringify!($peripheral), " peripheral.")]
225                $peripheral($peripheral<'d>)
226            ),*
227        }
228
229        impl embedded_io_async::ErrorType for Uart<'_> {
230            type Error = embassy_rp::uart::Error;
231        }
232
233        impl_async_uart_for_driver_enum!(Uart, $( $peripheral ),*);
234    }
235}
236
237define_uart_drivers!(
238   UART0_IRQ => UART0,
239   UART1_IRQ => UART1,
240);
241
242#[doc(hidden)]
243pub fn init(peripherals: &mut crate::OptionalPeripherals) {
244    // Take all UART peripherals and do nothing with them.
245    cfg_if::cfg_if! {
246        if #[cfg(any(context = "rp2040", context = "rp235xa"))] {
247            let _ = peripherals.UART0.take().unwrap();
248            let _ = peripherals.UART1.take().unwrap();
249        } else {
250            compile_error!("this RP chip is not supported");
251        }
252    }
253}