Skip to content

Commit

Permalink
Use embedded-dma traits for DMA buffers
Browse files Browse the repository at this point in the history
  • Loading branch information
kalkyl committed Sep 4, 2020
1 parent e020ccc commit 3b4cc4b
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 202 deletions.
8 changes: 4 additions & 4 deletions examples/i2s-peripheral-demo/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,12 @@ const RED: [u8; 9] = [0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x10, 0xFF];
const APP: () = {
struct Resources {
rgb: Spim<SPIM0>,
transfer: Option<Transfer<&'static mut [i16]>>,
transfer: Option<Transfer<&'static mut [i16; 128]>>,
}

#[init]
fn init(ctx: init::Context) -> init::LateResources {
static mut RX_BUF: [i16; 128] = [0i16; 128];
static mut RX_BUF: [i16; 128] = [0; 128];

let _clocks = hal::clocks::Clocks::new(ctx.device.CLOCK).enable_ext_hfosc();
rtt_init_print!();
Expand Down Expand Up @@ -78,7 +78,7 @@ const APP: () = {
);
init::LateResources {
rgb,
transfer: i2s.rx(&mut RX_BUF[..]).ok(),
transfer: i2s.rx(RX_BUF).ok(),
}
}

Expand All @@ -87,7 +87,7 @@ const APP: () = {
let (rx_buf, i2s) = ctx.resources.transfer.take().unwrap().wait();
if i2s.is_event_triggered(I2SEvent::RxPtrUpdated) {
i2s.reset_event(I2SEvent::RxPtrUpdated);
// Calculate mono summed average of received buffer
//Calculate mono summed average of received buffer
let avg = (rx_buf.iter().map(|x| (*x).abs() as u32).sum::<u32>() / rx_buf.len() as u32)
as u16;
let color = match avg {
Expand Down
5 changes: 1 addition & 4 deletions nrf-hal-common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,7 @@ nb = "1.0.0"
fixed = "1.0.0"
rand_core = "0.5.1"
cfg-if = "0.1.10"

[dependencies.stable_deref_trait]
version = "1.2.0"
default-features = false
embedded-dma = "0.1.1"

[dependencies.void]
default-features = false
Expand Down
228 changes: 34 additions & 194 deletions nrf-hal-common/src/i2s.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,8 @@ use crate::{
pac::generic::Reg,
target_constants::{SRAM_LOWER, SRAM_UPPER},
};
use core::{
ops::{Deref, DerefMut},
sync::atomic::{compiler_fence, Ordering},
};
pub use stable_deref_trait::StableDeref;
use core::sync::atomic::{compiler_fence, Ordering};
use embedded_dma::*;

use i2s::{_EVENTS_RXPTRUPD, _EVENTS_STOPPED, _EVENTS_TXPTRUPD, _TASKS_START, _TASKS_STOP};

Expand All @@ -23,7 +20,7 @@ pub struct I2S {
}

// I2S EasyDMA MAXCNT bit length = 14
const MAX_DMA_MAXCNT: u32 = 16_384;
const MAX_DMA_MAXCNT: u32 = 1 << 14;

impl I2S {
/// Takes ownership of the raw I2S peripheral, returning a safe wrapper in controller mode.
Expand Down Expand Up @@ -265,23 +262,20 @@ impl I2S {
/// Receives data into the given `buffer` until it's filled.
/// Returns a value that represents the in-progress DMA transfer.
#[allow(unused_mut)]
pub fn rx<Word, B>(mut self, mut buffer: B) -> Result<Transfer<B>, Error>
pub fn rx<W, B>(mut self, mut buffer: B) -> Result<Transfer<B>, Error>
where
Word: SupportedWordSize,
B: DerefMut + StableDeref + 'static + I2SBuffer,
B::Target: AsMut<[Word]>,
B: WriteBuffer<Word = W>,
{
if buffer.maxcnt() > MAX_DMA_MAXCNT {
let (ptr, len) = unsafe { buffer.write_buffer() };
let maxcnt = (len / (core::mem::size_of::<u32>() / core::mem::size_of::<W>())) as u32;
if maxcnt > MAX_DMA_MAXCNT {
return Err(Error::BufferTooLong);
}
self.i2s
.rxd
.ptr
.write(|w| unsafe { w.ptr().bits(buffer.ptr()) });
self.i2s
.rxtxd
.maxcnt
.write(|w| unsafe { w.bits(buffer.maxcnt()) });
.write(|w| unsafe { w.ptr().bits(ptr as u32) });
self.i2s.rxtxd.maxcnt.write(|w| unsafe { w.bits(maxcnt) });
Ok(Transfer {
inner: Some(Inner { buffer, i2s: self }),
})
Expand All @@ -292,42 +286,37 @@ impl I2S {
/// into the given `rx_buffer` until it is filled. The buffers must be of equal size.
/// Returns a value that represents the in-progress DMA transfer.
#[allow(unused_mut)]
pub fn transfer<Word, TxB, RxB>(
pub fn transfer<W, TxB, RxB>(
mut self,
tx_buffer: TxB,
mut rx_buffer: RxB,
) -> Result<TransferFullDuplex<TxB, RxB>, Error>
where
Word: SupportedWordSize,
TxB: Deref + StableDeref + 'static + I2SBuffer,
TxB::Target: AsRef<[Word]>,
RxB: DerefMut + StableDeref + 'static + I2SBuffer,
RxB::Target: AsMut<[Word]>,
TxB: ReadBuffer<Word = W>,
RxB: WriteBuffer<Word = W>,
{
if (tx_buffer.ptr() as usize) < SRAM_LOWER || (tx_buffer.ptr() as usize) > SRAM_UPPER {
return Err(Error::DMABufferNotInDataMemory);
}
if tx_buffer.maxcnt() != rx_buffer.maxcnt() {
let (rx_ptr, rx_len) = unsafe { rx_buffer.write_buffer() };
let (tx_ptr, tx_len) = unsafe { tx_buffer.read_buffer() };
let maxcnt = (tx_len / (core::mem::size_of::<u32>() / core::mem::size_of::<W>())) as u32;
if tx_len != rx_len {
return Err(Error::BuffersDontMatch);
}
if tx_buffer.maxcnt() > MAX_DMA_MAXCNT {
if maxcnt > MAX_DMA_MAXCNT {
return Err(Error::BufferTooLong);
}
if (tx_ptr as usize) < SRAM_LOWER || (tx_ptr as usize) > SRAM_UPPER {
return Err(Error::DMABufferNotInDataMemory);
}

self.i2s
.txd
.ptr
.write(|w| unsafe { w.ptr().bits(tx_buffer.ptr()) });
.write(|w| unsafe { w.ptr().bits(tx_ptr as u32) });
self.i2s
.rxd
.ptr
.write(|w| unsafe { w.ptr().bits(rx_buffer.ptr()) });
self.i2s
.rxtxd
.maxcnt
.write(|w| unsafe { w.bits(rx_buffer.maxcnt()) });

self.start();
.write(|w| unsafe { w.ptr().bits(rx_ptr as u32) });
self.i2s.rxtxd.maxcnt.write(|w| unsafe { w.bits(maxcnt) });

Ok(TransferFullDuplex {
inner: Some(InnerFullDuplex {
Expand All @@ -341,28 +330,24 @@ impl I2S {
/// Transmits the given `tx_buffer`.
/// Returns a value that represents the in-progress DMA transfer.
#[allow(unused_mut)]
pub fn tx<Word, B>(mut self, buffer: B) -> Result<Transfer<B>, Error>
pub fn tx<W, B>(mut self, buffer: B) -> Result<Transfer<B>, Error>
where
Word: SupportedWordSize,
B: Deref + StableDeref + 'static + I2SBuffer,
B::Target: AsRef<[Word]>,
B: ReadBuffer<Word = W>,
{
if (buffer.ptr() as usize) < SRAM_LOWER || (buffer.ptr() as usize) > SRAM_UPPER {
return Err(Error::DMABufferNotInDataMemory);
}

if buffer.maxcnt() > MAX_DMA_MAXCNT {
let (ptr, len) = unsafe { buffer.read_buffer() };
let maxcnt = (len / (core::mem::size_of::<u32>() / core::mem::size_of::<W>())) as u32;
if maxcnt > MAX_DMA_MAXCNT {
return Err(Error::BufferTooLong);
}
if (ptr as usize) < SRAM_LOWER || (ptr as usize) > SRAM_UPPER {
return Err(Error::DMABufferNotInDataMemory);
}

self.i2s
.txd
.ptr
.write(|w| unsafe { w.ptr().bits(buffer.ptr()) });
self.i2s
.rxtxd
.maxcnt
.write(|w| unsafe { w.bits(buffer.maxcnt()) });
.write(|w| unsafe { w.ptr().bits(ptr as u32) });
self.i2s.rxtxd.maxcnt.write(|w| unsafe { w.bits(maxcnt) });

Ok(Transfer {
inner: Some(Inner { buffer, i2s: self }),
Expand Down Expand Up @@ -598,132 +583,6 @@ pub enum I2SEvent {
Stopped,
}

/// Trait to represent valid sample buffers.
pub trait I2SBuffer: private::Sealed {
fn ptr(&self) -> u32;
fn maxcnt(&self) -> u32;
}

impl private::Sealed for &[i8] {}
impl I2SBuffer for &[i8] {
fn ptr(&self) -> u32 {
self.as_ptr() as u32
}
fn maxcnt(&self) -> u32 {
self.len() as u32 / 4
}
}

impl private::Sealed for &[i16] {}
impl I2SBuffer for &[i16] {
fn ptr(&self) -> u32 {
self.as_ptr() as u32
}
fn maxcnt(&self) -> u32 {
self.len() as u32 / 2
}
}

impl private::Sealed for &[i32] {}
impl I2SBuffer for &[i32] {
fn ptr(&self) -> u32 {
self.as_ptr() as u32
}
fn maxcnt(&self) -> u32 {
self.len() as u32
}
}

impl private::Sealed for &[u8] {}
impl I2SBuffer for &[u8] {
fn ptr(&self) -> u32 {
self.as_ptr() as u32
}
fn maxcnt(&self) -> u32 {
self.len() as u32 / 8
}
}

impl private::Sealed for &[u16] {}
impl I2SBuffer for &[u16] {
fn ptr(&self) -> u32 {
self.as_ptr() as u32
}
fn maxcnt(&self) -> u32 {
self.len() as u32 / 4
}
}

impl private::Sealed for &[u32] {}
impl I2SBuffer for &[u32] {
fn ptr(&self) -> u32 {
self.as_ptr() as u32
}
fn maxcnt(&self) -> u32 {
self.len() as u32 / 2
}
}

impl private::Sealed for &mut [i8] {}
impl I2SBuffer for &mut [i8] {
fn ptr(&self) -> u32 {
self.as_ptr() as u32
}
fn maxcnt(&self) -> u32 {
self.len() as u32 / 4
}
}

impl private::Sealed for &mut [i16] {}
impl I2SBuffer for &mut [i16] {
fn ptr(&self) -> u32 {
self.as_ptr() as u32
}
fn maxcnt(&self) -> u32 {
self.len() as u32 / 2
}
}

impl private::Sealed for &mut [i32] {}
impl I2SBuffer for &mut [i32] {
fn ptr(&self) -> u32 {
self.as_ptr() as u32
}
fn maxcnt(&self) -> u32 {
self.len() as u32
}
}

impl private::Sealed for &mut [u8] {}
impl I2SBuffer for &mut [u8] {
fn ptr(&self) -> u32 {
self.as_ptr() as u32
}
fn maxcnt(&self) -> u32 {
self.len() as u32 / 8
}
}

impl private::Sealed for &mut [u16] {}
impl I2SBuffer for &mut [u16] {
fn ptr(&self) -> u32 {
self.as_ptr() as u32
}
fn maxcnt(&self) -> u32 {
self.len() as u32 / 4
}
}

impl private::Sealed for &mut [u32] {}
impl I2SBuffer for &mut [u32] {
fn ptr(&self) -> u32 {
self.as_ptr() as u32
}
fn maxcnt(&self) -> u32 {
self.len() as u32 / 2
}
}

/// A DMA transfer
pub struct Transfer<B> {
inner: Option<Inner<B>>,
Expand Down Expand Up @@ -791,22 +650,3 @@ impl<TxB, RxB> Drop for TransferFullDuplex<TxB, RxB> {
}
}
}

pub trait SupportedWordSize: private::Sealed {}
impl private::Sealed for i8 {}
impl SupportedWordSize for i8 {}
impl private::Sealed for i16 {}
impl SupportedWordSize for i16 {}
impl private::Sealed for i32 {}
impl SupportedWordSize for i32 {}
impl private::Sealed for u8 {}
impl SupportedWordSize for u8 {}
impl private::Sealed for u16 {}
impl SupportedWordSize for u16 {}
impl private::Sealed for u32 {}
impl SupportedWordSize for u32 {}

mod private {
/// Prevents code outside of the parent module from implementing traits.
pub trait Sealed {}
}

0 comments on commit 3b4cc4b

Please sign in to comment.