Add UART and DMA drivers
Both blocking and async versions are supported. Add separate examples for each mode.
This commit is contained in:
parent
d1c2ce927a
commit
d64ae225b3
@ -10,5 +10,7 @@ The link: link:https://github.com/embassy-rs/embassy/tree/main/embassy-imxrt[Emb
|
|||||||
The following peripherals have a HAL implementation at present
|
The following peripherals have a HAL implementation at present
|
||||||
|
|
||||||
* CRC
|
* CRC
|
||||||
|
* DMA
|
||||||
* GPIO
|
* GPIO
|
||||||
* RNG
|
* RNG
|
||||||
|
* UART
|
||||||
|
|||||||
418
embassy-imxrt/src/dma.rs
Normal file
418
embassy-imxrt/src/dma.rs
Normal file
@ -0,0 +1,418 @@
|
|||||||
|
//! DMA driver.
|
||||||
|
|
||||||
|
use core::future::Future;
|
||||||
|
use core::pin::Pin;
|
||||||
|
use core::sync::atomic::{compiler_fence, Ordering};
|
||||||
|
use core::task::{Context, Poll};
|
||||||
|
|
||||||
|
use embassy_hal_internal::{impl_peripheral, Peri, PeripheralType};
|
||||||
|
use embassy_sync::waitqueue::AtomicWaker;
|
||||||
|
use pac::dma0::channel::cfg::Periphreqen;
|
||||||
|
use pac::dma0::channel::xfercfg::{Dstinc, Srcinc, Width};
|
||||||
|
|
||||||
|
use crate::clocks::enable_and_reset;
|
||||||
|
use crate::interrupt::InterruptExt;
|
||||||
|
use crate::peripherals::DMA0;
|
||||||
|
use crate::sealed::Sealed;
|
||||||
|
use crate::{interrupt, pac, peripherals, BitIter};
|
||||||
|
|
||||||
|
#[cfg(feature = "rt")]
|
||||||
|
#[interrupt]
|
||||||
|
fn DMA0() {
|
||||||
|
let reg = unsafe { crate::pac::Dma0::steal() };
|
||||||
|
|
||||||
|
if reg.intstat().read().activeerrint().bit() {
|
||||||
|
let err = reg.errint0().read().bits();
|
||||||
|
|
||||||
|
for channel in BitIter(err) {
|
||||||
|
error!("DMA error interrupt on channel {}!", channel);
|
||||||
|
reg.errint0().write(|w| unsafe { w.err().bits(1 << channel) });
|
||||||
|
CHANNEL_WAKERS[channel as usize].wake();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if reg.intstat().read().activeint().bit() {
|
||||||
|
let ia = reg.inta0().read().bits();
|
||||||
|
|
||||||
|
for channel in BitIter(ia) {
|
||||||
|
reg.inta0().write(|w| unsafe { w.ia().bits(1 << channel) });
|
||||||
|
CHANNEL_WAKERS[channel as usize].wake();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Initialize DMA controllers (DMA0 only, for now)
|
||||||
|
pub(crate) unsafe fn init() {
|
||||||
|
let sysctl0 = crate::pac::Sysctl0::steal();
|
||||||
|
let dmactl0 = crate::pac::Dma0::steal();
|
||||||
|
|
||||||
|
enable_and_reset::<DMA0>();
|
||||||
|
|
||||||
|
interrupt::DMA0.disable();
|
||||||
|
interrupt::DMA0.set_priority(interrupt::Priority::P3);
|
||||||
|
|
||||||
|
dmactl0.ctrl().modify(|_, w| w.enable().set_bit());
|
||||||
|
|
||||||
|
// Set channel descriptor SRAM base address
|
||||||
|
// Descriptor base must be 1K aligned
|
||||||
|
let descriptor_base = core::ptr::addr_of!(DESCRIPTORS.descs) as u32;
|
||||||
|
dmactl0.srambase().write(|w| w.bits(descriptor_base));
|
||||||
|
|
||||||
|
// Ensure AHB priority it highest (M4 == DMAC0)
|
||||||
|
sysctl0.ahbmatrixprior().modify(|_, w| w.m4().bits(0));
|
||||||
|
|
||||||
|
interrupt::DMA0.unpend();
|
||||||
|
interrupt::DMA0.enable();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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> {
|
||||||
|
let count = ((to.len() / W::size() as usize) - 1) as isize;
|
||||||
|
|
||||||
|
copy_inner(
|
||||||
|
ch,
|
||||||
|
from as *const u32,
|
||||||
|
(to as *mut u32).byte_offset(count * W::size()),
|
||||||
|
W::width(),
|
||||||
|
count,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
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> {
|
||||||
|
let count = ((from.len() / W::size() as usize) - 1) as isize;
|
||||||
|
|
||||||
|
copy_inner(
|
||||||
|
ch,
|
||||||
|
(from as *const u32).byte_offset(count * W::size()),
|
||||||
|
to as *mut u32,
|
||||||
|
W::width(),
|
||||||
|
count,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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);
|
||||||
|
|
||||||
|
let count = ((from_len / W::size() as usize) - 1) as isize;
|
||||||
|
|
||||||
|
copy_inner(
|
||||||
|
ch,
|
||||||
|
from.as_ptr().byte_offset(count * W::size()) as *const u32,
|
||||||
|
to.as_mut_ptr().byte_offset(count * W::size()) as *mut u32,
|
||||||
|
W::width(),
|
||||||
|
count,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn copy_inner<'a, C: Channel>(
|
||||||
|
ch: Peri<'a, C>,
|
||||||
|
from: *const u32,
|
||||||
|
to: *mut u32,
|
||||||
|
width: Width,
|
||||||
|
count: isize,
|
||||||
|
incr_read: bool,
|
||||||
|
incr_write: bool,
|
||||||
|
periph: bool,
|
||||||
|
) -> Transfer<'a, C> {
|
||||||
|
let p = ch.regs();
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
DESCRIPTORS.descs[ch.number() as usize].src = from as u32;
|
||||||
|
DESCRIPTORS.descs[ch.number() as usize].dest = to as u32;
|
||||||
|
}
|
||||||
|
|
||||||
|
compiler_fence(Ordering::SeqCst);
|
||||||
|
|
||||||
|
p.errint0().write(|w| unsafe { w.err().bits(1 << ch.number()) });
|
||||||
|
p.inta0().write(|w| unsafe { w.ia().bits(1 << ch.number()) });
|
||||||
|
|
||||||
|
p.channel(ch.number().into()).cfg().write(|w| {
|
||||||
|
unsafe { w.chpriority().bits(0) }
|
||||||
|
.periphreqen()
|
||||||
|
.variant(match periph {
|
||||||
|
false => Periphreqen::Disabled,
|
||||||
|
true => Periphreqen::Enabled,
|
||||||
|
})
|
||||||
|
.hwtrigen()
|
||||||
|
.clear_bit()
|
||||||
|
});
|
||||||
|
|
||||||
|
p.intenset0().write(|w| unsafe { w.inten().bits(1 << ch.number()) });
|
||||||
|
|
||||||
|
p.channel(ch.number().into()).xfercfg().write(|w| {
|
||||||
|
unsafe { w.xfercount().bits(count as u16) }
|
||||||
|
.cfgvalid()
|
||||||
|
.set_bit()
|
||||||
|
.clrtrig()
|
||||||
|
.set_bit()
|
||||||
|
.reload()
|
||||||
|
.clear_bit()
|
||||||
|
.setinta()
|
||||||
|
.set_bit()
|
||||||
|
.width()
|
||||||
|
.variant(width)
|
||||||
|
.srcinc()
|
||||||
|
.variant(match incr_read {
|
||||||
|
false => Srcinc::NoIncrement,
|
||||||
|
true => Srcinc::WidthX1,
|
||||||
|
// REVISIT: what about WidthX2 and WidthX4?
|
||||||
|
})
|
||||||
|
.dstinc()
|
||||||
|
.variant(match incr_write {
|
||||||
|
false => Dstinc::NoIncrement,
|
||||||
|
true => Dstinc::WidthX1,
|
||||||
|
// REVISIT: what about WidthX2 and WidthX4?
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
p.enableset0().write(|w| unsafe { w.ena().bits(1 << ch.number()) });
|
||||||
|
|
||||||
|
p.channel(ch.number().into())
|
||||||
|
.xfercfg()
|
||||||
|
.modify(|_, w| w.swtrig().set_bit());
|
||||||
|
|
||||||
|
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 }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn abort(&mut self) -> usize {
|
||||||
|
let p = self.channel.regs();
|
||||||
|
|
||||||
|
p.abort0().write(|w| w.channel(self.channel.number()).set_bit());
|
||||||
|
while p.busy0().read().bsy().bits() & (1 << self.channel.number()) != 0 {}
|
||||||
|
|
||||||
|
p.enableclr0()
|
||||||
|
.write(|w| unsafe { w.clr().bits(1 << self.channel.number()) });
|
||||||
|
|
||||||
|
let width: u8 = p
|
||||||
|
.channel(self.channel.number().into())
|
||||||
|
.xfercfg()
|
||||||
|
.read()
|
||||||
|
.width()
|
||||||
|
.variant()
|
||||||
|
.unwrap()
|
||||||
|
.into();
|
||||||
|
|
||||||
|
let count = p
|
||||||
|
.channel(self.channel.number().into())
|
||||||
|
.xfercfg()
|
||||||
|
.read()
|
||||||
|
.xfercount()
|
||||||
|
.bits()
|
||||||
|
+ 1;
|
||||||
|
|
||||||
|
usize::from(count) * usize::from(width)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, C: Channel> Drop for Transfer<'a, C> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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> {
|
||||||
|
// Re-register the waker on each call to poll() because any calls to
|
||||||
|
// wake will deregister the waker.
|
||||||
|
CHANNEL_WAKERS[self.channel.number() as usize].register(cx.waker());
|
||||||
|
|
||||||
|
if self.channel.regs().active0().read().act().bits() & (1 << self.channel.number()) == 0 {
|
||||||
|
Poll::Ready(())
|
||||||
|
} else {
|
||||||
|
Poll::Pending
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// DMA channel descriptor
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
#[repr(C)]
|
||||||
|
struct Descriptor {
|
||||||
|
reserved: u32,
|
||||||
|
src: u32,
|
||||||
|
dest: u32,
|
||||||
|
link: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Descriptor {
|
||||||
|
const fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
reserved: 0,
|
||||||
|
src: 0,
|
||||||
|
dest: 0,
|
||||||
|
link: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(align(1024))]
|
||||||
|
struct Descriptors {
|
||||||
|
descs: [Descriptor; CHANNEL_COUNT],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Descriptors {
|
||||||
|
const fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
descs: [const { Descriptor::new() }; CHANNEL_COUNT],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static mut DESCRIPTORS: Descriptors = Descriptors::new();
|
||||||
|
static CHANNEL_WAKERS: [AtomicWaker; CHANNEL_COUNT] = [const { AtomicWaker::new() }; CHANNEL_COUNT];
|
||||||
|
pub(crate) const CHANNEL_COUNT: usize = 33;
|
||||||
|
|
||||||
|
/// DMA channel interface.
|
||||||
|
#[allow(private_bounds)]
|
||||||
|
pub trait Channel: PeripheralType + Sealed + Into<AnyChannel> + Sized + 'static {
|
||||||
|
/// Channel number.
|
||||||
|
fn number(&self) -> u8;
|
||||||
|
|
||||||
|
/// Channel registry block.
|
||||||
|
fn regs(&self) -> &'static pac::dma0::RegisterBlock {
|
||||||
|
unsafe { &*crate::pac::Dma0::ptr() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// DMA word.
|
||||||
|
#[allow(private_bounds)]
|
||||||
|
pub trait Word: Sealed {
|
||||||
|
/// Transfer width.
|
||||||
|
fn width() -> Width;
|
||||||
|
|
||||||
|
/// Size in bytes for the width.
|
||||||
|
fn size() -> isize;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sealed for u8 {}
|
||||||
|
impl Word for u8 {
|
||||||
|
fn width() -> Width {
|
||||||
|
Width::Bit8
|
||||||
|
}
|
||||||
|
|
||||||
|
fn size() -> isize {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sealed for u16 {}
|
||||||
|
impl Word for u16 {
|
||||||
|
fn width() -> Width {
|
||||||
|
Width::Bit16
|
||||||
|
}
|
||||||
|
|
||||||
|
fn size() -> isize {
|
||||||
|
2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sealed for u32 {}
|
||||||
|
impl Word for u32 {
|
||||||
|
fn width() -> Width {
|
||||||
|
Width::Bit32
|
||||||
|
}
|
||||||
|
|
||||||
|
fn size() -> isize {
|
||||||
|
4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Type erased DMA channel.
|
||||||
|
pub struct AnyChannel {
|
||||||
|
number: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_peripheral!(AnyChannel);
|
||||||
|
|
||||||
|
impl Sealed for AnyChannel {}
|
||||||
|
impl Channel for AnyChannel {
|
||||||
|
fn number(&self) -> u8 {
|
||||||
|
self.number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! channel {
|
||||||
|
($name:ident, $num:expr) => {
|
||||||
|
impl Sealed 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!(DMA0_CH0, 0);
|
||||||
|
channel!(DMA0_CH1, 1);
|
||||||
|
channel!(DMA0_CH2, 2);
|
||||||
|
channel!(DMA0_CH3, 3);
|
||||||
|
channel!(DMA0_CH4, 4);
|
||||||
|
channel!(DMA0_CH5, 5);
|
||||||
|
channel!(DMA0_CH6, 6);
|
||||||
|
channel!(DMA0_CH7, 7);
|
||||||
|
channel!(DMA0_CH8, 8);
|
||||||
|
channel!(DMA0_CH9, 9);
|
||||||
|
channel!(DMA0_CH10, 10);
|
||||||
|
channel!(DMA0_CH11, 11);
|
||||||
|
channel!(DMA0_CH12, 12);
|
||||||
|
channel!(DMA0_CH13, 13);
|
||||||
|
channel!(DMA0_CH14, 14);
|
||||||
|
channel!(DMA0_CH15, 15);
|
||||||
|
channel!(DMA0_CH16, 16);
|
||||||
|
channel!(DMA0_CH17, 17);
|
||||||
|
channel!(DMA0_CH18, 18);
|
||||||
|
channel!(DMA0_CH19, 19);
|
||||||
|
channel!(DMA0_CH20, 20);
|
||||||
|
channel!(DMA0_CH21, 21);
|
||||||
|
channel!(DMA0_CH22, 22);
|
||||||
|
channel!(DMA0_CH23, 23);
|
||||||
|
channel!(DMA0_CH24, 24);
|
||||||
|
channel!(DMA0_CH25, 25);
|
||||||
|
channel!(DMA0_CH26, 26);
|
||||||
|
channel!(DMA0_CH27, 27);
|
||||||
|
channel!(DMA0_CH28, 28);
|
||||||
|
channel!(DMA0_CH29, 29);
|
||||||
|
channel!(DMA0_CH30, 30);
|
||||||
|
channel!(DMA0_CH31, 31);
|
||||||
|
channel!(DMA0_CH32, 32);
|
||||||
252
embassy-imxrt/src/flexcomm/mod.rs
Normal file
252
embassy-imxrt/src/flexcomm/mod.rs
Normal file
@ -0,0 +1,252 @@
|
|||||||
|
//! Implements Flexcomm interface wrapper for easier usage across modules
|
||||||
|
|
||||||
|
pub mod uart;
|
||||||
|
|
||||||
|
use paste::paste;
|
||||||
|
|
||||||
|
use crate::clocks::{enable_and_reset, SysconPeripheral};
|
||||||
|
use crate::peripherals::{
|
||||||
|
FLEXCOMM0, FLEXCOMM1, FLEXCOMM14, FLEXCOMM15, FLEXCOMM2, FLEXCOMM3, FLEXCOMM4, FLEXCOMM5, FLEXCOMM6, FLEXCOMM7,
|
||||||
|
};
|
||||||
|
use crate::{pac, PeripheralType};
|
||||||
|
|
||||||
|
/// clock selection option
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub enum Clock {
|
||||||
|
/// SFRO
|
||||||
|
Sfro,
|
||||||
|
|
||||||
|
/// FFRO
|
||||||
|
Ffro,
|
||||||
|
|
||||||
|
/// `AUDIO_PLL`
|
||||||
|
AudioPll,
|
||||||
|
|
||||||
|
/// MASTER
|
||||||
|
Master,
|
||||||
|
|
||||||
|
/// FCn_FRG with Main clock source
|
||||||
|
FcnFrgMain,
|
||||||
|
|
||||||
|
/// FCn_FRG with Pll clock source
|
||||||
|
FcnFrgPll,
|
||||||
|
|
||||||
|
/// FCn_FRG with Sfro clock source
|
||||||
|
FcnFrgSfro,
|
||||||
|
|
||||||
|
/// FCn_FRG with Ffro clock source
|
||||||
|
FcnFrgFfro,
|
||||||
|
|
||||||
|
/// disabled
|
||||||
|
None,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// do not allow implementation of trait outside this mod
|
||||||
|
mod sealed {
|
||||||
|
/// trait does not get re-exported outside flexcomm mod, allowing us to safely expose only desired APIs
|
||||||
|
pub trait Sealed {}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// primary low-level flexcomm interface
|
||||||
|
pub(crate) trait FlexcommLowLevel: sealed::Sealed + PeripheralType + SysconPeripheral + 'static + Send {
|
||||||
|
// fetch the flexcomm register block for direct manipulation
|
||||||
|
fn reg() -> &'static pac::flexcomm0::RegisterBlock;
|
||||||
|
|
||||||
|
// set the clock select for this flexcomm instance and remove from reset
|
||||||
|
fn enable(clk: Clock);
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! impl_flexcomm {
|
||||||
|
($($idx:expr),*) => {
|
||||||
|
$(
|
||||||
|
paste!{
|
||||||
|
impl sealed::Sealed for crate::peripherals::[<FLEXCOMM $idx>] {}
|
||||||
|
|
||||||
|
impl FlexcommLowLevel for crate::peripherals::[<FLEXCOMM $idx>] {
|
||||||
|
fn reg() -> &'static crate::pac::flexcomm0::RegisterBlock {
|
||||||
|
// SAFETY: safe from single executor, enforce
|
||||||
|
// via peripheral reference lifetime tracking
|
||||||
|
unsafe {
|
||||||
|
&*crate::pac::[<Flexcomm $idx>]::ptr()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn enable(clk: Clock) {
|
||||||
|
// SAFETY: safe from single executor
|
||||||
|
let clkctl1 = unsafe { crate::pac::Clkctl1::steal() };
|
||||||
|
|
||||||
|
clkctl1.flexcomm($idx).fcfclksel().write(|w| match clk {
|
||||||
|
Clock::Sfro => w.sel().sfro_clk(),
|
||||||
|
Clock::Ffro => w.sel().ffro_clk(),
|
||||||
|
Clock::AudioPll => w.sel().audio_pll_clk(),
|
||||||
|
Clock::Master => w.sel().master_clk(),
|
||||||
|
Clock::FcnFrgMain => w.sel().fcn_frg_clk(),
|
||||||
|
Clock::FcnFrgPll => w.sel().fcn_frg_clk(),
|
||||||
|
Clock::FcnFrgSfro => w.sel().fcn_frg_clk(),
|
||||||
|
Clock::FcnFrgFfro => w.sel().fcn_frg_clk(),
|
||||||
|
Clock::None => w.sel().none(), // no clock? throw an error?
|
||||||
|
});
|
||||||
|
|
||||||
|
clkctl1.flexcomm($idx).frgclksel().write(|w| match clk {
|
||||||
|
Clock::FcnFrgMain => w.sel().main_clk(),
|
||||||
|
Clock::FcnFrgPll => w.sel().frg_pll_clk(),
|
||||||
|
Clock::FcnFrgSfro => w.sel().sfro_clk(),
|
||||||
|
Clock::FcnFrgFfro => w.sel().ffro_clk(),
|
||||||
|
_ => w.sel().none(), // not using frg ...
|
||||||
|
});
|
||||||
|
|
||||||
|
// todo: add support for frg div/mult
|
||||||
|
clkctl1
|
||||||
|
.flexcomm($idx)
|
||||||
|
.frgctl()
|
||||||
|
.write(|w|
|
||||||
|
// SAFETY: unsafe only used for .bits() call
|
||||||
|
unsafe { w.mult().bits(0) });
|
||||||
|
|
||||||
|
enable_and_reset::<[<FLEXCOMM $idx>]>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_flexcomm!(0, 1, 2, 3, 4, 5, 6, 7);
|
||||||
|
|
||||||
|
// TODO: FLEXCOMM 14 is untested. Enable SPI support on FLEXCOMM14
|
||||||
|
// Add special case FLEXCOMM14
|
||||||
|
impl sealed::Sealed for crate::peripherals::FLEXCOMM14 {}
|
||||||
|
|
||||||
|
impl FlexcommLowLevel for crate::peripherals::FLEXCOMM14 {
|
||||||
|
fn reg() -> &'static crate::pac::flexcomm0::RegisterBlock {
|
||||||
|
// SAFETY: safe from single executor, enforce
|
||||||
|
// via peripheral reference lifetime tracking
|
||||||
|
unsafe { &*crate::pac::Flexcomm14::ptr() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn enable(clk: Clock) {
|
||||||
|
// SAFETY: safe from single executor
|
||||||
|
let clkctl1 = unsafe { crate::pac::Clkctl1::steal() };
|
||||||
|
|
||||||
|
clkctl1.fc14fclksel().write(|w| match clk {
|
||||||
|
Clock::Sfro => w.sel().sfro_clk(),
|
||||||
|
Clock::Ffro => w.sel().ffro_clk(),
|
||||||
|
Clock::AudioPll => w.sel().audio_pll_clk(),
|
||||||
|
Clock::Master => w.sel().master_clk(),
|
||||||
|
Clock::FcnFrgMain => w.sel().fcn_frg_clk(),
|
||||||
|
Clock::FcnFrgPll => w.sel().fcn_frg_clk(),
|
||||||
|
Clock::FcnFrgSfro => w.sel().fcn_frg_clk(),
|
||||||
|
Clock::FcnFrgFfro => w.sel().fcn_frg_clk(),
|
||||||
|
Clock::None => w.sel().none(), // no clock? throw an error?
|
||||||
|
});
|
||||||
|
|
||||||
|
clkctl1.frg14clksel().write(|w| match clk {
|
||||||
|
Clock::FcnFrgMain => w.sel().main_clk(),
|
||||||
|
Clock::FcnFrgPll => w.sel().frg_pll_clk(),
|
||||||
|
Clock::FcnFrgSfro => w.sel().sfro_clk(),
|
||||||
|
Clock::FcnFrgFfro => w.sel().ffro_clk(),
|
||||||
|
_ => w.sel().none(), // not using frg ...
|
||||||
|
});
|
||||||
|
|
||||||
|
// todo: add support for frg div/mult
|
||||||
|
clkctl1.frg14ctl().write(|w|
|
||||||
|
// SAFETY: unsafe only used for .bits() call
|
||||||
|
unsafe { w.mult().bits(0) });
|
||||||
|
|
||||||
|
enable_and_reset::<FLEXCOMM14>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add special case FLEXCOMM15
|
||||||
|
impl sealed::Sealed for crate::peripherals::FLEXCOMM15 {}
|
||||||
|
|
||||||
|
impl FlexcommLowLevel for crate::peripherals::FLEXCOMM15 {
|
||||||
|
fn reg() -> &'static crate::pac::flexcomm0::RegisterBlock {
|
||||||
|
// SAFETY: safe from single executor, enforce
|
||||||
|
// via peripheral reference lifetime tracking
|
||||||
|
unsafe { &*crate::pac::Flexcomm15::ptr() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn enable(clk: Clock) {
|
||||||
|
// SAFETY: safe from single executor
|
||||||
|
let clkctl1 = unsafe { crate::pac::Clkctl1::steal() };
|
||||||
|
|
||||||
|
clkctl1.fc15fclksel().write(|w| match clk {
|
||||||
|
Clock::Sfro => w.sel().sfro_clk(),
|
||||||
|
Clock::Ffro => w.sel().ffro_clk(),
|
||||||
|
Clock::AudioPll => w.sel().audio_pll_clk(),
|
||||||
|
Clock::Master => w.sel().master_clk(),
|
||||||
|
Clock::FcnFrgMain => w.sel().fcn_frg_clk(),
|
||||||
|
Clock::FcnFrgPll => w.sel().fcn_frg_clk(),
|
||||||
|
Clock::FcnFrgSfro => w.sel().fcn_frg_clk(),
|
||||||
|
Clock::FcnFrgFfro => w.sel().fcn_frg_clk(),
|
||||||
|
Clock::None => w.sel().none(), // no clock? throw an error?
|
||||||
|
});
|
||||||
|
clkctl1.frg15clksel().write(|w| match clk {
|
||||||
|
Clock::FcnFrgMain => w.sel().main_clk(),
|
||||||
|
Clock::FcnFrgPll => w.sel().frg_pll_clk(),
|
||||||
|
Clock::FcnFrgSfro => w.sel().sfro_clk(),
|
||||||
|
Clock::FcnFrgFfro => w.sel().ffro_clk(),
|
||||||
|
_ => w.sel().none(), // not using frg ...
|
||||||
|
});
|
||||||
|
// todo: add support for frg div/mult
|
||||||
|
clkctl1.frg15ctl().write(|w|
|
||||||
|
// SAFETY: unsafe only used for .bits() call
|
||||||
|
unsafe { w.mult().bits(0) });
|
||||||
|
|
||||||
|
enable_and_reset::<FLEXCOMM15>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! into_mode {
|
||||||
|
($mode:ident, $($fc:ident),*) => {
|
||||||
|
paste! {
|
||||||
|
/// Sealed Mode trait
|
||||||
|
trait [<SealedInto $mode:camel>]: FlexcommLowLevel {}
|
||||||
|
|
||||||
|
/// Select mode of operation
|
||||||
|
#[allow(private_bounds)]
|
||||||
|
pub trait [<Into $mode:camel>]: [<SealedInto $mode:camel>] {
|
||||||
|
/// Set mode of operation
|
||||||
|
fn [<into_ $mode>]() {
|
||||||
|
Self::reg().pselid().write(|w| w.persel().[<$mode>]());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$(
|
||||||
|
paste!{
|
||||||
|
impl [<SealedInto $mode:camel>] for crate::peripherals::$fc {}
|
||||||
|
impl [<Into $mode:camel>] for crate::peripherals::$fc {}
|
||||||
|
}
|
||||||
|
)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
into_mode!(usart, FLEXCOMM0, FLEXCOMM1, FLEXCOMM2, FLEXCOMM3, FLEXCOMM4, FLEXCOMM5, FLEXCOMM6, FLEXCOMM7);
|
||||||
|
into_mode!(spi, FLEXCOMM0, FLEXCOMM1, FLEXCOMM2, FLEXCOMM3, FLEXCOMM4, FLEXCOMM5, FLEXCOMM6, FLEXCOMM7, FLEXCOMM14);
|
||||||
|
into_mode!(i2c, FLEXCOMM0, FLEXCOMM1, FLEXCOMM2, FLEXCOMM3, FLEXCOMM4, FLEXCOMM5, FLEXCOMM6, FLEXCOMM7, FLEXCOMM15);
|
||||||
|
|
||||||
|
into_mode!(
|
||||||
|
i2s_transmit,
|
||||||
|
FLEXCOMM0,
|
||||||
|
FLEXCOMM1,
|
||||||
|
FLEXCOMM2,
|
||||||
|
FLEXCOMM3,
|
||||||
|
FLEXCOMM4,
|
||||||
|
FLEXCOMM5,
|
||||||
|
FLEXCOMM6,
|
||||||
|
FLEXCOMM7
|
||||||
|
);
|
||||||
|
|
||||||
|
into_mode!(
|
||||||
|
i2s_receive,
|
||||||
|
FLEXCOMM0,
|
||||||
|
FLEXCOMM1,
|
||||||
|
FLEXCOMM2,
|
||||||
|
FLEXCOMM3,
|
||||||
|
FLEXCOMM4,
|
||||||
|
FLEXCOMM5,
|
||||||
|
FLEXCOMM6,
|
||||||
|
FLEXCOMM7
|
||||||
|
);
|
||||||
1230
embassy-imxrt/src/flexcomm/uart.rs
Normal file
1230
embassy-imxrt/src/flexcomm/uart.rs
Normal file
File diff suppressed because it is too large
Load Diff
@ -13,7 +13,7 @@ use crate::clocks::enable_and_reset;
|
|||||||
use crate::iopctl::IopctlPin;
|
use crate::iopctl::IopctlPin;
|
||||||
pub use crate::iopctl::{AnyPin, DriveMode, DriveStrength, Function, Inverter, Pull, SlewRate};
|
pub use crate::iopctl::{AnyPin, DriveMode, DriveStrength, Function, Inverter, Pull, SlewRate};
|
||||||
use crate::sealed::Sealed;
|
use crate::sealed::Sealed;
|
||||||
use crate::{interrupt, peripherals, Peri, PeripheralType};
|
use crate::{interrupt, peripherals, BitIter, Peri, PeripheralType};
|
||||||
|
|
||||||
// This should be unique per IMXRT package
|
// This should be unique per IMXRT package
|
||||||
const PORT_COUNT: usize = 8;
|
const PORT_COUNT: usize = 8;
|
||||||
@ -63,24 +63,6 @@ fn GPIO_INTA() {
|
|||||||
irq_handler(&GPIO_WAKERS);
|
irq_handler(&GPIO_WAKERS);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "rt")]
|
|
||||||
struct BitIter(u32);
|
|
||||||
|
|
||||||
#[cfg(feature = "rt")]
|
|
||||||
impl Iterator for BitIter {
|
|
||||||
type Item = u32;
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
|
||||||
match self.0.trailing_zeros() {
|
|
||||||
32 => None,
|
|
||||||
b => {
|
|
||||||
self.0 &= !(1 << b);
|
|
||||||
Some(b)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "rt")]
|
#[cfg(feature = "rt")]
|
||||||
fn irq_handler(port_wakers: &[Option<&PortWaker>]) {
|
fn irq_handler(port_wakers: &[Option<&PortWaker>]) {
|
||||||
let reg = unsafe { crate::pac::Gpio::steal() };
|
let reg = unsafe { crate::pac::Gpio::steal() };
|
||||||
|
|||||||
@ -19,6 +19,8 @@ pub(crate) mod fmt;
|
|||||||
|
|
||||||
pub mod clocks;
|
pub mod clocks;
|
||||||
pub mod crc;
|
pub mod crc;
|
||||||
|
pub mod dma;
|
||||||
|
pub mod flexcomm;
|
||||||
pub mod gpio;
|
pub mod gpio;
|
||||||
pub mod iopctl;
|
pub mod iopctl;
|
||||||
pub mod rng;
|
pub mod rng;
|
||||||
@ -129,14 +131,16 @@ pub fn init(config: config::Config) -> Peripherals {
|
|||||||
// before doing anything important.
|
// before doing anything important.
|
||||||
let peripherals = Peripherals::take();
|
let peripherals = Peripherals::take();
|
||||||
|
|
||||||
|
#[cfg(feature = "_time-driver")]
|
||||||
|
time_driver::init(config.time_interrupt_priority);
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
if let Err(e) = clocks::init(config.clocks) {
|
if let Err(e) = clocks::init(config.clocks) {
|
||||||
error!("unable to initialize Clocks for reason: {:?}", e);
|
error!("unable to initialize Clocks for reason: {:?}", e);
|
||||||
// Panic here?
|
// Panic here?
|
||||||
}
|
}
|
||||||
|
dma::init();
|
||||||
}
|
}
|
||||||
#[cfg(feature = "_time-driver")]
|
|
||||||
time_driver::init(config.time_interrupt_priority);
|
|
||||||
gpio::init();
|
gpio::init();
|
||||||
|
|
||||||
peripherals
|
peripherals
|
||||||
@ -145,3 +149,21 @@ pub fn init(config: config::Config) -> Peripherals {
|
|||||||
pub(crate) mod sealed {
|
pub(crate) mod sealed {
|
||||||
pub trait Sealed {}
|
pub trait Sealed {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "rt")]
|
||||||
|
struct BitIter(u32);
|
||||||
|
|
||||||
|
#[cfg(feature = "rt")]
|
||||||
|
impl Iterator for BitIter {
|
||||||
|
type Item = u32;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
match self.0.trailing_zeros() {
|
||||||
|
32 => None,
|
||||||
|
b => {
|
||||||
|
self.0 &= !(1 << b);
|
||||||
|
Some(b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
87
examples/mimxrt6/src/bin/uart-async.rs
Normal file
87
examples/mimxrt6/src/bin/uart-async.rs
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
extern crate embassy_imxrt_examples;
|
||||||
|
|
||||||
|
use defmt::info;
|
||||||
|
use embassy_executor::Spawner;
|
||||||
|
use embassy_imxrt::flexcomm::uart::{self, Async, Uart};
|
||||||
|
use embassy_imxrt::{bind_interrupts, peripherals};
|
||||||
|
use embassy_time::Timer;
|
||||||
|
use {defmt_rtt as _, panic_probe as _};
|
||||||
|
|
||||||
|
bind_interrupts!(struct Irqs {
|
||||||
|
FLEXCOMM2 => uart::InterruptHandler<peripherals::FLEXCOMM2>;
|
||||||
|
FLEXCOMM4 => uart::InterruptHandler<peripherals::FLEXCOMM4>;
|
||||||
|
});
|
||||||
|
|
||||||
|
const BUFLEN: usize = 16;
|
||||||
|
|
||||||
|
#[embassy_executor::task]
|
||||||
|
async fn usart4_task(mut uart: Uart<'static, Async>) {
|
||||||
|
info!("RX Task");
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let mut rx_buf = [0; BUFLEN];
|
||||||
|
uart.read(&mut rx_buf).await.unwrap();
|
||||||
|
info!("usart4: rx_buf {:02x}", rx_buf);
|
||||||
|
|
||||||
|
Timer::after_millis(10).await;
|
||||||
|
|
||||||
|
let tx_buf = [0xaa; BUFLEN];
|
||||||
|
uart.write(&tx_buf).await.unwrap();
|
||||||
|
info!("usart4: tx_buf {:02x}", tx_buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[embassy_executor::task]
|
||||||
|
async fn usart2_task(mut uart: Uart<'static, Async>) {
|
||||||
|
info!("TX Task");
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let tx_buf = [0x55; BUFLEN];
|
||||||
|
uart.write(&tx_buf).await.unwrap();
|
||||||
|
info!("usart2: tx_buf {:02x}", tx_buf);
|
||||||
|
|
||||||
|
Timer::after_millis(10).await;
|
||||||
|
|
||||||
|
let mut rx_buf = [0x00; BUFLEN];
|
||||||
|
uart.read(&mut rx_buf).await.unwrap();
|
||||||
|
info!("usart2: rx_buf {:02x}", rx_buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[embassy_executor::main]
|
||||||
|
async fn main(spawner: Spawner) {
|
||||||
|
let p = embassy_imxrt::init(Default::default());
|
||||||
|
|
||||||
|
info!("UART test start");
|
||||||
|
|
||||||
|
let usart4 = Uart::new_with_rtscts(
|
||||||
|
p.FLEXCOMM4,
|
||||||
|
p.PIO0_29,
|
||||||
|
p.PIO0_30,
|
||||||
|
p.PIO1_0,
|
||||||
|
p.PIO0_31,
|
||||||
|
Irqs,
|
||||||
|
p.DMA0_CH9,
|
||||||
|
p.DMA0_CH8,
|
||||||
|
Default::default(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
spawner.must_spawn(usart4_task(usart4));
|
||||||
|
|
||||||
|
let usart2 = Uart::new_with_rtscts(
|
||||||
|
p.FLEXCOMM2,
|
||||||
|
p.PIO0_15,
|
||||||
|
p.PIO0_16,
|
||||||
|
p.PIO0_18,
|
||||||
|
p.PIO0_17,
|
||||||
|
Irqs,
|
||||||
|
p.DMA0_CH5,
|
||||||
|
p.DMA0_CH4,
|
||||||
|
Default::default(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
spawner.must_spawn(usart2_task(usart2));
|
||||||
|
}
|
||||||
55
examples/mimxrt6/src/bin/uart.rs
Normal file
55
examples/mimxrt6/src/bin/uart.rs
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
extern crate embassy_imxrt_examples;
|
||||||
|
|
||||||
|
use defmt::info;
|
||||||
|
use embassy_executor::Spawner;
|
||||||
|
use embassy_imxrt::flexcomm::uart::{Blocking, Uart, UartRx, UartTx};
|
||||||
|
use embassy_time::Timer;
|
||||||
|
use {defmt_rtt as _, panic_probe as _};
|
||||||
|
|
||||||
|
#[embassy_executor::task]
|
||||||
|
async fn usart4_task(mut uart: UartRx<'static, Blocking>) {
|
||||||
|
info!("RX Task");
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let mut buf = [0; 8];
|
||||||
|
|
||||||
|
Timer::after_millis(10).await;
|
||||||
|
|
||||||
|
uart.blocking_read(&mut buf).unwrap();
|
||||||
|
|
||||||
|
let s = core::str::from_utf8(&buf).unwrap();
|
||||||
|
|
||||||
|
info!("Received '{}'", s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[embassy_executor::task]
|
||||||
|
async fn usart2_task(mut uart: UartTx<'static, Blocking>) {
|
||||||
|
info!("TX Task");
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let buf = "Testing\0".as_bytes();
|
||||||
|
|
||||||
|
uart.blocking_write(buf).unwrap();
|
||||||
|
|
||||||
|
Timer::after_millis(10).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[embassy_executor::main]
|
||||||
|
async fn main(spawner: Spawner) {
|
||||||
|
let p = embassy_imxrt::init(Default::default());
|
||||||
|
|
||||||
|
info!("UART test start");
|
||||||
|
|
||||||
|
let usart4 = Uart::new_blocking(p.FLEXCOMM4, p.PIO0_29, p.PIO0_30, Default::default()).unwrap();
|
||||||
|
|
||||||
|
let (_, usart4) = usart4.split();
|
||||||
|
spawner.must_spawn(usart4_task(usart4));
|
||||||
|
|
||||||
|
let usart2 = UartTx::new_blocking(p.FLEXCOMM2, p.PIO0_15, Default::default()).unwrap();
|
||||||
|
spawner.must_spawn(usart2_task(usart2));
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user