Skip to main content

esp_hal/uart/
lp_uart.rs

1//! Low-power UART
2
3use crate::{
4    gpio::{
5        InputPin,
6        LpPin,
7        OutputPin,
8        lp_io::{LpFunction, low_level},
9    },
10    peripherals::{LP_CLKRST, LP_UART, LPWR},
11    uart::{DataBits, Parity, StopBits},
12};
13
14/// Trait representing the LP_UART TX pin.
15pub trait Tx: LpPin + OutputPin {
16    #[doc(hidden)]
17    fn connect_tx(&self);
18}
19
20/// Trait representing the LP_UART RX pin.
21pub trait Rx: LpPin + InputPin {
22    #[doc(hidden)]
23    fn connect_rx(&self);
24}
25
26// Chips with an LP GPIO matrix can route the LP UART signals to any LP pin. When a pad's LP IO MUX
27// has an LP UART function, use that; otherwise route through the matrix.
28#[cfg(lp_io_has_gpio_matrix)]
29for_each_lp_function! {
30    (($_signal:ident, LP_GPIOn, $_pin:literal), $gpio:ident, $_af:ident, $_lp_in:tt $_lp_out:tt) => {
31        impl Tx for crate::peripherals::$gpio<'_> {
32            fn connect_tx(&self) {
33                crate::gpio::lp_io::connect_output_signal(
34                    self,
35                    crate::gpio::lp_io::LpOutputSignal::LP_UART_TXD,
36                );
37            }
38        }
39
40        impl Rx for crate::peripherals::$gpio<'_> {
41            fn connect_rx(&self) {
42                crate::gpio::lp_io::connect_input_signal(
43                    self,
44                    crate::gpio::lp_io::LpInputSignal::LP_UART_RXD,
45                );
46            }
47        }
48    };
49}
50
51#[cfg(not(lp_io_has_gpio_matrix))]
52for_each_lp_function! {
53    (LP_UART_TXD, $gpio:ident, $af:ident) => {
54        impl Tx for crate::peripherals::$gpio<'_> {
55            fn connect_tx(&self) {
56                // The output enable is left to the peripheral: selecting a function other than
57                // LP GPIO takes the pad's direction out of the LP GPIO peripheral's hands.
58                low_level::set_config(self.lp_number(), false, true, LpFunction::$af);
59            }
60        }
61    };
62    (LP_UART_RXD, $gpio:ident, $af:ident) => {
63        impl Rx for crate::peripherals::$gpio<'_> {
64            fn connect_rx(&self) {
65                low_level::set_config(self.lp_number(), true, true, LpFunction::$af);
66            }
67        }
68    };
69}
70
71/// LP-UART Configuration
72#[derive(Debug, Clone, Copy, procmacros::BuilderLite)]
73#[cfg_attr(feature = "defmt", derive(defmt::Format))]
74#[non_exhaustive]
75pub struct Config {
76    /// The baud rate (speed) of the UART communication in bits per second
77    /// (bps).
78    baudrate: u32,
79    /// Number of data bits in each frame (5, 6, 7, or 8 bits).
80    data_bits: DataBits,
81    /// Parity setting (None, Even, or Odd).
82    parity: Parity,
83    /// Number of stop bits in each frame (1, 1.5, or 2 bits).
84    stop_bits: StopBits,
85    /// Clock source used by the UART peripheral.
86    #[builder_lite(unstable)]
87    clock_source: ClockSource,
88}
89
90impl Default for Config {
91    fn default() -> Config {
92        Config {
93            baudrate: 115_200,
94            data_bits: Default::default(),
95            parity: Default::default(),
96            stop_bits: Default::default(),
97            clock_source: Default::default(),
98        }
99    }
100}
101
102/// LP-UART clock source
103#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
104#[cfg_attr(feature = "defmt", derive(defmt::Format))]
105#[non_exhaustive]
106#[instability::unstable]
107pub enum ClockSource {
108    /// RC_FAST_CLK clock source
109    RcFast,
110
111    /// XTAL_D2 clock source
112    #[default]
113    Xtal,
114}
115
116/// LP-UART driver
117pub struct LpUart {
118    uart: LP_UART<'static>,
119}
120
121impl LpUart {
122    /// Initialize the UART driver using the provided configuration
123    // TODO: CTS and RTS pins
124    pub fn new(
125        uart: LP_UART<'static>,
126        config: Config,
127        tx: impl Tx + 'static,
128        rx: impl Rx + 'static,
129    ) -> Self {
130        rx.connect_rx();
131        tx.connect_tx();
132
133        let mut me = Self { uart };
134        let uart = me.uart.register_block();
135
136        // Set UART mode - do nothing for LP
137
138        // Disable UART parity
139        // 8-bit world
140        // 1-bit stop bit
141        uart.conf0().modify(|_, w| unsafe {
142            w.parity().clear_bit();
143            w.parity_en().clear_bit();
144            w.bit_num().bits(0x3);
145            w.stop_bit_num().bits(0x1)
146        });
147        // Set tx idle
148        uart.idle_conf()
149            .modify(|_, w| unsafe { w.tx_idle_num().bits(0) });
150        // Disable hw-flow control
151        uart.hwfc_conf().modify(|_, w| w.rx_flow_en().clear_bit());
152
153        // Get source clock frequency
154        // default == SOC_MOD_CLK_RTC_FAST == 2
155
156        // LPWR.lpperi.lp_uart_clk_sel = 0;
157        LPWR::regs()
158            .lpperi()
159            .modify(|_, w| w.lp_uart_clk_sel().clear_bit());
160
161        // Override protocol parameters from the configuration
162        // uart_hal_set_baudrate(&hal, cfg->uart_proto_cfg.baud_rate, sclk_freq);
163        me.change_baud_internal(&config);
164        // uart_hal_set_parity(&hal, cfg->uart_proto_cfg.parity);
165        me.change_parity(config.parity);
166        // uart_hal_set_data_bit_num(&hal, cfg->uart_proto_cfg.data_bits);
167        me.change_data_bits(config.data_bits);
168        // uart_hal_set_stop_bits(&hal, cfg->uart_proto_cfg.stop_bits);
169        me.change_stop_bits(config.stop_bits);
170        // uart_hal_set_tx_idle_num(&hal, LP_UART_TX_IDLE_NUM_DEFAULT);
171        me.change_tx_idle(0); // LP_UART_TX_IDLE_NUM_DEFAULT == 0
172
173        // Reset Tx/Rx FIFOs
174        me.rxfifo_reset();
175        me.txfifo_reset();
176
177        me
178    }
179
180    fn rxfifo_reset(&mut self) {
181        self.uart
182            .register_block()
183            .conf0()
184            .modify(|_, w| w.rxfifo_rst().set_bit());
185        self.update();
186
187        self.uart
188            .register_block()
189            .conf0()
190            .modify(|_, w| w.rxfifo_rst().clear_bit());
191        self.update();
192    }
193
194    fn txfifo_reset(&mut self) {
195        self.uart
196            .register_block()
197            .conf0()
198            .modify(|_, w| w.txfifo_rst().set_bit());
199        self.update();
200
201        self.uart
202            .register_block()
203            .conf0()
204            .modify(|_, w| w.txfifo_rst().clear_bit());
205        self.update();
206    }
207
208    fn update(&mut self) {
209        let register_block = self.uart.register_block();
210        register_block
211            .reg_update()
212            .modify(|_, w| w.reg_update().set_bit());
213        while register_block.reg_update().read().reg_update().bit_is_set() {
214            // wait
215        }
216    }
217
218    fn change_baud_internal(&mut self, config: &Config) {
219        let clk = match config.clock_source {
220            ClockSource::RcFast => crate::soc::clocks::rc_fast_clk_frequency(),
221            ClockSource::Xtal => crate::soc::clocks::xtal_d2_clk_frequency(),
222        };
223
224        LP_CLKRST::regs().lpperi().modify(|_, w| {
225            w.lp_uart_clk_sel().bit(match config.clock_source {
226                ClockSource::RcFast => false,
227                ClockSource::Xtal => true,
228            })
229        });
230        self.uart.register_block().clk_conf().modify(|_, w| {
231            w.rx_sclk_en().set_bit();
232            w.tx_sclk_en().set_bit()
233        });
234
235        let divider = clk / config.baudrate;
236        let divider = divider as u16;
237
238        self.uart
239            .register_block()
240            .clkdiv()
241            .write(|w| unsafe { w.clkdiv().bits(divider).frag().bits(0) });
242
243        self.update();
244    }
245
246    /// Modify UART baud rate and reset TX/RX fifo.
247    pub fn change_baud(&mut self, config: &Config) {
248        self.change_baud_internal(config);
249        self.txfifo_reset();
250        self.rxfifo_reset();
251    }
252
253    fn change_parity(&mut self, parity: Parity) -> &mut Self {
254        if parity != Parity::None {
255            self.uart
256                .register_block()
257                .conf0()
258                .modify(|_, w| w.parity().bit((parity as u8 & 0x1) != 0));
259        }
260
261        self.uart
262            .register_block()
263            .conf0()
264            .modify(|_, w| match parity {
265                Parity::None => w.parity_en().clear_bit(),
266                Parity::Even => w.parity_en().set_bit().parity().clear_bit(),
267                Parity::Odd => w.parity_en().set_bit().parity().set_bit(),
268            });
269
270        self
271    }
272
273    fn change_data_bits(&mut self, data_bits: DataBits) -> &mut Self {
274        self.uart
275            .register_block()
276            .conf0()
277            .modify(|_, w| unsafe { w.bit_num().bits(data_bits as u8) });
278
279        self.update();
280        self
281    }
282
283    fn change_stop_bits(&mut self, stop_bits: StopBits) -> &mut Self {
284        self.uart
285            .register_block()
286            .conf0()
287            .modify(|_, w| unsafe { w.stop_bit_num().bits(stop_bits as u8 + 1) });
288
289        self.update();
290        self
291    }
292
293    fn change_tx_idle(&mut self, idle_num: u16) -> &mut Self {
294        self.uart
295            .register_block()
296            .idle_conf()
297            .modify(|_, w| unsafe { w.tx_idle_num().bits(idle_num) });
298
299        self.update();
300        self
301    }
302}