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