DMA an I2S 2-ch ADC to separate blocks of memory to avoid post-sorting

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

DMA an I2S 2-ch ADC to separate blocks of memory to avoid post-sorting

1,007 Views
madisonchou
Contributor III

Hi, I have an ADC with I2S interface on K22.   I can DMA the data into memory with ping-pong scheme.  K22 processes the data at INTHALF and INTMAJOR with leisure.   The ADC has Left and Right channels.   So the data in memory is L R L R... alternatively.  It will be really nice if the DMA can put the L and R channel data into a different block.  This will save processing time for not having to sort the L R.   I read thru K22 Ref Manual and couldn't figure out a straight forward way of doing so.   Has anyone done such a thing?   If so, do you mind sharing??

Labels (1)
0 Kudos
7 Replies

772 Views
madisonchou
Contributor III

the attachment...

0 Kudos

772 Views
madisonchou
Contributor III

Code using UART0 is attached.

0 Kudos

772 Views
jorge_a_vazquez
NXP Employee
NXP Employee

Hi Jei Chou 

Regarding your application, are you using SDK driver? or you have a baremetal project?

You can do this implementation with the DMA feature minor destination offset, please check the "Destination Minor Loop Offset enable" bit in the DMA_TCDn_NBYTES_MLOFFYES register.

You will have one buffer split in two parts ("L" and "R"), when the transfer is made, you will manage the change of the destination address with the MLOFFYES and the DOFF features. DMA will wirte the first word L and then the MLOFF will change the destination address to the half of your buffer to write the first word R, after this your DOFF will change the destination address to return to transfer the second word L, and so on.

Your configuration will be something like:

Source Address: I2S_RDR

Source Offset: 0

Source Last: 0

Destination Address: &buffer[0]

Destination Offset (DOFF): your_buffer_size/2 * 4 bytes

NBYTES: 2 * 4 bytes

SSIZE: 4 bytes

MLOFF: - ( your_buffer_size/2 * 4 bytes * 2 - 4) 

I recommend you to check the following document, and if you have any question.

https://www.nxp.com/docs/en/application-note/AN4520.pdf

Best Regards

Jorge Alcala

0 Kudos

772 Views
madisonchou
Contributor III

Hi Jorge,

Thanks for the support.   I have tried and I am not getting the expected result.   I reduced the code to bare minimum.  It is listed below.   In it, I just use LPUART0 in place of I2S.   It is set to 115200 BAUD.  It can be easily reproduced using any Freedom boards.  I am using FRDM-K22.   I am using MCUxpresso V10.0.2_411 with recent SDK for K22 (SDK_2.2_MK22FN512xxx12.zip, Build Date: 2017-07-06).

Start a bare metal "Hello World" empty project.  Add the subroutine code below.  Add a call to the subroutine after printf("Hello World\n") in main().  Once the code is built and run, in a Terminal program, just type "0", "1", "0", "1",....  The idea is have 0x30, 0x31, 0x30, 0x31, .... separated into different blocks.

Also shown below is the memory browser of the DMA buffer.   In 1st location (0x2000 0400) is 0x00000830, which is char "0".  A block away at 0x2000 0440, it has 0x00001000, not the expected 0x00000831 (char "1").  Further, all other locations are 0.   In other words, the DMA keeps dumping the received characters in the first location of the two blocks.

Anything you can see that I am not doing correctly??

Jei

/* TODO: insert other definitions and declarations here. */
#define DMA_SIZE 32 // DMA size
__attribute__ ((aligned( 1024 ))) int32_t m_iDmaBuf[ DMA_SIZE ]; // 32*4 = 128B

