mirror of
https://github.com/embassy-rs/embassy.git
synced 2025-09-27 04:10:25 +00:00
lpc55: dma and async usart
This commit is contained in:
parent
56019ba197
commit
f3d3b88993
@ -7,5 +7,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
<!-- next-header -->
|
<!-- next-header -->
|
||||||
## Unreleased - ReleaseDate
|
## Unreleased - ReleaseDate
|
||||||
|
- LPC55: DMA Controller and asynchronous version of USART
|
||||||
- Moved NXP LPC55S69 from `lpc55-pac` to `nxp-pac`
|
- Moved NXP LPC55S69 from `lpc55-pac` to `nxp-pac`
|
||||||
- First release with changelog.
|
- First release with changelog.
|
||||||
|
@ -29,6 +29,7 @@ cortex-m-rt = "0.7.0"
|
|||||||
critical-section = "1.1.2"
|
critical-section = "1.1.2"
|
||||||
embassy-hal-internal = { version = "0.3.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-2"] }
|
embassy-hal-internal = { version = "0.3.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-2"] }
|
||||||
embassy-sync = { version = "0.7.2", path = "../embassy-sync" }
|
embassy-sync = { version = "0.7.2", path = "../embassy-sync" }
|
||||||
|
embassy-futures = { version = "0.1.2", path = "../embassy-futures"}
|
||||||
defmt = { version = "1", optional = true }
|
defmt = { version = "1", optional = true }
|
||||||
log = { version = "0.4.27", optional = true }
|
log = { version = "0.4.27", optional = true }
|
||||||
embassy-time = { version = "0.5.0", path = "../embassy-time", optional = true }
|
embassy-time = { version = "0.5.0", path = "../embassy-time", optional = true }
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
pub use nxp_pac as pac;
|
pub use nxp_pac as pac;
|
||||||
|
|
||||||
|
embassy_hal_internal::interrupt_mod!(
|
||||||
|
FLEXCOMM0, FLEXCOMM1, FLEXCOMM2, FLEXCOMM3, FLEXCOMM4, FLEXCOMM5, FLEXCOMM6, FLEXCOMM7
|
||||||
|
);
|
||||||
|
|
||||||
embassy_hal_internal::peripherals! {
|
embassy_hal_internal::peripherals! {
|
||||||
// External pins. These are not only GPIOs, they are multi-purpose pins and can be used by other
|
// External pins. These are not only GPIOs, they are multi-purpose pins and can be used by other
|
||||||
// peripheral types (e.g. I2C).
|
// peripheral types (e.g. I2C).
|
||||||
@ -68,6 +72,32 @@ embassy_hal_internal::peripherals! {
|
|||||||
PIO1_30,
|
PIO1_30,
|
||||||
PIO1_31,
|
PIO1_31,
|
||||||
|
|
||||||
|
// Direct Memory Access (DMA) channels. They are used for asynchronous modes of peripherals.
|
||||||
|
DMA_CH0,
|
||||||
|
DMA_CH1,
|
||||||
|
DMA_CH2,
|
||||||
|
DMA_CH3,
|
||||||
|
DMA_CH4,
|
||||||
|
DMA_CH5,
|
||||||
|
DMA_CH6,
|
||||||
|
DMA_CH7,
|
||||||
|
DMA_CH8,
|
||||||
|
DMA_CH9,
|
||||||
|
DMA_CH10,
|
||||||
|
DMA_CH11,
|
||||||
|
DMA_CH12,
|
||||||
|
DMA_CH13,
|
||||||
|
DMA_CH14,
|
||||||
|
DMA_CH15,
|
||||||
|
DMA_CH16,
|
||||||
|
DMA_CH17,
|
||||||
|
DMA_CH18,
|
||||||
|
DMA_CH19,
|
||||||
|
DMA_CH20,
|
||||||
|
DMA_CH21,
|
||||||
|
DMA_CH22,
|
||||||
|
|
||||||
|
// Universal Synchronous/Asynchronous Receiver/Transmitter (USART) instances.
|
||||||
USART0,
|
USART0,
|
||||||
USART1,
|
USART1,
|
||||||
USART2,
|
USART2,
|
||||||
|
5
embassy-nxp/src/dma.rs
Normal file
5
embassy-nxp/src/dma.rs
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
//! Direct Memory Access (DMA) driver.
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "lpc55-core0", path = "./dma/lpc55.rs")]
|
||||||
|
mod inner;
|
||||||
|
pub use inner::*;
|
377
embassy-nxp/src/dma/lpc55.rs
Normal file
377
embassy-nxp/src/dma/lpc55.rs
Normal file
@ -0,0 +1,377 @@
|
|||||||
|
use core::cell::RefCell;
|
||||||
|
use core::future::Future;
|
||||||
|
use core::pin::Pin;
|
||||||
|
use core::sync::atomic::{compiler_fence, Ordering};
|
||||||
|
use core::task::{Context, Poll};
|
||||||
|
|
||||||
|
use critical_section::Mutex;
|
||||||
|
use embassy_hal_internal::interrupt::InterruptExt;
|
||||||
|
use embassy_hal_internal::{impl_peripheral, PeripheralType};
|
||||||
|
use embassy_sync::waitqueue::AtomicWaker;
|
||||||
|
|
||||||
|
use crate::pac::{DMA0, SYSCON, *};
|
||||||
|
use crate::{peripherals, Peri};
|
||||||
|
|
||||||
|
#[interrupt]
|
||||||
|
fn DMA0() {
|
||||||
|
let inta = DMA0.inta0().read().ia();
|
||||||
|
for channel in 0..CHANNEL_COUNT {
|
||||||
|
if (DMA0.errint0().read().err() & (1 << channel)) != 0 {
|
||||||
|
panic!("DMA: error on DMA_0 channel {}", channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inta & (1 << channel)) != 0 {
|
||||||
|
CHANNEL_WAKERS[channel].wake();
|
||||||
|
DMA0.inta0().modify(|w| w.set_ia(1 << channel));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn init() {
|
||||||
|
assert_eq!(core::mem::size_of::<DmaDescriptor>(), 16, "Descriptor must be 16 bytes");
|
||||||
|
assert_eq!(
|
||||||
|
core::mem::align_of::<DmaDescriptor>(),
|
||||||
|
16,
|
||||||
|
"Descriptor must be 16-byte aligned"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
core::mem::align_of::<DmaDescriptorTable>(),
|
||||||
|
512,
|
||||||
|
"Table must be 512-byte aligned"
|
||||||
|
);
|
||||||
|
// Start clock for DMA
|
||||||
|
SYSCON.ahbclkctrl0().modify(|w| w.set_dma0(true));
|
||||||
|
// Reset DMA
|
||||||
|
SYSCON
|
||||||
|
.presetctrl0()
|
||||||
|
.modify(|w| w.set_dma0_rst(syscon::vals::Dma0Rst::ASSERTED));
|
||||||
|
SYSCON
|
||||||
|
.presetctrl0()
|
||||||
|
.modify(|w| w.set_dma0_rst(syscon::vals::Dma0Rst::RELEASED));
|
||||||
|
|
||||||
|
// Address bits 31:9 of the beginning of the DMA descriptor table
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
DMA0.srambase()
|
||||||
|
.write(|w| w.set_offset((DMA_DESCRIPTORS.borrow(cs).as_ptr() as u32) >> 9));
|
||||||
|
});
|
||||||
|
// Enable DMA controller
|
||||||
|
DMA0.ctrl().modify(|w| w.set_enable(true));
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
crate::pac::interrupt::DMA0.enable();
|
||||||
|
}
|
||||||
|
info!("DMA initialized");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// DMA read.
|
||||||
|
///
|
||||||
|
/// SAFETY: Slice must point to a valid location reachable by DMA.
|
||||||
|
pub unsafe fn read<'a, C: Channel, W: Word>(ch: Peri<'a, C>, from: *const W, to: *mut [W]) -> Transfer<'a, C> {
|
||||||
|
copy_inner(
|
||||||
|
ch,
|
||||||
|
from as *const u32,
|
||||||
|
to as *mut W as *mut u32,
|
||||||
|
to.len(),
|
||||||
|
W::size(),
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// DMA write.
|
||||||
|
///
|
||||||
|
/// SAFETY: Slice must point to a valid location reachable by DMA.
|
||||||
|
pub unsafe fn write<'a, C: Channel, W: Word>(ch: Peri<'a, C>, from: *const [W], to: *mut W) -> Transfer<'a, C> {
|
||||||
|
copy_inner(
|
||||||
|
ch,
|
||||||
|
from as *const W as *const u32,
|
||||||
|
to as *mut u32,
|
||||||
|
from.len(),
|
||||||
|
W::size(),
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// DMA copy between slices.
|
||||||
|
///
|
||||||
|
/// SAFETY: Slices must point to locations reachable by DMA.
|
||||||
|
pub unsafe fn copy<'a, C: Channel, W: Word>(ch: Peri<'a, C>, from: &[W], to: &mut [W]) -> Transfer<'a, C> {
|
||||||
|
let from_len = from.len();
|
||||||
|
let to_len = to.len();
|
||||||
|
assert_eq!(from_len, to_len);
|
||||||
|
copy_inner(
|
||||||
|
ch,
|
||||||
|
from.as_ptr() as *const u32,
|
||||||
|
to.as_mut_ptr() as *mut u32,
|
||||||
|
from_len,
|
||||||
|
W::size(),
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn copy_inner<'a, C: Channel>(
|
||||||
|
ch: Peri<'a, C>,
|
||||||
|
from: *const u32,
|
||||||
|
to: *mut u32,
|
||||||
|
len: usize,
|
||||||
|
data_size: crate::pac::dma::vals::Width,
|
||||||
|
incr_src: bool,
|
||||||
|
incr_dest: bool,
|
||||||
|
) -> Transfer<'a, C> {
|
||||||
|
let p = ch.regs();
|
||||||
|
|
||||||
|
// Buffer ending address = buffer starting address + (XFERCOUNT * the transfer increment)
|
||||||
|
// XREFCOUNT = the number of transfers performed - 1.
|
||||||
|
// The 1st transfer is included in the starting address.
|
||||||
|
let source_end_addr = if incr_src {
|
||||||
|
from as u32 + len as u32 - 1
|
||||||
|
} else {
|
||||||
|
from as u32
|
||||||
|
};
|
||||||
|
let dest_end_addr = if incr_dest {
|
||||||
|
to as u32 + len as u32 - 1
|
||||||
|
} else {
|
||||||
|
to as u32
|
||||||
|
};
|
||||||
|
|
||||||
|
compiler_fence(Ordering::SeqCst);
|
||||||
|
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
DMA_DESCRIPTORS.borrow(cs).borrow_mut().descriptors[ch.number() as usize] = DmaDescriptor {
|
||||||
|
reserved: 0,
|
||||||
|
source_end_addr,
|
||||||
|
dest_end_addr,
|
||||||
|
next_desc: 0, // Since only single transfers are made, there is no need for reload descriptor address.
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
compiler_fence(Ordering::SeqCst);
|
||||||
|
|
||||||
|
p.cfg().modify(|w| {
|
||||||
|
// Peripheral DMA requests are enabled.
|
||||||
|
// DMA requests that pace transfers can be interpreted then.
|
||||||
|
w.set_periphreqen(true);
|
||||||
|
// There is no need to have them on.
|
||||||
|
// No complex transfers are performed for now.
|
||||||
|
w.set_hwtrigen(false);
|
||||||
|
w.set_chpriority(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
p.xfercfg().modify(|w| {
|
||||||
|
// This bit indicates whether the current channel descriptor is
|
||||||
|
// valid and can potentially be acted upon,
|
||||||
|
// if all other activation criteria are fulfilled.
|
||||||
|
w.set_cfgvalid(true);
|
||||||
|
// Indicates whether the channel’s control structure will be reloaded
|
||||||
|
// when the current descriptor is exhausted.
|
||||||
|
// Reloading allows ping-pong and linked transfers.
|
||||||
|
w.set_reload(false);
|
||||||
|
// There is no hardware distinction between interrupt A and B.
|
||||||
|
// They can be used by software to assist with more complex descriptor usage.
|
||||||
|
// By convention, interrupt A may be used when only one interrupt flag is needed.
|
||||||
|
w.set_setinta(true);
|
||||||
|
w.set_setintb(false);
|
||||||
|
w.set_width(data_size);
|
||||||
|
w.set_srcinc(if incr_src {
|
||||||
|
dma::vals::Srcinc::WIDTH_X_1
|
||||||
|
} else {
|
||||||
|
dma::vals::Srcinc::NO_INCREMENT
|
||||||
|
});
|
||||||
|
w.set_dstinc(if incr_dest {
|
||||||
|
dma::vals::Dstinc::WIDTH_X_1
|
||||||
|
} else {
|
||||||
|
dma::vals::Dstinc::NO_INCREMENT
|
||||||
|
});
|
||||||
|
// Total number of transfers to be performed, minus 1 encoded.
|
||||||
|
w.set_xfercount((len as u16) - 1);
|
||||||
|
// Before triggering the channel, it has to be enabled.
|
||||||
|
w.set_swtrig(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
compiler_fence(Ordering::SeqCst);
|
||||||
|
DMA0.enableset0().write(|w| w.set_ena(1 << ch.number()));
|
||||||
|
DMA0.intenset0().write(|w| w.set_inten(1 << ch.number()));
|
||||||
|
|
||||||
|
compiler_fence(Ordering::SeqCst);
|
||||||
|
// Start transfer.
|
||||||
|
DMA0.settrig0().write(|w| w.set_trig(1 << ch.number()));
|
||||||
|
compiler_fence(Ordering::SeqCst);
|
||||||
|
Transfer::new(ch)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// DMA transfer driver.
|
||||||
|
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
||||||
|
pub struct Transfer<'a, C: Channel> {
|
||||||
|
channel: Peri<'a, C>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, C: Channel> Transfer<'a, C> {
|
||||||
|
pub(crate) fn new(channel: Peri<'a, C>) -> Self {
|
||||||
|
Self { channel }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, C: Channel> Drop for Transfer<'a, C> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
DMA0.enableclr0().write(|w| w.set_clr(1 << self.channel.number()));
|
||||||
|
while (DMA0.busy0().read().bsy() & (1 << self.channel.number())) != 0 {}
|
||||||
|
DMA0.abort0().write(|w| w.set_abortctrl(1 << self.channel.number()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, C: Channel> Unpin for Transfer<'a, C> {}
|
||||||
|
impl<'a, C: Channel> Future for Transfer<'a, C> {
|
||||||
|
type Output = ();
|
||||||
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
|
// We need to register/re-register the waker for each poll because any
|
||||||
|
// calls to wake will deregister the waker.
|
||||||
|
CHANNEL_WAKERS[self.channel.number() as usize].register(cx.waker());
|
||||||
|
// Check if it is busy or not.
|
||||||
|
if (DMA0.busy0().read().bsy() & (1 << self.channel.number())) != 0 {
|
||||||
|
Poll::Pending
|
||||||
|
} else {
|
||||||
|
Poll::Ready(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Total number of channles including both DMA0 and DMA1.
|
||||||
|
// In spite of using only DMA0 channels, the descriptor table
|
||||||
|
// should be of this size.
|
||||||
|
pub(crate) const CHANNEL_COUNT: usize = 32;
|
||||||
|
|
||||||
|
static CHANNEL_WAKERS: [AtomicWaker; CHANNEL_COUNT] = [const { AtomicWaker::new() }; CHANNEL_COUNT];
|
||||||
|
|
||||||
|
// See section 22.5.2 (table 450)
|
||||||
|
// UM11126, Rev. 2.8
|
||||||
|
// The size of a descriptor must be aligned to a multiple of 16 bytes.
|
||||||
|
#[repr(C, align(16))]
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
struct DmaDescriptor {
|
||||||
|
/// 0x0 Reserved.
|
||||||
|
reserved: u32,
|
||||||
|
/// 0x4 Source data end address.
|
||||||
|
source_end_addr: u32,
|
||||||
|
/// 0x8 Destination end address.
|
||||||
|
dest_end_addr: u32,
|
||||||
|
/// 0xC Link to next descriptor.
|
||||||
|
next_desc: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
// See section 22.6.3
|
||||||
|
// UM11126, Rev. 2.8
|
||||||
|
// The table must begin on a 512 byte boundary.
|
||||||
|
#[repr(C, align(512))]
|
||||||
|
struct DmaDescriptorTable {
|
||||||
|
descriptors: [DmaDescriptor; CHANNEL_COUNT],
|
||||||
|
}
|
||||||
|
|
||||||
|
// DMA descriptors are stored in on-chip SRAM.
|
||||||
|
static DMA_DESCRIPTORS: Mutex<RefCell<DmaDescriptorTable>> = Mutex::new(RefCell::new(DmaDescriptorTable {
|
||||||
|
descriptors: [DmaDescriptor {
|
||||||
|
reserved: 0,
|
||||||
|
source_end_addr: 0,
|
||||||
|
dest_end_addr: 0,
|
||||||
|
next_desc: 0,
|
||||||
|
}; CHANNEL_COUNT],
|
||||||
|
}));
|
||||||
|
|
||||||
|
trait SealedChannel {}
|
||||||
|
trait SealedWord {}
|
||||||
|
|
||||||
|
/// DMA channel interface.
|
||||||
|
#[allow(private_bounds)]
|
||||||
|
pub trait Channel: PeripheralType + SealedChannel + Into<AnyChannel> + Sized + 'static {
|
||||||
|
/// Channel number.
|
||||||
|
fn number(&self) -> u8;
|
||||||
|
|
||||||
|
/// Channel registry block.
|
||||||
|
fn regs(&self) -> crate::pac::dma::Channel {
|
||||||
|
crate::pac::DMA0.channel(self.number() as _)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// DMA word.
|
||||||
|
#[allow(private_bounds)]
|
||||||
|
pub trait Word: SealedWord {
|
||||||
|
/// Word size.
|
||||||
|
fn size() -> crate::pac::dma::vals::Width;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SealedWord for u8 {}
|
||||||
|
impl Word for u8 {
|
||||||
|
fn size() -> crate::pac::dma::vals::Width {
|
||||||
|
crate::pac::dma::vals::Width::BIT_8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SealedWord for u16 {}
|
||||||
|
impl Word for u16 {
|
||||||
|
fn size() -> crate::pac::dma::vals::Width {
|
||||||
|
crate::pac::dma::vals::Width::BIT_16
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SealedWord for u32 {}
|
||||||
|
impl Word for u32 {
|
||||||
|
fn size() -> crate::pac::dma::vals::Width {
|
||||||
|
crate::pac::dma::vals::Width::BIT_32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Type erased DMA channel.
|
||||||
|
pub struct AnyChannel {
|
||||||
|
number: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_peripheral!(AnyChannel);
|
||||||
|
|
||||||
|
impl SealedChannel for AnyChannel {}
|
||||||
|
impl Channel for AnyChannel {
|
||||||
|
fn number(&self) -> u8 {
|
||||||
|
self.number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! channel {
|
||||||
|
($name:ident, $num:expr) => {
|
||||||
|
impl SealedChannel for peripherals::$name {}
|
||||||
|
impl Channel for peripherals::$name {
|
||||||
|
fn number(&self) -> u8 {
|
||||||
|
$num
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<peripherals::$name> for crate::dma::AnyChannel {
|
||||||
|
fn from(val: peripherals::$name) -> Self {
|
||||||
|
Self { number: val.number() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
channel!(DMA_CH0, 0);
|
||||||
|
channel!(DMA_CH1, 1);
|
||||||
|
channel!(DMA_CH2, 2);
|
||||||
|
channel!(DMA_CH3, 3);
|
||||||
|
channel!(DMA_CH4, 4);
|
||||||
|
channel!(DMA_CH5, 5);
|
||||||
|
channel!(DMA_CH6, 6);
|
||||||
|
channel!(DMA_CH7, 7);
|
||||||
|
channel!(DMA_CH8, 8);
|
||||||
|
channel!(DMA_CH9, 9);
|
||||||
|
channel!(DMA_CH10, 10);
|
||||||
|
channel!(DMA_CH11, 11);
|
||||||
|
channel!(DMA_CH12, 12);
|
||||||
|
channel!(DMA_CH13, 13);
|
||||||
|
channel!(DMA_CH14, 14);
|
||||||
|
channel!(DMA_CH15, 15);
|
||||||
|
channel!(DMA_CH16, 16);
|
||||||
|
channel!(DMA_CH17, 17);
|
||||||
|
channel!(DMA_CH18, 18);
|
||||||
|
channel!(DMA_CH19, 19);
|
||||||
|
channel!(DMA_CH20, 20);
|
||||||
|
channel!(DMA_CH21, 21);
|
||||||
|
channel!(DMA_CH22, 22);
|
@ -3,6 +3,8 @@
|
|||||||
// This mod MUST go first, so that the others see its macros.
|
// This mod MUST go first, so that the others see its macros.
|
||||||
pub(crate) mod fmt;
|
pub(crate) mod fmt;
|
||||||
|
|
||||||
|
#[cfg(feature = "lpc55-core0")]
|
||||||
|
pub mod dma;
|
||||||
pub mod gpio;
|
pub mod gpio;
|
||||||
#[cfg(feature = "lpc55-core0")]
|
#[cfg(feature = "lpc55-core0")]
|
||||||
pub mod pint;
|
pub mod pint;
|
||||||
@ -20,6 +22,9 @@ mod time_driver;
|
|||||||
#[cfg_attr(feature = "mimxrt1062", path = "chips/mimxrt1062.rs")]
|
#[cfg_attr(feature = "mimxrt1062", path = "chips/mimxrt1062.rs")]
|
||||||
mod chip;
|
mod chip;
|
||||||
|
|
||||||
|
// TODO: Remove when this module is implemented for other chips
|
||||||
|
#[cfg(feature = "lpc55-core0")]
|
||||||
|
pub use chip::interrupt;
|
||||||
#[cfg(feature = "unstable-pac")]
|
#[cfg(feature = "unstable-pac")]
|
||||||
pub use chip::pac;
|
pub use chip::pac;
|
||||||
#[cfg(not(feature = "unstable-pac"))]
|
#[cfg(not(feature = "unstable-pac"))]
|
||||||
@ -27,6 +32,67 @@ pub(crate) use chip::pac;
|
|||||||
pub use chip::{peripherals, Peripherals};
|
pub use chip::{peripherals, Peripherals};
|
||||||
pub use embassy_hal_internal::{Peri, PeripheralType};
|
pub use embassy_hal_internal::{Peri, PeripheralType};
|
||||||
|
|
||||||
|
/// Macro to bind interrupts to handlers.
|
||||||
|
/// (Copied from `embassy-rp`)
|
||||||
|
/// This defines the right interrupt handlers, and creates a unit struct (like `struct Irqs;`)
|
||||||
|
/// and implements the right [`Binding`]s for it. You can pass this struct to drivers to
|
||||||
|
/// prove at compile-time that the right interrupts have been bound.
|
||||||
|
///
|
||||||
|
/// Example of how to bind one interrupt:
|
||||||
|
///
|
||||||
|
/// ```rust,ignore
|
||||||
|
/// use embassy_nxp::{bind_interrupts, usart, peripherals};
|
||||||
|
///
|
||||||
|
/// bind_interrupts!(
|
||||||
|
/// /// Binds the USART Interrupts.
|
||||||
|
/// struct Irqs {
|
||||||
|
/// FLEXCOMM0 => usart::InterruptHandler<peripherals::USART0>;
|
||||||
|
/// }
|
||||||
|
/// );
|
||||||
|
/// ```
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! bind_interrupts {
|
||||||
|
($(#[$attr:meta])* $vis:vis struct $name:ident {
|
||||||
|
$(
|
||||||
|
$(#[cfg($cond_irq:meta)])?
|
||||||
|
$irq:ident => $(
|
||||||
|
$(#[cfg($cond_handler:meta)])?
|
||||||
|
$handler:ty
|
||||||
|
),*;
|
||||||
|
)*
|
||||||
|
}) => {
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
$(#[$attr])*
|
||||||
|
$vis struct $name;
|
||||||
|
|
||||||
|
$(
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
#[no_mangle]
|
||||||
|
$(#[cfg($cond_irq)])?
|
||||||
|
unsafe extern "C" fn $irq() {
|
||||||
|
unsafe {
|
||||||
|
$(
|
||||||
|
$(#[cfg($cond_handler)])?
|
||||||
|
<$handler as $crate::interrupt::typelevel::Handler<$crate::interrupt::typelevel::$irq>>::on_interrupt();
|
||||||
|
|
||||||
|
)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$(#[cfg($cond_irq)])?
|
||||||
|
$crate::bind_interrupts!(@inner
|
||||||
|
$(
|
||||||
|
$(#[cfg($cond_handler)])?
|
||||||
|
unsafe impl $crate::interrupt::typelevel::Binding<$crate::interrupt::typelevel::$irq, $handler> for $name {}
|
||||||
|
)*
|
||||||
|
);
|
||||||
|
)*
|
||||||
|
};
|
||||||
|
(@inner $($t:tt)*) => {
|
||||||
|
$($t)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Initialize the `embassy-nxp` HAL with the provided configuration.
|
/// Initialize the `embassy-nxp` HAL with the provided configuration.
|
||||||
///
|
///
|
||||||
/// This returns the peripheral singletons that can be used for creating drivers.
|
/// This returns the peripheral singletons that can be used for creating drivers.
|
||||||
@ -92,6 +158,9 @@ pub fn init(_config: config::Config) -> Peripherals {
|
|||||||
#[cfg(feature = "_time_driver")]
|
#[cfg(feature = "_time_driver")]
|
||||||
time_driver::init();
|
time_driver::init();
|
||||||
|
|
||||||
|
#[cfg(feature = "lpc55-core0")]
|
||||||
|
dma::init();
|
||||||
|
|
||||||
peripherals
|
peripherals
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,5 +202,8 @@ macro_rules! impl_mode {
|
|||||||
|
|
||||||
/// Blocking mode.
|
/// Blocking mode.
|
||||||
pub struct Blocking;
|
pub struct Blocking;
|
||||||
|
/// Asynchronous mode.
|
||||||
|
pub struct Async;
|
||||||
|
|
||||||
impl_mode!(Blocking);
|
impl_mode!(Blocking);
|
||||||
|
impl_mode!(Async);
|
||||||
|
@ -1,14 +1,24 @@
|
|||||||
|
use core::fmt::Debug;
|
||||||
|
use core::future::poll_fn;
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
|
use core::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
use core::task::Poll;
|
||||||
|
|
||||||
|
use embassy_futures::select::{select, Either};
|
||||||
|
use embassy_hal_internal::interrupt::InterruptExt;
|
||||||
use embassy_hal_internal::{Peri, PeripheralType};
|
use embassy_hal_internal::{Peri, PeripheralType};
|
||||||
|
use embassy_sync::waitqueue::AtomicWaker;
|
||||||
use embedded_io::{self, ErrorKind};
|
use embedded_io::{self, ErrorKind};
|
||||||
|
|
||||||
|
use crate::dma::{AnyChannel, Channel};
|
||||||
use crate::gpio::{match_iocon, AnyPin, Bank, SealedPin};
|
use crate::gpio::{match_iocon, AnyPin, Bank, SealedPin};
|
||||||
|
use crate::interrupt::typelevel::{Binding, Interrupt as _};
|
||||||
|
use crate::interrupt::Interrupt;
|
||||||
use crate::pac::flexcomm::Flexcomm as FlexcommReg;
|
use crate::pac::flexcomm::Flexcomm as FlexcommReg;
|
||||||
use crate::pac::iocon::vals::PioFunc;
|
use crate::pac::iocon::vals::PioFunc;
|
||||||
use crate::pac::usart::Usart as UsartReg;
|
use crate::pac::usart::Usart as UsartReg;
|
||||||
use crate::pac::*;
|
use crate::pac::*;
|
||||||
use crate::{Blocking, Mode};
|
use crate::{Async, Blocking, Mode};
|
||||||
|
|
||||||
/// Serial error
|
/// Serial error
|
||||||
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
|
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
|
||||||
@ -101,6 +111,12 @@ impl Default for Config {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Internal DMA state of UART RX.
|
||||||
|
pub struct DmaState {
|
||||||
|
rx_err_waker: AtomicWaker,
|
||||||
|
rx_err: AtomicBool,
|
||||||
|
}
|
||||||
|
|
||||||
/// # Type parameters
|
/// # Type parameters
|
||||||
/// 'd: the lifetime marker ensuring correct borrow checking for peripherals used at compile time
|
/// 'd: the lifetime marker ensuring correct borrow checking for peripherals used at compile time
|
||||||
/// T: the peripheral instance type allowing usage of peripheral specific registers
|
/// T: the peripheral instance type allowing usage of peripheral specific registers
|
||||||
@ -112,24 +128,33 @@ pub struct Usart<'d, M: Mode> {
|
|||||||
|
|
||||||
pub struct UsartTx<'d, M: Mode> {
|
pub struct UsartTx<'d, M: Mode> {
|
||||||
info: &'static Info,
|
info: &'static Info,
|
||||||
phantom: PhantomData<(&'d (), M)>,
|
tx_dma: Option<Peri<'d, AnyChannel>>,
|
||||||
|
phantom: PhantomData<M>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct UsartRx<'d, M: Mode> {
|
pub struct UsartRx<'d, M: Mode> {
|
||||||
info: &'static Info,
|
info: &'static Info,
|
||||||
phantom: PhantomData<(&'d (), M)>,
|
dma_state: &'static DmaState,
|
||||||
|
rx_dma: Option<Peri<'d, AnyChannel>>,
|
||||||
|
phantom: PhantomData<M>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, M: Mode> UsartTx<'d, M> {
|
impl<'d, M: Mode> UsartTx<'d, M> {
|
||||||
pub fn new<T: Instance>(_usart: Peri<'d, T>, tx: Peri<'d, impl TxPin<T>>, config: Config) -> Self {
|
pub fn new<T: Instance>(
|
||||||
|
_usart: Peri<'d, T>,
|
||||||
|
tx: Peri<'d, impl TxPin<T>>,
|
||||||
|
tx_dma: Peri<'d, impl Channel>,
|
||||||
|
config: Config,
|
||||||
|
) -> Self {
|
||||||
Usart::<M>::init::<T>(Some(tx.into()), None, config);
|
Usart::<M>::init::<T>(Some(tx.into()), None, config);
|
||||||
Self::new_inner(T::info())
|
Self::new_inner(T::info(), Some(tx_dma.into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn new_inner(info: &'static Info) -> Self {
|
fn new_inner(info: &'static Info, tx_dma: Option<Peri<'d, AnyChannel>>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
info,
|
info,
|
||||||
|
tx_dma,
|
||||||
phantom: PhantomData,
|
phantom: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -155,20 +180,65 @@ impl<'d, M: Mode> UsartTx<'d, M> {
|
|||||||
impl<'d> UsartTx<'d, Blocking> {
|
impl<'d> UsartTx<'d, Blocking> {
|
||||||
pub fn new_blocking<T: Instance>(_usart: Peri<'d, T>, tx: Peri<'d, impl TxPin<T>>, config: Config) -> Self {
|
pub fn new_blocking<T: Instance>(_usart: Peri<'d, T>, tx: Peri<'d, impl TxPin<T>>, config: Config) -> Self {
|
||||||
Usart::<Blocking>::init::<T>(Some(tx.into()), None, config);
|
Usart::<Blocking>::init::<T>(Some(tx.into()), None, config);
|
||||||
Self::new_inner(T::info())
|
Self::new_inner(T::info(), None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'d> UsartTx<'d, Async> {
|
||||||
|
/// Write to UART TX from the provided buffer using DMA.
|
||||||
|
pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> {
|
||||||
|
// Unwrap() can be used because UsartTx::new() in Async mode always sets it to Some
|
||||||
|
let ch = self.tx_dma.as_mut().unwrap().reborrow();
|
||||||
|
let transfer = unsafe {
|
||||||
|
// Enable to pace DMA transfers.
|
||||||
|
self.info.usart_reg.fifocfg().modify(|w| w.set_dmatx(true));
|
||||||
|
// If future is not assigned to a variable, the data register pointer
|
||||||
|
// is held across an await and makes the future non-Send.
|
||||||
|
crate::dma::write(ch, buffer, self.info.usart_reg.fifowr().as_ptr() as *mut _)
|
||||||
|
};
|
||||||
|
transfer.await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
impl<'d, M: Mode> UsartRx<'d, M> {
|
impl<'d, M: Mode> UsartRx<'d, M> {
|
||||||
pub fn new<T: Instance>(_usart: Peri<'d, T>, rx: Peri<'d, impl RxPin<T>>, config: Config) -> Self {
|
pub fn new<T: Instance>(
|
||||||
|
_usart: Peri<'d, T>,
|
||||||
|
rx: Peri<'d, impl RxPin<T>>,
|
||||||
|
has_irq: bool,
|
||||||
|
rx_dma: Peri<'d, impl Channel>,
|
||||||
|
config: Config,
|
||||||
|
) -> Self {
|
||||||
Usart::<M>::init::<T>(None, Some(rx.into()), config);
|
Usart::<M>::init::<T>(None, Some(rx.into()), config);
|
||||||
Self::new_inner(T::info())
|
Self::new_inner(T::info(), T::dma_state(), has_irq, Some(rx_dma.into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
fn new_inner(
|
||||||
fn new_inner(info: &'static Info) -> Self {
|
info: &'static Info,
|
||||||
|
dma_state: &'static DmaState,
|
||||||
|
has_irq: bool,
|
||||||
|
rx_dma: Option<Peri<'d, AnyChannel>>,
|
||||||
|
) -> Self {
|
||||||
|
core::debug_assert_eq!(has_irq, rx_dma.is_some());
|
||||||
|
if has_irq {
|
||||||
|
// Disable all the related interrupts for now.
|
||||||
|
info.usart_reg.intenclr().write(|w| {
|
||||||
|
w.set_framerrclr(true);
|
||||||
|
w.set_parityerrclr(true);
|
||||||
|
w.set_rxnoiseclr(true);
|
||||||
|
});
|
||||||
|
info.usart_reg.fifointenclr().modify(|w| {
|
||||||
|
w.set_rxlvl(true);
|
||||||
|
w.set_rxerr(true);
|
||||||
|
});
|
||||||
|
info.interrupt.unpend();
|
||||||
|
unsafe {
|
||||||
|
info.interrupt.enable();
|
||||||
|
}
|
||||||
|
}
|
||||||
Self {
|
Self {
|
||||||
info,
|
info,
|
||||||
|
dma_state,
|
||||||
|
rx_dma,
|
||||||
phantom: PhantomData,
|
phantom: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -211,7 +281,120 @@ impl<'d, M: Mode> UsartRx<'d, M> {
|
|||||||
impl<'d> UsartRx<'d, Blocking> {
|
impl<'d> UsartRx<'d, Blocking> {
|
||||||
pub fn new_blocking<T: Instance>(_usart: Peri<'d, T>, rx: Peri<'d, impl RxPin<T>>, config: Config) -> Self {
|
pub fn new_blocking<T: Instance>(_usart: Peri<'d, T>, rx: Peri<'d, impl RxPin<T>>, config: Config) -> Self {
|
||||||
Usart::<Blocking>::init::<T>(None, Some(rx.into()), config);
|
Usart::<Blocking>::init::<T>(None, Some(rx.into()), config);
|
||||||
Self::new_inner(T::info())
|
Self::new_inner(T::info(), T::dma_state(), false, None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Interrupt handler.
|
||||||
|
pub struct InterruptHandler<T: Instance> {
|
||||||
|
_uart: PhantomData<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Instance> crate::interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
|
||||||
|
unsafe fn on_interrupt() {
|
||||||
|
let regs = T::info().usart_reg;
|
||||||
|
if !regs.fifocfg().read().dmarx() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let state = T::dma_state();
|
||||||
|
state.rx_err.store(true, Ordering::Relaxed);
|
||||||
|
state.rx_err_waker.wake();
|
||||||
|
// Disable the error interrupts instead of clearing the flags. Clearing the
|
||||||
|
// flags would allow the DMA transfer to continue, potentially signaling
|
||||||
|
// completion before we can check for errors that happened *during* the transfer.
|
||||||
|
regs.intenclr().write(|w| {
|
||||||
|
w.set_framerrclr(true);
|
||||||
|
w.set_rxnoiseclr(true);
|
||||||
|
w.set_parityerrclr(true);
|
||||||
|
});
|
||||||
|
regs.fifointenclr().write(|w| w.set_rxerr(true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d> UsartRx<'d, Async> {
|
||||||
|
/// Read from USART RX into the provided buffer.
|
||||||
|
pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
|
||||||
|
// Clear error flags before the FIFO is drained. Errors that have accumulated
|
||||||
|
// in the flags will also be present in the FIFO.
|
||||||
|
self.dma_state.rx_err.store(false, Ordering::Relaxed);
|
||||||
|
self.info.usart_reg.intenclr().write(|w| {
|
||||||
|
w.set_framerrclr(true);
|
||||||
|
w.set_parityerrclr(true);
|
||||||
|
w.set_rxnoiseclr(true);
|
||||||
|
});
|
||||||
|
self.info.usart_reg.fifointenclr().modify(|w| w.set_rxerr(true));
|
||||||
|
// Then drain the fifo. It is necessary to read at most 16 bytes (the size of FIFO).
|
||||||
|
// Errors that apply to FIFO bytes will be reported directly.
|
||||||
|
let buffer = match {
|
||||||
|
let limit = buffer.len().min(16);
|
||||||
|
self.drain_fifo(&mut buffer[0..limit])
|
||||||
|
} {
|
||||||
|
Ok(len) if len < buffer.len() => &mut buffer[len..],
|
||||||
|
Ok(_) => return Ok(()),
|
||||||
|
Err((_i, e)) => return Err(e),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Start a DMA transfer. If errors have happened in the interim some error
|
||||||
|
// interrupt flags will have been raised, and those will be picked up immediately
|
||||||
|
// by the interrupt handler.
|
||||||
|
// Unwrap() can be used because UsartRx::new() in Async mode always sets it to Some
|
||||||
|
let ch = self.rx_dma.as_mut().unwrap().reborrow();
|
||||||
|
|
||||||
|
self.info.usart_reg.intenset().write(|w| {
|
||||||
|
w.set_framerren(true);
|
||||||
|
w.set_parityerren(true);
|
||||||
|
w.set_rxnoiseen(true);
|
||||||
|
});
|
||||||
|
self.info.usart_reg.fifointenset().modify(|w| w.set_rxerr(true));
|
||||||
|
self.info.usart_reg.fifocfg().modify(|w| w.set_dmarx(true));
|
||||||
|
let transfer = unsafe {
|
||||||
|
// If we don't assign future to a variable, the data register pointer
|
||||||
|
// is held across an await and makes the future non-Send.
|
||||||
|
crate::dma::read(ch, self.info.usart_reg.fiford().as_ptr() as *const _, buffer)
|
||||||
|
};
|
||||||
|
|
||||||
|
// wait for either the transfer to complete or an error to happen.
|
||||||
|
let transfer_result = select(
|
||||||
|
transfer,
|
||||||
|
poll_fn(|cx| {
|
||||||
|
self.dma_state.rx_err_waker.register(cx.waker());
|
||||||
|
match self.dma_state.rx_err.swap(false, Ordering::Relaxed) {
|
||||||
|
false => Poll::Pending,
|
||||||
|
e => Poll::Ready(e),
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let errors = match transfer_result {
|
||||||
|
Either::First(()) => {
|
||||||
|
// The DMA controller finished, BUT if an error occurred on the LAST
|
||||||
|
// byte, then we may still need to grab the error state!
|
||||||
|
self.dma_state.rx_err.swap(false, Ordering::Relaxed)
|
||||||
|
}
|
||||||
|
Either::Second(e) => {
|
||||||
|
// There is an error, which means this is the error that
|
||||||
|
// was problematic.
|
||||||
|
e
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// If we got no error, just return at this point
|
||||||
|
if !errors {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we DID get an error, we need to figure out which one it was.
|
||||||
|
if self.info.usart_reg.intstat().read().framerrint() {
|
||||||
|
return Err(Error::Framing);
|
||||||
|
} else if self.info.usart_reg.intstat().read().parityerrint() {
|
||||||
|
return Err(Error::Parity);
|
||||||
|
} else if self.info.usart_reg.intstat().read().rxnoiseint() {
|
||||||
|
return Err(Error::Noise);
|
||||||
|
} else if self.info.usart_reg.fifointstat().read().rxerr() {
|
||||||
|
return Err(Error::Overrun);
|
||||||
|
}
|
||||||
|
unreachable!("unrecognized rx error");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -222,7 +405,29 @@ impl<'d> Usart<'d, Blocking> {
|
|||||||
rx: Peri<'d, impl RxPin<T>>,
|
rx: Peri<'d, impl RxPin<T>>,
|
||||||
config: Config,
|
config: Config,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self::new_inner(usart, tx.into(), rx.into(), config)
|
Self::new_inner(usart, tx.into(), rx.into(), false, None, None, config)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d> Usart<'d, Async> {
|
||||||
|
pub fn new<T: Instance>(
|
||||||
|
uart: Peri<'d, T>,
|
||||||
|
tx: Peri<'d, impl TxPin<T>>,
|
||||||
|
rx: Peri<'d, impl RxPin<T>>,
|
||||||
|
_irq: impl Binding<T::Interrupt, InterruptHandler<T>>,
|
||||||
|
tx_dma: Peri<'d, impl TxChannel<T>>,
|
||||||
|
rx_dma: Peri<'d, impl RxChannel<T>>,
|
||||||
|
config: Config,
|
||||||
|
) -> Self {
|
||||||
|
Self::new_inner(
|
||||||
|
uart,
|
||||||
|
tx.into(),
|
||||||
|
rx.into(),
|
||||||
|
true,
|
||||||
|
Some(tx_dma.into()),
|
||||||
|
Some(rx_dma.into()),
|
||||||
|
config,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -231,12 +436,15 @@ impl<'d, M: Mode> Usart<'d, M> {
|
|||||||
_usart: Peri<'d, T>,
|
_usart: Peri<'d, T>,
|
||||||
mut tx: Peri<'d, AnyPin>,
|
mut tx: Peri<'d, AnyPin>,
|
||||||
mut rx: Peri<'d, AnyPin>,
|
mut rx: Peri<'d, AnyPin>,
|
||||||
|
has_irq: bool,
|
||||||
|
tx_dma: Option<Peri<'d, AnyChannel>>,
|
||||||
|
rx_dma: Option<Peri<'d, AnyChannel>>,
|
||||||
config: Config,
|
config: Config,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self::init::<T>(Some(tx.reborrow()), Some(rx.reborrow()), config);
|
Self::init::<T>(Some(tx.reborrow()), Some(rx.reborrow()), config);
|
||||||
Self {
|
Self {
|
||||||
tx: UsartTx::new_inner(T::info()),
|
tx: UsartTx::new_inner(T::info(), tx_dma),
|
||||||
rx: UsartRx::new_inner(T::info()),
|
rx: UsartRx::new_inner(T::info(), T::dma_state(), has_irq, rx_dma),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -390,9 +598,11 @@ impl<'d, M: Mode> Usart<'d, M> {
|
|||||||
SYSCON
|
SYSCON
|
||||||
.presetctrl1()
|
.presetctrl1()
|
||||||
.modify(|w| w.set_fc_rst(instance_number, syscon::vals::FcRst::RELEASED));
|
.modify(|w| w.set_fc_rst(instance_number, syscon::vals::FcRst::RELEASED));
|
||||||
flexcomm_register
|
flexcomm_register.pselid().modify(|w| {
|
||||||
.pselid()
|
w.set_persel(flexcomm::vals::Persel::USART);
|
||||||
.modify(|w| w.set_persel(flexcomm::vals::Persel::USART));
|
// This will lock the peripheral PERSEL and will not allow any changes until the board is reset.
|
||||||
|
w.set_lock(true);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn configure_usart(info: &'static Info, config: &Config) {
|
fn configure_usart(info: &'static Info, config: &Config) {
|
||||||
@ -471,6 +681,8 @@ impl<'d, M: Mode> Usart<'d, M> {
|
|||||||
});
|
});
|
||||||
registers.cfg().modify(|w| w.set_enable(true));
|
registers.cfg().modify(|w| w.set_enable(true));
|
||||||
|
|
||||||
|
registers.fifointenset().modify(|w| w.set_rxerr(true));
|
||||||
|
|
||||||
// Drain RX FIFO in case it still has some unrelevant data
|
// Drain RX FIFO in case it still has some unrelevant data
|
||||||
while registers.fifostat().read().rxnotempty() {
|
while registers.fifostat().read().rxnotempty() {
|
||||||
let _ = registers.fiford().read().0;
|
let _ = registers.fiford().read().0;
|
||||||
@ -513,6 +725,17 @@ impl<'d, M: Mode> Usart<'d, M> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'d> Usart<'d, Async> {
|
||||||
|
/// Write to UART TX from the provided buffer.
|
||||||
|
pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> {
|
||||||
|
self.tx.write(buffer).await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read from UART RX into the provided buffer.
|
||||||
|
pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
|
||||||
|
self.rx.read(buffer).await
|
||||||
|
}
|
||||||
|
}
|
||||||
impl<'d, M: Mode> embedded_hal_02::blocking::serial::Write<u8> for UsartTx<'d, M> {
|
impl<'d, M: Mode> embedded_hal_02::blocking::serial::Write<u8> for UsartTx<'d, M> {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
|
|
||||||
@ -584,10 +807,12 @@ impl<'d> embedded_io::Read for Usart<'d, Blocking> {
|
|||||||
struct Info {
|
struct Info {
|
||||||
usart_reg: UsartReg,
|
usart_reg: UsartReg,
|
||||||
fc_reg: FlexcommReg,
|
fc_reg: FlexcommReg,
|
||||||
|
interrupt: Interrupt,
|
||||||
}
|
}
|
||||||
|
|
||||||
trait SealedInstance {
|
trait SealedInstance {
|
||||||
fn info() -> &'static Info;
|
fn info() -> &'static Info;
|
||||||
|
fn dma_state() -> &'static DmaState;
|
||||||
fn instance_number() -> usize;
|
fn instance_number() -> usize;
|
||||||
fn tx_pin_func() -> PioFunc;
|
fn tx_pin_func() -> PioFunc;
|
||||||
fn rx_pin_func() -> PioFunc;
|
fn rx_pin_func() -> PioFunc;
|
||||||
@ -595,7 +820,10 @@ trait SealedInstance {
|
|||||||
|
|
||||||
/// UART instance.
|
/// UART instance.
|
||||||
#[allow(private_bounds)]
|
#[allow(private_bounds)]
|
||||||
pub trait Instance: SealedInstance + PeripheralType {}
|
pub trait Instance: SealedInstance + PeripheralType {
|
||||||
|
/// Interrupt for this instance.
|
||||||
|
type Interrupt: crate::interrupt::typelevel::Interrupt;
|
||||||
|
}
|
||||||
|
|
||||||
macro_rules! impl_instance {
|
macro_rules! impl_instance {
|
||||||
($inst:ident, $fc:ident, $tx_pin:ident, $rx_pin:ident, $fc_num:expr) => {
|
($inst:ident, $fc:ident, $tx_pin:ident, $rx_pin:ident, $fc_num:expr) => {
|
||||||
@ -604,9 +832,18 @@ macro_rules! impl_instance {
|
|||||||
static INFO: Info = Info {
|
static INFO: Info = Info {
|
||||||
usart_reg: crate::pac::$inst,
|
usart_reg: crate::pac::$inst,
|
||||||
fc_reg: crate::pac::$fc,
|
fc_reg: crate::pac::$fc,
|
||||||
|
interrupt: crate::interrupt::typelevel::$fc::IRQ,
|
||||||
};
|
};
|
||||||
&INFO
|
&INFO
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn dma_state() -> &'static DmaState {
|
||||||
|
static STATE: DmaState = DmaState {
|
||||||
|
rx_err_waker: AtomicWaker::new(),
|
||||||
|
rx_err: AtomicBool::new(false),
|
||||||
|
};
|
||||||
|
&STATE
|
||||||
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
fn instance_number() -> usize {
|
fn instance_number() -> usize {
|
||||||
$fc_num
|
$fc_num
|
||||||
@ -620,7 +857,9 @@ macro_rules! impl_instance {
|
|||||||
PioFunc::$rx_pin
|
PioFunc::$rx_pin
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl $crate::usart::Instance for $crate::peripherals::$inst {}
|
impl $crate::usart::Instance for $crate::peripherals::$inst {
|
||||||
|
type Interrupt = crate::interrupt::typelevel::$fc;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -663,3 +902,34 @@ impl_pin!(PIO1_16, USART6, Tx);
|
|||||||
impl_pin!(PIO1_13, USART6, Rx);
|
impl_pin!(PIO1_13, USART6, Rx);
|
||||||
impl_pin!(PIO0_19, USART7, Tx);
|
impl_pin!(PIO0_19, USART7, Tx);
|
||||||
impl_pin!(PIO0_20, USART7, Rx);
|
impl_pin!(PIO0_20, USART7, Rx);
|
||||||
|
|
||||||
|
/// Trait for TX DMA channels.
|
||||||
|
pub trait TxChannel<T: Instance>: crate::dma::Channel {}
|
||||||
|
/// Trait for RX DMA channels.
|
||||||
|
pub trait RxChannel<T: Instance>: crate::dma::Channel {}
|
||||||
|
|
||||||
|
macro_rules! impl_channel {
|
||||||
|
($dma:ident, $instance:ident, Tx) => {
|
||||||
|
impl TxChannel<crate::peripherals::$instance> for crate::peripherals::$dma {}
|
||||||
|
};
|
||||||
|
($dma:ident, $instance:ident, Rx) => {
|
||||||
|
impl RxChannel<crate::peripherals::$instance> for crate::peripherals::$dma {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_channel!(DMA_CH4, USART0, Rx);
|
||||||
|
impl_channel!(DMA_CH5, USART0, Tx);
|
||||||
|
impl_channel!(DMA_CH6, USART1, Rx);
|
||||||
|
impl_channel!(DMA_CH7, USART1, Tx);
|
||||||
|
impl_channel!(DMA_CH10, USART2, Rx);
|
||||||
|
impl_channel!(DMA_CH11, USART2, Tx);
|
||||||
|
impl_channel!(DMA_CH8, USART3, Rx);
|
||||||
|
impl_channel!(DMA_CH9, USART3, Tx);
|
||||||
|
impl_channel!(DMA_CH12, USART4, Rx);
|
||||||
|
impl_channel!(DMA_CH13, USART4, Tx);
|
||||||
|
impl_channel!(DMA_CH14, USART5, Rx);
|
||||||
|
impl_channel!(DMA_CH15, USART5, Tx);
|
||||||
|
impl_channel!(DMA_CH16, USART6, Rx);
|
||||||
|
impl_channel!(DMA_CH17, USART6, Tx);
|
||||||
|
impl_channel!(DMA_CH18, USART7, Rx);
|
||||||
|
impl_channel!(DMA_CH19, USART7, Tx);
|
||||||
|
70
examples/lpc55s69/src/bin/usart_async.rs
Normal file
70
examples/lpc55s69/src/bin/usart_async.rs
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
use core::str::from_utf8_mut;
|
||||||
|
|
||||||
|
use defmt::*;
|
||||||
|
use embassy_executor::Spawner;
|
||||||
|
use embassy_nxp::bind_interrupts;
|
||||||
|
use embassy_nxp::gpio::{Level, Output};
|
||||||
|
use embassy_nxp::peripherals::USART2;
|
||||||
|
use embassy_nxp::usart::{Config, InterruptHandler, Usart};
|
||||||
|
use embassy_time::Timer;
|
||||||
|
use {defmt_rtt as _, panic_halt as _};
|
||||||
|
|
||||||
|
bind_interrupts!(struct Irqs {
|
||||||
|
FLEXCOMM2 => InterruptHandler<USART2>;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
#[embassy_executor::task]
|
||||||
|
async fn blinky_task(mut led: Output<'static>) {
|
||||||
|
loop {
|
||||||
|
info!("[TASK] led off!");
|
||||||
|
led.set_high();
|
||||||
|
Timer::after_millis(500).await;
|
||||||
|
|
||||||
|
info!("[TASK] led on!");
|
||||||
|
led.set_low();
|
||||||
|
Timer::after_millis(500).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[embassy_executor::main]
|
||||||
|
async fn main(spawner: Spawner) {
|
||||||
|
let p = embassy_nxp::init(Default::default());
|
||||||
|
let mut usart = Usart::new(
|
||||||
|
p.USART2,
|
||||||
|
p.PIO0_27,
|
||||||
|
p.PIO1_24,
|
||||||
|
Irqs,
|
||||||
|
p.DMA_CH11,
|
||||||
|
p.DMA_CH10,
|
||||||
|
Config::default(),
|
||||||
|
);
|
||||||
|
let led = Output::new(p.PIO1_6, Level::Low);
|
||||||
|
spawner.spawn(blinky_task(led).unwrap());
|
||||||
|
info!("[MAIN] Entering main loop");
|
||||||
|
loop {
|
||||||
|
let tx_buf = b"Hello, Ferris!";
|
||||||
|
let mut rx_buf = [0u8; 14];
|
||||||
|
info!("[MAIN] Write a message");
|
||||||
|
usart.write(tx_buf).await.unwrap();
|
||||||
|
Timer::after_millis(500).await;
|
||||||
|
|
||||||
|
info!("[MAIN] Read a message");
|
||||||
|
match usart.read(&mut rx_buf).await {
|
||||||
|
Ok(_) => match from_utf8_mut(&mut rx_buf) {
|
||||||
|
Ok(str) => {
|
||||||
|
info!("[MAIN] The message is: {}", str);
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
error!("[MAIN] Error in converting to UTF8");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(e) => warn!("[MAIN] Error: {}", e),
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer::after_millis(500).await;
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user