diff options
| author | Roi Bachynskyi <[email protected]> | 2025-09-15 00:38:23 +0300 |
|---|---|---|
| committer | Roi Bachynskyi <[email protected]> | 2025-09-25 12:20:10 +0300 |
| commit | f3d3b8899358ce8540bf5b2107a71b02ff941213 (patch) | |
| tree | cd3e8900071ec0896ed88e4cbeca5ed985cf3e8f | |
| parent | 56019ba197443e16b4f0b3a0fe3ff85985f6e45c (diff) | |
lpc55: dma and async usart
| -rw-r--r-- | embassy-nxp/CHANGELOG.md | 1 | ||||
| -rw-r--r-- | embassy-nxp/Cargo.toml | 1 | ||||
| -rw-r--r-- | embassy-nxp/src/chips/lpc55.rs | 30 | ||||
| -rw-r--r-- | embassy-nxp/src/dma.rs | 5 | ||||
| -rw-r--r-- | embassy-nxp/src/dma/lpc55.rs | 377 | ||||
| -rw-r--r-- | embassy-nxp/src/lib.rs | 72 | ||||
| -rw-r--r-- | embassy-nxp/src/usart/lpc55.rs | 310 | ||||
| -rw-r--r-- | examples/lpc55s69/src/bin/usart_async.rs | 70 |
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" | |||
| 29 | critical-section = "1.1.2" | 29 | critical-section = "1.1.2" |
| 30 | embassy-hal-internal = { version = "0.3.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-2"] } | 30 | embassy-hal-internal = { version = "0.3.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-2"] } |
| 31 | embassy-sync = { version = "0.7.2", path = "../embassy-sync" } | 31 | embassy-sync = { version = "0.7.2", path = "../embassy-sync" } |
| 32 | embassy-futures = { version = "0.1.2", path = "../embassy-futures"} | ||
| 32 | defmt = { version = "1", optional = true } | 33 | defmt = { version = "1", optional = true } |
| 33 | log = { version = "0.4.27", optional = true } | 34 | log = { version = "0.4.27", optional = true } |
| 34 | embassy-time = { version = "0.5.0", path = "../embassy-time", optional = true } | 35 | embassy-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 @@ | |||
| 1 | pub use nxp_pac as pac; | 1 | pub use nxp_pac as pac; |
| 2 | 2 | ||
| 3 | embassy_hal_internal::interrupt_mod!( | ||
| 4 | FLEXCOMM0, FLEXCOMM1, FLEXCOMM2, FLEXCOMM3, FLEXCOMM4, FLEXCOMM5, FLEXCOMM6, FLEXCOMM7 | ||
| 5 | ); | ||
| 6 | |||
| 3 | embassy_hal_internal::peripherals! { | 7 | embassy_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")] | ||
| 4 | mod inner; | ||
| 5 | pub 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 @@ | |||
| 1 | use core::cell::RefCell; | ||
| 2 | use core::future::Future; | ||
| 3 | use core::pin::Pin; | ||
| 4 | use core::sync::atomic::{compiler_fence, Ordering}; | ||
| 5 | use core::task::{Context, Poll}; | ||
| 6 | |||
| 7 | use critical_section::Mutex; | ||
| 8 | use embassy_hal_internal::interrupt::InterruptExt; | ||
| 9 | use embassy_hal_internal::{impl_peripheral, PeripheralType}; | ||
| 10 | use embassy_sync::waitqueue::AtomicWaker; | ||
| 11 | |||
| 12 | use crate::pac::{DMA0, SYSCON, *}; | ||
| 13 | use crate::{peripherals, Peri}; | ||
| 14 | |||
| 15 | #[interrupt] | ||
| 16 | fn 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 | |||
| 30 | pub(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. | ||
| 69 | pub 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. | ||
| 84 | pub 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. | ||
| 99 | pub 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 | |||
| 114 | fn 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"] | ||
| 206 | pub struct Transfer<'a, C: Channel> { | ||
| 207 | channel: Peri<'a, C>, | ||
| 208 | } | ||
| 209 | |||
| 210 | impl<'a, C: Channel> Transfer<'a, C> { | ||
| 211 | pub(crate) fn new(channel: Peri<'a, C>) -> Self { | ||
| 212 | Self { channel } | ||
| 213 | } | ||
| 214 | } | ||
| 215 | |||
| 216 | impl<'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 | |||
| 224 | impl<'a, C: Channel> Unpin for Transfer<'a, C> {} | ||
| 225 | impl<'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. | ||
| 243 | pub(crate) const CHANNEL_COUNT: usize = 32; | ||
| 244 | |||
| 245 | static 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)] | ||
| 252 | struct 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))] | ||
| 267 | struct DmaDescriptorTable { | ||
| 268 | descriptors: [DmaDescriptor; CHANNEL_COUNT], | ||
| 269 | } | ||
| 270 | |||
| 271 | // DMA descriptors are stored in on-chip SRAM. | ||
| 272 | static 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 | |||
| 281 | trait SealedChannel {} | ||
| 282 | trait SealedWord {} | ||
| 283 | |||
| 284 | /// DMA channel interface. | ||
| 285 | #[allow(private_bounds)] | ||
| 286 | pub 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)] | ||
| 298 | pub trait Word: SealedWord { | ||
| 299 | /// Word size. | ||
| 300 | fn size() -> crate::pac::dma::vals::Width; | ||
| 301 | } | ||
| 302 | |||
| 303 | impl SealedWord for u8 {} | ||
| 304 | impl Word for u8 { | ||
| 305 | fn size() -> crate::pac::dma::vals::Width { | ||
| 306 | crate::pac::dma::vals::Width::BIT_8 | ||
| 307 | } | ||
| 308 | } | ||
| 309 | |||
| 310 | impl SealedWord for u16 {} | ||
| 311 | impl Word for u16 { | ||
| 312 | fn size() -> crate::pac::dma::vals::Width { | ||
| 313 | crate::pac::dma::vals::Width::BIT_16 | ||
| 314 | } | ||
| 315 | } | ||
| 316 | |||
| 317 | impl SealedWord for u32 {} | ||
| 318 | impl 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. | ||
| 325 | pub struct AnyChannel { | ||
| 326 | number: u8, | ||
| 327 | } | ||
| 328 | |||
| 329 | impl_peripheral!(AnyChannel); | ||
| 330 | |||
| 331 | impl SealedChannel for AnyChannel {} | ||
| 332 | impl Channel for AnyChannel { | ||
| 333 | fn number(&self) -> u8 { | ||
| 334 | self.number | ||
| 335 | } | ||
| 336 | } | ||
| 337 | |||
| 338 | macro_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 | |||
| 355 | channel!(DMA_CH0, 0); | ||
| 356 | channel!(DMA_CH1, 1); | ||
| 357 | channel!(DMA_CH2, 2); | ||
| 358 | channel!(DMA_CH3, 3); | ||
| 359 | channel!(DMA_CH4, 4); | ||
| 360 | channel!(DMA_CH5, 5); | ||
| 361 | channel!(DMA_CH6, 6); | ||
| 362 | channel!(DMA_CH7, 7); | ||
| 363 | channel!(DMA_CH8, 8); | ||
| 364 | channel!(DMA_CH9, 9); | ||
| 365 | channel!(DMA_CH10, 10); | ||
| 366 | channel!(DMA_CH11, 11); | ||
| 367 | channel!(DMA_CH12, 12); | ||
| 368 | channel!(DMA_CH13, 13); | ||
| 369 | channel!(DMA_CH14, 14); | ||
| 370 | channel!(DMA_CH15, 15); | ||
| 371 | channel!(DMA_CH16, 16); | ||
| 372 | channel!(DMA_CH17, 17); | ||
| 373 | channel!(DMA_CH18, 18); | ||
| 374 | channel!(DMA_CH19, 19); | ||
| 375 | channel!(DMA_CH20, 20); | ||
| 376 | channel!(DMA_CH21, 21); | ||
| 377 | channel!(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. |
| 4 | pub(crate) mod fmt; | 4 | pub(crate) mod fmt; |
| 5 | 5 | ||
| 6 | #[cfg(feature = "lpc55-core0")] | ||
| 7 | pub mod dma; | ||
| 6 | pub mod gpio; | 8 | pub mod gpio; |
| 7 | #[cfg(feature = "lpc55-core0")] | 9 | #[cfg(feature = "lpc55-core0")] |
| 8 | pub mod pint; | 10 | pub 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")] |
| 21 | mod chip; | 23 | mod chip; |
| 22 | 24 | ||
| 25 | // TODO: Remove when this module is implemented for other chips | ||
| 26 | #[cfg(feature = "lpc55-core0")] | ||
| 27 | pub use chip::interrupt; | ||
| 23 | #[cfg(feature = "unstable-pac")] | 28 | #[cfg(feature = "unstable-pac")] |
| 24 | pub use chip::pac; | 29 | pub use chip::pac; |
| 25 | #[cfg(not(feature = "unstable-pac"))] | 30 | #[cfg(not(feature = "unstable-pac"))] |
| @@ -27,6 +32,67 @@ pub(crate) use chip::pac; | |||
| 27 | pub use chip::{peripherals, Peripherals}; | 32 | pub use chip::{peripherals, Peripherals}; |
| 28 | pub use embassy_hal_internal::{Peri, PeripheralType}; | 33 | pub 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] | ||
| 54 | macro_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. |
| 135 | pub struct Blocking; | 204 | pub struct Blocking; |
| 205 | /// Asynchronous mode. | ||
| 206 | pub struct Async; | ||
| 136 | 207 | ||
| 137 | impl_mode!(Blocking); | 208 | impl_mode!(Blocking); |
| 209 | impl_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 @@ | |||
| 1 | use core::fmt::Debug; | ||
| 2 | use core::future::poll_fn; | ||
| 1 | use core::marker::PhantomData; | 3 | use core::marker::PhantomData; |
| 4 | use core::sync::atomic::{AtomicBool, Ordering}; | ||
| 5 | use core::task::Poll; | ||
| 2 | 6 | ||
| 7 | use embassy_futures::select::{select, Either}; | ||
| 8 | use embassy_hal_internal::interrupt::InterruptExt; | ||
| 3 | use embassy_hal_internal::{Peri, PeripheralType}; | 9 | use embassy_hal_internal::{Peri, PeripheralType}; |
| 10 | use embassy_sync::waitqueue::AtomicWaker; | ||
| 4 | use embedded_io::{self, ErrorKind}; | 11 | use embedded_io::{self, ErrorKind}; |
| 5 | 12 | ||
| 13 | use crate::dma::{AnyChannel, Channel}; | ||
| 6 | use crate::gpio::{match_iocon, AnyPin, Bank, SealedPin}; | 14 | use crate::gpio::{match_iocon, AnyPin, Bank, SealedPin}; |
| 15 | use crate::interrupt::typelevel::{Binding, Interrupt as _}; | ||
| 16 | use crate::interrupt::Interrupt; | ||
| 7 | use crate::pac::flexcomm::Flexcomm as FlexcommReg; | 17 | use crate::pac::flexcomm::Flexcomm as FlexcommReg; |
| 8 | use crate::pac::iocon::vals::PioFunc; | 18 | use crate::pac::iocon::vals::PioFunc; |
| 9 | use crate::pac::usart::Usart as UsartReg; | 19 | use crate::pac::usart::Usart as UsartReg; |
| 10 | use crate::pac::*; | 20 | use crate::pac::*; |
| 11 | use crate::{Blocking, Mode}; | 21 | use 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. | ||
| 115 | pub 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 | ||
| 113 | pub struct UsartTx<'d, M: Mode> { | 129 | pub 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 | ||
| 118 | pub struct UsartRx<'d, M: Mode> { | 135 | pub 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 | ||
| 123 | impl<'d, M: Mode> UsartTx<'d, M> { | 142 | impl<'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> { | |||
| 155 | impl<'d> UsartTx<'d, Blocking> { | 180 | impl<'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 | ||
| 187 | impl<'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 | } | ||
| 162 | impl<'d, M: Mode> UsartRx<'d, M> { | 203 | impl<'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> { | |||
| 211 | impl<'d> UsartRx<'d, Blocking> { | 281 | impl<'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. | ||
| 289 | pub struct InterruptHandler<T: Instance> { | ||
| 290 | _uart: PhantomData<T>, | ||
| 291 | } | ||
| 292 | |||
| 293 | impl<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 | |||
| 314 | impl<'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 | |||
| 412 | impl<'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 | ||
| 728 | impl<'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 | } | ||
| 516 | impl<'d, M: Mode> embedded_hal_02::blocking::serial::Write<u8> for UsartTx<'d, M> { | 739 | impl<'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> { | |||
| 584 | struct Info { | 807 | struct Info { |
| 585 | usart_reg: UsartReg, | 808 | usart_reg: UsartReg, |
| 586 | fc_reg: FlexcommReg, | 809 | fc_reg: FlexcommReg, |
| 810 | interrupt: Interrupt, | ||
| 587 | } | 811 | } |
| 588 | 812 | ||
| 589 | trait SealedInstance { | 813 | trait 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)] |
| 598 | pub trait Instance: SealedInstance + PeripheralType {} | 823 | pub trait Instance: SealedInstance + PeripheralType { |
| 824 | /// Interrupt for this instance. | ||
| 825 | type Interrupt: crate::interrupt::typelevel::Interrupt; | ||
| 826 | } | ||
| 599 | 827 | ||
| 600 | macro_rules! impl_instance { | 828 | macro_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); | |||
| 663 | impl_pin!(PIO1_13, USART6, Rx); | 902 | impl_pin!(PIO1_13, USART6, Rx); |
| 664 | impl_pin!(PIO0_19, USART7, Tx); | 903 | impl_pin!(PIO0_19, USART7, Tx); |
| 665 | impl_pin!(PIO0_20, USART7, Rx); | 904 | impl_pin!(PIO0_20, USART7, Rx); |
| 905 | |||
| 906 | /// Trait for TX DMA channels. | ||
| 907 | pub trait TxChannel<T: Instance>: crate::dma::Channel {} | ||
| 908 | /// Trait for RX DMA channels. | ||
| 909 | pub trait RxChannel<T: Instance>: crate::dma::Channel {} | ||
| 910 | |||
| 911 | macro_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 | |||
| 920 | impl_channel!(DMA_CH4, USART0, Rx); | ||
| 921 | impl_channel!(DMA_CH5, USART0, Tx); | ||
| 922 | impl_channel!(DMA_CH6, USART1, Rx); | ||
| 923 | impl_channel!(DMA_CH7, USART1, Tx); | ||
| 924 | impl_channel!(DMA_CH10, USART2, Rx); | ||
| 925 | impl_channel!(DMA_CH11, USART2, Tx); | ||
| 926 | impl_channel!(DMA_CH8, USART3, Rx); | ||
| 927 | impl_channel!(DMA_CH9, USART3, Tx); | ||
| 928 | impl_channel!(DMA_CH12, USART4, Rx); | ||
| 929 | impl_channel!(DMA_CH13, USART4, Tx); | ||
| 930 | impl_channel!(DMA_CH14, USART5, Rx); | ||
| 931 | impl_channel!(DMA_CH15, USART5, Tx); | ||
| 932 | impl_channel!(DMA_CH16, USART6, Rx); | ||
| 933 | impl_channel!(DMA_CH17, USART6, Tx); | ||
| 934 | impl_channel!(DMA_CH18, USART7, Rx); | ||
| 935 | impl_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 | |||
| 4 | use core::str::from_utf8_mut; | ||
| 5 | |||
| 6 | use defmt::*; | ||
| 7 | use embassy_executor::Spawner; | ||
| 8 | use embassy_nxp::bind_interrupts; | ||
| 9 | use embassy_nxp::gpio::{Level, Output}; | ||
| 10 | use embassy_nxp::peripherals::USART2; | ||
| 11 | use embassy_nxp::usart::{Config, InterruptHandler, Usart}; | ||
| 12 | use embassy_time::Timer; | ||
| 13 | use {defmt_rtt as _, panic_halt as _}; | ||
| 14 | |||
| 15 | bind_interrupts!(struct Irqs { | ||
| 16 | FLEXCOMM2 => InterruptHandler<USART2>; | ||
| 17 | } | ||
| 18 | ); | ||
| 19 | |||
| 20 | #[embassy_executor::task] | ||
| 21 | async 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] | ||
| 34 | async 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 | } | ||