void Init_and_Start_DMA_on_LPUART0( void )
{
int i = 0 ;
for ( i = 0; i < DMA_SIZE; i++ ) m_iDmaBuf[ i ] = 0;

SIM->SOPT2 |= SIM_SOPT2_LPUARTSRC( 1 ) | SIM_SOPT2_PLLFLLSEL( 3 ); // LPUART0 uses MCGPLLCLK off IRC48M
SIM->SCGC5 |= SIM_SCGC5_PORTC_MASK | SIM_SCGC5_PORTD_MASK; // Clock to GPIO ports C & D
SIM->SCGC6 |= SIM_SCGC6_DMAMUX_MASK | SIM_SCGC6_LPUART0_MASK; // Clock to LPUART0, DMAMUX
SIM->SCGC7 |= SIM_SCGC7_DMA_MASK; // Clock to DMA

// Init LPUART0 pins (debug port)...
PORTC->PCR[ 3 ] = PORT_PCR_MUX( 7 ); // This pin as LPUART0 Rx, FRDM-K22 J1-14
PORTC->PCR[ 4 ] = PORT_PCR_MUX( 7 ); // This pin as LPUART0 Tx, FRDM-K22 J24-9

// Initialize LPUART0 for 115200 BAUD
LPUART0->BAUD &= ~LPUART_BAUD_SBR_MASK; // SBR bits is not 0 by default
LPUART0->BAUD |= LPUART_BAUD_SBR( 26 ); // 48M/115200/16 = 26.0416666
LPUART0->BAUD |= LPUART_BAUD_RDMAE_MASK; // Enable RX DMA
LPUART0->CTRL |= LPUART_CTRL_TE_MASK | LPUART_CTRL_RE_MASK; // Enable tx/rx

// Initialize DMA
DMA0->TCD[ 0 ].SADDR = (uint32_t) &LPUART0->DATA; // LPUART0's DATA register
DMA0->TCD[ 0 ].SOFF = 0; // Src addr doesn't change
DMA0->TCD[ 0 ].SLAST = 0; // Src addr doens't change
DMA0->TCD[ 0 ].ATTR = DMA_ATTR_SSIZE( 2 ) | DMA_ATTR_DSIZE( 2 ); // 32-bit src & dst
DMA0->TCD[ 0 ].DADDR = (uint32_t) &m_iDmaBuf[ 0 ]; // Dst addr
DMA0->TCD[ 0 ].DOFF = ( DMA_SIZE / 2 ) * 4; // Dst addr changed to a block away after xfer; This separate L & R data to separate blocks
DMA0->TCD[ 0 ].NBYTES_MLOFFYES = \
DMA_NBYTES_MLOFFYES_MLOFF( -( DMA_SIZE / 2 ) * 4 * 2 - 4 ) | \
DMA_NBYTES_MLOFFYES_NBYTES( 2 * 4 );
DMA0->TCD[ 0 ].CITER_ELINKNO = DMA_SIZE; // # xfer
DMA0->TCD[ 0 ].DLAST_SGA = 0; // After major loop completed (n xfers), go back to &m_iDmaBuf[ 0 ]
DMA0->TCD[ 0 ].BITER_ELINKNO = DMA_SIZE; // # xfer
DMA0->TCD[ 0 ].CSR = 0; // Leave ERQ set after done w/ DMA block; no int

DMAMUX->CHCFG[ 0 ] = DMAMUX_CHCFG_ENBL_MASK | DMAMUX_CHCFG_SOURCE( 58 ); // DMA_CH_REQ_LPUART0_RX

DMA0->ERQ |= DMA_ERQ_ERQ0_MASK; // Enable DMA CH0; DMA never stop
}

pastedImage_4.png

0 Kudos

772 Views
jorge_a_vazquez
NXP Employee
NXP Employee

Hi Jei Chou

Sorry, I forget to mentioned that you need to have a fifo buffer set with watermark of 2 elements. Problem here is that DMA trigger need transfer L and R values, one in your m_iDmaBuf and the other one in the half of this buffer, so basically, you can see this as one transfer of 8 bytes split in to parts. Also, please consider that LPUART is set to work with 8bit data, and in I2S you work with 32 bit data.

Could you please try this setup in a peripheral with FIFO buffer? and set the FIFO to 2 values.

Best Regards

Jorge Alcala

0 Kudos

772 Views
madisonchou
Contributor III

Hi Jorge,

LPUART doesn't have FIFO.  So I can't try using the minimum bare metal.   Later, I will switch to UART0 and try.

But I did try it on my I2S setup w/ watermark set to 2.   It doesn't seems to work.  Behavior is the same in that only the 1st-pair of locations are filled.

Jei

0 Kudos

772 Views
madisonchou
Contributor III

Hi Jorge,

Result is the same when use UART0.

I also retry my I2S0.   It is re-confirmed the DMA xfer goes only to the 1st-pair of locations.

Any other idea?

Jei

0 Kudos