aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoi Bachynskyi <[email protected]>2025-09-15 00:38:23 +0300
committerRoi Bachynskyi <[email protected]>2025-09-25 12:20:10 +0300
commitf3d3b8899358ce8540bf5b2107a71b02ff941213 (patch)
treecd3e8900071ec0896ed88e4cbeca5ed985cf3e8f
parent56019ba197443e16b4f0b3a0fe3ff85985f6e45c (diff)
lpc55: dma and async usart
-rw-r--r--embassy-nxp/CHANGELOG.md1
-rw-r--r--embassy-nxp/Cargo.toml1
-rw-r--r--embassy-nxp/src/chips/lpc55.rs30
-rw-r--r--embassy-nxp/src/dma.rs5
-rw-r--r--embassy-nxp/src/dma/lpc55.rs377
-rw-r--r--embassy-nxp/src/lib.rs72
-rw-r--r--embassy-nxp/src/usart/lpc55.rs310
-rw-r--r--examples/lpc55s69/src/bin/usart_async.rs70
8 files changed, 846 insertions, 20 deletions
diff --git a/embassy-nxp/CHANGELOG.md b/embassy-nxp/CHANGELOG.md
index ab97c4185..0fb677cd8 100644
--- a/embassy-nxp/CHANGELOG.md
+++ b/embassy-nxp/CHANGELOG.md
@@ -7,5 +7,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7 7
8<!-- next-header --> 8<!-- next-header -->
9## Unreleased - ReleaseDate 9## Unreleased - ReleaseDate
10- LPC55: DMA Controller and asynchronous version of USART
10- Moved NXP LPC55S69 from `lpc55-pac` to `nxp-pac` 11- Moved NXP LPC55S69 from `lpc55-pac` to `nxp-pac`
11- First release with changelog. 12- First release with changelog.
diff --git a/embassy-nxp/Cargo.toml b/embassy-nxp/Cargo.toml
index 073fdabe4..f3c828313 100644
--- a/embassy-nxp/Cargo.toml
+++ b/embassy-nxp/Cargo.toml
@@ -29,6 +29,7 @@ cortex-m-rt = "0.7.0"
29critical-section = "1.1.2" 29critical-section = "1.1.2"
30embassy-hal-internal = { version = "0.3.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-2"] } 30embassy-hal-internal = { version = "0.3.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-2"] }
31embassy-sync = { version = "0.7.2", path = "../embassy-sync" } 31embassy-sync = { version = "0.7.2", path = "../embassy-sync" }
32embassy-futures = { version = "0.1.2", path = "../embassy-futures"}
32defmt = { version = "1", optional = true } 33defmt = { version = "1", optional = true }
33log = { version = "0.4.27", optional = true } 34log = { version = "0.4.27", optional = true }
34embassy-time = { version = "0.5.0", path = "../embassy-time", optional = true } 35embassy-time = { version = "0.5.0", path = "../embassy-time", optional = true }
diff --git a/embassy-nxp/src/chips/lpc55.rs b/embassy-nxp/src/chips/lpc55.rs
index 711bff3e7..9f4e7269f 100644
--- a/embassy-nxp/src/chips/lpc55.rs
+++ b/embassy-nxp/src/chips/lpc55.rs
@@ -1,5 +1,9 @@
1pub use nxp_pac as pac; 1pub use nxp_pac as pac;
2 2
3embassy_hal_internal::interrupt_mod!(
4 FLEXCOMM0, FLEXCOMM1, FLEXCOMM2, FLEXCOMM3, FLEXCOMM4, FLEXCOMM5, FLEXCOMM6, FLEXCOMM7
5);
6
3embassy_hal_internal::peripherals! { 7embassy_hal_internal::peripherals! {
4 // External pins. These are not only GPIOs, they are multi-purpose pins and can be used by other 8 // External pins. These are not only GPIOs, they are multi-purpose pins and can be used by other
5 // peripheral types (e.g. I2C). 9 // peripheral types (e.g. I2C).
@@ -68,6 +72,32 @@ embassy_hal_internal::peripherals! {
68 PIO1_30, 72 PIO1_30,
69 PIO1_31, 73 PIO1_31,
70 74
75 // Direct Memory Access (DMA) channels. They are used for asynchronous modes of peripherals.
76 DMA_CH0,
77 DMA_CH1,
78 DMA_CH2,
79 DMA_CH3,
80 DMA_CH4,
81 DMA_CH5,
82 DMA_CH6,
83 DMA_CH7,
84 DMA_CH8,
85 DMA_CH9,
86 DMA_CH10,
87 DMA_CH11,
88 DMA_CH12,
89 DMA_CH13,
90 DMA_CH14,
91 DMA_CH15,
92 DMA_CH16,
93 DMA_CH17,
94 DMA_CH18,
95 DMA_CH19,
96 DMA_CH20,
97 DMA_CH21,
98 DMA_CH22,
99
100 // Universal Synchronous/Asynchronous Receiver/Transmitter (USART) instances.
71 USART0, 101 USART0,
72 USART1, 102 USART1,
73 USART2, 103 USART2,
diff --git a/embassy-nxp/src/dma.rs b/embassy-nxp/src/dma.rs
new file mode 100644
index 000000000..e2df65fc9
--- /dev/null
+++ b/embassy-nxp/src/dma.rs
@@ -0,0 +1,5 @@
1//! Direct Memory Access (DMA) driver.
2
3#[cfg_attr(feature = "lpc55-core0", path = "./dma/lpc55.rs")]
4mod inner;
5pub use inner::*;
diff --git a/embassy-nxp/src/dma/lpc55.rs b/embassy-nxp/src/dma/lpc55.rs
new file mode 100644
index 000000000..578d1fd88
--- /dev/null
+++ b/embassy-nxp/src/dma/lpc55.rs
@@ -0,0 +1,377 @@
1use core::cell::RefCell;
2use core::future::Future;
3use core::pin::Pin;
4use core::sync::atomic::{compiler_fence, Ordering};
5use core::task::{Context, Poll};
6
7use critical_section::Mutex;
8use embassy_hal_internal::interrupt::InterruptExt;
9use embassy_hal_internal::{impl_peripheral, PeripheralType};
10use embassy_sync::waitqueue::AtomicWaker;
11
12use crate::pac::{DMA0, SYSCON, *};
13use crate::{peripherals, Peri};
14
15#[interrupt]
16fn DMA0() {
17 let inta = DMA0.inta0().read().ia();
18 for channel in 0..CHANNEL_COUNT {
19 if (DMA0.errint0().read().err() & (1 << channel)) != 0 {
20 panic!("DMA: error on DMA_0 channel {}", channel);
21 }
22
23 if (inta & (1 << channel)) != 0 {
24 CHANNEL_WAKERS[channel].wake();
25 DMA0.inta0().modify(|w| w.set_ia(1 << channel));
26 }
27 }
28}
29
30pub(crate) fn init() {
31 assert_eq!(core::mem::size_of::<DmaDescriptor>(), 16, "Descriptor must be 16 bytes");
32 assert_eq!(
33 core::mem::align_of::<DmaDescriptor>(),
34 16,
35 "Descriptor must be 16-byte aligned"
36 );
37 assert_eq!(
38 core::mem::align_of::<DmaDescriptorTable>(),
39 512,
40 "Table must be 512-byte aligned"
41 );
42 // Start clock for DMA
43 SYSCON.ahbclkctrl0().modify(|w| w.set_dma0(true));
44 // Reset DMA
45 SYSCON
46 .presetctrl0()
47 .modify(|w| w.set_dma0_rst(syscon::vals::Dma0Rst::ASSERTED));
48 SYSCON
49 .presetctrl0()
50 .modify(|w| w.set_dma0_rst(syscon::vals::Dma0Rst::RELEASED));
51
52 // Address bits 31:9 of the beginning of the DMA descriptor table
53 critical_section::with(|cs| {
54 DMA0.srambase()
55 .write(|w| w.set_offset((DMA_DESCRIPTORS.borrow(cs).as_ptr() as u32) >> 9));
56 });
57 // Enable DMA controller
58 DMA0.ctrl().modify(|w| w.set_enable(true));
59
60 unsafe {
61 crate::pac::interrupt::DMA0.enable();
62 }
63 info!("DMA initialized");
64}
65
66/// DMA read.
67///
68/// SAFETY: Slice must point to a valid location reachable by DMA.
69pub unsafe fn read<'a, C: Channel, W: Word>(ch: Peri<'a, C>, from: *const W, to: *mut [W]) -> Transfer<'a, C> {
70 copy_inner(
71 ch,
72 from as *const u32,
73 to as *mut W as *mut u32,
74 to.len(),
75 W::size(),
76 false,
77 true,
78 )
79}
80
81/// DMA write.
82///
83/// SAFETY: Slice must point to a valid location reachable by DMA.
84pub unsafe fn write<'a, C: Channel, W: Word>(ch: Peri<'a, C>, from: *const [W], to: *mut W) -> Transfer<'a, C> {
85 copy_inner(
86 ch,
87 from as *const W as *const u32,
88 to as *mut u32,
89 from.len(),
90 W::size(),
91 true,
92 false,
93 )
94}
95
96/// DMA copy between slices.
97///
98/// SAFETY: Slices must point to locations reachable by DMA.
99pub unsafe fn copy<'a, C: Channel, W: Word>(ch: Peri<'a, C>, from: &[W], to: &mut [W]) -> Transfer<'a, C> {
100 let from_len = from.len();
101 let to_len = to.len();
102 assert_eq!(from_len, to_len);
103 copy_inner(
104 ch,
105 from.as_ptr() as *const u32,
106 to.as_mut_ptr() as *mut u32,
107 from_len,
108 W::size(),
109 true,
110 true,
111 )
112}
113
114fn copy_inner<'a, C: Channel>(
115 ch: Peri<'a, C>,
116 from: *const u32,
117 to: *mut u32,
118 len: usize,
119 data_size: crate::pac::dma::vals::Width,
120 incr_src: bool,
121 incr_dest: bool,
122) -> Transfer<'a, C> {
123 let p = ch.regs();
124
125 // Buffer ending address = buffer starting address + (XFERCOUNT * the transfer increment)
126 // XREFCOUNT = the number of transfers performed - 1.
127 // The 1st transfer is included in the starting address.
128 let source_end_addr = if incr_src {
129 from as u32 + len as u32 - 1
130 } else {
131 from as u32
132 };
133 let dest_end_addr = if incr_dest {
134 to as u32 + len as u32 - 1
135 } else {
136 to as u32
137 };
138
139 compiler_fence(Ordering::SeqCst);
140
141 critical_section::with(|cs| {
142 DMA_DESCRIPTORS.borrow(cs).borrow_mut().descriptors[ch.number() as usize] = DmaDescriptor {
143 reserved: 0,
144 source_end_addr,
145 dest_end_addr,
146 next_desc: 0, // Since only single transfers are made, there is no need for reload descriptor address.
147 }
148 });
149
150 compiler_fence(Ordering::SeqCst);
151
152 p.cfg().modify(|w| {
153 // Peripheral DMA requests are enabled.
154 // DMA requests that pace transfers can be interpreted then.
155 w.set_periphreqen(true);
156 // There is no need to have them on.
157 // No complex transfers are performed for now.
158 w.set_hwtrigen(false);
159 w.set_chpriority(0);
160 });
161
162 p.xfercfg().modify(|w| {
163 // This bit indicates whether the current channel descriptor is
164 // valid and can potentially be acted upon,
165 // if all other activation criteria are fulfilled.
166 w.set_cfgvalid(true);
167 // Indicates whether the channel’s control structure will be reloaded
168 // when the current descriptor is exhausted.
169 // Reloading allows ping-pong and linked transfers.
170 w.set_reload(false);
171 // There is no hardware distinction between interrupt A and B.
172 // They can be used by software to assist with more complex descriptor usage.
173 // By convention, interrupt A may be used when only one interrupt flag is needed.
174 w.set_setinta(true);
175 w.set_setintb(false);
176 w.set_width(data_size);
177 w.set_srcinc(if incr_src {
178 dma::vals::Srcinc::WIDTH_X_1
179 } else {
180 dma::vals::Srcinc::NO_INCREMENT
181 });
182 w.set_dstinc(if incr_dest {
183 dma::vals::Dstinc::WIDTH_X_1
184 } else {
185 dma::vals::Dstinc::NO_INCREMENT
186 });
187 // Total number of transfers to be performed, minus 1 encoded.
188 w.set_xfercount((len as u16) - 1);
189 // Before triggering the channel, it has to be enabled.
190 w.set_swtrig(false);
191 });
192
193 compiler_fence(Ordering::SeqCst);
194 DMA0.enableset0().write(|w| w.set_ena(1 << ch.number()));
195 DMA0.intenset0().write(|w| w.set_inten(1 << ch.number()));
196
197 compiler_fence(Ordering::SeqCst);
198 // Start transfer.
199 DMA0.settrig0().write(|w| w.set_trig(1 << ch.number()));
200 compiler_fence(Ordering::SeqCst);
201 Transfer::new(ch)
202}
203
204/// DMA transfer driver.
205#[must_use = "futures do nothing unless you `.await` or poll them"]
206pub struct Transfer<'a, C: Channel> {
207 channel: Peri<'a, C>,
208}
209
210impl<'a, C: Channel> Transfer<'a, C> {
211 pub(crate) fn new(channel: Peri<'a, C>) -> Self {
212 Self { channel }
213 }
214}
215
216impl<'a, C: Channel> Drop for Transfer<'a, C> {
217 fn drop(&mut self) {
218 DMA0.enableclr0().write(|w| w.set_clr(1 << self.channel.number()));
219 while (DMA0.busy0().read().bsy() & (1 << self.channel.number())) != 0 {}
220 DMA0.abort0().write(|w| w.set_abortctrl(1 << self.channel.number()));
221 }
222}
223
224impl<'a, C: Channel> Unpin for Transfer<'a, C> {}
225impl<'a, C: Channel> Future for Transfer<'a, C> {
226 type Output = ();
227 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
228 // We need to register/re-register the waker for each poll because any
229 // calls to wake will deregister the waker.
230 CHANNEL_WAKERS[self.channel.number() as usize].register(cx.waker());
231 // Check if it is busy or not.
232 if (DMA0.busy0().read().bsy() & (1 << self.channel.number())) != 0 {
233 Poll::Pending
234 } else {
235 Poll::Ready(())
236 }
237 }
238}
239
240// Total number of channles including both DMA0 and DMA1.
241// In spite of using only DMA0 channels, the descriptor table
242// should be of this size.
243pub(crate) const CHANNEL_COUNT: usize = 32;
244
245static CHANNEL_WAKERS: [AtomicWaker; CHANNEL_COUNT] = [const { AtomicWaker::new() }; CHANNEL_COUNT];
246
247// See section 22.5.2 (table 450)
248// UM11126, Rev. 2.8
249// The size of a descriptor must be aligned to a multiple of 16 bytes.
250#[repr(C, align(16))]
251#[derive(Clone, Copy)]
252struct DmaDescriptor {
253 /// 0x0 Reserved.
254 reserved: u32,
255 /// 0x4 Source data end address.
256 source_end_addr: u32,
257 /// 0x8 Destination end address.
258 dest_end_addr: u32,
259 /// 0xC Link to next descriptor.
260 next_desc: u32,
261}
262
263// See section 22.6.3
264// UM11126, Rev. 2.8
265// The table must begin on a 512 byte boundary.
266#[repr(C, align(512))]
267struct DmaDescriptorTable {
268 descriptors: [DmaDescriptor; CHANNEL_COUNT],
269}
270
271// DMA descriptors are stored in on-chip SRAM.
272static DMA_DESCRIPTORS: Mutex<RefCell<DmaDescriptorTable>> = Mutex::new(RefCell::new(DmaDescriptorTable {
273 descriptors: [DmaDescriptor {
274 reserved: 0,
275 source_end_addr: 0,
276 dest_end_addr: 0,
277 next_desc: 0,
278 }; CHANNEL_COUNT],
279}));
280
281trait SealedChannel {}
282trait SealedWord {}
283
284/// DMA channel interface.
285#[allow(private_bounds)]
286pub trait Channel: PeripheralType + SealedChannel + Into<AnyChannel> + Sized + 'static {
287 /// Channel number.
288 fn number(&self) -> u8;
289
290 /// Channel registry block.
291 fn regs(&self) -> crate::pac::dma::Channel {
292 crate::pac::DMA0.channel(self.number() as _)
293 }
294}
295
296/// DMA word.
297#[allow(private_bounds)]
298pub trait Word: SealedWord {
299 /// Word size.
300 fn size() -> crate::pac::dma::vals::Width;
301}
302
303impl SealedWord for u8 {}
304impl Word for u8 {
305 fn size() -> crate::pac::dma::vals::Width {
306 crate::pac::dma::vals::Width::BIT_8
307 }
308}
309
310impl SealedWord for u16 {}
311impl Word for u16 {
312 fn size() -> crate::pac::dma::vals::Width {
313 crate::pac::dma::vals::Width::BIT_16
314 }
315}
316
317impl SealedWord for u32 {}
318impl Word for u32 {
319 fn size() -> crate::pac::dma::vals::Width {
320 crate::pac::dma::vals::Width::BIT_32
321 }
322}
323
324/// Type erased DMA channel.
325pub struct AnyChannel {
326 number: u8,
327}
328
329impl_peripheral!(AnyChannel);
330
331impl SealedChannel for AnyChannel {}
332impl Channel for AnyChannel {
333 fn number(&self) -> u8 {
334 self.number
335 }
336}
337
338macro_rules! channel {
339 ($name:ident, $num:expr) => {
340 impl SealedChannel for peripherals::$name {}
341 impl Channel for peripherals::$name {
342 fn number(&self) -> u8 {
343 $num
344 }
345 }
346
347 impl From<peripherals::$name> for crate::dma::AnyChannel {
348 fn from(val: peripherals::$name) -> Self {
349 Self { number: val.number() }
350 }
351 }
352 };
353}
354
355channel!(DMA_CH0, 0);
356channel!(DMA_CH1, 1);
357channel!(DMA_CH2, 2);
358channel!(DMA_CH3, 3);
359channel!(DMA_CH4, 4);
360channel!(DMA_CH5, 5);
361channel!(DMA_CH6, 6);
362channel!(DMA_CH7, 7);
363channel!(DMA_CH8, 8);
364channel!(DMA_CH9, 9);
365channel!(DMA_CH10, 10);
366channel!(DMA_CH11, 11);
367channel!(DMA_CH12, 12);
368channel!(DMA_CH13, 13);
369channel!(DMA_CH14, 14);
370channel!(DMA_CH15, 15);
371channel!(DMA_CH16, 16);
372channel!(DMA_CH17, 17);
373channel!(DMA_CH18, 18);
374channel!(DMA_CH19, 19);
375channel!(DMA_CH20, 20);
376channel!(DMA_CH21, 21);
377channel!(DMA_CH22, 22);
diff --git a/embassy-nxp/src/lib.rs b/embassy-nxp/src/lib.rs
index 74142a10b..f0f0afb6c 100644
--- a/embassy-nxp/src/lib.rs
+++ b/embassy-nxp/src/lib.rs
@@ -3,6 +3,8 @@
3// This mod MUST go first, so that the others see its macros. 3// This mod MUST go first, so that the others see its macros.
4pub(crate) mod fmt; 4pub(crate) mod fmt;
5 5
6#[cfg(feature = "lpc55-core0")]
7pub mod dma;
6pub mod gpio; 8pub mod gpio;
7#[cfg(feature = "lpc55-core0")] 9#[cfg(feature = "lpc55-core0")]
8pub mod pint; 10pub mod pint;
@@ -20,6 +22,9 @@ mod time_driver;
20#[cfg_attr(feature = "mimxrt1062", path = "chips/mimxrt1062.rs")] 22#[cfg_attr(feature = "mimxrt1062", path = "chips/mimxrt1062.rs")]
21mod chip; 23mod chip;
22 24
25// TODO: Remove when this module is implemented for other chips
26#[cfg(feature = "lpc55-core0")]
27pub use chip::interrupt;
23#[cfg(feature = "unstable-pac")] 28#[cfg(feature = "unstable-pac")]
24pub use chip::pac; 29pub use chip::pac;
25#[cfg(not(feature = "unstable-pac"))] 30#[cfg(not(feature = "unstable-pac"))]
@@ -27,6 +32,67 @@ pub(crate) use chip::pac;
27pub use chip::{peripherals, Peripherals}; 32pub use chip::{peripherals, Peripherals};
28pub use embassy_hal_internal::{Peri, PeripheralType}; 33pub use embassy_hal_internal::{Peri, PeripheralType};
29 34
35/// Macro to bind interrupts to handlers.
36/// (Copied from `embassy-rp`)
37/// This defines the right interrupt handlers, and creates a unit struct (like `struct Irqs;`)
38/// and implements the right [`Binding`]s for it. You can pass this struct to drivers to
39/// prove at compile-time that the right interrupts have been bound.
40///
41/// Example of how to bind one interrupt:
42///
43/// ```rust,ignore
44/// use embassy_nxp::{bind_interrupts, usart, peripherals};
45///
46/// bind_interrupts!(
47/// /// Binds the USART Interrupts.
48/// struct Irqs {
49/// FLEXCOMM0 => usart::InterruptHandler<peripherals::USART0>;
50/// }
51/// );
52/// ```
53#[macro_export]
54macro_rules! bind_interrupts {
55 ($(#[$attr:meta])* $vis:vis struct $name:ident {
56 $(
57 $(#[cfg($cond_irq:meta)])?
58 $irq:ident => $(
59 $(#[cfg($cond_handler:meta)])?
60 $handler:ty
61 ),*;
62 )*
63 }) => {
64 #[derive(Copy, Clone)]
65 $(#[$attr])*
66 $vis struct $name;
67
68 $(
69 #[allow(non_snake_case)]
70 #[no_mangle]
71 $(#[cfg($cond_irq)])?
72 unsafe extern "C" fn $irq() {
73 unsafe {
74 $(
75 $(#[cfg($cond_handler)])?
76 <$handler as $crate::interrupt::typelevel::Handler<$crate::interrupt::typelevel::$irq>>::on_interrupt();
77
78 )*
79 }
80 }
81
82 $(#[cfg($cond_irq)])?
83 $crate::bind_interrupts!(@inner
84 $(
85 $(#[cfg($cond_handler)])?
86 unsafe impl $crate::interrupt::typelevel::Binding<$crate::interrupt::typelevel::$irq, $handler> for $name {}
87 )*
88 );
89 )*
90 };
91 (@inner $($t:tt)*) => {
92 $($t)*
93 }
94}
95
30/// Initialize the `embassy-nxp` HAL with the provided configuration. 96/// Initialize the `embassy-nxp` HAL with the provided configuration.
31/// 97///
32/// This returns the peripheral singletons that can be used for creating drivers. 98/// This returns the peripheral singletons that can be used for creating drivers.
@@ -92,6 +158,9 @@ pub fn init(_config: config::Config) -> Peripherals {
92 #[cfg(feature = "_time_driver")] 158 #[cfg(feature = "_time_driver")]
93 time_driver::init(); 159 time_driver::init();
94 160
161 #[cfg(feature = "lpc55-core0")]
162 dma::init();
163
95 peripherals 164 peripherals
96} 165}
97 166
@@ -133,5 +202,8 @@ macro_rules! impl_mode {
133 202
134/// Blocking mode. 203/// Blocking mode.
135pub struct Blocking; 204pub struct Blocking;
205/// Asynchronous mode.
206pub struct Async;
136 207
137impl_mode!(Blocking); 208impl_mode!(Blocking);
209impl_mode!(Async);
diff --git a/embassy-nxp/src/usart/lpc55.rs b/embassy-nxp/src/usart/lpc55.rs
index 428b80c4b..9034ed429 100644
--- a/embassy-nxp/src/usart/lpc55.rs
+++ b/embassy-nxp/src/usart/lpc55.rs
@@ -1,14 +1,24 @@
1use core::fmt::Debug;
2use core::future::poll_fn;
1use core::marker::PhantomData; 3use core::marker::PhantomData;
4use core::sync::atomic::{AtomicBool, Ordering};
5use core::task::Poll;
2 6
7use embassy_futures::select::{select, Either};
8use embassy_hal_internal::interrupt::InterruptExt;
3use embassy_hal_internal::{Peri, PeripheralType}; 9use embassy_hal_internal::{Peri, PeripheralType};
10use embassy_sync::waitqueue::AtomicWaker;
4use embedded_io::{self, ErrorKind}; 11use embedded_io::{self, ErrorKind};
5 12
13use crate::dma::{AnyChannel, Channel};
6use crate::gpio::{match_iocon, AnyPin, Bank, SealedPin}; 14use crate::gpio::{match_iocon, AnyPin, Bank, SealedPin};
15use crate::interrupt::typelevel::{Binding, Interrupt as _};
16use crate::interrupt::Interrupt;
7use crate::pac::flexcomm::Flexcomm as FlexcommReg; 17use crate::pac::flexcomm::Flexcomm as FlexcommReg;
8use crate::pac::iocon::vals::PioFunc; 18use crate::pac::iocon::vals::PioFunc;
9use crate::pac::usart::Usart as UsartReg; 19use crate::pac::usart::Usart as UsartReg;
10use crate::pac::*; 20use crate::pac::*;
11use crate::{Blocking, Mode}; 21use crate::{Async, Blocking, Mode};
12 22
13/// Serial error 23/// Serial error
14#[derive(Debug, Eq, PartialEq, Copy, Clone)] 24#[derive(Debug, Eq, PartialEq, Copy, Clone)]
@@ -101,6 +111,12 @@ impl Default for Config {
101 } 111 }
102} 112}
103 113
114/// Internal DMA state of UART RX.
115pub struct DmaState {
116 rx_err_waker: AtomicWaker,
117 rx_err: AtomicBool,
118}
119
104/// # Type parameters 120/// # Type parameters
105/// 'd: the lifetime marker ensuring correct borrow checking for peripherals used at compile time 121/// 'd: the lifetime marker ensuring correct borrow checking for peripherals used at compile time
106/// T: the peripheral instance type allowing usage of peripheral specific registers 122/// T: the peripheral instance type allowing usage of peripheral specific registers
@@ -112,24 +128,33 @@ pub struct Usart<'d, M: Mode> {
112 128
113pub struct UsartTx<'d, M: Mode> { 129pub struct UsartTx<'d, M: Mode> {
114 info: &'static Info, 130 info: &'static Info,
115 phantom: PhantomData<(&'d (), M)>, 131 tx_dma: Option<Peri<'d, AnyChannel>>,
132 phantom: PhantomData<M>,
116} 133}
117 134
118pub struct UsartRx<'d, M: Mode> { 135pub struct UsartRx<'d, M: Mode> {
119 info: &'static Info, 136 info: &'static Info,
120 phantom: PhantomData<(&'d (), M)>, 137 dma_state: &'static DmaState,
138 rx_dma: Option<Peri<'d, AnyChannel>>,
139 phantom: PhantomData<M>,
121} 140}
122 141
123impl<'d, M: Mode> UsartTx<'d, M> { 142impl<'d, M: Mode> UsartTx<'d, M> {
124 pub fn new<T: Instance>(_usart: Peri<'d, T>, tx: Peri<'d, impl TxPin<T>>, config: Config) -> Self { 143 pub fn new<T: Instance>(
144 _usart: Peri<'d, T>,
145 tx: Peri<'d, impl TxPin<T>>,
146 tx_dma: Peri<'d, impl Channel>,
147 config: Config,
148 ) -> Self {
125 Usart::<M>::init::<T>(Some(tx.into()), None, config); 149 Usart::<M>::init::<T>(Some(tx.into()), None, config);
126 Self::new_inner(T::info()) 150 Self::new_inner(T::info(), Some(tx_dma.into()))
127 } 151 }
128 152
129 #[inline] 153 #[inline]
130 fn new_inner(info: &'static Info) -> Self { 154 fn new_inner(info: &'static Info, tx_dma: Option<Peri<'d, AnyChannel>>) -> Self {
131 Self { 155 Self {
132 info, 156 info,
157 tx_dma,
133 phantom: PhantomData, 158 phantom: PhantomData,
134 } 159 }
135 } 160 }
@@ -155,20 +180,65 @@ impl<'d, M: Mode> UsartTx<'d, M> {
155impl<'d> UsartTx<'d, Blocking> { 180impl<'d> UsartTx<'d, Blocking> {
156 pub fn new_blocking<T: Instance>(_usart: Peri<'d, T>, tx: Peri<'d, impl TxPin<T>>, config: Config) -> Self { 181 pub fn new_blocking<T: Instance>(_usart: Peri<'d, T>, tx: Peri<'d, impl TxPin<T>>, config: Config) -> Self {
157 Usart::<Blocking>::init::<T>(Some(tx.into()), None, config); 182 Usart::<Blocking>::init::<T>(Some(tx.into()), None, config);
158 Self::new_inner(T::info()) 183 Self::new_inner(T::info(), None)
159 } 184 }
160} 185}
161 186
187impl<'d> UsartTx<'d, Async> {
188 /// Write to UART TX from the provided buffer using DMA.
189 pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> {
190 // Unwrap() can be used because UsartTx::new() in Async mode always sets it to Some
191 let ch = self.tx_dma.as_mut().unwrap().reborrow();
192 let transfer = unsafe {
193 // Enable to pace DMA transfers.
194 self.info.usart_reg.fifocfg().modify(|w| w.set_dmatx(true));
195 // If future is not assigned to a variable, the data register pointer
196 // is held across an await and makes the future non-Send.
197 crate::dma::write(ch, buffer, self.info.usart_reg.fifowr().as_ptr() as *mut _)
198 };
199 transfer.await;
200 Ok(())
201 }
202}
162impl<'d, M: Mode> UsartRx<'d, M> { 203impl<'d, M: Mode> UsartRx<'d, M> {
163 pub fn new<T: Instance>(_usart: Peri<'d, T>, rx: Peri<'d, impl RxPin<T>>, config: Config) -> Self { 204 pub fn new<T: Instance>(
205 _usart: Peri<'d, T>,
206 rx: Peri<'d, impl RxPin<T>>,
207 has_irq: bool,
208 rx_dma: Peri<'d, impl Channel>,
209 config: Config,
210 ) -> Self {
164 Usart::<M>::init::<T>(None, Some(rx.into()), config); 211 Usart::<M>::init::<T>(None, Some(rx.into()), config);
165 Self::new_inner(T::info()) 212 Self::new_inner(T::info(), T::dma_state(), has_irq, Some(rx_dma.into()))
166 } 213 }
167 214
168 #[inline] 215 fn new_inner(
169 fn new_inner(info: &'static Info) -> Self { 216 info: &'static Info,
217 dma_state: &'static DmaState,
218 has_irq: bool,
219 rx_dma: Option<Peri<'d, AnyChannel>>,
220 ) -> Self {
221 core::debug_assert_eq!(has_irq, rx_dma.is_some());
222 if has_irq {
223 // Disable all the related interrupts for now.
224 info.usart_reg.intenclr().write(|w| {
225 w.set_framerrclr(true);
226 w.set_parityerrclr(true);
227 w.set_rxnoiseclr(true);
228 });
229 info.usart_reg.fifointenclr().modify(|w| {
230 w.set_rxlvl(true);
231 w.set_rxerr(true);
232 });
233 info.interrupt.unpend();
234 unsafe {
235 info.interrupt.enable();
236 }
237 }
170 Self { 238 Self {
171 info, 239 info,
240 dma_state,
241 rx_dma,
172 phantom: PhantomData, 242 phantom: PhantomData,
173 } 243 }
174 } 244 }
@@ -211,7 +281,120 @@ impl<'d, M: Mode> UsartRx<'d, M> {
211impl<'d> UsartRx<'d, Blocking> { 281impl<'d> UsartRx<'d, Blocking> {
212 pub fn new_blocking<T: Instance>(_usart: Peri<'d, T>, rx: Peri<'d, impl RxPin<T>>, config: Config) -> Self { 282 pub fn new_blocking<T: Instance>(_usart: Peri<'d, T>, rx: Peri<'d, impl RxPin<T>>, config: Config) -> Self {
213 Usart::<Blocking>::init::<T>(None, Some(rx.into()), config); 283 Usart::<Blocking>::init::<T>(None, Some(rx.into()), config);
214 Self::new_inner(T::info()) 284 Self::new_inner(T::info(), T::dma_state(), false, None)
285 }
286}
287
288/// Interrupt handler.
289pub struct InterruptHandler<T: Instance> {
290 _uart: PhantomData<T>,
291}
292
293impl<T: Instance> crate::interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
294 unsafe fn on_interrupt() {
295 let regs = T::info().usart_reg;
296 if !regs.fifocfg().read().dmarx() {
297 return;
298 }
299 let state = T::dma_state();
300 state.rx_err.store(true, Ordering::Relaxed);
301 state.rx_err_waker.wake();
302 // Disable the error interrupts instead of clearing the flags. Clearing the
303 // flags would allow the DMA transfer to continue, potentially signaling
304 // completion before we can check for errors that happened *during* the transfer.
305 regs.intenclr().write(|w| {
306 w.set_framerrclr(true);
307 w.set_rxnoiseclr(true);
308 w.set_parityerrclr(true);
309 });
310 regs.fifointenclr().write(|w| w.set_rxerr(true));
311 }
312}
313
314impl<'d> UsartRx<'d, Async> {
315 /// Read from USART RX into the provided buffer.
316 pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
317 // Clear error flags before the FIFO is drained. Errors that have accumulated
318 // in the flags will also be present in the FIFO.
319 self.dma_state.rx_err.store(false, Ordering::Relaxed);
320 self.info.usart_reg.intenclr().write(|w| {
321 w.set_framerrclr(true);
322 w.set_parityerrclr(true);
323 w.set_rxnoiseclr(true);
324 });
325 self.info.usart_reg.fifointenclr().modify(|w| w.set_rxerr(true));
326 // Then drain the fifo. It is necessary to read at most 16 bytes (the size of FIFO).
327 // Errors that apply to FIFO bytes will be reported directly.
328 let buffer = match {
329 let limit = buffer.len().min(16);
330 self.drain_fifo(&mut buffer[0..limit])
331 } {
332 Ok(len) if len < buffer.len() => &mut buffer[len..],
333 Ok(_) => return Ok(()),
334 Err((_i, e)) => return Err(e),
335 };
336
337 // Start a DMA transfer. If errors have happened in the interim some error
338 // interrupt flags will have been raised, and those will be picked up immediately
339 // by the interrupt handler.
340 // Unwrap() can be used because UsartRx::new() in Async mode always sets it to Some
341 let ch = self.rx_dma.as_mut().unwrap().reborrow();
342
343 self.info.usart_reg.intenset().write(|w| {
344 w.set_framerren(true);
345 w.set_parityerren(true);
346 w.set_rxnoiseen(true);
347 });
348 self.info.usart_reg.fifointenset().modify(|w| w.set_rxerr(true));
349 self.info.usart_reg.fifocfg().modify(|w| w.set_dmarx(true));
350 let transfer = unsafe {
351 // If we don't assign future to a variable, the data register pointer
352 // is held across an await and makes the future non-Send.
353 crate::dma::read(ch, self.info.usart_reg.fiford().as_ptr() as *const _, buffer)
354 };
355
356 // wait for either the transfer to complete or an error to happen.
357 let transfer_result = select(
358 transfer,
359 poll_fn(|cx| {
360 self.dma_state.rx_err_waker.register(cx.waker());
361 match self.dma_state.rx_err.swap(false, Ordering::Relaxed) {
362 false => Poll::Pending,
363 e => Poll::Ready(e),
364 }
365 }),
366 )
367 .await;
368
369 let errors = match transfer_result {
370 Either::First(()) => {
371 // The DMA controller finished, BUT if an error occurred on the LAST
372 // byte, then we may still need to grab the error state!
373 self.dma_state.rx_err.swap(false, Ordering::Relaxed)
374 }
375 Either::Second(e) => {
376 // There is an error, which means this is the error that
377 // was problematic.
378 e
379 }
380 };
381
382 // If we got no error, just return at this point
383 if !errors {
384 return Ok(());
385 }
386
387 // If we DID get an error, we need to figure out which one it was.
388 if self.info.usart_reg.intstat().read().framerrint() {
389 return Err(Error::Framing);
390 } else if self.info.usart_reg.intstat().read().parityerrint() {
391 return Err(Error::Parity);
392 } else if self.info.usart_reg.intstat().read().rxnoiseint() {
393 return Err(Error::Noise);
394 } else if self.info.usart_reg.fifointstat().read().rxerr() {
395 return Err(Error::Overrun);
396 }
397 unreachable!("unrecognized rx error");
215 } 398 }
216} 399}
217 400
@@ -222,7 +405,29 @@ impl<'d> Usart<'d, Blocking> {
222 rx: Peri<'d, impl RxPin<T>>, 405 rx: Peri<'d, impl RxPin<T>>,
223 config: Config, 406 config: Config,
224 ) -> Self { 407 ) -> Self {
225 Self::new_inner(usart, tx.into(), rx.into(), config) 408 Self::new_inner(usart, tx.into(), rx.into(), false, None, None, config)
409 }
410}
411
412impl<'d> Usart<'d, Async> {
413 pub fn new<T: Instance>(
414 uart: Peri<'d, T>,
415 tx: Peri<'d, impl TxPin<T>>,
416 rx: Peri<'d, impl RxPin<T>>,
417 _irq: impl Binding<T::Interrupt, InterruptHandler<T>>,
418 tx_dma: Peri<'d, impl TxChannel<T>>,
419 rx_dma: Peri<'d, impl RxChannel<T>>,
420 config: Config,
421 ) -> Self {
422 Self::new_inner(
423 uart,
424 tx.into(),
425 rx.into(),
426 true,
427 Some(tx_dma.into()),
428 Some(rx_dma.into()),
429 config,
430 )
226 } 431 }
227} 432}
228 433
@@ -231,12 +436,15 @@ impl<'d, M: Mode> Usart<'d, M> {
231 _usart: Peri<'d, T>, 436 _usart: Peri<'d, T>,
232 mut tx: Peri<'d, AnyPin>, 437 mut tx: Peri<'d, AnyPin>,
233 mut rx: Peri<'d, AnyPin>, 438 mut rx: Peri<'d, AnyPin>,
439 has_irq: bool,
440 tx_dma: Option<Peri<'d, AnyChannel>>,
441 rx_dma: Option<Peri<'d, AnyChannel>>,
234 config: Config, 442 config: Config,
235 ) -> Self { 443 ) -> Self {
236 Self::init::<T>(Some(tx.reborrow()), Some(rx.reborrow()), config); 444 Self::init::<T>(Some(tx.reborrow()), Some(rx.reborrow()), config);
237 Self { 445 Self {
238 tx: UsartTx::new_inner(T::info()), 446 tx: UsartTx::new_inner(T::info(), tx_dma),
239 rx: UsartRx::new_inner(T::info()), 447 rx: UsartRx::new_inner(T::info(), T::dma_state(), has_irq, rx_dma),
240 } 448 }
241 } 449 }
242 450
@@ -390,9 +598,11 @@ impl<'d, M: Mode> Usart<'d, M> {
390 SYSCON 598 SYSCON
391 .presetctrl1() 599 .presetctrl1()
392 .modify(|w| w.set_fc_rst(instance_number, syscon::vals::FcRst::RELEASED)); 600 .modify(|w| w.set_fc_rst(instance_number, syscon::vals::FcRst::RELEASED));
393 flexcomm_register 601 flexcomm_register.pselid().modify(|w| {
394 .pselid() 602 w.set_persel(flexcomm::vals::Persel::USART);
395 .modify(|w| w.set_persel(flexcomm::vals::Persel::USART)); 603 // This will lock the peripheral PERSEL and will not allow any changes until the board is reset.
604 w.set_lock(true);
605 });
396 } 606 }
397 607
398 fn configure_usart(info: &'static Info, config: &Config) { 608 fn configure_usart(info: &'static Info, config: &Config) {
@@ -471,6 +681,8 @@ impl<'d, M: Mode> Usart<'d, M> {
471 }); 681 });
472 registers.cfg().modify(|w| w.set_enable(true)); 682 registers.cfg().modify(|w| w.set_enable(true));
473 683
684 registers.fifointenset().modify(|w| w.set_rxerr(true));
685
474 // Drain RX FIFO in case it still has some unrelevant data 686 // Drain RX FIFO in case it still has some unrelevant data
475 while registers.fifostat().read().rxnotempty() { 687 while registers.fifostat().read().rxnotempty() {
476 let _ = registers.fiford().read().0; 688 let _ = registers.fiford().read().0;
@@ -513,6 +725,17 @@ impl<'d, M: Mode> Usart<'d, M> {
513 } 725 }
514} 726}
515 727
728impl<'d> Usart<'d, Async> {
729 /// Write to UART TX from the provided buffer.
730 pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> {
731 self.tx.write(buffer).await
732 }
733
734 /// Read from UART RX into the provided buffer.
735 pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
736 self.rx.read(buffer).await
737 }
738}
516impl<'d, M: Mode> embedded_hal_02::blocking::serial::Write<u8> for UsartTx<'d, M> { 739impl<'d, M: Mode> embedded_hal_02::blocking::serial::Write<u8> for UsartTx<'d, M> {
517 type Error = Error; 740 type Error = Error;
518 741
@@ -584,10 +807,12 @@ impl<'d> embedded_io::Read for Usart<'d, Blocking> {
584struct Info { 807struct Info {
585 usart_reg: UsartReg, 808 usart_reg: UsartReg,
586 fc_reg: FlexcommReg, 809 fc_reg: FlexcommReg,
810 interrupt: Interrupt,
587} 811}
588 812
589trait SealedInstance { 813trait SealedInstance {
590 fn info() -> &'static Info; 814 fn info() -> &'static Info;
815 fn dma_state() -> &'static DmaState;
591 fn instance_number() -> usize; 816 fn instance_number() -> usize;
592 fn tx_pin_func() -> PioFunc; 817 fn tx_pin_func() -> PioFunc;
593 fn rx_pin_func() -> PioFunc; 818 fn rx_pin_func() -> PioFunc;
@@ -595,7 +820,10 @@ trait SealedInstance {
595 820
596/// UART instance. 821/// UART instance.
597#[allow(private_bounds)] 822#[allow(private_bounds)]
598pub trait Instance: SealedInstance + PeripheralType {} 823pub trait Instance: SealedInstance + PeripheralType {
824 /// Interrupt for this instance.
825 type Interrupt: crate::interrupt::typelevel::Interrupt;
826}
599 827
600macro_rules! impl_instance { 828macro_rules! impl_instance {
601 ($inst:ident, $fc:ident, $tx_pin:ident, $rx_pin:ident, $fc_num:expr) => { 829 ($inst:ident, $fc:ident, $tx_pin:ident, $rx_pin:ident, $fc_num:expr) => {
@@ -604,9 +832,18 @@ macro_rules! impl_instance {
604 static INFO: Info = Info { 832 static INFO: Info = Info {
605 usart_reg: crate::pac::$inst, 833 usart_reg: crate::pac::$inst,
606 fc_reg: crate::pac::$fc, 834 fc_reg: crate::pac::$fc,
835 interrupt: crate::interrupt::typelevel::$fc::IRQ,
607 }; 836 };
608 &INFO 837 &INFO
609 } 838 }
839
840 fn dma_state() -> &'static DmaState {
841 static STATE: DmaState = DmaState {
842 rx_err_waker: AtomicWaker::new(),
843 rx_err: AtomicBool::new(false),
844 };
845 &STATE
846 }
610 #[inline] 847 #[inline]
611 fn instance_number() -> usize { 848 fn instance_number() -> usize {
612 $fc_num 849 $fc_num
@@ -620,7 +857,9 @@ macro_rules! impl_instance {
620 PioFunc::$rx_pin 857 PioFunc::$rx_pin
621 } 858 }
622 } 859 }
623 impl $crate::usart::Instance for $crate::peripherals::$inst {} 860 impl $crate::usart::Instance for $crate::peripherals::$inst {
861 type Interrupt = crate::interrupt::typelevel::$fc;
862 }
624 }; 863 };
625} 864}
626 865
@@ -663,3 +902,34 @@ impl_pin!(PIO1_16, USART6, Tx);
663impl_pin!(PIO1_13, USART6, Rx); 902impl_pin!(PIO1_13, USART6, Rx);
664impl_pin!(PIO0_19, USART7, Tx); 903impl_pin!(PIO0_19, USART7, Tx);
665impl_pin!(PIO0_20, USART7, Rx); 904impl_pin!(PIO0_20, USART7, Rx);
905
906/// Trait for TX DMA channels.
907pub trait TxChannel<T: Instance>: crate::dma::Channel {}
908/// Trait for RX DMA channels.
909pub trait RxChannel<T: Instance>: crate::dma::Channel {}
910
911macro_rules! impl_channel {
912 ($dma:ident, $instance:ident, Tx) => {
913 impl TxChannel<crate::peripherals::$instance> for crate::peripherals::$dma {}
914 };
915 ($dma:ident, $instance:ident, Rx) => {
916 impl RxChannel<crate::peripherals::$instance> for crate::peripherals::$dma {}
917 };
918}
919
920impl_channel!(DMA_CH4, USART0, Rx);
921impl_channel!(DMA_CH5, USART0, Tx);
922impl_channel!(DMA_CH6, USART1, Rx);
923impl_channel!(DMA_CH7, USART1, Tx);
924impl_channel!(DMA_CH10, USART2, Rx);
925impl_channel!(DMA_CH11, USART2, Tx);
926impl_channel!(DMA_CH8, USART3, Rx);
927impl_channel!(DMA_CH9, USART3, Tx);
928impl_channel!(DMA_CH12, USART4, Rx);
929impl_channel!(DMA_CH13, USART4, Tx);
930impl_channel!(DMA_CH14, USART5, Rx);
931impl_channel!(DMA_CH15, USART5, Tx);
932impl_channel!(DMA_CH16, USART6, Rx);
933impl_channel!(DMA_CH17, USART6, Tx);
934impl_channel!(DMA_CH18, USART7, Rx);
935impl_channel!(DMA_CH19, USART7, Tx);
diff --git a/examples/lpc55s69/src/bin/usart_async.rs b/examples/lpc55s69/src/bin/usart_async.rs
new file mode 100644
index 000000000..b06abd477
--- /dev/null
+++ b/examples/lpc55s69/src/bin/usart_async.rs
@@ -0,0 +1,70 @@
1#![no_std]
2#![no_main]
3
4use core::str::from_utf8_mut;
5
6use defmt::*;
7use embassy_executor::Spawner;
8use embassy_nxp::bind_interrupts;
9use embassy_nxp::gpio::{Level, Output};
10use embassy_nxp::peripherals::USART2;
11use embassy_nxp::usart::{Config, InterruptHandler, Usart};
12use embassy_time::Timer;
13use {defmt_rtt as _, panic_halt as _};
14
15bind_interrupts!(struct Irqs {
16 FLEXCOMM2 => InterruptHandler<USART2>;
17 }
18);
19
20#[embassy_executor::task]
21async fn blinky_task(mut led: Output<'static>) {
22 loop {
23 info!("[TASK] led off!");
24 led.set_high();
25 Timer::after_millis(500).await;
26
27 info!("[TASK] led on!");
28 led.set_low();
29 Timer::after_millis(500).await;
30 }
31}
32
33#[embassy_executor::main]
34async fn main(spawner: Spawner) {
35 let p = embassy_nxp::init(Default::default());
36 let mut usart = Usart::new(
37 p.USART2,
38 p.PIO0_27,
39 p.PIO1_24,
40 Irqs,
41 p.DMA_CH11,
42 p.DMA_CH10,
43 Config::default(),
44 );
45 let led = Output::new(p.PIO1_6, Level::Low);
46 spawner.spawn(blinky_task(led).unwrap());
47 info!("[MAIN] Entering main loop");
48 loop {
49 let tx_buf = b"Hello, Ferris!";
50 let mut rx_buf = [0u8; 14];
51 info!("[MAIN] Write a message");
52 usart.write(tx_buf).await.unwrap();
53 Timer::after_millis(500).await;
54
55 info!("[MAIN] Read a message");
56 match usart.read(&mut rx_buf).await {
57 Ok(_) => match from_utf8_mut(&mut rx_buf) {
58 Ok(str) => {
59 info!("[MAIN] The message is: {}", str);
60 }
61 Err(_) => {
62 error!("[MAIN] Error in converting to UTF8");
63 }
64 },
65 Err(e) => warn!("[MAIN] Error: {}", e),
66 }
67
68 Timer::after_millis(500).await;
69 }
70}