aboutsummaryrefslogtreecommitdiff
path: root/embassy-mcxa/src/lpuart
diff options
context:
space:
mode:
authorJames Munns <[email protected]>2025-12-05 14:28:47 +0100
committerJames Munns <[email protected]>2025-12-05 14:28:47 +0100
commitb252db845e19603faf528cf93fe0c44757a27430 (patch)
tree99e646d17bed747df244dd607a15f5a67baa530a /embassy-mcxa/src/lpuart
parent6a1eed83b9df8ffa81b93860f530f5bb3252d996 (diff)
Move
Diffstat (limited to 'embassy-mcxa/src/lpuart')
-rw-r--r--embassy-mcxa/src/lpuart/buffered.rs780
-rw-r--r--embassy-mcxa/src/lpuart/mod.rs1733
2 files changed, 2513 insertions, 0 deletions
diff --git a/embassy-mcxa/src/lpuart/buffered.rs b/embassy-mcxa/src/lpuart/buffered.rs
new file mode 100644
index 000000000..8eb443ca7
--- /dev/null
+++ b/embassy-mcxa/src/lpuart/buffered.rs
@@ -0,0 +1,780 @@
1use core::future::poll_fn;
2use core::marker::PhantomData;
3use core::sync::atomic::{AtomicBool, Ordering};
4use core::task::Poll;
5
6use embassy_hal_internal::atomic_ring_buffer::RingBuffer;
7use embassy_hal_internal::Peri;
8use embassy_sync::waitqueue::AtomicWaker;
9
10use super::*;
11use crate::interrupt;
12
13// ============================================================================
14// STATIC STATE MANAGEMENT
15// ============================================================================
16
17/// State for buffered LPUART operations
18pub struct State {
19 tx_waker: AtomicWaker,
20 tx_buf: RingBuffer,
21 tx_done: AtomicBool,
22 rx_waker: AtomicWaker,
23 rx_buf: RingBuffer,
24 initialized: AtomicBool,
25}
26
27impl Default for State {
28 fn default() -> Self {
29 Self::new()
30 }
31}
32
33impl State {
34 /// Create a new state instance
35 pub const fn new() -> Self {
36 Self {
37 tx_waker: AtomicWaker::new(),
38 tx_buf: RingBuffer::new(),
39 tx_done: AtomicBool::new(true),
40 rx_waker: AtomicWaker::new(),
41 rx_buf: RingBuffer::new(),
42 initialized: AtomicBool::new(false),
43 }
44 }
45}
46// ============================================================================
47// BUFFERED DRIVER STRUCTURES
48// ============================================================================
49
50/// Buffered LPUART driver
51pub struct BufferedLpuart<'a> {
52 tx: BufferedLpuartTx<'a>,
53 rx: BufferedLpuartRx<'a>,
54}
55
56/// Buffered LPUART TX driver
57pub struct BufferedLpuartTx<'a> {
58 info: Info,
59 state: &'static State,
60 _tx_pin: Peri<'a, AnyPin>,
61 _cts_pin: Option<Peri<'a, AnyPin>>,
62}
63
64/// Buffered LPUART RX driver
65pub struct BufferedLpuartRx<'a> {
66 info: Info,
67 state: &'static State,
68 _rx_pin: Peri<'a, AnyPin>,
69 _rts_pin: Option<Peri<'a, AnyPin>>,
70}
71
72// ============================================================================
73// BUFFERED LPUART IMPLEMENTATION
74// ============================================================================
75
76impl<'a> BufferedLpuart<'a> {
77 /// Common initialization logic
78 fn init_common<T: Instance>(
79 _inner: &Peri<'a, T>,
80 tx_buffer: Option<&'a mut [u8]>,
81 rx_buffer: Option<&'a mut [u8]>,
82 config: &Config,
83 enable_tx: bool,
84 enable_rx: bool,
85 enable_rts: bool,
86 enable_cts: bool,
87 ) -> Result<&'static State> {
88 let state = T::buffered_state();
89
90 if state.initialized.load(Ordering::Relaxed) {
91 return Err(Error::InvalidArgument);
92 }
93
94 // Initialize buffers
95 if let Some(tx_buffer) = tx_buffer {
96 if tx_buffer.is_empty() {
97 return Err(Error::InvalidArgument);
98 }
99 unsafe { state.tx_buf.init(tx_buffer.as_mut_ptr(), tx_buffer.len()) };
100 }
101
102 if let Some(rx_buffer) = rx_buffer {
103 if rx_buffer.is_empty() {
104 return Err(Error::InvalidArgument);
105 }
106 unsafe { state.rx_buf.init(rx_buffer.as_mut_ptr(), rx_buffer.len()) };
107 }
108
109 state.initialized.store(true, Ordering::Relaxed);
110
111 // Enable clocks and initialize hardware
112 let conf = LpuartConfig {
113 power: config.power,
114 source: config.source,
115 div: config.div,
116 instance: T::CLOCK_INSTANCE,
117 };
118 let clock_freq = unsafe { enable_and_reset::<T>(&conf).map_err(Error::ClockSetup)? };
119
120 Self::init_hardware(
121 T::info().regs,
122 *config,
123 clock_freq,
124 enable_tx,
125 enable_rx,
126 enable_rts,
127 enable_cts,
128 )?;
129
130 Ok(state)
131 }
132
133 /// Helper for full-duplex initialization
134 fn new_inner<T: Instance>(
135 inner: Peri<'a, T>,
136 tx_pin: Peri<'a, AnyPin>,
137 rx_pin: Peri<'a, AnyPin>,
138 rts_pin: Option<Peri<'a, AnyPin>>,
139 cts_pin: Option<Peri<'a, AnyPin>>,
140 tx_buffer: &'a mut [u8],
141 rx_buffer: &'a mut [u8],
142 config: Config,
143 ) -> Result<(BufferedLpuartTx<'a>, BufferedLpuartRx<'a>)> {
144 let state = Self::init_common::<T>(
145 &inner,
146 Some(tx_buffer),
147 Some(rx_buffer),
148 &config,
149 true,
150 true,
151 rts_pin.is_some(),
152 cts_pin.is_some(),
153 )?;
154
155 let tx = BufferedLpuartTx {
156 info: T::info(),
157 state,
158 _tx_pin: tx_pin,
159 _cts_pin: cts_pin,
160 };
161
162 let rx = BufferedLpuartRx {
163 info: T::info(),
164 state,
165 _rx_pin: rx_pin,
166 _rts_pin: rts_pin,
167 };
168
169 Ok((tx, rx))
170 }
171
172 /// Common hardware initialization logic
173 fn init_hardware(
174 regs: &'static mcxa_pac::lpuart0::RegisterBlock,
175 config: Config,
176 clock_freq: u32,
177 enable_tx: bool,
178 enable_rx: bool,
179 enable_rts: bool,
180 enable_cts: bool,
181 ) -> Result<()> {
182 // Perform standard initialization
183 perform_software_reset(regs);
184 disable_transceiver(regs);
185 configure_baudrate(regs, config.baudrate_bps, clock_freq)?;
186 configure_frame_format(regs, &config);
187 configure_control_settings(regs, &config);
188 configure_fifo(regs, &config);
189 clear_all_status_flags(regs);
190 configure_flow_control(regs, enable_rts, enable_cts, &config);
191 configure_bit_order(regs, config.msb_first);
192
193 // Enable interrupts for buffered operation
194 cortex_m::interrupt::free(|_| {
195 regs.ctrl().modify(|_, w| {
196 w.rie()
197 .enabled() // RX interrupt
198 .orie()
199 .enabled() // Overrun interrupt
200 .peie()
201 .enabled() // Parity error interrupt
202 .feie()
203 .enabled() // Framing error interrupt
204 .neie()
205 .enabled() // Noise error interrupt
206 });
207 });
208
209 // Enable the transceiver
210 enable_transceiver(regs, enable_rx, enable_tx);
211
212 Ok(())
213 }
214
215 /// Create a new full duplex buffered LPUART
216 pub fn new<T: Instance>(
217 inner: Peri<'a, T>,
218 tx_pin: Peri<'a, impl TxPin<T>>,
219 rx_pin: Peri<'a, impl RxPin<T>>,
220 _irq: impl interrupt::typelevel::Binding<T::Interrupt, BufferedInterruptHandler<T>> + 'a,
221 tx_buffer: &'a mut [u8],
222 rx_buffer: &'a mut [u8],
223 config: Config,
224 ) -> Result<Self> {
225 tx_pin.as_tx();
226 rx_pin.as_rx();
227
228 let (tx, rx) = Self::new_inner::<T>(
229 inner,
230 tx_pin.into(),
231 rx_pin.into(),
232 None,
233 None,
234 tx_buffer,
235 rx_buffer,
236 config,
237 )?;
238
239 Ok(Self { tx, rx })
240 }
241
242 /// Create a new buffered LPUART instance with RTS/CTS flow control
243 pub fn new_with_rtscts<T: Instance>(
244 inner: Peri<'a, T>,
245 tx_pin: Peri<'a, impl TxPin<T>>,
246 rx_pin: Peri<'a, impl RxPin<T>>,
247 rts_pin: Peri<'a, impl RtsPin<T>>,
248 cts_pin: Peri<'a, impl CtsPin<T>>,
249 _irq: impl interrupt::typelevel::Binding<T::Interrupt, BufferedInterruptHandler<T>> + 'a,
250 tx_buffer: &'a mut [u8],
251 rx_buffer: &'a mut [u8],
252 config: Config,
253 ) -> Result<Self> {
254 tx_pin.as_tx();
255 rx_pin.as_rx();
256 rts_pin.as_rts();
257 cts_pin.as_cts();
258
259 let (tx, rx) = Self::new_inner::<T>(
260 inner,
261 tx_pin.into(),
262 rx_pin.into(),
263 Some(rts_pin.into()),
264 Some(cts_pin.into()),
265 tx_buffer,
266 rx_buffer,
267 config,
268 )?;
269
270 Ok(Self { tx, rx })
271 }
272
273 /// Create a new buffered LPUART with only RTS flow control (RX flow control)
274 pub fn new_with_rts<T: Instance>(
275 inner: Peri<'a, T>,
276 tx_pin: Peri<'a, impl TxPin<T>>,
277 rx_pin: Peri<'a, impl RxPin<T>>,
278 rts_pin: Peri<'a, impl RtsPin<T>>,
279 _irq: impl interrupt::typelevel::Binding<T::Interrupt, BufferedInterruptHandler<T>> + 'a,
280 tx_buffer: &'a mut [u8],
281 rx_buffer: &'a mut [u8],
282 config: Config,
283 ) -> Result<Self> {
284 tx_pin.as_tx();
285 rx_pin.as_rx();
286 rts_pin.as_rts();
287
288 let (tx, rx) = Self::new_inner::<T>(
289 inner,
290 tx_pin.into(),
291 rx_pin.into(),
292 Some(rts_pin.into()),
293 None,
294 tx_buffer,
295 rx_buffer,
296 config,
297 )?;
298
299 Ok(Self { tx, rx })
300 }
301
302 /// Create a new buffered LPUART with only CTS flow control (TX flow control)
303 pub fn new_with_cts<T: Instance>(
304 inner: Peri<'a, T>,
305 tx_pin: Peri<'a, impl TxPin<T>>,
306 rx_pin: Peri<'a, impl RxPin<T>>,
307 cts_pin: Peri<'a, impl CtsPin<T>>,
308 _irq: impl interrupt::typelevel::Binding<T::Interrupt, BufferedInterruptHandler<T>> + 'a,
309 tx_buffer: &'a mut [u8],
310 rx_buffer: &'a mut [u8],
311 config: Config,
312 ) -> Result<Self> {
313 tx_pin.as_tx();
314 rx_pin.as_rx();
315 cts_pin.as_cts();
316
317 let (tx, rx) = Self::new_inner::<T>(
318 inner,
319 tx_pin.into(),
320 rx_pin.into(),
321 None,
322 Some(cts_pin.into()),
323 tx_buffer,
324 rx_buffer,
325 config,
326 )?;
327
328 Ok(Self { tx, rx })
329 }
330
331 /// Split the buffered LPUART into separate TX and RX parts
332 pub fn split(self) -> (BufferedLpuartTx<'a>, BufferedLpuartRx<'a>) {
333 (self.tx, self.rx)
334 }
335
336 /// Get mutable references to TX and RX parts
337 pub fn split_ref(&mut self) -> (&mut BufferedLpuartTx<'a>, &mut BufferedLpuartRx<'a>) {
338 (&mut self.tx, &mut self.rx)
339 }
340}
341
342// ============================================================================
343// BUFFERED TX IMPLEMENTATION
344// ============================================================================
345
346impl<'a> BufferedLpuartTx<'a> {
347 /// Helper for TX-only initialization
348 fn new_inner<T: Instance>(
349 inner: Peri<'a, T>,
350 tx_pin: Peri<'a, AnyPin>,
351 cts_pin: Option<Peri<'a, AnyPin>>,
352 tx_buffer: &'a mut [u8],
353 config: Config,
354 ) -> Result<BufferedLpuartTx<'a>> {
355 let state = BufferedLpuart::init_common::<T>(
356 &inner,
357 Some(tx_buffer),
358 None,
359 &config,
360 true,
361 false,
362 false,
363 cts_pin.is_some(),
364 )?;
365
366 Ok(BufferedLpuartTx {
367 info: T::info(),
368 state,
369 _tx_pin: tx_pin,
370 _cts_pin: cts_pin,
371 })
372 }
373
374 pub fn new<T: Instance>(
375 inner: Peri<'a, T>,
376 tx_pin: Peri<'a, impl TxPin<T>>,
377 _irq: impl interrupt::typelevel::Binding<T::Interrupt, BufferedInterruptHandler<T>> + 'a,
378 tx_buffer: &'a mut [u8],
379 config: Config,
380 ) -> Result<Self> {
381 tx_pin.as_tx();
382
383 Self::new_inner::<T>(inner, tx_pin.into(), None, tx_buffer, config)
384 }
385
386 /// Create a new TX-only buffered LPUART with CTS flow control
387 pub fn new_with_cts<T: Instance>(
388 inner: Peri<'a, T>,
389 tx_pin: Peri<'a, impl TxPin<T>>,
390 cts_pin: Peri<'a, impl CtsPin<T>>,
391 _irq: impl interrupt::typelevel::Binding<T::Interrupt, BufferedInterruptHandler<T>> + 'a,
392 tx_buffer: &'a mut [u8],
393 config: Config,
394 ) -> Result<Self> {
395 tx_pin.as_tx();
396 cts_pin.as_cts();
397
398 Self::new_inner::<T>(inner, tx_pin.into(), Some(cts_pin.into()), tx_buffer, config)
399 }
400}
401
402impl<'a> BufferedLpuartTx<'a> {
403 /// Write data asynchronously
404 pub async fn write(&mut self, buf: &[u8]) -> Result<usize> {
405 let mut written = 0;
406
407 for &byte in buf {
408 // Wait for space in the buffer
409 poll_fn(|cx| {
410 self.state.tx_waker.register(cx.waker());
411
412 let mut writer = unsafe { self.state.tx_buf.writer() };
413 if writer.push_one(byte) {
414 // Enable TX interrupt to start transmission
415 cortex_m::interrupt::free(|_| {
416 self.info.regs.ctrl().modify(|_, w| w.tie().enabled());
417 });
418 Poll::Ready(Ok(()))
419 } else {
420 Poll::Pending
421 }
422 })
423 .await?;
424
425 written += 1;
426 }
427
428 Ok(written)
429 }
430
431 /// Flush the TX buffer and wait for transmission to complete
432 pub async fn flush(&mut self) -> Result<()> {
433 // Wait for TX buffer to empty and transmission to complete
434 poll_fn(|cx| {
435 self.state.tx_waker.register(cx.waker());
436
437 let tx_empty = self.state.tx_buf.is_empty();
438 let fifo_empty = self.info.regs.water().read().txcount().bits() == 0;
439 let tc_complete = self.info.regs.stat().read().tc().is_complete();
440
441 if tx_empty && fifo_empty && tc_complete {
442 Poll::Ready(Ok(()))
443 } else {
444 // Enable appropriate interrupt
445 cortex_m::interrupt::free(|_| {
446 if !tx_empty {
447 self.info.regs.ctrl().modify(|_, w| w.tie().enabled());
448 } else {
449 self.info.regs.ctrl().modify(|_, w| w.tcie().enabled());
450 }
451 });
452 Poll::Pending
453 }
454 })
455 .await
456 }
457
458 /// Try to write without blocking
459 pub fn try_write(&mut self, buf: &[u8]) -> Result<usize> {
460 let mut writer = unsafe { self.state.tx_buf.writer() };
461 let mut written = 0;
462
463 for &byte in buf {
464 if writer.push_one(byte) {
465 written += 1;
466 } else {
467 break;
468 }
469 }
470
471 if written > 0 {
472 // Enable TX interrupt to start transmission
473 cortex_m::interrupt::free(|_| {
474 self.info.regs.ctrl().modify(|_, w| w.tie().enabled());
475 });
476 }
477
478 Ok(written)
479 }
480}
481
482// ============================================================================
483// BUFFERED RX IMPLEMENTATION
484// ============================================================================
485
486impl<'a> BufferedLpuartRx<'a> {
487 /// Helper for RX-only initialization
488 fn new_inner<T: Instance>(
489 inner: Peri<'a, T>,
490 rx_pin: Peri<'a, AnyPin>,
491 rts_pin: Option<Peri<'a, AnyPin>>,
492 rx_buffer: &'a mut [u8],
493 config: Config,
494 ) -> Result<BufferedLpuartRx<'a>> {
495 let state = BufferedLpuart::init_common::<T>(
496 &inner,
497 None,
498 Some(rx_buffer),
499 &config,
500 false,
501 true,
502 rts_pin.is_some(),
503 false,
504 )?;
505
506 Ok(BufferedLpuartRx {
507 info: T::info(),
508 state,
509 _rx_pin: rx_pin,
510 _rts_pin: rts_pin,
511 })
512 }
513
514 /// Create a new RX-only buffered LPUART
515 pub fn new<T: Instance>(
516 inner: Peri<'a, T>,
517 rx_pin: Peri<'a, impl RxPin<T>>,
518 _irq: impl interrupt::typelevel::Binding<T::Interrupt, BufferedInterruptHandler<T>> + 'a,
519 rx_buffer: &'a mut [u8],
520 config: Config,
521 ) -> Result<Self> {
522 rx_pin.as_rx();
523
524 Self::new_inner::<T>(inner, rx_pin.into(), None, rx_buffer, config)
525 }
526
527 /// Create a new RX-only buffered LPUART with RTS flow control
528 pub fn new_with_rts<T: Instance>(
529 inner: Peri<'a, T>,
530 rx_pin: Peri<'a, impl RxPin<T>>,
531 rts_pin: Peri<'a, impl RtsPin<T>>,
532 _irq: impl interrupt::typelevel::Binding<T::Interrupt, BufferedInterruptHandler<T>> + 'a,
533 rx_buffer: &'a mut [u8],
534 config: Config,
535 ) -> Result<Self> {
536 rx_pin.as_rx();
537 rts_pin.as_rts();
538
539 Self::new_inner::<T>(inner, rx_pin.into(), Some(rts_pin.into()), rx_buffer, config)
540 }
541}
542
543impl<'a> BufferedLpuartRx<'a> {
544 /// Read data asynchronously
545 pub async fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
546 if buf.is_empty() {
547 return Ok(0);
548 }
549
550 let mut read = 0;
551
552 // Try to read available data
553 poll_fn(|cx| {
554 self.state.rx_waker.register(cx.waker());
555
556 // Disable RX interrupt while reading from buffer
557 cortex_m::interrupt::free(|_| {
558 self.info.regs.ctrl().modify(|_, w| w.rie().disabled());
559 });
560
561 let mut reader = unsafe { self.state.rx_buf.reader() };
562 let available = reader.pop(|data| {
563 let to_copy = core::cmp::min(data.len(), buf.len() - read);
564 if to_copy > 0 {
565 buf[read..read + to_copy].copy_from_slice(&data[..to_copy]);
566 read += to_copy;
567 }
568 to_copy
569 });
570
571 // Re-enable RX interrupt
572 cortex_m::interrupt::free(|_| {
573 self.info.regs.ctrl().modify(|_, w| w.rie().enabled());
574 });
575
576 if read > 0 {
577 Poll::Ready(Ok(read))
578 } else if available == 0 {
579 Poll::Pending
580 } else {
581 Poll::Ready(Ok(0))
582 }
583 })
584 .await
585 }
586
587 /// Try to read without blocking
588 pub fn try_read(&mut self, buf: &mut [u8]) -> Result<usize> {
589 if buf.is_empty() {
590 return Ok(0);
591 }
592
593 // Disable RX interrupt while reading from buffer
594 cortex_m::interrupt::free(|_| {
595 self.info.regs.ctrl().modify(|_, w| w.rie().disabled());
596 });
597
598 let mut reader = unsafe { self.state.rx_buf.reader() };
599 let read = reader.pop(|data| {
600 let to_copy = core::cmp::min(data.len(), buf.len());
601 if to_copy > 0 {
602 buf[..to_copy].copy_from_slice(&data[..to_copy]);
603 }
604 to_copy
605 });
606
607 // Re-enable RX interrupt
608 cortex_m::interrupt::free(|_| {
609 self.info.regs.ctrl().modify(|_, w| w.rie().enabled());
610 });
611
612 Ok(read)
613 }
614}
615
616// ============================================================================
617// INTERRUPT HANDLER
618// ============================================================================
619
620/// Buffered UART interrupt handler
621pub struct BufferedInterruptHandler<T: Instance> {
622 _phantom: PhantomData<T>,
623}
624
625impl<T: Instance> crate::interrupt::typelevel::Handler<T::Interrupt> for BufferedInterruptHandler<T> {
626 unsafe fn on_interrupt() {
627 let regs = T::info().regs;
628 let state = T::buffered_state();
629
630 // Check if this instance is initialized
631 if !state.initialized.load(Ordering::Relaxed) {
632 return;
633 }
634
635 let ctrl = regs.ctrl().read();
636 let stat = regs.stat().read();
637 let has_fifo = regs.param().read().rxfifo().bits() > 0;
638
639 // Handle overrun error
640 if stat.or().is_overrun() {
641 regs.stat().write(|w| w.or().clear_bit_by_one());
642 state.rx_waker.wake();
643 return;
644 }
645
646 // Clear other error flags
647 if stat.pf().is_parity() {
648 regs.stat().write(|w| w.pf().clear_bit_by_one());
649 }
650 if stat.fe().is_error() {
651 regs.stat().write(|w| w.fe().clear_bit_by_one());
652 }
653 if stat.nf().is_noise() {
654 regs.stat().write(|w| w.nf().clear_bit_by_one());
655 }
656
657 // Handle RX data
658 if ctrl.rie().is_enabled() && (has_data(regs) || stat.idle().is_idle()) {
659 let mut pushed_any = false;
660 let mut writer = state.rx_buf.writer();
661
662 if has_fifo {
663 // Read from FIFO
664 while regs.water().read().rxcount().bits() > 0 {
665 let byte = (regs.data().read().bits() & 0xFF) as u8;
666 if writer.push_one(byte) {
667 pushed_any = true;
668 } else {
669 // Buffer full, stop reading
670 break;
671 }
672 }
673 } else {
674 // Read single byte
675 if regs.stat().read().rdrf().is_rxdata() {
676 let byte = (regs.data().read().bits() & 0xFF) as u8;
677 if writer.push_one(byte) {
678 pushed_any = true;
679 }
680 }
681 }
682
683 if pushed_any {
684 state.rx_waker.wake();
685 }
686
687 // Clear idle flag if set
688 if stat.idle().is_idle() {
689 regs.stat().write(|w| w.idle().clear_bit_by_one());
690 }
691 }
692
693 // Handle TX data
694 if ctrl.tie().is_enabled() {
695 let mut sent_any = false;
696 let mut reader = state.tx_buf.reader();
697
698 // Send data while TX buffer is ready and we have data
699 while regs.stat().read().tdre().is_no_txdata() {
700 if let Some(byte) = reader.pop_one() {
701 regs.data().write(|w| w.bits(u32::from(byte)));
702 sent_any = true;
703 } else {
704 // No more data to send
705 break;
706 }
707 }
708
709 if sent_any {
710 state.tx_waker.wake();
711 }
712
713 // If buffer is empty, switch to TC interrupt or disable
714 if state.tx_buf.is_empty() {
715 cortex_m::interrupt::free(|_| {
716 regs.ctrl().modify(|_, w| w.tie().disabled().tcie().enabled());
717 });
718 }
719 }
720
721 // Handle transmission complete
722 if ctrl.tcie().is_enabled() && regs.stat().read().tc().is_complete() {
723 state.tx_done.store(true, Ordering::Release);
724 state.tx_waker.wake();
725
726 // Disable TC interrupt
727 cortex_m::interrupt::free(|_| {
728 regs.ctrl().modify(|_, w| w.tcie().disabled());
729 });
730 }
731 }
732}
733
734// ============================================================================
735// EMBEDDED-IO ASYNC TRAIT IMPLEMENTATIONS
736// ============================================================================
737
738impl embedded_io_async::ErrorType for BufferedLpuartTx<'_> {
739 type Error = Error;
740}
741
742impl embedded_io_async::ErrorType for BufferedLpuartRx<'_> {
743 type Error = Error;
744}
745
746impl embedded_io_async::ErrorType for BufferedLpuart<'_> {
747 type Error = Error;
748}
749
750impl embedded_io_async::Write for BufferedLpuartTx<'_> {
751 async fn write(&mut self, buf: &[u8]) -> core::result::Result<usize, Self::Error> {
752 self.write(buf).await
753 }
754
755 async fn flush(&mut self) -> core::result::Result<(), Self::Error> {
756 self.flush().await
757 }
758}
759
760impl embedded_io_async::Read for BufferedLpuartRx<'_> {
761 async fn read(&mut self, buf: &mut [u8]) -> core::result::Result<usize, Self::Error> {
762 self.read(buf).await
763 }
764}
765
766impl embedded_io_async::Write for BufferedLpuart<'_> {
767 async fn write(&mut self, buf: &[u8]) -> core::result::Result<usize, Self::Error> {
768 self.tx.write(buf).await
769 }
770
771 async fn flush(&mut self) -> core::result::Result<(), Self::Error> {
772 self.tx.flush().await
773 }
774}
775
776impl embedded_io_async::Read for BufferedLpuart<'_> {
777 async fn read(&mut self, buf: &mut [u8]) -> core::result::Result<usize, Self::Error> {
778 self.rx.read(buf).await
779 }
780}
diff --git a/embassy-mcxa/src/lpuart/mod.rs b/embassy-mcxa/src/lpuart/mod.rs
new file mode 100644
index 000000000..5722b759c
--- /dev/null
+++ b/embassy-mcxa/src/lpuart/mod.rs
@@ -0,0 +1,1733 @@
1use core::marker::PhantomData;
2
3use embassy_hal_internal::{Peri, PeripheralType};
4use paste::paste;
5
6use crate::clocks::periph_helpers::{Div4, LpuartClockSel, LpuartConfig};
7use crate::clocks::{enable_and_reset, ClockError, Gate, PoweredClock};
8use crate::gpio::SealedPin;
9use crate::pac::lpuart0::baud::Sbns as StopBits;
10use crate::pac::lpuart0::ctrl::{Idlecfg as IdleConfig, Ilt as IdleType, Pt as Parity, M as DataBits};
11use crate::pac::lpuart0::modir::{Txctsc as TxCtsConfig, Txctssrc as TxCtsSource};
12use crate::pac::lpuart0::stat::Msbf as MsbFirst;
13use crate::{interrupt, pac, AnyPin};
14
15pub mod buffered;
16
17// ============================================================================
18// DMA INTEGRATION
19// ============================================================================
20
21use crate::dma::{
22 Channel as DmaChannelTrait, DmaChannel, DmaRequest, EnableInterrupt, RingBuffer, DMA_MAX_TRANSFER_SIZE,
23};
24
25// ============================================================================
26// MISC
27// ============================================================================
28
29mod sealed {
30 /// Simply seal a trait to prevent external implementations
31 pub trait Sealed {}
32}
33
34// ============================================================================
35// INSTANCE TRAIT
36// ============================================================================
37
38pub type Regs = &'static crate::pac::lpuart0::RegisterBlock;
39
40pub trait SealedInstance {
41 fn info() -> Info;
42 fn index() -> usize;
43 fn buffered_state() -> &'static buffered::State;
44}
45
46pub struct Info {
47 pub regs: Regs,
48}
49
50/// Trait for LPUART peripheral instances
51#[allow(private_bounds)]
52pub trait Instance: SealedInstance + PeripheralType + 'static + Send + Gate<MrccPeriphConfig = LpuartConfig> {
53 const CLOCK_INSTANCE: crate::clocks::periph_helpers::LpuartInstance;
54 type Interrupt: interrupt::typelevel::Interrupt;
55 /// Type-safe DMA request source for TX
56 type TxDmaRequest: DmaRequest;
57 /// Type-safe DMA request source for RX
58 type RxDmaRequest: DmaRequest;
59}
60
61macro_rules! impl_instance {
62 ($($n:expr);* $(;)?) => {
63 $(
64 paste!{
65 impl SealedInstance for crate::peripherals::[<LPUART $n>] {
66 fn info() -> Info {
67 Info {
68 regs: unsafe { &*pac::[<Lpuart $n>]::ptr() },
69 }
70 }
71
72 #[inline]
73 fn index() -> usize {
74 $n
75 }
76
77 fn buffered_state() -> &'static buffered::State {
78 static BUFFERED_STATE: buffered::State = buffered::State::new();
79 &BUFFERED_STATE
80 }
81 }
82
83 impl Instance for crate::peripherals::[<LPUART $n>] {
84 const CLOCK_INSTANCE: crate::clocks::periph_helpers::LpuartInstance
85 = crate::clocks::periph_helpers::LpuartInstance::[<Lpuart $n>];
86 type Interrupt = crate::interrupt::typelevel::[<LPUART $n>];
87 type TxDmaRequest = crate::dma::[<Lpuart $n TxRequest>];
88 type RxDmaRequest = crate::dma::[<Lpuart $n RxRequest>];
89 }
90 }
91 )*
92 };
93}
94
95// DMA request sources are now type-safe via associated types.
96// The request source numbers are defined in src/dma.rs:
97// LPUART0: RX=21, TX=22 -> Lpuart0RxRequest, Lpuart0TxRequest
98// LPUART1: RX=23, TX=24 -> Lpuart1RxRequest, Lpuart1TxRequest
99// LPUART2: RX=25, TX=26 -> Lpuart2RxRequest, Lpuart2TxRequest
100// LPUART3: RX=27, TX=28 -> Lpuart3RxRequest, Lpuart3TxRequest
101// LPUART4: RX=29, TX=30 -> Lpuart4RxRequest, Lpuart4TxRequest
102// LPUART5: RX=31, TX=32 -> Lpuart5RxRequest, Lpuart5TxRequest
103impl_instance!(0; 1; 2; 3; 4; 5);
104
105// ============================================================================
106// INSTANCE HELPER FUNCTIONS
107// ============================================================================
108
109/// Perform software reset on the LPUART peripheral
110pub fn perform_software_reset(regs: Regs) {
111 // Software reset - set and clear RST bit (Global register)
112 regs.global().write(|w| w.rst().reset());
113 regs.global().write(|w| w.rst().no_effect());
114}
115
116/// Disable both transmitter and receiver
117pub fn disable_transceiver(regs: Regs) {
118 regs.ctrl().modify(|_, w| w.te().disabled().re().disabled());
119}
120
121/// Calculate and configure baudrate settings
122pub fn configure_baudrate(regs: Regs, baudrate_bps: u32, clock_freq: u32) -> Result<()> {
123 let (osr, sbr) = calculate_baudrate(baudrate_bps, clock_freq)?;
124
125 // Configure BAUD register
126 regs.baud().modify(|_, w| unsafe {
127 // Clear and set OSR
128 w.osr().bits(osr - 1);
129 // Clear and set SBR
130 w.sbr().bits(sbr);
131 // Set BOTHEDGE if OSR is between 4 and 7
132 if osr > 3 && osr < 8 {
133 w.bothedge().enabled()
134 } else {
135 w.bothedge().disabled()
136 }
137 });
138
139 Ok(())
140}
141
142/// Configure frame format (stop bits, data bits)
143pub fn configure_frame_format(regs: Regs, config: &Config) {
144 // Configure stop bits
145 regs.baud().modify(|_, w| w.sbns().variant(config.stop_bits_count));
146
147 // Clear M10 for now (10-bit mode)
148 regs.baud().modify(|_, w| w.m10().disabled());
149}
150
151/// Configure control settings (parity, data bits, idle config, pin swap)
152pub fn configure_control_settings(regs: Regs, config: &Config) {
153 regs.ctrl().modify(|_, w| {
154 // Parity configuration
155 let mut w = if let Some(parity) = config.parity_mode {
156 w.pe().enabled().pt().variant(parity)
157 } else {
158 w.pe().disabled()
159 };
160
161 // Data bits configuration
162 w = match config.data_bits_count {
163 DataBits::Data8 => {
164 if config.parity_mode.is_some() {
165 w.m().data9() // 8 data + 1 parity = 9 bits
166 } else {
167 w.m().data8() // 8 data bits only
168 }
169 }
170 DataBits::Data9 => w.m().data9(),
171 };
172
173 // Idle configuration
174 w = w.idlecfg().variant(config.rx_idle_config);
175 w = w.ilt().variant(config.rx_idle_type);
176
177 // Swap TXD/RXD if configured
178 if config.swap_txd_rxd {
179 w.swap().swap()
180 } else {
181 w.swap().standard()
182 }
183 });
184}
185
186/// Configure FIFO settings and watermarks
187pub fn configure_fifo(regs: Regs, config: &Config) {
188 // Configure WATER register for FIFO watermarks
189 regs.water().write(|w| unsafe {
190 w.rxwater()
191 .bits(config.rx_fifo_watermark)
192 .txwater()
193 .bits(config.tx_fifo_watermark)
194 });
195
196 // Enable TX/RX FIFOs
197 regs.fifo().modify(|_, w| w.txfe().enabled().rxfe().enabled());
198
199 // Flush FIFOs
200 regs.fifo()
201 .modify(|_, w| w.txflush().txfifo_rst().rxflush().rxfifo_rst());
202}
203
204/// Clear all status flags
205pub fn clear_all_status_flags(regs: Regs) {
206 regs.stat().reset();
207}
208
209/// Configure hardware flow control if enabled
210pub fn configure_flow_control(regs: Regs, enable_tx_cts: bool, enable_rx_rts: bool, config: &Config) {
211 if enable_rx_rts || enable_tx_cts {
212 regs.modir().modify(|_, w| {
213 let mut w = w;
214
215 // Configure TX CTS
216 w = w.txctsc().variant(config.tx_cts_config);
217 w = w.txctssrc().variant(config.tx_cts_source);
218
219 if enable_rx_rts {
220 w = w.rxrtse().enabled();
221 } else {
222 w = w.rxrtse().disabled();
223 }
224
225 if enable_tx_cts {
226 w = w.txctse().enabled();
227 } else {
228 w = w.txctse().disabled();
229 }
230
231 w
232 });
233 }
234}
235
236/// Configure bit order (MSB first or LSB first)
237pub fn configure_bit_order(regs: Regs, msb_first: MsbFirst) {
238 regs.stat().modify(|_, w| w.msbf().variant(msb_first));
239}
240
241/// Enable transmitter and/or receiver based on configuration
242pub fn enable_transceiver(regs: Regs, enable_tx: bool, enable_rx: bool) {
243 regs.ctrl().modify(|_, w| {
244 let mut w = w;
245 if enable_tx {
246 w = w.te().enabled();
247 }
248 if enable_rx {
249 w = w.re().enabled();
250 }
251 w
252 });
253}
254
255pub fn calculate_baudrate(baudrate: u32, src_clock_hz: u32) -> Result<(u8, u16)> {
256 let mut baud_diff = baudrate;
257 let mut osr = 0u8;
258 let mut sbr = 0u16;
259
260 // Try OSR values from 4 to 32
261 for osr_temp in 4u8..=32u8 {
262 // Calculate SBR: (srcClock_Hz * 2 / (baudRate * osr) + 1) / 2
263 let sbr_calc = ((src_clock_hz * 2) / (baudrate * osr_temp as u32)).div_ceil(2);
264
265 let sbr_temp = if sbr_calc == 0 {
266 1
267 } else if sbr_calc > 0x1FFF {
268 0x1FFF
269 } else {
270 sbr_calc as u16
271 };
272
273 // Calculate actual baud rate
274 let calculated_baud = src_clock_hz / (osr_temp as u32 * sbr_temp as u32);
275
276 let temp_diff = calculated_baud.abs_diff(baudrate);
277
278 if temp_diff <= baud_diff {
279 baud_diff = temp_diff;
280 osr = osr_temp;
281 sbr = sbr_temp;
282 }
283 }
284
285 // Check if baud rate difference is within 3%
286 if baud_diff > (baudrate / 100) * 3 {
287 return Err(Error::UnsupportedBaudrate);
288 }
289
290 Ok((osr, sbr))
291}
292
293/// Wait for all transmit operations to complete
294pub fn wait_for_tx_complete(regs: Regs) {
295 // Wait for TX FIFO to empty
296 while regs.water().read().txcount().bits() != 0 {
297 // Wait for TX FIFO to drain
298 }
299
300 // Wait for last character to shift out (TC = Transmission Complete)
301 while regs.stat().read().tc().is_active() {
302 // Wait for transmission to complete
303 }
304}
305
306pub fn check_and_clear_rx_errors(regs: Regs) -> Result<()> {
307 let stat = regs.stat().read();
308 let mut status = Ok(());
309
310 // Check for overrun first - other error flags are prevented when OR is set
311 if stat.or().is_overrun() {
312 regs.stat().write(|w| w.or().clear_bit_by_one());
313
314 return Err(Error::Overrun);
315 }
316
317 if stat.pf().is_parity() {
318 regs.stat().write(|w| w.pf().clear_bit_by_one());
319 status = Err(Error::Parity);
320 }
321
322 if stat.fe().is_error() {
323 regs.stat().write(|w| w.fe().clear_bit_by_one());
324 status = Err(Error::Framing);
325 }
326
327 if stat.nf().is_noise() {
328 regs.stat().write(|w| w.nf().clear_bit_by_one());
329 status = Err(Error::Noise);
330 }
331
332 status
333}
334
335pub fn has_data(regs: Regs) -> bool {
336 if regs.param().read().rxfifo().bits() > 0 {
337 // FIFO is available - check RXCOUNT in WATER register
338 regs.water().read().rxcount().bits() > 0
339 } else {
340 // No FIFO - check RDRF flag in STAT register
341 regs.stat().read().rdrf().is_rxdata()
342 }
343}
344
345// ============================================================================
346// PIN TRAITS FOR LPUART FUNCTIONALITY
347// ============================================================================
348
349impl<T: SealedPin> sealed::Sealed for T {}
350
351/// io configuration trait for Lpuart Tx configuration
352pub trait TxPin<T: Instance>: Into<AnyPin> + sealed::Sealed + PeripheralType {
353 /// convert the pin to appropriate function for Lpuart Tx usage
354 fn as_tx(&self);
355}
356
357/// io configuration trait for Lpuart Rx configuration
358pub trait RxPin<T: Instance>: Into<AnyPin> + sealed::Sealed + PeripheralType {
359 /// convert the pin to appropriate function for Lpuart Rx usage
360 fn as_rx(&self);
361}
362
363/// io configuration trait for Lpuart Cts
364pub trait CtsPin<T: Instance>: Into<AnyPin> + sealed::Sealed + PeripheralType {
365 /// convert the pin to appropriate function for Lpuart Cts usage
366 fn as_cts(&self);
367}
368
369/// io configuration trait for Lpuart Rts
370pub trait RtsPin<T: Instance>: Into<AnyPin> + sealed::Sealed + PeripheralType {
371 /// convert the pin to appropriate function for Lpuart Rts usage
372 fn as_rts(&self);
373}
374
375macro_rules! impl_tx_pin {
376 ($inst:ident, $pin:ident, $alt:ident) => {
377 impl TxPin<crate::peripherals::$inst> for crate::peripherals::$pin {
378 fn as_tx(&self) {
379 // TODO: Check these are right
380 self.set_pull(crate::gpio::Pull::Up);
381 self.set_slew_rate(crate::gpio::SlewRate::Fast.into());
382 self.set_drive_strength(crate::gpio::DriveStrength::Double.into());
383 self.set_function(crate::pac::port0::pcr0::Mux::$alt);
384 self.set_enable_input_buffer();
385 }
386 }
387 };
388}
389
390macro_rules! impl_rx_pin {
391 ($inst:ident, $pin:ident, $alt:ident) => {
392 impl RxPin<crate::peripherals::$inst> for crate::peripherals::$pin {
393 fn as_rx(&self) {
394 // TODO: Check these are right
395 self.set_pull(crate::gpio::Pull::Up);
396 self.set_slew_rate(crate::gpio::SlewRate::Fast.into());
397 self.set_drive_strength(crate::gpio::DriveStrength::Double.into());
398 self.set_function(crate::pac::port0::pcr0::Mux::$alt);
399 self.set_enable_input_buffer();
400 }
401 }
402 };
403}
404
405// TODO: Macro and impls for CTS/RTS pins
406macro_rules! impl_cts_pin {
407 ($inst:ident, $pin:ident, $alt:ident) => {
408 impl CtsPin<crate::peripherals::$inst> for crate::peripherals::$pin {
409 fn as_cts(&self) {
410 todo!()
411 }
412 }
413 };
414}
415
416macro_rules! impl_rts_pin {
417 ($inst:ident, $pin:ident, $alt:ident) => {
418 impl RtsPin<crate::peripherals::$inst> for crate::peripherals::$pin {
419 fn as_rts(&self) {
420 todo!()
421 }
422 }
423 };
424}
425
426// LPUART 0
427impl_tx_pin!(LPUART0, P0_3, Mux2);
428impl_tx_pin!(LPUART0, P0_21, Mux3);
429impl_tx_pin!(LPUART0, P2_1, Mux2);
430
431impl_rx_pin!(LPUART0, P0_2, Mux2);
432impl_rx_pin!(LPUART0, P0_20, Mux3);
433impl_rx_pin!(LPUART0, P2_0, Mux2);
434
435impl_cts_pin!(LPUART0, P0_1, Mux2);
436impl_cts_pin!(LPUART0, P0_23, Mux3);
437impl_cts_pin!(LPUART0, P2_3, Mux2);
438
439impl_rts_pin!(LPUART0, P0_0, Mux2);
440impl_rts_pin!(LPUART0, P0_22, Mux3);
441impl_rts_pin!(LPUART0, P2_2, Mux2);
442
443// LPUART 1
444impl_tx_pin!(LPUART1, P1_9, Mux2);
445impl_tx_pin!(LPUART1, P2_13, Mux3);
446impl_tx_pin!(LPUART1, P3_9, Mux3);
447impl_tx_pin!(LPUART1, P3_21, Mux3);
448
449impl_rx_pin!(LPUART1, P1_8, Mux2);
450impl_rx_pin!(LPUART1, P2_12, Mux3);
451impl_rx_pin!(LPUART1, P3_8, Mux3);
452impl_rx_pin!(LPUART1, P3_20, Mux3);
453
454impl_cts_pin!(LPUART1, P1_11, Mux2);
455impl_cts_pin!(LPUART1, P2_17, Mux3);
456impl_cts_pin!(LPUART1, P3_11, Mux3);
457impl_cts_pin!(LPUART1, P3_23, Mux3);
458
459impl_rts_pin!(LPUART1, P1_10, Mux2);
460impl_rts_pin!(LPUART1, P2_15, Mux3);
461impl_rts_pin!(LPUART1, P2_16, Mux3);
462impl_rts_pin!(LPUART1, P3_10, Mux3);
463
464// LPUART 2
465impl_tx_pin!(LPUART2, P1_5, Mux3);
466impl_tx_pin!(LPUART2, P1_13, Mux3);
467impl_tx_pin!(LPUART2, P2_2, Mux3);
468impl_tx_pin!(LPUART2, P2_10, Mux3);
469impl_tx_pin!(LPUART2, P3_15, Mux2);
470
471impl_rx_pin!(LPUART2, P1_4, Mux3);
472impl_rx_pin!(LPUART2, P1_12, Mux3);
473impl_rx_pin!(LPUART2, P2_3, Mux3);
474impl_rx_pin!(LPUART2, P2_11, Mux3);
475impl_rx_pin!(LPUART2, P3_14, Mux2);
476
477impl_cts_pin!(LPUART2, P1_7, Mux3);
478impl_cts_pin!(LPUART2, P1_15, Mux3);
479impl_cts_pin!(LPUART2, P2_4, Mux3);
480impl_cts_pin!(LPUART2, P3_13, Mux2);
481
482impl_rts_pin!(LPUART2, P1_6, Mux3);
483impl_rts_pin!(LPUART2, P1_14, Mux3);
484impl_rts_pin!(LPUART2, P2_5, Mux3);
485impl_rts_pin!(LPUART2, P3_12, Mux2);
486
487// LPUART 3
488impl_tx_pin!(LPUART3, P3_1, Mux3);
489impl_tx_pin!(LPUART3, P3_12, Mux3);
490impl_tx_pin!(LPUART3, P4_5, Mux3);
491
492impl_rx_pin!(LPUART3, P3_0, Mux3);
493impl_rx_pin!(LPUART3, P3_13, Mux3);
494impl_rx_pin!(LPUART3, P4_2, Mux3);
495
496impl_cts_pin!(LPUART3, P3_7, Mux3);
497impl_cts_pin!(LPUART3, P3_14, Mux3);
498impl_cts_pin!(LPUART3, P4_6, Mux3);
499
500impl_rts_pin!(LPUART3, P3_6, Mux3);
501impl_rts_pin!(LPUART3, P3_15, Mux3);
502impl_rts_pin!(LPUART3, P4_7, Mux3);
503
504// LPUART 4
505impl_tx_pin!(LPUART4, P2_7, Mux3);
506impl_tx_pin!(LPUART4, P3_19, Mux2);
507impl_tx_pin!(LPUART4, P3_27, Mux3);
508impl_tx_pin!(LPUART4, P4_3, Mux3);
509
510impl_rx_pin!(LPUART4, P2_6, Mux3);
511impl_rx_pin!(LPUART4, P3_18, Mux2);
512impl_rx_pin!(LPUART4, P3_28, Mux3);
513impl_rx_pin!(LPUART4, P4_4, Mux3);
514
515impl_cts_pin!(LPUART4, P2_0, Mux3);
516impl_cts_pin!(LPUART4, P3_17, Mux2);
517impl_cts_pin!(LPUART4, P3_31, Mux3);
518
519impl_rts_pin!(LPUART4, P2_1, Mux3);
520impl_rts_pin!(LPUART4, P3_16, Mux2);
521impl_rts_pin!(LPUART4, P3_30, Mux3);
522
523// LPUART 5
524impl_tx_pin!(LPUART5, P1_10, Mux8);
525impl_tx_pin!(LPUART5, P1_17, Mux8);
526
527impl_rx_pin!(LPUART5, P1_11, Mux8);
528impl_rx_pin!(LPUART5, P1_16, Mux8);
529
530impl_cts_pin!(LPUART5, P1_12, Mux8);
531impl_cts_pin!(LPUART5, P1_19, Mux8);
532
533impl_rts_pin!(LPUART5, P1_13, Mux8);
534impl_rts_pin!(LPUART5, P1_18, Mux8);
535
536// ============================================================================
537// ERROR TYPES AND RESULTS
538// ============================================================================
539
540/// LPUART error types
541#[derive(Debug, Copy, Clone, Eq, PartialEq)]
542#[cfg_attr(feature = "defmt", derive(defmt::Format))]
543pub enum Error {
544 /// Read error
545 Read,
546 /// Buffer overflow
547 Overrun,
548 /// Noise error
549 Noise,
550 /// Framing error
551 Framing,
552 /// Parity error
553 Parity,
554 /// Failure
555 Fail,
556 /// Invalid argument
557 InvalidArgument,
558 /// Lpuart baud rate cannot be supported with the given clock
559 UnsupportedBaudrate,
560 /// RX FIFO Empty
561 RxFifoEmpty,
562 /// TX FIFO Full
563 TxFifoFull,
564 /// TX Busy
565 TxBusy,
566 /// Clock Error
567 ClockSetup(ClockError),
568}
569
570/// A specialized Result type for LPUART operations
571pub type Result<T> = core::result::Result<T, Error>;
572
573// ============================================================================
574// CONFIGURATION STRUCTURES
575// ============================================================================
576
577/// Lpuart config
578#[derive(Debug, Clone, Copy)]
579pub struct Config {
580 /// Power state required for this peripheral
581 pub power: PoweredClock,
582 /// Clock source
583 pub source: LpuartClockSel,
584 /// Clock divisor
585 pub div: Div4,
586 /// Baud rate in bits per second
587 pub baudrate_bps: u32,
588 /// Parity configuration
589 pub parity_mode: Option<Parity>,
590 /// Number of data bits
591 pub data_bits_count: DataBits,
592 /// MSB First or LSB First configuration
593 pub msb_first: MsbFirst,
594 /// Number of stop bits
595 pub stop_bits_count: StopBits,
596 /// TX FIFO watermark
597 pub tx_fifo_watermark: u8,
598 /// RX FIFO watermark
599 pub rx_fifo_watermark: u8,
600 /// TX CTS source
601 pub tx_cts_source: TxCtsSource,
602 /// TX CTS configure
603 pub tx_cts_config: TxCtsConfig,
604 /// RX IDLE type
605 pub rx_idle_type: IdleType,
606 /// RX IDLE configuration
607 pub rx_idle_config: IdleConfig,
608 /// Swap TXD and RXD pins
609 pub swap_txd_rxd: bool,
610}
611
612impl Default for Config {
613 fn default() -> Self {
614 Self {
615 baudrate_bps: 115_200u32,
616 parity_mode: None,
617 data_bits_count: DataBits::Data8,
618 msb_first: MsbFirst::LsbFirst,
619 stop_bits_count: StopBits::One,
620 tx_fifo_watermark: 0,
621 rx_fifo_watermark: 1,
622 tx_cts_source: TxCtsSource::Cts,
623 tx_cts_config: TxCtsConfig::Start,
624 rx_idle_type: IdleType::FromStart,
625 rx_idle_config: IdleConfig::Idle1,
626 swap_txd_rxd: false,
627 power: PoweredClock::NormalEnabledDeepSleepDisabled,
628 source: LpuartClockSel::FroLfDiv,
629 div: Div4::no_div(),
630 }
631 }
632}
633
634/// LPUART status flags
635#[derive(Debug, Clone, Copy)]
636#[cfg_attr(feature = "defmt", derive(defmt::Format))]
637pub struct Status {
638 /// Transmit data register empty
639 pub tx_empty: bool,
640 /// Transmission complete
641 pub tx_complete: bool,
642 /// Receive data register full
643 pub rx_full: bool,
644 /// Idle line detected
645 pub idle: bool,
646 /// Receiver overrun
647 pub overrun: bool,
648 /// Noise error
649 pub noise: bool,
650 /// Framing error
651 pub framing: bool,
652 /// Parity error
653 pub parity: bool,
654}
655
656// ============================================================================
657// MODE TRAITS (BLOCKING/ASYNC)
658// ============================================================================
659
660/// Driver move trait.
661#[allow(private_bounds)]
662pub trait Mode: sealed::Sealed {}
663
664/// Blocking mode.
665pub struct Blocking;
666impl sealed::Sealed for Blocking {}
667impl Mode for Blocking {}
668
669/// Async mode.
670pub struct Async;
671impl sealed::Sealed for Async {}
672impl Mode for Async {}
673
674// ============================================================================
675// CORE DRIVER STRUCTURES
676// ============================================================================
677
678/// Lpuart driver.
679pub struct Lpuart<'a, M: Mode> {
680 info: Info,
681 tx: LpuartTx<'a, M>,
682 rx: LpuartRx<'a, M>,
683}
684
685/// Lpuart TX driver.
686pub struct LpuartTx<'a, M: Mode> {
687 info: Info,
688 _tx_pin: Peri<'a, AnyPin>,
689 _cts_pin: Option<Peri<'a, AnyPin>>,
690 mode: PhantomData<(&'a (), M)>,
691}
692
693/// Lpuart Rx driver.
694pub struct LpuartRx<'a, M: Mode> {
695 info: Info,
696 _rx_pin: Peri<'a, AnyPin>,
697 _rts_pin: Option<Peri<'a, AnyPin>>,
698 mode: PhantomData<(&'a (), M)>,
699}
700
701/// Lpuart TX driver with DMA support.
702pub struct LpuartTxDma<'a, T: Instance, C: DmaChannelTrait> {
703 info: Info,
704 _tx_pin: Peri<'a, AnyPin>,
705 tx_dma: DmaChannel<C>,
706 _instance: core::marker::PhantomData<T>,
707}
708
709/// Lpuart RX driver with DMA support.
710pub struct LpuartRxDma<'a, T: Instance, C: DmaChannelTrait> {
711 info: Info,
712 _rx_pin: Peri<'a, AnyPin>,
713 rx_dma: DmaChannel<C>,
714 _instance: core::marker::PhantomData<T>,
715}
716
717/// Lpuart driver with DMA support for both TX and RX.
718pub struct LpuartDma<'a, T: Instance, TxC: DmaChannelTrait, RxC: DmaChannelTrait> {
719 tx: LpuartTxDma<'a, T, TxC>,
720 rx: LpuartRxDma<'a, T, RxC>,
721}
722
723// ============================================================================
724// LPUART CORE IMPLEMENTATION
725// ============================================================================
726
727impl<'a, M: Mode> Lpuart<'a, M> {
728 fn init<T: Instance>(
729 enable_tx: bool,
730 enable_rx: bool,
731 enable_tx_cts: bool,
732 enable_rx_rts: bool,
733 config: Config,
734 ) -> Result<()> {
735 let regs = T::info().regs;
736
737 // Enable clocks
738 let conf = LpuartConfig {
739 power: config.power,
740 source: config.source,
741 div: config.div,
742 instance: T::CLOCK_INSTANCE,
743 };
744 let clock_freq = unsafe { enable_and_reset::<T>(&conf).map_err(Error::ClockSetup)? };
745
746 // Perform initialization sequence
747 perform_software_reset(regs);
748 disable_transceiver(regs);
749 configure_baudrate(regs, config.baudrate_bps, clock_freq)?;
750 configure_frame_format(regs, &config);
751 configure_control_settings(regs, &config);
752 configure_fifo(regs, &config);
753 clear_all_status_flags(regs);
754 configure_flow_control(regs, enable_tx_cts, enable_rx_rts, &config);
755 configure_bit_order(regs, config.msb_first);
756 enable_transceiver(regs, enable_rx, enable_tx);
757
758 Ok(())
759 }
760
761 /// Deinitialize the LPUART peripheral
762 pub fn deinit(&self) -> Result<()> {
763 let regs = self.info.regs;
764
765 // Wait for TX operations to complete
766 wait_for_tx_complete(regs);
767
768 // Clear all status flags
769 clear_all_status_flags(regs);
770
771 // Disable the module - clear all CTRL register bits
772 regs.ctrl().reset();
773
774 Ok(())
775 }
776
777 /// Split the Lpuart into a transmitter and receiver
778 pub fn split(self) -> (LpuartTx<'a, M>, LpuartRx<'a, M>) {
779 (self.tx, self.rx)
780 }
781
782 /// Split the Lpuart into a transmitter and receiver by mutable reference
783 pub fn split_ref(&mut self) -> (&mut LpuartTx<'a, M>, &mut LpuartRx<'a, M>) {
784 (&mut self.tx, &mut self.rx)
785 }
786}
787
788// ============================================================================
789// BLOCKING MODE IMPLEMENTATIONS
790// ============================================================================
791
792impl<'a> Lpuart<'a, Blocking> {
793 /// Create a new blocking LPUART instance with RX/TX pins.
794 pub fn new_blocking<T: Instance>(
795 _inner: Peri<'a, T>,
796 tx_pin: Peri<'a, impl TxPin<T>>,
797 rx_pin: Peri<'a, impl RxPin<T>>,
798 config: Config,
799 ) -> Result<Self> {
800 // Configure the pins for LPUART usage
801 tx_pin.as_tx();
802 rx_pin.as_rx();
803
804 // Initialize the peripheral
805 Self::init::<T>(true, true, false, false, config)?;
806
807 Ok(Self {
808 info: T::info(),
809 tx: LpuartTx::new_inner(T::info(), tx_pin.into(), None),
810 rx: LpuartRx::new_inner(T::info(), rx_pin.into(), None),
811 })
812 }
813
814 /// Create a new blocking LPUART instance with RX, TX and RTS/CTS flow control pins
815 pub fn new_blocking_with_rtscts<T: Instance>(
816 _inner: Peri<'a, T>,
817 tx_pin: Peri<'a, impl TxPin<T>>,
818 rx_pin: Peri<'a, impl RxPin<T>>,
819 cts_pin: Peri<'a, impl CtsPin<T>>,
820 rts_pin: Peri<'a, impl RtsPin<T>>,
821 config: Config,
822 ) -> Result<Self> {
823 // Configure the pins for LPUART usage
824 rx_pin.as_rx();
825 tx_pin.as_tx();
826 rts_pin.as_rts();
827 cts_pin.as_cts();
828
829 // Initialize the peripheral with flow control
830 Self::init::<T>(true, true, true, true, config)?;
831
832 Ok(Self {
833 info: T::info(),
834 rx: LpuartRx::new_inner(T::info(), rx_pin.into(), Some(rts_pin.into())),
835 tx: LpuartTx::new_inner(T::info(), tx_pin.into(), Some(cts_pin.into())),
836 })
837 }
838}
839
840// ----------------------------------------------------------------------------
841// Blocking TX Implementation
842// ----------------------------------------------------------------------------
843
844impl<'a, M: Mode> LpuartTx<'a, M> {
845 fn new_inner(info: Info, tx_pin: Peri<'a, AnyPin>, cts_pin: Option<Peri<'a, AnyPin>>) -> Self {
846 Self {
847 info,
848 _tx_pin: tx_pin,
849 _cts_pin: cts_pin,
850 mode: PhantomData,
851 }
852 }
853}
854
855impl<'a> LpuartTx<'a, Blocking> {
856 /// Create a new blocking LPUART transmitter instance
857 pub fn new_blocking<T: Instance>(
858 _inner: Peri<'a, T>,
859 tx_pin: Peri<'a, impl TxPin<T>>,
860 config: Config,
861 ) -> Result<Self> {
862 // Configure the pins for LPUART usage
863 tx_pin.as_tx();
864
865 // Initialize the peripheral
866 Lpuart::<Blocking>::init::<T>(true, false, false, false, config)?;
867
868 Ok(Self::new_inner(T::info(), tx_pin.into(), None))
869 }
870
871 /// Create a new blocking LPUART transmitter instance with CTS flow control
872 pub fn new_blocking_with_cts<T: Instance>(
873 _inner: Peri<'a, T>,
874 tx_pin: Peri<'a, impl TxPin<T>>,
875 cts_pin: Peri<'a, impl CtsPin<T>>,
876 config: Config,
877 ) -> Result<Self> {
878 tx_pin.as_tx();
879 cts_pin.as_cts();
880
881 Lpuart::<Blocking>::init::<T>(true, false, true, false, config)?;
882
883 Ok(Self::new_inner(T::info(), tx_pin.into(), Some(cts_pin.into())))
884 }
885
886 fn write_byte_internal(&mut self, byte: u8) -> Result<()> {
887 self.info.regs.data().modify(|_, w| unsafe { w.bits(u32::from(byte)) });
888
889 Ok(())
890 }
891
892 fn blocking_write_byte(&mut self, byte: u8) -> Result<()> {
893 while self.info.regs.stat().read().tdre().is_txdata() {}
894 self.write_byte_internal(byte)
895 }
896
897 fn write_byte(&mut self, byte: u8) -> Result<()> {
898 if self.info.regs.stat().read().tdre().is_txdata() {
899 Err(Error::TxFifoFull)
900 } else {
901 self.write_byte_internal(byte)
902 }
903 }
904
905 /// Write data to LPUART TX blocking execution until all data is sent.
906 pub fn blocking_write(&mut self, buf: &[u8]) -> Result<()> {
907 for x in buf {
908 self.blocking_write_byte(*x)?;
909 }
910
911 Ok(())
912 }
913
914 pub fn write_str_blocking(&mut self, buf: &str) {
915 let _ = self.blocking_write(buf.as_bytes());
916 }
917
918 /// Write data to LPUART TX without blocking.
919 pub fn write(&mut self, buf: &[u8]) -> Result<()> {
920 for x in buf {
921 self.write_byte(*x)?;
922 }
923
924 Ok(())
925 }
926
927 /// Flush LPUART TX blocking execution until all data has been transmitted.
928 pub fn blocking_flush(&mut self) -> Result<()> {
929 while self.info.regs.water().read().txcount().bits() != 0 {
930 // Wait for TX FIFO to drain
931 }
932
933 // Wait for last character to shift out
934 while self.info.regs.stat().read().tc().is_active() {
935 // Wait for transmission to complete
936 }
937
938 Ok(())
939 }
940
941 /// Flush LPUART TX.
942 pub fn flush(&mut self) -> Result<()> {
943 // Check if TX FIFO is empty
944 if self.info.regs.water().read().txcount().bits() != 0 {
945 return Err(Error::TxBusy);
946 }
947
948 // Check if transmission is complete
949 if self.info.regs.stat().read().tc().is_active() {
950 return Err(Error::TxBusy);
951 }
952
953 Ok(())
954 }
955}
956
957// ----------------------------------------------------------------------------
958// Blocking RX Implementation
959// ----------------------------------------------------------------------------
960
961impl<'a, M: Mode> LpuartRx<'a, M> {
962 fn new_inner(info: Info, rx_pin: Peri<'a, AnyPin>, rts_pin: Option<Peri<'a, AnyPin>>) -> Self {
963 Self {
964 info,
965 _rx_pin: rx_pin,
966 _rts_pin: rts_pin,
967 mode: PhantomData,
968 }
969 }
970}
971
972impl<'a> LpuartRx<'a, Blocking> {
973 /// Create a new blocking LPUART Receiver instance
974 pub fn new_blocking<T: Instance>(
975 _inner: Peri<'a, T>,
976 rx_pin: Peri<'a, impl RxPin<T>>,
977 config: Config,
978 ) -> Result<Self> {
979 rx_pin.as_rx();
980
981 Lpuart::<Blocking>::init::<T>(false, true, false, false, config)?;
982
983 Ok(Self::new_inner(T::info(), rx_pin.into(), None))
984 }
985
986 /// Create a new blocking LPUART Receiver instance with RTS flow control
987 pub fn new_blocking_with_rts<T: Instance>(
988 _inner: Peri<'a, T>,
989 rx_pin: Peri<'a, impl RxPin<T>>,
990 rts_pin: Peri<'a, impl RtsPin<T>>,
991 config: Config,
992 ) -> Result<Self> {
993 rx_pin.as_rx();
994 rts_pin.as_rts();
995
996 Lpuart::<Blocking>::init::<T>(false, true, false, true, config)?;
997
998 Ok(Self::new_inner(T::info(), rx_pin.into(), Some(rts_pin.into())))
999 }
1000
1001 fn read_byte_internal(&mut self) -> Result<u8> {
1002 let data = self.info.regs.data().read();
1003
1004 Ok((data.bits() & 0xFF) as u8)
1005 }
1006
1007 fn read_byte(&mut self) -> Result<u8> {
1008 check_and_clear_rx_errors(self.info.regs)?;
1009
1010 if !has_data(self.info.regs) {
1011 return Err(Error::RxFifoEmpty);
1012 }
1013
1014 self.read_byte_internal()
1015 }
1016
1017 fn blocking_read_byte(&mut self) -> Result<u8> {
1018 loop {
1019 if has_data(self.info.regs) {
1020 return self.read_byte_internal();
1021 }
1022
1023 check_and_clear_rx_errors(self.info.regs)?;
1024 }
1025 }
1026
1027 /// Read data from LPUART RX without blocking.
1028 pub fn read(&mut self, buf: &mut [u8]) -> Result<()> {
1029 for byte in buf.iter_mut() {
1030 *byte = self.read_byte()?;
1031 }
1032 Ok(())
1033 }
1034
1035 /// Read data from LPUART RX blocking execution until the buffer is filled.
1036 pub fn blocking_read(&mut self, buf: &mut [u8]) -> Result<()> {
1037 for byte in buf.iter_mut() {
1038 *byte = self.blocking_read_byte()?;
1039 }
1040 Ok(())
1041 }
1042}
1043
1044impl<'a> Lpuart<'a, Blocking> {
1045 /// Read data from LPUART RX blocking execution until the buffer is filled
1046 pub fn blocking_read(&mut self, buf: &mut [u8]) -> Result<()> {
1047 self.rx.blocking_read(buf)
1048 }
1049
1050 /// Read data from LPUART RX without blocking
1051 pub fn read(&mut self, buf: &mut [u8]) -> Result<()> {
1052 self.rx.read(buf)
1053 }
1054
1055 /// Write data to LPUART TX blocking execution until all data is sent
1056 pub fn blocking_write(&mut self, buf: &[u8]) -> Result<()> {
1057 self.tx.blocking_write(buf)
1058 }
1059
1060 pub fn write_byte(&mut self, byte: u8) -> Result<()> {
1061 self.tx.write_byte(byte)
1062 }
1063
1064 pub fn read_byte_blocking(&mut self) -> u8 {
1065 loop {
1066 if let Ok(b) = self.rx.read_byte() {
1067 return b;
1068 }
1069 }
1070 }
1071
1072 pub fn write_str_blocking(&mut self, buf: &str) {
1073 self.tx.write_str_blocking(buf);
1074 }
1075
1076 /// Write data to LPUART TX without blocking
1077 pub fn write(&mut self, buf: &[u8]) -> Result<()> {
1078 self.tx.write(buf)
1079 }
1080
1081 /// Flush LPUART TX blocking execution until all data has been transmitted
1082 pub fn blocking_flush(&mut self) -> Result<()> {
1083 self.tx.blocking_flush()
1084 }
1085
1086 /// Flush LPUART TX without blocking
1087 pub fn flush(&mut self) -> Result<()> {
1088 self.tx.flush()
1089 }
1090}
1091
1092// ============================================================================
1093// ASYNC MODE IMPLEMENTATIONS (DMA-based)
1094// ============================================================================
1095
1096/// Guard struct that ensures DMA is stopped if the async future is cancelled.
1097///
1098/// This implements the RAII pattern: if the future is dropped before completion
1099/// (e.g., due to a timeout), the DMA transfer is automatically aborted to prevent
1100/// use-after-free when the buffer goes out of scope.
1101struct TxDmaGuard<'a, C: DmaChannelTrait> {
1102 dma: &'a DmaChannel<C>,
1103 regs: Regs,
1104}
1105
1106impl<'a, C: DmaChannelTrait> TxDmaGuard<'a, C> {
1107 fn new(dma: &'a DmaChannel<C>, regs: Regs) -> Self {
1108 Self { dma, regs }
1109 }
1110
1111 /// Complete the transfer normally (don't abort on drop).
1112 fn complete(self) {
1113 // Cleanup
1114 self.regs.baud().modify(|_, w| w.tdmae().disabled());
1115 unsafe {
1116 self.dma.disable_request();
1117 self.dma.clear_done();
1118 }
1119 // Don't run drop since we've cleaned up
1120 core::mem::forget(self);
1121 }
1122}
1123
1124impl<C: DmaChannelTrait> Drop for TxDmaGuard<'_, C> {
1125 fn drop(&mut self) {
1126 // Abort the DMA transfer if still running
1127 unsafe {
1128 self.dma.disable_request();
1129 self.dma.clear_done();
1130 self.dma.clear_interrupt();
1131 }
1132 // Disable UART TX DMA request
1133 self.regs.baud().modify(|_, w| w.tdmae().disabled());
1134 }
1135}
1136
1137/// Guard struct for RX DMA transfers.
1138struct RxDmaGuard<'a, C: DmaChannelTrait> {
1139 dma: &'a DmaChannel<C>,
1140 regs: Regs,
1141}
1142
1143impl<'a, C: DmaChannelTrait> RxDmaGuard<'a, C> {
1144 fn new(dma: &'a DmaChannel<C>, regs: Regs) -> Self {
1145 Self { dma, regs }
1146 }
1147
1148 /// Complete the transfer normally (don't abort on drop).
1149 fn complete(self) {
1150 // Ensure DMA writes are visible to CPU
1151 cortex_m::asm::dsb();
1152 // Cleanup
1153 self.regs.baud().modify(|_, w| w.rdmae().disabled());
1154 unsafe {
1155 self.dma.disable_request();
1156 self.dma.clear_done();
1157 }
1158 // Don't run drop since we've cleaned up
1159 core::mem::forget(self);
1160 }
1161}
1162
1163impl<C: DmaChannelTrait> Drop for RxDmaGuard<'_, C> {
1164 fn drop(&mut self) {
1165 // Abort the DMA transfer if still running
1166 unsafe {
1167 self.dma.disable_request();
1168 self.dma.clear_done();
1169 self.dma.clear_interrupt();
1170 }
1171 // Disable UART RX DMA request
1172 self.regs.baud().modify(|_, w| w.rdmae().disabled());
1173 }
1174}
1175
1176impl<'a, T: Instance, C: DmaChannelTrait> LpuartTxDma<'a, T, C> {
1177 /// Create a new LPUART TX driver with DMA support.
1178 pub fn new(
1179 _inner: Peri<'a, T>,
1180 tx_pin: Peri<'a, impl TxPin<T>>,
1181 tx_dma_ch: Peri<'a, C>,
1182 config: Config,
1183 ) -> Result<Self> {
1184 tx_pin.as_tx();
1185 let tx_pin: Peri<'a, AnyPin> = tx_pin.into();
1186
1187 // Initialize LPUART with TX enabled, RX disabled, no flow control
1188 Lpuart::<Async>::init::<T>(true, false, false, false, config)?;
1189
1190 Ok(Self {
1191 info: T::info(),
1192 _tx_pin: tx_pin,
1193 tx_dma: DmaChannel::new(tx_dma_ch),
1194 _instance: core::marker::PhantomData,
1195 })
1196 }
1197
1198 /// Write data using DMA.
1199 ///
1200 /// This configures the DMA channel for a memory-to-peripheral transfer
1201 /// and waits for completion asynchronously. Large buffers are automatically
1202 /// split into chunks that fit within the DMA transfer limit.
1203 ///
1204 /// The DMA request source is automatically derived from the LPUART instance type.
1205 ///
1206 /// # Safety
1207 ///
1208 /// If the returned future is dropped before completion (e.g., due to a timeout),
1209 /// the DMA transfer is automatically aborted to prevent use-after-free.
1210 ///
1211 /// # Arguments
1212 /// * `buf` - Data buffer to transmit
1213 pub async fn write_dma(&mut self, buf: &[u8]) -> Result<usize> {
1214 if buf.is_empty() {
1215 return Ok(0);
1216 }
1217
1218 let mut total = 0;
1219 for chunk in buf.chunks(DMA_MAX_TRANSFER_SIZE) {
1220 total += self.write_dma_inner(chunk).await?;
1221 }
1222
1223 Ok(total)
1224 }
1225
1226 /// Internal helper to write a single chunk (max 0x7FFF bytes) using DMA.
1227 async fn write_dma_inner(&mut self, buf: &[u8]) -> Result<usize> {
1228 let len = buf.len();
1229 let peri_addr = self.info.regs.data().as_ptr() as *mut u8;
1230
1231 unsafe {
1232 // Clean up channel state
1233 self.tx_dma.disable_request();
1234 self.tx_dma.clear_done();
1235 self.tx_dma.clear_interrupt();
1236
1237 // Set DMA request source from instance type (type-safe)
1238 self.tx_dma.set_request_source::<T::TxDmaRequest>();
1239
1240 // Configure TCD for memory-to-peripheral transfer
1241 self.tx_dma
1242 .setup_write_to_peripheral(buf, peri_addr, EnableInterrupt::Yes);
1243
1244 // Enable UART TX DMA request
1245 self.info.regs.baud().modify(|_, w| w.tdmae().enabled());
1246
1247 // Enable DMA channel request
1248 self.tx_dma.enable_request();
1249 }
1250
1251 // Create guard that will abort DMA if this future is dropped
1252 let guard = TxDmaGuard::new(&self.tx_dma, self.info.regs);
1253
1254 // Wait for completion asynchronously
1255 core::future::poll_fn(|cx| {
1256 self.tx_dma.waker().register(cx.waker());
1257 if self.tx_dma.is_done() {
1258 core::task::Poll::Ready(())
1259 } else {
1260 core::task::Poll::Pending
1261 }
1262 })
1263 .await;
1264
1265 // Transfer completed successfully - clean up without aborting
1266 guard.complete();
1267
1268 Ok(len)
1269 }
1270
1271 /// Blocking write (fallback when DMA is not needed)
1272 pub fn blocking_write(&mut self, buf: &[u8]) -> Result<()> {
1273 for &byte in buf {
1274 while self.info.regs.stat().read().tdre().is_txdata() {}
1275 self.info.regs.data().modify(|_, w| unsafe { w.bits(u32::from(byte)) });
1276 }
1277 Ok(())
1278 }
1279
1280 /// Flush TX blocking
1281 pub fn blocking_flush(&mut self) -> Result<()> {
1282 while self.info.regs.water().read().txcount().bits() != 0 {}
1283 while self.info.regs.stat().read().tc().is_active() {}
1284 Ok(())
1285 }
1286}
1287
1288impl<'a, T: Instance, C: DmaChannelTrait> LpuartRxDma<'a, T, C> {
1289 /// Create a new LPUART RX driver with DMA support.
1290 pub fn new(
1291 _inner: Peri<'a, T>,
1292 rx_pin: Peri<'a, impl RxPin<T>>,
1293 rx_dma_ch: Peri<'a, C>,
1294 config: Config,
1295 ) -> Result<Self> {
1296 rx_pin.as_rx();
1297 let rx_pin: Peri<'a, AnyPin> = rx_pin.into();
1298
1299 // Initialize LPUART with TX disabled, RX enabled, no flow control
1300 Lpuart::<Async>::init::<T>(false, true, false, false, config)?;
1301
1302 Ok(Self {
1303 info: T::info(),
1304 _rx_pin: rx_pin,
1305 rx_dma: DmaChannel::new(rx_dma_ch),
1306 _instance: core::marker::PhantomData,
1307 })
1308 }
1309
1310 /// Read data using DMA.
1311 ///
1312 /// This configures the DMA channel for a peripheral-to-memory transfer
1313 /// and waits for completion asynchronously. Large buffers are automatically
1314 /// split into chunks that fit within the DMA transfer limit.
1315 ///
1316 /// The DMA request source is automatically derived from the LPUART instance type.
1317 ///
1318 /// # Safety
1319 ///
1320 /// If the returned future is dropped before completion (e.g., due to a timeout),
1321 /// the DMA transfer is automatically aborted to prevent use-after-free.
1322 ///
1323 /// # Arguments
1324 /// * `buf` - Buffer to receive data into
1325 pub async fn read_dma(&mut self, buf: &mut [u8]) -> Result<usize> {
1326 if buf.is_empty() {
1327 return Ok(0);
1328 }
1329
1330 let mut total = 0;
1331 for chunk in buf.chunks_mut(DMA_MAX_TRANSFER_SIZE) {
1332 total += self.read_dma_inner(chunk).await?;
1333 }
1334
1335 Ok(total)
1336 }
1337
1338 /// Internal helper to read a single chunk (max 0x7FFF bytes) using DMA.
1339 async fn read_dma_inner(&mut self, buf: &mut [u8]) -> Result<usize> {
1340 let len = buf.len();
1341 let peri_addr = self.info.regs.data().as_ptr() as *const u8;
1342
1343 unsafe {
1344 // Clean up channel state
1345 self.rx_dma.disable_request();
1346 self.rx_dma.clear_done();
1347 self.rx_dma.clear_interrupt();
1348
1349 // Set DMA request source from instance type (type-safe)
1350 self.rx_dma.set_request_source::<T::RxDmaRequest>();
1351
1352 // Configure TCD for peripheral-to-memory transfer
1353 self.rx_dma
1354 .setup_read_from_peripheral(peri_addr, buf, EnableInterrupt::Yes);
1355
1356 // Enable UART RX DMA request
1357 self.info.regs.baud().modify(|_, w| w.rdmae().enabled());
1358
1359 // Enable DMA channel request
1360 self.rx_dma.enable_request();
1361 }
1362
1363 // Create guard that will abort DMA if this future is dropped
1364 let guard = RxDmaGuard::new(&self.rx_dma, self.info.regs);
1365
1366 // Wait for completion asynchronously
1367 core::future::poll_fn(|cx| {
1368 self.rx_dma.waker().register(cx.waker());
1369 if self.rx_dma.is_done() {
1370 core::task::Poll::Ready(())
1371 } else {
1372 core::task::Poll::Pending
1373 }
1374 })
1375 .await;
1376
1377 // Transfer completed successfully - clean up without aborting
1378 guard.complete();
1379
1380 Ok(len)
1381 }
1382
1383 /// Blocking read (fallback when DMA is not needed)
1384 pub fn blocking_read(&mut self, buf: &mut [u8]) -> Result<()> {
1385 for byte in buf.iter_mut() {
1386 loop {
1387 if has_data(self.info.regs) {
1388 *byte = (self.info.regs.data().read().bits() & 0xFF) as u8;
1389 break;
1390 }
1391 check_and_clear_rx_errors(self.info.regs)?;
1392 }
1393 }
1394 Ok(())
1395 }
1396
1397 /// Set up a ring buffer for continuous DMA reception.
1398 ///
1399 /// This configures the DMA channel for circular operation, enabling continuous
1400 /// reception of data without gaps. The DMA will continuously write received
1401 /// bytes into the buffer, wrapping around when it reaches the end.
1402 ///
1403 /// This method encapsulates all the low-level setup:
1404 /// - Configures the DMA request source for this LPUART instance
1405 /// - Enables the RX DMA request in the LPUART peripheral
1406 /// - Sets up the circular DMA transfer
1407 /// - Enables the NVIC interrupt for async wakeups
1408 ///
1409 /// # Arguments
1410 ///
1411 /// * `buf` - Destination buffer for received data (power-of-2 size is ideal for efficiency)
1412 ///
1413 /// # Returns
1414 ///
1415 /// A [`RingBuffer`] that can be used to asynchronously read received data.
1416 ///
1417 /// # Example
1418 ///
1419 /// ```no_run
1420 /// static mut RX_BUF: [u8; 64] = [0; 64];
1421 ///
1422 /// let rx = LpuartRxDma::new(p.LPUART2, p.P2_3, p.DMA_CH0, config).unwrap();
1423 /// let ring_buf = unsafe { rx.setup_ring_buffer(&mut RX_BUF) };
1424 ///
1425 /// // Read data as it arrives
1426 /// let mut buf = [0u8; 16];
1427 /// let n = ring_buf.read(&mut buf).await.unwrap();
1428 /// ```
1429 ///
1430 /// # Safety
1431 ///
1432 /// - The buffer must remain valid for the lifetime of the returned RingBuffer.
1433 /// - Only one RingBuffer should exist per LPUART RX channel at a time.
1434 /// - The caller must ensure the static buffer is not accessed elsewhere while
1435 /// the ring buffer is active.
1436 pub unsafe fn setup_ring_buffer<'b>(&self, buf: &'b mut [u8]) -> RingBuffer<'b, u8> {
1437 // Get the peripheral data register address
1438 let peri_addr = self.info.regs.data().as_ptr() as *const u8;
1439
1440 // Configure DMA request source for this LPUART instance (type-safe)
1441 self.rx_dma.set_request_source::<T::RxDmaRequest>();
1442
1443 // Enable RX DMA request in the LPUART peripheral
1444 self.info.regs.baud().modify(|_, w| w.rdmae().enabled());
1445
1446 // Set up circular DMA transfer (this also enables NVIC interrupt)
1447 self.rx_dma.setup_circular_read(peri_addr, buf)
1448 }
1449
1450 /// Enable the DMA channel request.
1451 ///
1452 /// Call this after `setup_ring_buffer()` to start continuous reception.
1453 /// This is separated from setup to allow for any additional configuration
1454 /// before starting the transfer.
1455 pub unsafe fn enable_dma_request(&self) {
1456 self.rx_dma.enable_request();
1457 }
1458}
1459
1460impl<'a, T: Instance, TxC: DmaChannelTrait, RxC: DmaChannelTrait> LpuartDma<'a, T, TxC, RxC> {
1461 /// Create a new LPUART driver with DMA support for both TX and RX.
1462 pub fn new(
1463 _inner: Peri<'a, T>,
1464 tx_pin: Peri<'a, impl TxPin<T>>,
1465 rx_pin: Peri<'a, impl RxPin<T>>,
1466 tx_dma_ch: Peri<'a, TxC>,
1467 rx_dma_ch: Peri<'a, RxC>,
1468 config: Config,
1469 ) -> Result<Self> {
1470 tx_pin.as_tx();
1471 rx_pin.as_rx();
1472
1473 let tx_pin: Peri<'a, AnyPin> = tx_pin.into();
1474 let rx_pin: Peri<'a, AnyPin> = rx_pin.into();
1475
1476 // Initialize LPUART with both TX and RX enabled, no flow control
1477 Lpuart::<Async>::init::<T>(true, true, false, false, config)?;
1478
1479 Ok(Self {
1480 tx: LpuartTxDma {
1481 info: T::info(),
1482 _tx_pin: tx_pin,
1483 tx_dma: DmaChannel::new(tx_dma_ch),
1484 _instance: core::marker::PhantomData,
1485 },
1486 rx: LpuartRxDma {
1487 info: T::info(),
1488 _rx_pin: rx_pin,
1489 rx_dma: DmaChannel::new(rx_dma_ch),
1490 _instance: core::marker::PhantomData,
1491 },
1492 })
1493 }
1494
1495 /// Split into separate TX and RX drivers
1496 pub fn split(self) -> (LpuartTxDma<'a, T, TxC>, LpuartRxDma<'a, T, RxC>) {
1497 (self.tx, self.rx)
1498 }
1499
1500 /// Write data using DMA
1501 pub async fn write_dma(&mut self, buf: &[u8]) -> Result<usize> {
1502 self.tx.write_dma(buf).await
1503 }
1504
1505 /// Read data using DMA
1506 pub async fn read_dma(&mut self, buf: &mut [u8]) -> Result<usize> {
1507 self.rx.read_dma(buf).await
1508 }
1509}
1510
1511// ============================================================================
1512// EMBEDDED-IO-ASYNC TRAIT IMPLEMENTATIONS
1513// ============================================================================
1514
1515impl<T: Instance, C: DmaChannelTrait> embedded_io::ErrorType for LpuartTxDma<'_, T, C> {
1516 type Error = Error;
1517}
1518
1519impl<T: Instance, C: DmaChannelTrait> embedded_io::ErrorType for LpuartRxDma<'_, T, C> {
1520 type Error = Error;
1521}
1522
1523impl<T: Instance, TxC: DmaChannelTrait, RxC: DmaChannelTrait> embedded_io::ErrorType for LpuartDma<'_, T, TxC, RxC> {
1524 type Error = Error;
1525}
1526
1527// ============================================================================
1528// EMBEDDED-HAL 0.2 TRAIT IMPLEMENTATIONS
1529// ============================================================================
1530
1531impl embedded_hal_02::serial::Read<u8> for LpuartRx<'_, Blocking> {
1532 type Error = Error;
1533
1534 fn read(&mut self) -> core::result::Result<u8, nb::Error<Self::Error>> {
1535 let mut buf = [0; 1];
1536 match self.read(&mut buf) {
1537 Ok(_) => Ok(buf[0]),
1538 Err(Error::RxFifoEmpty) => Err(nb::Error::WouldBlock),
1539 Err(e) => Err(nb::Error::Other(e)),
1540 }
1541 }
1542}
1543
1544impl embedded_hal_02::serial::Write<u8> for LpuartTx<'_, Blocking> {
1545 type Error = Error;
1546
1547 fn write(&mut self, word: u8) -> core::result::Result<(), nb::Error<Self::Error>> {
1548 match self.write(&[word]) {
1549 Ok(_) => Ok(()),
1550 Err(Error::TxFifoFull) => Err(nb::Error::WouldBlock),
1551 Err(e) => Err(nb::Error::Other(e)),
1552 }
1553 }
1554
1555 fn flush(&mut self) -> core::result::Result<(), nb::Error<Self::Error>> {
1556 match self.flush() {
1557 Ok(_) => Ok(()),
1558 Err(Error::TxBusy) => Err(nb::Error::WouldBlock),
1559 Err(e) => Err(nb::Error::Other(e)),
1560 }
1561 }
1562}
1563
1564impl embedded_hal_02::blocking::serial::Write<u8> for LpuartTx<'_, Blocking> {
1565 type Error = Error;
1566
1567 fn bwrite_all(&mut self, buffer: &[u8]) -> core::result::Result<(), Self::Error> {
1568 self.blocking_write(buffer)
1569 }
1570
1571 fn bflush(&mut self) -> core::result::Result<(), Self::Error> {
1572 self.blocking_flush()
1573 }
1574}
1575
1576impl embedded_hal_02::serial::Read<u8> for Lpuart<'_, Blocking> {
1577 type Error = Error;
1578
1579 fn read(&mut self) -> core::result::Result<u8, nb::Error<Self::Error>> {
1580 embedded_hal_02::serial::Read::read(&mut self.rx)
1581 }
1582}
1583
1584impl embedded_hal_02::serial::Write<u8> for Lpuart<'_, Blocking> {
1585 type Error = Error;
1586
1587 fn write(&mut self, word: u8) -> core::result::Result<(), nb::Error<Self::Error>> {
1588 embedded_hal_02::serial::Write::write(&mut self.tx, word)
1589 }
1590
1591 fn flush(&mut self) -> core::result::Result<(), nb::Error<Self::Error>> {
1592 embedded_hal_02::serial::Write::flush(&mut self.tx)
1593 }
1594}
1595
1596impl embedded_hal_02::blocking::serial::Write<u8> for Lpuart<'_, Blocking> {
1597 type Error = Error;
1598
1599 fn bwrite_all(&mut self, buffer: &[u8]) -> core::result::Result<(), Self::Error> {
1600 self.blocking_write(buffer)
1601 }
1602
1603 fn bflush(&mut self) -> core::result::Result<(), Self::Error> {
1604 self.blocking_flush()
1605 }
1606}
1607
1608// ============================================================================
1609// EMBEDDED-HAL-NB TRAIT IMPLEMENTATIONS
1610// ============================================================================
1611
1612impl embedded_hal_nb::serial::Error for Error {
1613 fn kind(&self) -> embedded_hal_nb::serial::ErrorKind {
1614 match *self {
1615 Self::Framing => embedded_hal_nb::serial::ErrorKind::FrameFormat,
1616 Self::Overrun => embedded_hal_nb::serial::ErrorKind::Overrun,
1617 Self::Parity => embedded_hal_nb::serial::ErrorKind::Parity,
1618 Self::Noise => embedded_hal_nb::serial::ErrorKind::Noise,
1619 _ => embedded_hal_nb::serial::ErrorKind::Other,
1620 }
1621 }
1622}
1623
1624impl embedded_hal_nb::serial::ErrorType for LpuartRx<'_, Blocking> {
1625 type Error = Error;
1626}
1627
1628impl embedded_hal_nb::serial::ErrorType for LpuartTx<'_, Blocking> {
1629 type Error = Error;
1630}
1631
1632impl embedded_hal_nb::serial::ErrorType for Lpuart<'_, Blocking> {
1633 type Error = Error;
1634}
1635
1636impl embedded_hal_nb::serial::Read for LpuartRx<'_, Blocking> {
1637 fn read(&mut self) -> nb::Result<u8, Self::Error> {
1638 let mut buf = [0; 1];
1639 match self.read(&mut buf) {
1640 Ok(_) => Ok(buf[0]),
1641 Err(Error::RxFifoEmpty) => Err(nb::Error::WouldBlock),
1642 Err(e) => Err(nb::Error::Other(e)),
1643 }
1644 }
1645}
1646
1647impl embedded_hal_nb::serial::Write for LpuartTx<'_, Blocking> {
1648 fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> {
1649 match self.write(&[word]) {
1650 Ok(_) => Ok(()),
1651 Err(Error::TxFifoFull) => Err(nb::Error::WouldBlock),
1652 Err(e) => Err(nb::Error::Other(e)),
1653 }
1654 }
1655
1656 fn flush(&mut self) -> nb::Result<(), Self::Error> {
1657 match self.flush() {
1658 Ok(_) => Ok(()),
1659 Err(Error::TxBusy) => Err(nb::Error::WouldBlock),
1660 Err(e) => Err(nb::Error::Other(e)),
1661 }
1662 }
1663}
1664
1665impl embedded_hal_nb::serial::Read for Lpuart<'_, Blocking> {
1666 fn read(&mut self) -> nb::Result<u8, Self::Error> {
1667 embedded_hal_nb::serial::Read::read(&mut self.rx)
1668 }
1669}
1670
1671impl embedded_hal_nb::serial::Write for Lpuart<'_, Blocking> {
1672 fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> {
1673 embedded_hal_nb::serial::Write::write(&mut self.tx, char)
1674 }
1675
1676 fn flush(&mut self) -> nb::Result<(), Self::Error> {
1677 embedded_hal_nb::serial::Write::flush(&mut self.tx)
1678 }
1679}
1680
1681// ============================================================================
1682// EMBEDDED-IO TRAIT IMPLEMENTATIONS
1683// ============================================================================
1684
1685impl embedded_io::Error for Error {
1686 fn kind(&self) -> embedded_io::ErrorKind {
1687 embedded_io::ErrorKind::Other
1688 }
1689}
1690
1691impl embedded_io::ErrorType for LpuartRx<'_, Blocking> {
1692 type Error = Error;
1693}
1694
1695impl embedded_io::ErrorType for LpuartTx<'_, Blocking> {
1696 type Error = Error;
1697}
1698
1699impl embedded_io::ErrorType for Lpuart<'_, Blocking> {
1700 type Error = Error;
1701}
1702
1703impl embedded_io::Read for LpuartRx<'_, Blocking> {
1704 fn read(&mut self, buf: &mut [u8]) -> core::result::Result<usize, Self::Error> {
1705 self.blocking_read(buf).map(|_| buf.len())
1706 }
1707}
1708
1709impl embedded_io::Write for LpuartTx<'_, Blocking> {
1710 fn write(&mut self, buf: &[u8]) -> core::result::Result<usize, Self::Error> {
1711 self.blocking_write(buf).map(|_| buf.len())
1712 }
1713
1714 fn flush(&mut self) -> core::result::Result<(), Self::Error> {
1715 self.blocking_flush()
1716 }
1717}
1718
1719impl embedded_io::Read for Lpuart<'_, Blocking> {
1720 fn read(&mut self, buf: &mut [u8]) -> core::result::Result<usize, Self::Error> {
1721 embedded_io::Read::read(&mut self.rx, buf)
1722 }
1723}
1724
1725impl embedded_io::Write for Lpuart<'_, Blocking> {
1726 fn write(&mut self, buf: &[u8]) -> core::result::Result<usize, Self::Error> {
1727 embedded_io::Write::write(&mut self.tx, buf)
1728 }
1729
1730 fn flush(&mut self) -> core::result::Result<(), Self::Error> {
1731 embedded_io::Write::flush(&mut self.tx)
1732 }
1733}