lpc8xx_hal/usart/
peripheral.rs

1use core::fmt;
2
3use embedded_hal::{
4    blocking::serial::write::Default as BlockingWriteDefault,
5    serial::{Read, Write},
6};
7use void::Void;
8
9use crate::{
10    init_state::Disabled,
11    pac::{usart0::cfg, NVIC},
12    swm, syscon,
13};
14
15use super::{
16    clock::{Clock, ClockSource},
17    flags::{Flag, Interrupts},
18    instances::Instance,
19    rx::{Error, Rx},
20    settings::Settings,
21    state::{AsyncMode, Enabled, NoThrottle, SyncMode, Word},
22    tx::Tx,
23};
24
25/// Interface to a USART peripheral
26///
27/// Controls the USART.  Use [`Peripherals`] to gain access to an instance of
28/// this struct.
29///
30/// You can either use this struct as-is, if you need to send and receive in the
31/// same place, or you can move the `rx` and `tx` fields out of this struct, to
32/// use the sender and receiver from different contexts.
33///
34/// Please refer to the [module documentation] for more information.
35///
36/// # `embedded-hal` traits
37/// - [`embedded_hal::serial::Read`] for non-blocking reads
38/// - [`embedded_hal::serial::Write`] for non-blocking writes
39/// - [`embedded_hal::blocking::serial::Write`] for blocking writes
40///
41///
42/// [`Peripherals`]: ../struct.Peripherals.html
43/// [module documentation]: index.html
44/// [`embedded_hal::serial::Read`]: #impl-Read<W>
45/// [`embedded_hal::serial::Write`]: #impl-Write<W>
46/// [`embedded_hal::blocking::serial::Write`]: #impl-Write<Word>
47pub struct USART<I, State> {
48    /// The USART Receiver
49    pub rx: Rx<I, State>,
50
51    /// The USART Transmitter
52    pub tx: Tx<I, State, NoThrottle>,
53
54    usart: I,
55}
56
57impl<I> USART<I, Disabled>
58where
59    I: Instance,
60{
61    pub(crate) fn new(usart: I) -> Self {
62        USART {
63            rx: Rx::new(),
64            tx: Tx::new(),
65
66            usart,
67        }
68    }
69
70    /// Enable the USART in asynchronous mode
71    ///
72    /// Asynchronous mode works without an external clock signal. The word
73    /// "asynchronous" has no relation to blocking or non-blocking APIs, in this
74    /// context.
75    ///
76    /// This method is only available, if `USART` is in the [`Disabled`] state.
77    /// Code that attempts to call this method when the peripheral is already
78    /// enabled will not compile.
79    ///
80    /// Consumes this instance of `USART` and returns another instance that has
81    /// its `State` type parameter set to [`Enabled`].
82    ///
83    /// # Limitations
84    ///
85    /// For USART to function correctly, the UARTFRG reset must be cleared. This
86    /// is the default, so unless you have messed with those settings, you
87    /// should be good.
88    ///
89    /// # Examples
90    ///
91    /// Please refer to the [module documentation] for a full example.
92    ///
93    /// [`Disabled`]: ../init_state/struct.Disabled.html
94    /// [`Enabled`]: state/struct.Enabled.html
95    /// [`BaudRate`]: struct.BaudRate.html
96    /// [module documentation]: index.html
97    pub fn enable_async<RxPin, TxPin, CLOCK, W>(
98        mut self,
99        clock: &Clock<CLOCK, AsyncMode>,
100        syscon: &mut syscon::Handle,
101        _: swm::Function<I::Rx, swm::state::Assigned<RxPin>>,
102        _: swm::Function<I::Tx, swm::state::Assigned<TxPin>>,
103        settings: Settings<W>,
104    ) -> USART<I, Enabled<W, AsyncMode>>
105    where
106        CLOCK: ClockSource,
107        W: Word,
108    {
109        self.configure::<CLOCK>(syscon);
110
111        self.usart
112            .brg
113            .write(|w| unsafe { w.brgval().bits(clock.brgval) });
114        self.usart
115            .osr
116            .write(|w| unsafe { w.osrval().bits(clock.osrval) });
117
118        // We are not allowed to send or receive data when writing to CFG. This
119        // is ensured by type state, so no need to do anything here.
120
121        self.usart.cfg.modify(|_, w| {
122            w.syncen().asynchronous_mode();
123            Self::apply_general_config(w);
124            settings.apply(w);
125            w
126        });
127
128        USART {
129            rx: Rx::new(), // can't use `self.rx`, due to state
130            tx: Tx::new(), // can't use `self.tx`, due to state
131            usart: self.usart,
132        }
133    }
134
135    /// Enable the USART in synchronous mode as master
136    ///
137    /// Synchronous mode works with an external clock signal. The word
138    /// "synchronous" has no relation to blocking or non-blocking APIs, in this
139    /// context.
140    ///
141    /// This method is only available, if `USART` is in the [`Disabled`] state.
142    /// Code that attempts to call this method when the peripheral is already
143    /// enabled will not compile.
144    ///
145    /// Consumes this instance of `USART` and returns another instance that has
146    /// its `State` type parameter set to [`Enabled`].
147    ///
148    /// # Limitations
149    ///
150    /// For USART to function correctly, the UARTFRG reset must be cleared. This
151    /// is the default, so unless you have messed with those settings, you
152    /// should be good.
153    ///
154    /// [`Disabled`]: ../init_state/struct.Disabled.html
155    /// [`Enabled`]: state/struct.Enabled.html
156    /// [`BaudRate`]: struct.BaudRate.html
157    /// [module documentation]: index.html
158    pub fn enable_sync_as_master<RxPin, TxPin, SclkPin, C, W>(
159        mut self,
160        clock: &Clock<C, SyncMode>,
161        syscon: &mut syscon::Handle,
162        _: swm::Function<I::Rx, swm::state::Assigned<RxPin>>,
163        _: swm::Function<I::Tx, swm::state::Assigned<TxPin>>,
164        _: swm::Function<I::Sclk, swm::state::Assigned<SclkPin>>,
165        settings: Settings<W>,
166    ) -> USART<I, Enabled<W, SyncMode>>
167    where
168        C: ClockSource,
169        W: Word,
170    {
171        self.configure::<C>(syscon);
172
173        self.usart
174            .brg
175            .write(|w| unsafe { w.brgval().bits(clock.brgval) });
176
177        // We are not allowed to send or receive data when writing to CFG. This
178        // is ensured by type state, so no need to do anything here.
179
180        self.usart.cfg.modify(|_, w| {
181            w.syncen().synchronous_mode();
182            w.syncmst().master();
183            Self::apply_general_config(w);
184            settings.apply(w);
185            w
186        });
187
188        USART {
189            rx: Rx::new(), // can't use `self.rx`, due to state
190            tx: Tx::new(), // can't use `self.tx`, due to state
191            usart: self.usart,
192        }
193    }
194
195    /// Enable the USART in synchronous mode as slave
196    ///
197    /// Synchronous mode works with an external clock signal. The word
198    /// "synchronous" has no relation to blocking or non-blocking APIs, in this
199    /// context.
200    ///
201    /// This method is only available, if `USART` is in the [`Disabled`] state.
202    /// Code that attempts to call this method when the peripheral is already
203    /// enabled will not compile.
204    ///
205    /// Consumes this instance of `USART` and returns another instance that has
206    /// its `State` type parameter set to [`Enabled`].
207    ///
208    /// # Limitations
209    ///
210    /// For USART to function correctly, the UARTFRG reset must be cleared. This
211    /// is the default, so unless you have messed with those settings, you
212    /// should be good.
213    ///
214    /// [`Disabled`]: ../init_state/struct.Disabled.html
215    /// [`Enabled`]: state/struct.Enabled.html
216    /// [`BaudRate`]: struct.BaudRate.html
217    /// [module documentation]: index.html
218    pub fn enable_sync_as_slave<RxPin, TxPin, SclkPin, C, W>(
219        mut self,
220        _clock: &C,
221        syscon: &mut syscon::Handle,
222        _: swm::Function<I::Rx, swm::state::Assigned<RxPin>>,
223        _: swm::Function<I::Tx, swm::state::Assigned<TxPin>>,
224        _: swm::Function<I::Sclk, swm::state::Assigned<SclkPin>>,
225        settings: Settings<W>,
226    ) -> USART<I, Enabled<W, SyncMode>>
227    where
228        C: ClockSource,
229        W: Word,
230    {
231        self.configure::<C>(syscon);
232
233        // We are not allowed to send or receive data when writing to CFG. This
234        // is ensured by type state, so no need to do anything here.
235
236        self.usart.cfg.modify(|_, w| {
237            w.syncen().synchronous_mode();
238            w.syncmst().slave();
239            Self::apply_general_config(w);
240            settings.apply(w);
241            w
242        });
243
244        USART {
245            rx: Rx::new(), // can't use `self.rx`, due to state
246            tx: Tx::new(), // can't use `self.tx`, due to state
247            usart: self.usart,
248        }
249    }
250
251    fn configure<C>(&mut self, syscon: &mut syscon::Handle)
252    where
253        C: ClockSource,
254    {
255        syscon.enable_clock(&self.usart);
256        C::select(&self.usart, syscon);
257
258        self.usart.ctl.modify(|_, w| {
259            w.txbrken().normal();
260            w.addrdet().disabled();
261            w.txdis().enabled();
262            w.cc().continous_clock();
263            w.autobaud().disabled()
264        });
265    }
266
267    fn apply_general_config(w: &mut cfg::W) {
268        // Enable peripheral instance.
269        w.enable().enabled();
270
271        // Disable CTS; can be enabled by the user later.
272        w.ctsen().disabled();
273
274        // No loopback mode; currently it's not supported.
275        w.loop_().normal();
276
277        // Enable automatic address matching. This makes no difference until we
278        // set a separate bit in CTL, and address detection without automatic
279        // matching is currently not supported by this API.
280        w.autoaddr().enabled();
281    }
282}
283
284impl<I, W, Mode> USART<I, Enabled<W, Mode>>
285where
286    I: Instance,
287    W: Word,
288{
289    /// Disable the USART
290    ///
291    /// This method is only available, if `USART` is in the [`Enabled`] state.
292    /// Code that attempts to call this method when the peripheral is already
293    /// disabled will not compile.
294    ///
295    /// Consumes this instance of `USART` and returns another instance that has
296    /// its `State` type parameter set to [`Disabled`].
297    ///
298    /// [`Enabled`]: state/struct.Enabled.html
299    /// [`Disabled`]: ../init_state/struct.Disabled.html
300    pub fn disable(self, syscon: &mut syscon::Handle) -> USART<I, Disabled> {
301        syscon.disable_clock(&self.usart);
302
303        USART {
304            rx: Rx::new(), // can't use `self.rx`, due to state
305            tx: Tx::new(), // can't use `self.tx`, due to state
306            usart: self.usart,
307        }
308    }
309
310    /// Query whether the provided flag is set
311    ///
312    /// Flags that need to be reset by software will be reset by this operation.
313    pub fn is_flag_set(&self, flag: Flag) -> bool {
314        flag.is_set::<I>()
315    }
316
317    /// Enable interrupts for this instance in the NVIC
318    ///
319    /// This only enables the interrupts in the NVIC. It doesn't enable any
320    /// specific interrupt in this USART instance.
321    pub fn enable_in_nvic(&mut self) {
322        // Safe, because there's no critical section here that this could
323        // interfere with.
324        unsafe { NVIC::unmask(I::INTERRUPT) };
325    }
326
327    /// Disable interrupts for this instance in the NVIC
328    ///
329    /// This only disables the interrupts in the NVIC. It doesn't change
330    /// anything about the interrupt configuration within this USART instance.
331    pub fn disable_in_nvic(&mut self) {
332        NVIC::mask(I::INTERRUPT);
333    }
334
335    /// Clear's this instance's interrupt pending flag in the NVIC
336    ///
337    /// This only clears the interrupt's pending flag in the NVIC. It does not
338    /// affect any of the interrupt-related flags in the peripheral.
339    pub fn clear_nvic_pending(&mut self) {
340        NVIC::unpend(I::INTERRUPT);
341    }
342
343    /// Enable interrupts
344    ///
345    /// Enables all interrupts set to `true` in `interrupts`. Interrupts set to
346    /// `false` are not affected.
347    ///
348    /// # Example
349    ///
350    /// ``` no_run
351    /// use lpc8xx_hal::usart;
352    ///
353    /// # use lpc8xx_hal::Peripherals;
354    /// #
355    /// # let mut p = Peripherals::take().unwrap();
356    /// #
357    /// # let mut syscon = p.SYSCON.split();
358    /// # let mut swm    = p.SWM.split();
359    /// #
360    /// # #[cfg(feature = "82x")]
361    /// # let mut swm_handle = swm.handle;
362    /// # #[cfg(feature = "845")]
363    /// # let mut swm_handle = swm.handle.enable(&mut syscon.handle);
364    /// #
365    /// # #[cfg(feature = "82x")]
366    /// # let clock_config = {
367    /// #     syscon.uartfrg.set_clkdiv(6);
368    /// #     syscon.uartfrg.set_frgmult(22);
369    /// #     syscon.uartfrg.set_frgdiv(0xff);
370    /// #     usart::Clock::new(&syscon.uartfrg, 0, 16)
371    /// # };
372    /// # #[cfg(feature = "845")]
373    /// # let clock_config = usart::Clock::new_with_baudrate(115200);
374    /// #
375    /// # let (u0_rxd, _) = swm.movable_functions.u0_rxd.assign(
376    /// #     p.pins.pio0_0.into_swm_pin(),
377    /// #     &mut swm_handle,
378    /// # );
379    /// # let (u0_txd, _) = swm.movable_functions.u0_txd.assign(
380    /// #     p.pins.pio0_4.into_swm_pin(),
381    /// #     &mut swm_handle,
382    /// # );
383    /// #
384    /// # let mut usart = p.USART0.enable_async(
385    /// #     &clock_config,
386    /// #     &mut syscon.handle,
387    /// #     u0_rxd,
388    /// #     u0_txd,
389    /// #     usart::Settings::default(),
390    /// # );
391    /// #
392    /// // Enable only RXRDY and TXRDY, leave other interrupts untouched.
393    /// usart.enable_interrupts(usart::Interrupts {
394    ///     RXRDY: true,
395    ///     TXRDY: true,
396    ///     .. usart::Interrupts::default()
397    /// });
398    /// ```
399    pub fn enable_interrupts(&mut self, interrupts: Interrupts) {
400        interrupts.enable::<I>();
401    }
402
403    /// Disable interrupts
404    ///
405    /// Disables all interrupts set to `true` in `interrupts`. Interrupts set to
406    /// `false` are not affected.
407    ///
408    /// # Example
409    ///
410    /// ``` no_run
411    /// use lpc8xx_hal::usart;
412    ///
413    /// # use lpc8xx_hal::Peripherals;
414    /// #
415    /// # let mut p = Peripherals::take().unwrap();
416    /// #
417    /// # let mut syscon = p.SYSCON.split();
418    /// # let mut swm    = p.SWM.split();
419    /// #
420    /// # #[cfg(feature = "82x")]
421    /// # let mut swm_handle = swm.handle;
422    /// # #[cfg(feature = "845")]
423    /// # let mut swm_handle = swm.handle.enable(&mut syscon.handle);
424    /// #
425    /// # #[cfg(feature = "82x")]
426    /// # let clock_config = {
427    /// #     syscon.uartfrg.set_clkdiv(6);
428    /// #     syscon.uartfrg.set_frgmult(22);
429    /// #     syscon.uartfrg.set_frgdiv(0xff);
430    /// #     usart::Clock::new(&syscon.uartfrg, 0, 16)
431    /// # };
432    /// # #[cfg(feature = "845")]
433    /// # let clock_config = usart::Clock::new_with_baudrate(115200);
434    /// #
435    /// # let (u0_rxd, _) = swm.movable_functions.u0_rxd.assign(
436    /// #     p.pins.pio0_0.into_swm_pin(),
437    /// #     &mut swm_handle,
438    /// # );
439    /// # let (u0_txd, _) = swm.movable_functions.u0_txd.assign(
440    /// #     p.pins.pio0_4.into_swm_pin(),
441    /// #     &mut swm_handle,
442    /// # );
443    /// #
444    /// # let mut usart = p.USART0.enable_async(
445    /// #     &clock_config,
446    /// #     &mut syscon.handle,
447    /// #     u0_rxd,
448    /// #     u0_txd,
449    /// #     usart::Settings::default(),
450    /// # );
451    /// #
452    /// // Disable only RXRDY and TXRDY, leave other interrupts untouched.
453    /// usart.disable_interrupts(usart::Interrupts {
454    ///     RXRDY: true,
455    ///     TXRDY: true,
456    ///     .. usart::Interrupts::default()
457    /// });
458    /// ```
459    pub fn disable_interrupts(&mut self, interrupts: Interrupts) {
460        interrupts.disable::<I>();
461    }
462}
463
464impl<I, State> USART<I, State>
465where
466    I: Instance,
467{
468    /// Return the raw peripheral
469    ///
470    /// This method serves as an escape hatch from the HAL API. It returns the
471    /// raw peripheral, allowing you to do whatever you want with it, without
472    /// limitations imposed by the API.
473    ///
474    /// If you are using this method because a feature you need is missing from
475    /// the HAL API, please [open an issue] or, if an issue for your feature
476    /// request already exists, comment on the existing issue, so we can
477    /// prioritize it accordingly.
478    ///
479    /// [open an issue]: https://github.com/lpc-rs/lpc8xx-hal/issues
480    pub fn free(self) -> I {
481        self.usart
482    }
483}
484
485impl<I, W, Mode> Read<W> for USART<I, Enabled<W, Mode>>
486where
487    I: Instance,
488    W: Word,
489{
490    type Error = Error<W>;
491
492    /// Reads a single word from the serial interface
493    fn read(&mut self) -> nb::Result<W, Self::Error> {
494        self.rx.read()
495    }
496}
497
498impl<I, W, Mode> Write<W> for USART<I, Enabled<W, Mode>>
499where
500    I: Instance,
501    W: Word,
502{
503    type Error = Void;
504
505    /// Writes a single word to the serial interface
506    fn write(&mut self, word: W) -> nb::Result<(), Self::Error> {
507        self.tx.write(word)
508    }
509
510    /// Ensures that none of the previously written words are still buffered
511    fn flush(&mut self) -> nb::Result<(), Self::Error> {
512        self.tx.flush()
513    }
514}
515
516impl<I, W, Mode> BlockingWriteDefault<W> for USART<I, Enabled<W, Mode>>
517where
518    I: Instance,
519    W: Word,
520{
521}
522
523impl<I, Mode> fmt::Write for USART<I, Enabled<u8, Mode>>
524where
525    Self: BlockingWriteDefault<u8>,
526    I: Instance,
527{
528    /// Writes a string slice into this writer, returning whether the write succeeded.
529    fn write_str(&mut self, s: &str) -> fmt::Result {
530        self.tx.write_str(s)
531    }
532}