ariel_os_esp/
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 esp_hal::{
8    Async,
9    gpio::interconnect::{PeripheralInput, PeripheralOutput},
10    peripherals,
11    uart::Uart as EspUart,
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) -> esp_hal::uart::DataBits {
90    match databits {
91        DataBits::Data5 => esp_hal::uart::DataBits::_5,
92        DataBits::Data6 => esp_hal::uart::DataBits::_6,
93        DataBits::Data7 => esp_hal::uart::DataBits::_7,
94        DataBits::Data8 => esp_hal::uart::DataBits::_8,
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) -> esp_hal::uart::Parity {
120    match parity {
121        Parity::None => esp_hal::uart::Parity::None,
122        Parity::Even => esp_hal::uart::Parity::Even,
123        Parity::Odd => esp_hal::uart::Parity::Odd,
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    /// 1.5 stop bits.
144    Stop1P5,
145    /// Two stop bits.
146    Stop2,
147}
148
149fn from_stop_bits(stop_bits: StopBits) -> esp_hal::uart::StopBits {
150    match stop_bits {
151        StopBits::Stop1 => esp_hal::uart::StopBits::_1,
152        StopBits::Stop1P5 => esp_hal::uart::StopBits::_1p5,
153        StopBits::Stop2 => esp_hal::uart::StopBits::_2,
154    }
155}
156
157impl From<ariel_os_embassy_common::uart::StopBits<Self>> for StopBits {
158    fn from(stopbits: ariel_os_embassy_common::uart::StopBits<Self>) -> Self {
159        match stopbits {
160            ariel_os_embassy_common::uart::StopBits::Hal(stopbits) => stopbits,
161            ariel_os_embassy_common::uart::StopBits::Stop1 => StopBits::Stop1,
162        }
163    }
164}
165
166fn convert_error(_err: esp_hal::uart::ConfigError) -> ConfigError {
167    ConfigError::ConfigurationNotSupported
168}
169
170macro_rules! define_uart_drivers {
171    ($( $peripheral:ident ),* $(,)?) => {
172        $(
173            /// Peripheral-specific UART driver.
174            pub struct $peripheral<'d> {
175                uart: EspUart<'d, Async>
176            }
177
178            // Make this struct a compile-time-enforced singleton: having multiple statics
179            // defined with the same name would result in a compile-time error.
180            paste::paste! {
181                #[allow(dead_code)]
182                static [<PREVENT_MULTIPLE_ $peripheral>]: () = ();
183            }
184
185            impl<'d> $peripheral<'d> {
186                /// Returns a driver implementing embedded-io traits for this Uart
187                /// peripheral.
188                ///
189                /// # Errors
190                ///
191                /// Returns [`ConfigError::ConfigurationNotSupported`] when the requested configuration
192                /// cannot be applied to the peripheral.
193                /// If the baud rate is not supported, this may be reported as a distinct
194                /// [`ConfigError::BaudrateNotSupported`] error, or as
195                /// [`ConfigError::ConfigurationNotSupported`].
196                #[expect(clippy::new_ret_no_self)]
197                pub fn new<RX: PeripheralInput<'d>, TX: PeripheralOutput<'d>>(
198                    rx_pin: impl $crate::IntoPeripheral<'d, RX>,
199                    tx_pin: impl $crate::IntoPeripheral<'d, TX>,
200                    _rx_buf: &'d mut [u8],
201                    _tx_buf: &'d mut [u8],
202                    config: Config,
203                ) -> Result<Uart<'d>, ConfigError> {
204
205                    let uart_config = esp_hal::uart::Config::default()
206                        .with_baudrate(config.baudrate.into())
207                        .with_data_bits(from_data_bits(config.data_bits))
208                        .with_stop_bits(from_stop_bits(config.stop_bits))
209                        .with_parity(from_parity(config.parity));
210
211                    // FIXME(safety): enforce that the init code indeed has run
212                    // SAFETY: this struct being a singleton prevents us from stealing the
213                    // peripheral multiple times.
214                    let uart_peripheral = unsafe { peripherals::$peripheral::steal() };
215
216                    let uart = EspUart::new(
217                        uart_peripheral,
218                        uart_config
219                    )
220                        .map_err(convert_error)?
221                        .with_tx(tx_pin.into_hal_peripheral())
222                        .with_rx(rx_pin.into_hal_peripheral())
223                        .into_async();
224
225                    Ok(Uart::$peripheral(Self { uart }))
226                }
227            }
228        )*
229
230        /// Peripheral-agnostic UART driver.
231        pub enum Uart<'d> {
232            $(
233                #[doc = concat!(stringify!($peripheral), " peripheral.")]
234                $peripheral($peripheral<'d>)
235            ),*
236        }
237
238        impl embedded_io_async::ErrorType for Uart<'_> {
239            type Error = esp_hal::uart::IoError;
240        }
241
242        impl_async_uart_for_driver_enum!(Uart, $( $peripheral ),*);
243    }
244}
245
246#[cfg(context = "esp32")]
247define_uart_drivers!(UART0, UART1, UART2);
248#[cfg(context = "esp32c3")]
249define_uart_drivers!(UART0, UART1);
250#[cfg(context = "esp32c6")]
251define_uart_drivers!(UART0, UART1);
252#[cfg(context = "esp32s2")]
253define_uart_drivers!(UART0, UART1);
254#[cfg(context = "esp32s3")]
255define_uart_drivers!(UART0, UART1, UART2);
256
257#[doc(hidden)]
258pub fn init(peripherals: &mut crate::OptionalPeripherals) {
259    // Take all UART peripherals and do nothing with them.
260    cfg_if::cfg_if! {
261        if #[cfg(context = "esp32")] {
262            let _ = peripherals.UART0.take().unwrap();
263            let _ = peripherals.UART1.take().unwrap();
264            let _ = peripherals.UART2.take().unwrap();
265        } else if #[cfg(context = "esp32c3")] {
266            let _ = peripherals.UART0.take().unwrap();
267            let _ = peripherals.UART1.take().unwrap();
268        } else if #[cfg(context = "esp32c6")] {
269            let _ = peripherals.UART0.take().unwrap();
270            let _ = peripherals.UART1.take().unwrap();
271        } else if #[cfg(context = "esp32s2")] {
272            let _ = peripherals.UART0.take().unwrap();
273            let _ = peripherals.UART1.take().unwrap();
274        } else if #[cfg(context = "esp32s3")] {
275            let _ = peripherals.UART0.take().unwrap();
276            let _ = peripherals.UART1.take().unwrap();
277            let _ = peripherals.UART2.take().unwrap();
278        } else {
279            compile_error!("this ESP32 chip is not supported");
280        }
281    }
282}