Skip to content

Commit

Permalink
Merge pull request #225 from kalkyl/i2s-dma2
Browse files Browse the repository at this point in the history
Add check for I2S buffer pointer alignment
  • Loading branch information
jonas-schievink authored Sep 18, 2020
2 parents af83e6e + fe2e822 commit 2913282
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 16 deletions.
28 changes: 16 additions & 12 deletions examples/i2s-controller-demo/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,14 @@ use {
rtt_target::{rprintln, rtt_init_print},
};

#[repr(align(4))]
struct Aligned<T: ?Sized>(T);

#[rtic::app(device = crate::hal::pac, peripherals = true, monotonic = rtic::cyccnt::CYCCNT)]
const APP: () = {
struct Resources {
signal_buf: &'static [i16],
mute_buf: &'static [i16],
signal_buf: &'static [i16; 32],
mute_buf: &'static [i16; 32],
#[init(None)]
queue: Option<Queue<State, U256>>,
producer: Producer<'static, State, U256>,
Expand All @@ -46,19 +49,20 @@ const APP: () = {
btn1: Pin<Input<PullUp>>,
btn2: Pin<Input<PullUp>>,
led: Pin<Output<PushPull>>,
transfer: Option<Transfer<&'static [i16]>>,
transfer: Option<Transfer<&'static [i16; 32]>>,
}

#[init(resources = [queue], spawn = [tick])]
fn init(mut ctx: init::Context) -> init::LateResources {
static mut MUTE_BUF: [i16; 32] = [0i16; 32];
static mut SIGNAL_BUF: [i16; 32] = [0i16; 32];
// The I2S buffer address must be 4 byte aligned.
static mut MUTE_BUF: Aligned<[i16; 32]> = Aligned([0i16; 32]);
static mut SIGNAL_BUF: Aligned<[i16; 32]> = Aligned([0i16; 32]);

// Fill signal buffer with triangle waveform, 2 channels interleaved
let len = SIGNAL_BUF.len() / 2;
let len = SIGNAL_BUF.0.len() / 2;
for x in 0..len {
SIGNAL_BUF[2 * x] = triangle_wave(x as i32, len, 2048, 0, 1) as i16;
SIGNAL_BUF[2 * x + 1] = triangle_wave(x as i32, len, 2048, 0, 1) as i16;
SIGNAL_BUF.0[2 * x] = triangle_wave(x as i32, len, 2048, 0, 1) as i16;
SIGNAL_BUF.0[2 * x + 1] = triangle_wave(x as i32, len, 2048, 0, 1) as i16;
}

let _clocks = hal::clocks::Clocks::new(ctx.device.CLOCK).enable_ext_hfosc();
Expand All @@ -72,8 +76,8 @@ const APP: () = {
// Configure I2S controller
let mck_pin = p0.p0_28.into_push_pull_output(Level::Low).degrade();
let sck_pin = p0.p0_29.into_push_pull_output(Level::Low).degrade();
let lrck_pin = p0.p0_31.into_push_pull_output(Level::Low).degrade();
let sdout_pin = p0.p0_30.into_push_pull_output(Level::Low).degrade();
let lrck_pin = p0.p0_31.into_push_pull_output(Level::Low).degrade();

let i2s = I2S::new_controller(
ctx.device.I2S,
Expand Down Expand Up @@ -124,9 +128,9 @@ const APP: () = {
led: p0.p0_13.into_push_pull_output(Level::High).degrade(),
uarte,
uarte_timer: Timer::new(ctx.device.TIMER0),
transfer: i2s.tx(&MUTE_BUF[..]).ok(),
signal_buf: &SIGNAL_BUF[..],
mute_buf: &MUTE_BUF[..],
transfer: i2s.tx(&MUTE_BUF.0).ok(),
signal_buf: &SIGNAL_BUF.0,
mute_buf: &MUTE_BUF.0,
}
}

Expand Down
10 changes: 7 additions & 3 deletions examples/i2s-peripheral-demo/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ use {
rtt_target::{rprintln, rtt_init_print},
};

#[repr(align(4))]
struct Aligned<T: ?Sized>(T);

const OFF: [u8; 9] = [0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF];
const GREEN: [u8; 9] = [0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x10, 0x00, 0xFF];
const ORANGE: [u8; 9] = [0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x10, 0x10, 0xFF];
Expand All @@ -34,7 +37,8 @@ const APP: () = {

#[init]
fn init(ctx: init::Context) -> init::LateResources {
static mut RX_BUF: [i16; 128] = [0; 128];
// The I2S buffer address must be 4 byte aligned.
static mut RX_BUF: Aligned<[i16; 128]> = Aligned([0; 128]);

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

Expand All @@ -87,7 +91,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
38 changes: 37 additions & 1 deletion nrf-hal-common/src/i2s.rs
Original file line number Diff line number Diff line change
Expand Up @@ -259,14 +259,20 @@ impl I2S {
_ => Channels::Right,
}
}

/// Receives data into the given `buffer` until it's filled.
/// Buffer address must be 4 byte aligned and located in RAM.
/// Returns a value that represents the in-progress DMA transfer.
#[allow(unused_mut)]
pub fn rx<W, B>(mut self, mut buffer: B) -> Result<Transfer<B>, Error>
where
W: SupportedWordSize,
B: WriteBuffer<Word = W>,
{
let (ptr, len) = unsafe { buffer.write_buffer() };
if ptr as u32 % 4 != 0 {
return Err(Error::BufferMisaligned);
}
let maxcnt = (len / (core::mem::size_of::<u32>() / core::mem::size_of::<W>())) as u32;
if maxcnt > MAX_DMA_MAXCNT {
return Err(Error::BufferTooLong);
Expand All @@ -283,7 +289,8 @@ impl I2S {

/// Full duplex DMA transfer.
/// Transmits the given `tx_buffer` while simultaneously receiving data
/// into the given `rx_buffer` until it is filled. The buffers must be of equal size.
/// into the given `rx_buffer` until it is filled.
/// The buffers must be of equal size and their addresses must be 4 byte aligned and located in RAM.
/// Returns a value that represents the in-progress DMA transfer.
#[allow(unused_mut)]
pub fn transfer<W, TxB, RxB>(
Expand All @@ -292,11 +299,15 @@ impl I2S {
mut rx_buffer: RxB,
) -> Result<TransferFullDuplex<TxB, RxB>, Error>
where
W: SupportedWordSize,
TxB: ReadBuffer<Word = W>,
RxB: WriteBuffer<Word = W>,
{
let (rx_ptr, rx_len) = unsafe { rx_buffer.write_buffer() };
let (tx_ptr, tx_len) = unsafe { tx_buffer.read_buffer() };
if tx_ptr as u32 % 4 != 0 || rx_ptr as u32 % 4 != 0 {
return Err(Error::BufferMisaligned);
}
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);
Expand Down Expand Up @@ -328,13 +339,18 @@ impl I2S {
}

/// Transmits the given `tx_buffer`.
/// Buffer address must be 4 byte aligned and located in RAM.
/// Returns a value that represents the in-progress DMA transfer.
#[allow(unused_mut)]
pub fn tx<W, B>(mut self, buffer: B) -> Result<Transfer<B>, Error>
where
W: SupportedWordSize,
B: ReadBuffer<Word = W>,
{
let (ptr, len) = unsafe { buffer.read_buffer() };
if ptr as u32 % 4 != 0 {
return Err(Error::BufferMisaligned);
}
let maxcnt = (len / (core::mem::size_of::<u32>() / core::mem::size_of::<W>())) as u32;
if maxcnt > MAX_DMA_MAXCNT {
return Err(Error::BufferTooLong);
Expand Down Expand Up @@ -468,6 +484,7 @@ pub enum Error {
DMABufferNotInDataMemory,
BufferTooLong,
BuffersDontMatch,
BufferMisaligned,
}

/// I2S Mode
Expand Down Expand Up @@ -650,3 +667,22 @@ 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 u8 {}
impl SupportedWordSize for u8 {}
impl private::Sealed for i16 {}
impl SupportedWordSize for i16 {}
impl private::Sealed for u16 {}
impl SupportedWordSize for u16 {}
impl private::Sealed for i32 {}
impl SupportedWordSize for i32 {}
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 2913282

Please sign in to comment.