Skip to main content

ariel_os_nrf/
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};
8use embassy_nrf::{
9    bind_interrupts,
10    buffered_uarte::{BufferedUarte, InterruptHandler},
11    gpio::Pin as GpioPin,
12    peripherals,
13};
14
15/// UART interface configuration.
16#[derive(Debug, Copy, Clone, PartialEq, Eq)]
17#[cfg_attr(feature = "defmt", derive(defmt::Format))]
18#[non_exhaustive]
19pub struct Config {
20    /// The baud rate at which UART operates.
21    pub baudrate: ariel_os_embassy_common::uart::Baudrate<Baudrate>,
22    /// Number of data bits.
23    pub data_bits: DataBits,
24    /// Number of stop bits.
25    pub stop_bits: StopBits,
26    /// Parity mode used for the interface.
27    pub parity: Parity,
28}
29
30impl Default for Config {
31    fn default() -> Self {
32        Self {
33            baudrate: ariel_os_embassy_common::uart::Baudrate::_115200,
34            data_bits: DataBits::Data8,
35            stop_bits: StopBits::Stop1,
36            parity: Parity::None,
37        }
38    }
39}
40
41/// UART baud rate.
42#[derive(Debug, Copy, Clone, PartialEq, Eq)]
43#[cfg_attr(feature = "defmt", derive(defmt::Format))]
44#[non_exhaustive]
45pub enum Baudrate {
46    /// 1200 baud.
47    _1200,
48    /// 2400 baud.
49    _2400,
50    /// 4800 baud.
51    _4800,
52    /// 9600 baud.
53    _9600,
54    /// 14400 baud.
55    _14400,
56    /// 19200 baud.
57    _19200,
58    /// 28800 baud.
59    _28800,
60    /// 31250 baud.
61    _31250,
62    /// 38400 baud.
63    _38400,
64    /// 56000 baud.
65    _56000,
66    /// 57600 baud.
67    _57600,
68    /// 76800 baud.
69    _76800,
70    /// 115200 baud.
71    _115200,
72    /// 230400 baud.
73    _230400,
74    /// 250000 baud.
75    _250000,
76    /// 460800 baud.
77    _460800,
78    /// 921600 baud.
79    _921600,
80    /// 1 Megabaud.
81    _1000000,
82}
83
84impl From<Baudrate> for u32 {
85    fn from(baud: Baudrate) -> u32 {
86        match baud {
87            Baudrate::_1200 => 1200,
88            Baudrate::_2400 => 2400,
89            Baudrate::_4800 => 4800,
90            Baudrate::_9600 => 9600,
91            Baudrate::_14400 => 14_400,
92            Baudrate::_19200 => 19_200,
93            Baudrate::_28800 => 28_800,
94            Baudrate::_31250 => 31_250,
95            Baudrate::_38400 => 38_400,
96            Baudrate::_56000 => 56_000,
97            Baudrate::_57600 => 57_600,
98            Baudrate::_76800 => 76_800,
99            Baudrate::_115200 => 11_5200,
100            Baudrate::_230400 => 23_0400,
101            Baudrate::_250000 => 25_0000,
102            Baudrate::_460800 => 46_0800,
103            Baudrate::_921600 => 92_1600,
104            Baudrate::_1000000 => 1_000_000,
105        }
106    }
107}
108
109fn from_baudrate(baud: Baudrate) -> embassy_nrf::buffered_uarte::Baudrate {
110    match baud {
111        Baudrate::_1200 => embassy_nrf::uarte::Baudrate::BAUD1200,
112        Baudrate::_2400 => embassy_nrf::uarte::Baudrate::BAUD2400,
113        Baudrate::_4800 => embassy_nrf::uarte::Baudrate::BAUD4800,
114        Baudrate::_9600 => embassy_nrf::uarte::Baudrate::BAUD9600,
115        Baudrate::_14400 => embassy_nrf::uarte::Baudrate::BAUD14400,
116        Baudrate::_19200 => embassy_nrf::uarte::Baudrate::BAUD19200,
117        Baudrate::_28800 => embassy_nrf::uarte::Baudrate::BAUD28800,
118        Baudrate::_31250 => embassy_nrf::uarte::Baudrate::BAUD31250,
119        Baudrate::_38400 => embassy_nrf::uarte::Baudrate::BAUD38400,
120        Baudrate::_56000 => embassy_nrf::uarte::Baudrate::BAUD56000,
121        Baudrate::_57600 => embassy_nrf::uarte::Baudrate::BAUD57600,
122        Baudrate::_76800 => embassy_nrf::uarte::Baudrate::BAUD76800,
123        Baudrate::_115200 => embassy_nrf::uarte::Baudrate::BAUD115200,
124        Baudrate::_230400 => embassy_nrf::uarte::Baudrate::BAUD230400,
125        Baudrate::_250000 => embassy_nrf::uarte::Baudrate::BAUD250000,
126        Baudrate::_460800 => embassy_nrf::uarte::Baudrate::BAUD460800,
127        Baudrate::_921600 => embassy_nrf::uarte::Baudrate::BAUD921600,
128        Baudrate::_1000000 => embassy_nrf::uarte::Baudrate::BAUD1M,
129    }
130}
131
132impl From<ariel_os_embassy_common::uart::Baudrate<Self>> for Baudrate {
133    fn from(baud: ariel_os_embassy_common::uart::Baudrate<Self>) -> Baudrate {
134        match baud {
135            ariel_os_embassy_common::uart::Baudrate::Hal(baud) => baud,
136            ariel_os_embassy_common::uart::Baudrate::_2400 => Baudrate::_2400,
137            ariel_os_embassy_common::uart::Baudrate::_4800 => Baudrate::_4800,
138            ariel_os_embassy_common::uart::Baudrate::_9600 => Baudrate::_9600,
139            ariel_os_embassy_common::uart::Baudrate::_19200 => Baudrate::_19200,
140            ariel_os_embassy_common::uart::Baudrate::_38400 => Baudrate::_38400,
141            ariel_os_embassy_common::uart::Baudrate::_57600 => Baudrate::_57600,
142            ariel_os_embassy_common::uart::Baudrate::_115200 => Baudrate::_115200,
143        }
144    }
145}
146
147/// UART number of data bits.
148#[derive(Debug, Copy, Clone, PartialEq, Eq)]
149#[cfg_attr(feature = "defmt", derive(defmt::Format))]
150#[non_exhaustive]
151pub enum DataBits {
152    /// 8 bits per character.
153    Data8,
154}
155
156impl From<ariel_os_embassy_common::uart::DataBits<Self>> for DataBits {
157    fn from(databits: ariel_os_embassy_common::uart::DataBits<Self>) -> DataBits {
158        match databits {
159            ariel_os_embassy_common::uart::DataBits::Hal(bits) => bits,
160            ariel_os_embassy_common::uart::DataBits::Data8 => DataBits::Data8,
161        }
162    }
163}
164/// UART number of stop bits.
165#[derive(Debug, Copy, Clone, PartialEq, Eq)]
166#[cfg_attr(feature = "defmt", derive(defmt::Format))]
167#[non_exhaustive]
168pub enum StopBits {
169    /// One stop bit.
170    Stop1,
171}
172
173impl From<ariel_os_embassy_common::uart::StopBits<Self>> for StopBits {
174    fn from(stopbits: ariel_os_embassy_common::uart::StopBits<Self>) -> Self {
175        match stopbits {
176            ariel_os_embassy_common::uart::StopBits::Hal(stopbits) => stopbits,
177            ariel_os_embassy_common::uart::StopBits::Stop1 => StopBits::Stop1,
178        }
179    }
180}
181
182/// Parity bit.
183#[derive(Debug, Copy, Clone, PartialEq, Eq)]
184#[cfg_attr(feature = "defmt", derive(defmt::Format))]
185#[non_exhaustive]
186pub enum Parity {
187    /// No parity bit.
188    None,
189    /// Even parity bit.
190    Even,
191}
192
193fn from_parity(parity: Parity) -> embassy_nrf::uarte::Parity {
194    match parity {
195        Parity::None => embassy_nrf::uarte::Parity::EXCLUDED,
196        Parity::Even => embassy_nrf::uarte::Parity::INCLUDED,
197    }
198}
199
200impl From<ariel_os_embassy_common::uart::Parity<Self>> for Parity {
201    fn from(parity: ariel_os_embassy_common::uart::Parity<Self>) -> Self {
202        match parity {
203            ariel_os_embassy_common::uart::Parity::Hal(parity) => parity,
204            ariel_os_embassy_common::uart::Parity::None => Self::None,
205            ariel_os_embassy_common::uart::Parity::Even => Self::Even,
206        }
207    }
208}
209
210macro_rules! define_uart_drivers {
211    ($( $interrupt:ident => $peripheral:ident + $timer:ident + $ppi_ch1:ident + $ppi_ch2:ident + $ppi_group:ident),* $(,)?) => {
212        $(
213            /// Peripheral-specific UART driver.
214            pub struct $peripheral<'d> {
215                uart: BufferedUarte<'d>,
216            }
217
218            // Make this struct a compile-time-enforced singleton: having multiple statics
219            // defined with the same name would result in a compile-time error.
220            paste::paste! {
221                #[allow(dead_code)]
222                static [<PREVENT_MULTIPLE_ $peripheral>]: () = ();
223                #[allow(dead_code)]
224                static [<PREVENT_MULTIPLE_ $timer>]: () = ();
225                #[allow(dead_code)]
226                static [<PREVENT_MULTIPLE_ $ppi_ch1>]: () = ();
227                #[allow(dead_code)]
228                static [<PREVENT_MULTIPLE_ $ppi_ch2>]: () = ();
229                #[allow(dead_code)]
230                static [<PREVENT_MULTIPLE_ $ppi_group>]: () = ();
231            }
232
233            // Ensure this peripheral has only one active Instance.
234            paste::paste! {
235                static [< ACTIVE_ $peripheral >]: AtomicBool = AtomicBool::new(false);
236            }
237
238            impl<'d> $peripheral<'d> {
239                /// Returns a driver implementing [`embedded_io_async`] for this Uart
240                /// peripheral.
241                ///
242                /// # Errors
243                ///
244                /// This never returns an error.
245                #[expect(clippy::new_ret_no_self)]
246                pub fn new<RX: GpioPin, TX: GpioPin>(
247                    rx_pin: impl $crate::IntoPeripheral<'d, RX>,
248                    tx_pin: impl $crate::IntoPeripheral<'d, TX>,
249                    rx_buffer: &'d mut [u8],
250                    tx_buffer: &'d mut [u8],
251                    config: Config,
252                ) -> Result<Uart<'d>, ConfigError> {
253                    let mut uart_config = embassy_nrf::uarte::Config::default();
254                    uart_config.baudrate = from_baudrate(Baudrate::from(config.baudrate));
255                    uart_config.parity = from_parity(config.parity);
256                    bind_interrupts!(struct Irqs {
257                        $interrupt => InterruptHandler<peripherals::$peripheral>;
258                    });
259
260                    // Check if we can initialize this peripheral (check if the value was previously false, set it to true).
261                    paste::paste! {
262                        if [< ACTIVE_ $peripheral >].swap(true, Ordering::AcqRel) {
263                            panic!("UART peripheral already initialized")
264                        }
265                    }
266
267                    // FIXME(safety): enforce that the init code indeed has run
268                    // SAFETY: We check with an AtomicBool that only one instance of this peripheral
269                    // is active at once.
270                    let uart_peripheral = unsafe { peripherals::$peripheral::steal() };
271                    // SAFETY: We check with an AtomicBool that only one instance of this peripheral
272                    // is active at once.
273                    let timer_peripheral = unsafe { peripherals::$timer::steal() };
274                    // SAFETY: We check with an AtomicBool that only one instance of this peripheral
275                    // is active at once.
276                    let ppi_ch1_peripheral = unsafe { peripherals::$ppi_ch1::steal() };
277                    // SAFETY: We check with an AtomicBool that only one instance of this peripheral
278                    // is active at once.
279                    let ppi_ch2_peripheral = unsafe { peripherals::$ppi_ch2::steal() };
280                    // SAFETY: We check with an AtomicBool that only one instance of this peripheral
281                    // is active at once.
282                    let ppi_group_peripheral = unsafe { peripherals::$ppi_group::steal() };
283
284                    let uart = BufferedUarte::new(
285                        uart_peripheral,
286                        timer_peripheral,
287                        ppi_ch1_peripheral,
288                        ppi_ch2_peripheral,
289                        ppi_group_peripheral,
290                        rx_pin.into_hal_peripheral(),
291                        tx_pin.into_hal_peripheral(),
292                        Irqs,
293                        uart_config,
294                        rx_buffer,
295                        tx_buffer
296                    );
297
298                    Ok(Uart::$peripheral(Self { uart }))
299                }
300            }
301
302            impl<'d> Drop for $peripheral<'d> {
303                fn drop(&mut self) {
304                    paste::paste! {
305                        [< ACTIVE_ $peripheral >].store(false, Ordering::Release);
306                    }
307                }
308            }
309        )*
310
311        /// Peripheral-agnostic UART driver.
312        pub enum Uart<'d> {
313            $(
314                #[doc = concat!(stringify!($peripheral), " peripheral.")]
315                $peripheral($peripheral<'d>)
316            ),*
317        }
318
319        impl embedded_io_async::ErrorType for Uart<'_> {
320            type Error = embassy_nrf::buffered_uarte::Error;
321        }
322
323        impl_async_uart_for_driver_enum!(Uart, $( $peripheral ),*);
324    }
325}
326
327// Define a driver per peripheral
328#[cfg(context = "nrf52832")]
329define_uart_drivers!(
330   UARTE0 => UARTE0 + TIMER4 + PPI_CH14 + PPI_CH15 + PPI_GROUP5,
331);
332#[cfg(context = "nrf52833")]
333define_uart_drivers!(
334   UARTE0 => UARTE0 + TIMER3 + PPI_CH13 + PPI_CH14 + PPI_GROUP4,
335   UARTE1 => UARTE1 + TIMER4 + PPI_CH15 + PPI_CH16 + PPI_GROUP5,
336);
337#[cfg(context = "nrf52840")]
338define_uart_drivers!(
339   UARTE0 => UARTE0 + TIMER3 + PPI_CH13 + PPI_CH14 + PPI_GROUP4,
340   UARTE1 => UARTE1 + TIMER4 + PPI_CH15 + PPI_CH16 + PPI_GROUP5,
341);
342#[cfg(context = "nrf5340-app")]
343define_uart_drivers!(
344   SERIAL3 => SERIAL3 + TIMER2 + PPI_CH18 + PPI_CH19 + PPI_GROUP5,
345);
346#[cfg(any(context = "nrf9151", context = "nrf9160"))]
347define_uart_drivers!(
348   SERIAL3 => SERIAL3 + TIMER2 + PPI_CH14 + PPI_CH15 + PPI_GROUP5,
349);
350
351#[doc(hidden)]
352pub fn init(peripherals: &mut crate::OptionalPeripherals) {
353    // Take all UART peripherals and do nothing with them.
354    cfg_select! {
355        context = "nrf52832" => {
356            let _ = peripherals.UARTE0.take().unwrap();
357            let _ = peripherals.TIMER4.take().unwrap();
358            let _ = peripherals.PPI_CH14.take().unwrap();
359            let _ = peripherals.PPI_CH15.take().unwrap();
360            let _ = peripherals.PPI_GROUP5.take().unwrap();
361        }
362        context = "nrf52833" => {
363            let _ = peripherals.UARTE0.take().unwrap();
364            let _ = peripherals.TIMER3.take().unwrap();
365            let _ = peripherals.PPI_CH13.take().unwrap();
366            let _ = peripherals.PPI_CH14.take().unwrap();
367            let _ = peripherals.PPI_GROUP4.take().unwrap();
368
369            let _ = peripherals.UARTE1.take().unwrap();
370            let _ = peripherals.TIMER4.take().unwrap();
371            let _ = peripherals.PPI_CH15.take().unwrap();
372            let _ = peripherals.PPI_CH16.take().unwrap();
373            let _ = peripherals.PPI_GROUP5.take().unwrap();
374        }
375        context = "nrf52840" => {
376            let _ = peripherals.UARTE0.take().unwrap();
377            let _ = peripherals.TIMER3.take().unwrap();
378            let _ = peripherals.PPI_CH13.take().unwrap();
379            let _ = peripherals.PPI_CH14.take().unwrap();
380            let _ = peripherals.PPI_GROUP4.take().unwrap();
381
382            let _ = peripherals.UARTE1.take().unwrap();
383            let _ = peripherals.TIMER4.take().unwrap();
384            let _ = peripherals.PPI_CH15.take().unwrap();
385            let _ = peripherals.PPI_CH16.take().unwrap();
386            let _ = peripherals.PPI_GROUP5.take().unwrap();
387        }
388        context = "nrf5340-app" => {
389            let _ = peripherals.SERIAL3.take().unwrap();
390            let _ = peripherals.TIMER2.take().unwrap();
391            let _ = peripherals.PPI_CH18.take().unwrap();
392            let _ = peripherals.PPI_CH19.take().unwrap();
393            let _ = peripherals.PPI_GROUP5.take().unwrap();
394        }
395        any(context = "nrf9151", context = "nrf9160") => {
396            let _ = peripherals.SERIAL3.take().unwrap();
397            let _ = peripherals.TIMER2.take().unwrap();
398            let _ = peripherals.PPI_CH14.take().unwrap();
399            let _ = peripherals.PPI_CH15.take().unwrap();
400            let _ = peripherals.PPI_GROUP5.take().unwrap();
401        }
402        _ => {
403            compile_error!("this nRF chip is not supported");
404        }
405    }
406}