Skip to main content

ariel_os_esp/
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 esp_hal::{
10    Async,
11    gpio::interconnect::{PeripheralInput, PeripheralOutput},
12    peripherals,
13    uart::Uart as EspUart,
14};
15
16/// UART interface configuration.
17#[derive(Debug, Copy, Clone, PartialEq, Eq)]
18#[cfg_attr(feature = "defmt", derive(defmt::Format))]
19#[non_exhaustive]
20pub struct Config {
21    /// The baud rate at which UART should operate.
22    pub baudrate: ariel_os_embassy_common::uart::Baudrate<Baudrate>,
23    /// Number of data bits.
24    pub data_bits: DataBits,
25    /// Number of stop bits.
26    pub stop_bits: StopBits,
27    /// Parity mode used for the interface.
28    pub parity: Parity,
29}
30
31impl Default for Config {
32    fn default() -> Self {
33        Self {
34            baudrate: ariel_os_embassy_common::uart::Baudrate::_115200,
35            data_bits: DataBits::Data8,
36            stop_bits: StopBits::Stop1,
37            parity: Parity::None,
38        }
39    }
40}
41
42/// UART baud rate.
43#[derive(Debug, Copy, Clone, PartialEq, Eq)]
44#[cfg_attr(feature = "defmt", derive(defmt::Format))]
45pub struct Baudrate {
46    /// The baud rate at which UART should operate.
47    baud: u32,
48}
49
50impl From<Baudrate> for u32 {
51    fn from(baud: Baudrate) -> u32 {
52        baud.baud
53    }
54}
55
56impl From<u32> for Baudrate {
57    fn from(baudrate: u32) -> Baudrate {
58        Baudrate { baud: baudrate }
59    }
60}
61
62impl From<ariel_os_embassy_common::uart::Baudrate<Self>> for Baudrate {
63    fn from(baud: ariel_os_embassy_common::uart::Baudrate<Self>) -> Baudrate {
64        match baud {
65            ariel_os_embassy_common::uart::Baudrate::Hal(baud) => baud,
66            ariel_os_embassy_common::uart::Baudrate::_2400 => Baudrate { baud: 2400 },
67            ariel_os_embassy_common::uart::Baudrate::_4800 => Baudrate { baud: 4800 },
68            ariel_os_embassy_common::uart::Baudrate::_9600 => Baudrate { baud: 9600 },
69            ariel_os_embassy_common::uart::Baudrate::_19200 => Baudrate { baud: 19_200 },
70            ariel_os_embassy_common::uart::Baudrate::_38400 => Baudrate { baud: 38_400 },
71            ariel_os_embassy_common::uart::Baudrate::_57600 => Baudrate { baud: 57_600 },
72            ariel_os_embassy_common::uart::Baudrate::_115200 => Baudrate { baud: 115_200 },
73        }
74    }
75}
76
77/// UART number of data bits.
78#[derive(Debug, Copy, Clone, PartialEq, Eq)]
79#[cfg_attr(feature = "defmt", derive(defmt::Format))]
80pub enum DataBits {
81    /// 5 bits per character.
82    Data5,
83    /// 6 bits per character.
84    Data6,
85    /// 7 bits per character.
86    Data7,
87    /// 8 bits per character.
88    Data8,
89}
90
91fn from_data_bits(databits: DataBits) -> esp_hal::uart::DataBits {
92    match databits {
93        DataBits::Data5 => esp_hal::uart::DataBits::_5,
94        DataBits::Data6 => esp_hal::uart::DataBits::_6,
95        DataBits::Data7 => esp_hal::uart::DataBits::_7,
96        DataBits::Data8 => esp_hal::uart::DataBits::_8,
97    }
98}
99
100impl From<ariel_os_embassy_common::uart::DataBits<Self>> for DataBits {
101    fn from(databits: ariel_os_embassy_common::uart::DataBits<Self>) -> DataBits {
102        match databits {
103            ariel_os_embassy_common::uart::DataBits::Hal(bits) => bits,
104            ariel_os_embassy_common::uart::DataBits::Data8 => DataBits::Data8,
105        }
106    }
107}
108
109/// Parity bit.
110#[derive(Debug, Copy, Clone, PartialEq, Eq)]
111#[cfg_attr(feature = "defmt", derive(defmt::Format))]
112pub enum Parity {
113    /// No parity bit.
114    None,
115    /// Even parity bit.
116    Even,
117    /// Odd parity bit.
118    Odd,
119}
120
121fn from_parity(parity: Parity) -> esp_hal::uart::Parity {
122    match parity {
123        Parity::None => esp_hal::uart::Parity::None,
124        Parity::Even => esp_hal::uart::Parity::Even,
125        Parity::Odd => esp_hal::uart::Parity::Odd,
126    }
127}
128
129impl From<ariel_os_embassy_common::uart::Parity<Self>> for Parity {
130    fn from(parity: ariel_os_embassy_common::uart::Parity<Self>) -> Self {
131        match parity {
132            ariel_os_embassy_common::uart::Parity::Hal(parity) => parity,
133            ariel_os_embassy_common::uart::Parity::None => Self::None,
134            ariel_os_embassy_common::uart::Parity::Even => Self::Even,
135        }
136    }
137}
138
139/// UART number of stop bits.
140#[derive(Debug, Copy, Clone, PartialEq, Eq)]
141#[cfg_attr(feature = "defmt", derive(defmt::Format))]
142pub enum StopBits {
143    /// One stop bit.
144    Stop1,
145    /// 1.5 stop bits.
146    Stop1P5,
147    /// Two stop bits.
148    Stop2,
149}
150
151fn from_stop_bits(stop_bits: StopBits) -> esp_hal::uart::StopBits {
152    match stop_bits {
153        StopBits::Stop1 => esp_hal::uart::StopBits::_1,
154        StopBits::Stop1P5 => esp_hal::uart::StopBits::_1p5,
155        StopBits::Stop2 => esp_hal::uart::StopBits::_2,
156    }
157}
158
159impl From<ariel_os_embassy_common::uart::StopBits<Self>> for StopBits {
160    fn from(stopbits: ariel_os_embassy_common::uart::StopBits<Self>) -> Self {
161        match stopbits {
162            ariel_os_embassy_common::uart::StopBits::Hal(stopbits) => stopbits,
163            ariel_os_embassy_common::uart::StopBits::Stop1 => StopBits::Stop1,
164        }
165    }
166}
167
168fn convert_error(_err: esp_hal::uart::ConfigError) -> ConfigError {
169    ConfigError::ConfigurationNotSupported
170}
171
172macro_rules! define_uart_drivers {
173    ($( $peripheral:ident ),* $(,)?) => {
174        $(
175            /// Peripheral-specific UART driver.
176            pub struct $peripheral<'d> {
177                uart: EspUart<'d, Async>
178            }
179
180            // Make this struct a compile-time-enforced singleton: having multiple statics
181            // defined with the same name would result in a compile-time error.
182            paste::paste! {
183                #[allow(dead_code)]
184                static [<PREVENT_MULTIPLE_ $peripheral>]: () = ();
185            }
186
187            // Ensure this peripheral has only one active Instance.
188            paste::paste! {
189                static [< ACTIVE_ $peripheral >]: AtomicBool = AtomicBool::new(false);
190            }
191
192            impl<'d> $peripheral<'d> {
193                /// Returns a driver implementing embedded-io traits for this Uart
194                /// peripheral.
195                ///
196                /// # Errors
197                ///
198                /// Returns [`ConfigError::ConfigurationNotSupported`] when the requested configuration
199                /// cannot be applied to the peripheral.
200                /// If the baud rate is not supported, this may be reported as a distinct
201                /// [`ConfigError::BaudrateNotSupported`] error, or as
202                /// [`ConfigError::ConfigurationNotSupported`].
203                #[expect(clippy::new_ret_no_self)]
204                pub fn new<RX: PeripheralInput<'d>, TX: PeripheralOutput<'d>>(
205                    rx_pin: impl $crate::IntoPeripheral<'d, RX>,
206                    tx_pin: impl $crate::IntoPeripheral<'d, TX>,
207                    _rx_buf: &'d mut [u8],
208                    _tx_buf: &'d mut [u8],
209                    config: Config,
210                ) -> Result<Uart<'d>, ConfigError> {
211
212                    let uart_config = esp_hal::uart::Config::default()
213                        .with_baudrate(config.baudrate.into())
214                        .with_data_bits(from_data_bits(config.data_bits))
215                        .with_stop_bits(from_stop_bits(config.stop_bits))
216                        .with_parity(from_parity(config.parity));
217
218                    // Check if we can initialize this peripheral (check if the value was previously false, set it to true).
219                    paste::paste! {
220                        if [< ACTIVE_ $peripheral >].swap(true, Ordering::AcqRel) {
221                            panic!("UART peripheral already initialized")
222                        }
223                    }
224
225                    // FIXME(safety): enforce that the init code indeed has run
226                    // SAFETY: We check with an AtomicBool that only one instance of this peripheral
227                    // is active at once.
228                    let uart_peripheral = unsafe { peripherals::$peripheral::steal() };
229
230                    let uart = EspUart::new(
231                        uart_peripheral,
232                        uart_config
233                    )
234                        .map_err(|e| {
235                            // Turns out we didn't initialize it.
236                            paste::paste! {
237                                [< ACTIVE_ $peripheral >].store(false, Ordering::Release);
238                            }
239                            convert_error(e)
240                        })?
241                        .with_tx(tx_pin.into_hal_peripheral())
242                        .with_rx(rx_pin.into_hal_peripheral())
243                        .into_async();
244
245                    Ok(Uart::$peripheral(Self { uart }))
246                }
247            }
248
249            impl<'d> Drop for $peripheral<'d> {
250                fn drop(&mut self) {
251                    paste::paste! {
252                        [< ACTIVE_ $peripheral >].store(false, Ordering::Release);
253                    }
254                }
255            }
256        )*
257
258        /// Peripheral-agnostic UART driver.
259        pub enum Uart<'d> {
260            $(
261                #[doc = concat!(stringify!($peripheral), " peripheral.")]
262                $peripheral($peripheral<'d>)
263            ),*
264        }
265
266        impl embedded_io_async::ErrorType for Uart<'_> {
267            type Error = esp_hal::uart::IoError;
268        }
269
270        impl_async_uart_for_driver_enum!(Uart, $( $peripheral ),*);
271    }
272}
273
274#[cfg(context = "esp32")]
275define_uart_drivers!(UART0, UART1, UART2);
276#[cfg(context = "esp32c3")]
277define_uart_drivers!(UART0, UART1);
278#[cfg(context = "esp32c6")]
279define_uart_drivers!(UART0, UART1);
280#[cfg(context = "esp32s2")]
281define_uart_drivers!(UART0, UART1);
282#[cfg(context = "esp32s3")]
283define_uart_drivers!(UART0, UART1, UART2);
284
285#[doc(hidden)]
286pub fn init(peripherals: &mut crate::OptionalPeripherals) {
287    // Take all UART peripherals and do nothing with them.
288    cfg_select! {
289        context = "esp32" => {
290            let _ = peripherals.UART0.take().unwrap();
291            let _ = peripherals.UART1.take().unwrap();
292            let _ = peripherals.UART2.take().unwrap();
293        }
294        context = "esp32c3" => {
295            let _ = peripherals.UART0.take().unwrap();
296            let _ = peripherals.UART1.take().unwrap();
297        }
298        context = "esp32c6" => {
299            let _ = peripherals.UART0.take().unwrap();
300            let _ = peripherals.UART1.take().unwrap();
301        }
302        context = "esp32s2" => {
303            let _ = peripherals.UART0.take().unwrap();
304            let _ = peripherals.UART1.take().unwrap();
305        }
306        context = "esp32s3" => {
307            let _ = peripherals.UART0.take().unwrap();
308            let _ = peripherals.UART1.take().unwrap();
309            let _ = peripherals.UART2.take().unwrap();
310        }
311        _ => {
312            compile_error!("this ESP32 chip is not supported");
313        }
314    }
315}