use core::marker::PhantomData;
use crate::pac;
const DMA_BASE: usize = 0x0500;
#[cfg_attr(not(feature = "critical-section"), allow(dead_code))]
const DMACTL0: usize = DMA_BASE + 0x00; #[cfg_attr(not(feature = "critical-section"), allow(dead_code))]
const DMACTL1: usize = DMA_BASE + 0x02; const DMAIV: usize = DMA_BASE + 0x0E;
const CH_STRIDE: usize = 0x10;
const CH0_BASE: usize = DMA_BASE + 0x10;
const CTL: usize = 0x00; #[cfg_attr(not(feature = "critical-section"), allow(dead_code))]
const SA: usize = 0x02; #[cfg_attr(not(feature = "critical-section"), allow(dead_code))]
const DA: usize = 0x06; const SZ: usize = 0x0A;
#[cfg_attr(not(feature = "critical-section"), allow(dead_code))]
const DMAREQ: u16 = 1 << 0; #[cfg_attr(not(feature = "critical-section"), allow(dead_code))]
const DMAIE: u16 = 1 << 2; const DMAIFG: u16 = 1 << 3; const DMAEN: u16 = 1 << 4; #[cfg_attr(not(feature = "critical-section"), allow(dead_code))]
const DMALEVEL: u16 = 1 << 5; #[cfg_attr(not(feature = "critical-section"), allow(dead_code))]
const DMASRCBYTE: u16 = 1 << 6; #[cfg_attr(not(feature = "critical-section"), allow(dead_code))]
const DMADSTBYTE: u16 = 1 << 7; #[cfg_attr(not(feature = "critical-section"), allow(dead_code))]
const DMASRCINCR_SHIFT: u16 = 8; #[cfg_attr(not(feature = "critical-section"), allow(dead_code))]
const DMADSTINCR_SHIFT: u16 = 10; #[cfg_attr(not(feature = "critical-section"), allow(dead_code))]
const DMADT_SINGLE: u16 = 0 << 12; #[cfg_attr(not(feature = "critical-section"), allow(dead_code))]
const DMADT_BLOCK: u16 = 1 << 12;
#[cfg_attr(not(feature = "critical-section"), allow(dead_code))]
const TSEL_MASK: u16 = 0x001F;
#[inline(always)]
unsafe fn read_reg(addr: usize) -> u16 {
(addr as *const u16).read_volatile()
}
#[inline(always)]
#[cfg_attr(not(feature = "critical-section"), allow(dead_code))]
unsafe fn write_reg(addr: usize, val: u16) {
(addr as *mut u16).write_volatile(val);
}
#[inline(always)]
#[cfg_attr(not(feature = "critical-section"), allow(dead_code))]
fn barrier() {
unsafe { core::arch::asm!("", options(nostack, preserves_flags)) }
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[repr(u8)]
pub enum TriggerSource {
DmaReq = 0,
Ta0Ccr0 = 1,
Ta0Ccr2 = 2,
Ta1Ccr0 = 3,
Ta1Ccr2 = 4,
Ta2Ccr0 = 5,
Ta3Ccr0 = 6,
Tb0Ccr0 = 7,
Tb0Ccr2 = 8,
UcA0Rx = 14,
UcA0Tx = 15,
UcA1Rx = 16,
UcA1Tx = 17,
UcB0Rx0 = 18,
UcB0Tx0 = 19,
UcB0Rx1 = 20,
UcB0Tx1 = 21,
UcB0Rx2 = 22,
UcB0Tx2 = 23,
UcB0Rx3 = 24,
UcB0Tx3 = 25,
Adc12 = 26,
MpyReady = 29,
PrevChannelDone = 30,
DmaE0 = 31,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum AddrMode {
Fixed,
Decrement,
Increment,
}
impl AddrMode {
#[cfg_attr(not(feature = "critical-section"), allow(dead_code))]
const fn bits(self) -> u16 {
match self {
AddrMode::Fixed => 0b00,
AddrMode::Decrement => 0b10,
AddrMode::Increment => 0b11,
}
}
}
pub struct Channel<const N: u8> {
_not_send: PhantomData<*const ()>,
}
pub struct Channels {
pub ch0: Channel<0>,
pub ch1: Channel<1>,
pub ch2: Channel<2>,
}
pub trait DmaExt {
fn split(self) -> Channels;
}
impl DmaExt for pac::Dma {
fn split(self) -> Channels {
self.dmactl4().write(|w| w.dmarmwdis().set_bit());
Channels {
ch0: Channel { _not_send: PhantomData },
ch1: Channel { _not_send: PhantomData },
ch2: Channel { _not_send: PhantomData },
}
}
}
impl<const N: u8> Channel<N> {
const BASE: usize = CH0_BASE + CH_STRIDE * N as usize;
#[inline]
fn ctl(&self) -> u16 {
unsafe { read_reg(Self::BASE + CTL) }
}
pub fn remaining(&self) -> u16 {
unsafe { read_reg(Self::BASE + SZ) }
}
pub fn is_done(&self) -> bool {
self.ctl() & DMAIFG != 0
}
pub fn is_armed(&self) -> bool {
self.ctl() & DMAEN != 0
}
}
#[cfg(feature = "critical-section")]
impl<const N: u8> Channel<N> {
fn set_trigger(&mut self, trigger: TriggerSource) {
let (reg, shift) = match N {
0 => (DMACTL0, 0),
1 => (DMACTL0, 8),
_ => (DMACTL1, 0),
};
critical_section::with(|_| unsafe {
let cur = read_reg(reg);
write_reg(
reg,
(cur & !(TSEL_MASK << shift)) | ((trigger as u16) << shift),
);
});
}
unsafe fn set_addresses(&mut self, src: usize, dst: usize, count: u16) {
write_reg(Self::BASE + SA, src as u16);
write_reg(Self::BASE + SA + 2, 0);
write_reg(Self::BASE + DA, dst as u16);
write_reg(Self::BASE + DA + 2, 0);
write_reg(Self::BASE + SZ, count);
}
unsafe fn block_transfer(
&mut self,
src: usize,
src_mode: AddrMode,
dst: usize,
dst_mode: AddrMode,
width_bits: u16,
count: u16,
) {
self.set_trigger(TriggerSource::DmaReq);
self.set_addresses(src, dst, count);
let ctl = DMADT_BLOCK
| (src_mode.bits() << DMASRCINCR_SHIFT)
| (dst_mode.bits() << DMADSTINCR_SHIFT)
| width_bits
| DMAEN;
write_reg(Self::BASE + CTL, ctl);
barrier();
write_reg(Self::BASE + CTL, ctl | DMAREQ);
while !self.is_done() {}
self.clear_done();
barrier();
}
pub fn copy_bytes(&mut self, src: &[u8], dst: &mut [u8]) -> usize {
let n = src.len().min(dst.len());
if n > 0 {
unsafe {
self.block_transfer(
src.as_ptr() as usize,
AddrMode::Increment,
dst.as_mut_ptr() as usize,
AddrMode::Increment,
DMASRCBYTE | DMADSTBYTE,
n as u16,
);
}
}
n
}
pub fn copy_words(&mut self, src: &[u16], dst: &mut [u16]) -> usize {
let n = src.len().min(dst.len());
if n > 0 {
unsafe {
self.block_transfer(
src.as_ptr() as usize,
AddrMode::Increment,
dst.as_mut_ptr() as usize,
AddrMode::Increment,
0, n as u16,
);
}
}
n
}
pub fn copy_bytes_reversed(&mut self, src: &[u8], dst: &mut [u8]) -> usize {
let n = src.len().min(dst.len());
if n > 0 {
unsafe {
self.block_transfer(
src.as_ptr() as usize,
AddrMode::Increment,
dst.as_mut_ptr().add(n - 1) as usize,
AddrMode::Decrement,
DMASRCBYTE | DMADSTBYTE,
n as u16,
);
}
}
n
}
pub fn fill_bytes(&mut self, value: u8, dst: &mut [u8]) -> usize {
let n = dst.len();
if n > 0 {
unsafe {
self.block_transfer(
core::ptr::addr_of!(value) as usize,
AddrMode::Fixed,
dst.as_mut_ptr() as usize,
AddrMode::Increment,
DMASRCBYTE | DMADSTBYTE,
n as u16,
);
}
}
n
}
pub unsafe fn arm_single_bytes(
&mut self,
trigger: TriggerSource,
src: *const u8,
src_mode: AddrMode,
dst: *mut u8,
dst_mode: AddrMode,
count: u16,
) {
self.arm_single(
trigger,
src as usize,
src_mode,
dst as usize,
dst_mode,
DMASRCBYTE | DMADSTBYTE,
count,
);
}
pub unsafe fn arm_single_words(
&mut self,
trigger: TriggerSource,
src: *const u16,
src_mode: AddrMode,
dst: *mut u16,
dst_mode: AddrMode,
count: u16,
) {
self.arm_single(
trigger,
src as usize,
src_mode,
dst as usize,
dst_mode,
0, count,
);
}
unsafe fn arm_single(
&mut self,
trigger: TriggerSource,
src: usize,
src_mode: AddrMode,
dst: usize,
dst_mode: AddrMode,
width_bits: u16,
count: u16,
) {
self.set_trigger(trigger);
self.set_addresses(src, dst, count);
let ctl = DMADT_SINGLE
| (src_mode.bits() << DMASRCINCR_SHIFT)
| (dst_mode.bits() << DMADSTINCR_SHIFT)
| width_bits
| DMAEN;
barrier();
write_reg(Self::BASE + CTL, ctl);
}
pub unsafe fn consume_stale_trigger_word(
&mut self,
trigger: TriggerSource,
src: *const u16,
dst: *mut u16,
) -> bool {
self.set_trigger(trigger);
self.set_addresses(src as usize, dst as usize, 1);
write_reg(Self::BASE + CTL, DMADT_SINGLE | DMALEVEL | DMAEN);
let mut spins = 0u16;
while self.ctl() & DMAIFG == 0 && spins < 100 {
spins += 1;
}
let fired = self.ctl() & DMAIFG != 0;
critical_section::with(|_| unsafe {
write_reg(Self::BASE + CTL, 0);
});
barrier();
fired
}
pub fn request(&mut self) {
critical_section::with(|_| unsafe {
write_reg(Self::BASE + CTL, self.ctl() | DMAREQ);
});
}
pub fn disarm(&mut self) {
critical_section::with(|_| unsafe {
write_reg(Self::BASE + CTL, self.ctl() & !DMAEN);
});
}
pub fn clear_done(&mut self) {
critical_section::with(|_| unsafe {
write_reg(Self::BASE + CTL, self.ctl() & !DMAIFG);
});
}
pub fn wait_done(&mut self) {
while !self.is_done() {}
self.clear_done();
barrier();
}
pub fn wait_done_bounded(&mut self, max_spins: u32) -> bool {
let mut spins = 0u32;
while !self.is_done() {
spins += 1;
if spins >= max_spins {
self.disarm();
if !self.is_done() {
barrier();
return false;
}
break;
}
}
self.clear_done();
barrier();
true
}
pub fn enable_done_interrupt(&mut self) {
critical_section::with(|_| unsafe {
write_reg(Self::BASE + CTL, self.ctl() | DMAIE);
});
}
pub fn disable_done_interrupt(&mut self) {
critical_section::with(|_| unsafe {
write_reg(Self::BASE + CTL, self.ctl() & !DMAIE);
});
}
}
pub fn read_iv() -> u16 {
unsafe { read_reg(DMAIV) }
}