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}