mirror of
https://github.com/embassy-rs/embassy.git
synced 2025-09-26 20:00:27 +00:00
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
|
||||
|
||||
* CRC
|
||||
* DMA
|
||||
* GPIO
|
||||
* 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;
|
||||
pub use crate::iopctl::{AnyPin, DriveMode, DriveStrength, Function, Inverter, Pull, SlewRate};
|
||||
use crate::sealed::Sealed;
|
||||
use crate::{interrupt, peripherals, Peri, PeripheralType};
|
||||
use crate::{interrupt, peripherals, BitIter, Peri, PeripheralType};
|
||||
|
||||
// This should be unique per IMXRT package
|
||||
const PORT_COUNT: usize = 8;
|
||||
@ -63,24 +63,6 @@ fn GPIO_INTA() {
|
||||
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")]
|
||||
fn irq_handler(port_wakers: &[Option<&PortWaker>]) {
|
||||
let reg = unsafe { crate::pac::Gpio::steal() };
|
||||
|
@ -19,6 +19,8 @@ pub(crate) mod fmt;
|
||||
|
||||
pub mod clocks;
|
||||
pub mod crc;
|
||||
pub mod dma;
|
||||
pub mod flexcomm;
|
||||
pub mod gpio;
|
||||
pub mod iopctl;
|
||||
pub mod rng;
|
||||
@ -129,14 +131,16 @@ pub fn init(config: config::Config) -> Peripherals {
|
||||
// before doing anything important.
|
||||
let peripherals = Peripherals::take();
|
||||
|
||||
#[cfg(feature = "_time-driver")]
|
||||
time_driver::init(config.time_interrupt_priority);
|
||||
|
||||
unsafe {
|
||||
if let Err(e) = clocks::init(config.clocks) {
|
||||
error!("unable to initialize Clocks for reason: {:?}", e);
|
||||
// Panic here?
|
||||
}
|
||||
dma::init();
|
||||
}
|
||||
#[cfg(feature = "_time-driver")]
|
||||
time_driver::init(config.time_interrupt_priority);
|
||||
gpio::init();
|
||||
|
||||
peripherals
|
||||
@ -145,3 +149,21 @@ pub fn init(config: config::Config) -> Peripherals {
|
||||
pub(crate) mod 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