DMA Configuration for I2S

Tip / Sign in to post questions, reply, level up, and achieve exciting badges. Know more

cross mob
lock attach
Attachments are accessible only for community members.
Anonymous
Not applicable

Hello!

   

I've attached my project for perhaps a bit more ease of communication here. The application of the PSOC 5 chip I am using is for the front display panel of an audio tuner.

   


If you take a look, you will see that I am using four blocks: an SPI Master, and I2S Master, and two DMA blocks. The SPI is driving a panel of LED's, and functions properly. Code concerning the LEDs has been omitted from my main file (main1.c) for ease of use. The I2S reads in data from an SRC, and the WS, bit clock, and data-in are all connected to the appropriate hardware terminals in the .cydwr file.

Now, my trouble lies in processing the data read from the DMA's. I have two functions to set up the left (0) and right (1) channels' DMA configuration, titled I2S0( ) and I2S1( ). They are identical except for the sources, channel and TD names. I wish to store the incoming data in two buffers, L_Buf and R_Buf, where I can process further. However, when I debug (I am using the MiniProg3), and view the buffers in my 'Watch' window, the data that comes through is not as expected; the input to my tuner is a function generator producing a sine wave, and the data coming through in the end has some kind of information loss. The following specs are probably relevant:

Data bits: 16.
Word select period: 32
Audio sampling frequency: 44.1 kHz (comes through the SRC at about 37 kHz)

I am hoping that someone can help me identify what mistakes I am making in my DMA configuration functions; after testing, I feel like this is where the problem is. Yes, I have read the data sheets for all the blocks included in my design, as well as many application notes which concern configuration of DMA's.

   

Thank you in advance for even taking the time to look, and any genuine input is greatly appreciated.

0 Likes
10 Replies
Bob_Marlowe
Level 10
Level 10
First like given 50 questions asked 10 questions asked

Your main.c file is missing.

   

 

   

Bob

0 Likes
lock attach
Attachments are accessible only for community members.
Anonymous
Not applicable

Welp,  that's not embarrassing or anything. Not sure what happened, but I've attached the main file in case it happens again.

0 Likes
lock attach
Attachments are accessible only for community members.
Anonymous
Not applicable

Sorry, I seem to be having an off day...here is the actual main file.

0 Likes
Bob_Marlowe
Level 10
Level 10
First like given 50 questions asked 10 questions asked

At first sight:

   

/*enable global interrupts -- not quite sure if i need this. plus it's giving me an error*/
//CYGlobalIntEnable(); *** This is needed for I2C at least, but must be called in main()

void main ()
 

   

You use the same TD for both your DMA-channels. Declare TD[2] and use one in each of the channels.

   

 

   

Bob

0 Likes
Anonymous
Not applicable

I don't quite think I understand; I declare two different TD's and channels for the left and right stero options:

   

 

   

/* TD handles */
uint8 DMA0_Chan;
uint8 DMA0_TD[1];

uint8 DMA1_Chan;
uint8 DMA1_TD[1];

   

 

   

Is creating a new variable name (and therefore space in memory) not enough to differentiate the two channels and TD's? I obviously do not have an intricate understanding of how to navigate the DMA architecture.

   

Thanks for your feedback, I really appreciate you taking the time to look over it.

   

Raquel

0 Likes
Bob_Marlowe
Level 10
Level 10
First like given 50 questions asked 10 questions asked

OK, sorry I mis-red some definitions, the TDs you are using are quite right.

   

But

   

there is an example project for I2S that is a bit similar to yours. A difference is that the result buffer is organized as uint8 bytes. This is conform to the I2S register I2S_1_RX_CH0_F0_PTR  which is a pointer to an 8 bit wide register. Having not used I2S I am not quite sure whether the two bytes have to be combined to a 16-bit value which can be done by using another DMA channel.

   

This is explained here on pg. 10 "Using Intermediate Memory Location"

   

 

   

Bob

0 Likes
Anonymous
Not applicable

Thank you, that was very helpful reading, especially some of the diagrams. The data sheet for I2S says:

   

 

   

"For example I2S_RX_CH0_F0_PTR would specify FIFO 0 of Tx stereo channel 0 as the DMA source. Each FIFO has a 4-byte depth."

   

 

   

Does that not mean that the I2S block stores 4 bytes worth of data that can be transfered via DMA channel (which can store 32-bits) to a location of my choosing? I see that the macro I2S_RX_CH0_F0_PTR points to a location, but is it really only able to store 8-bits?

0 Likes
Bob_Marlowe
Level 10
Level 10
First like given 50 questions asked 10 questions asked

You already named it: "Each FIFO has a 4-byte depth." They are talking about bytes. and the resources needed for the component clearly states (I checked that) it is only 8 bits wide. So the larger values must be (seemingly) combined from the bytes. Read the trick I pointed you to, I tried a similar thing and that worked pretty good.

   

 

   

Bob

0 Likes
Anonymous
Not applicable

Bob,

   

 

   

It works! I tried doing the two channel DMA configuration as you suggested, but the data ended up coming through correctly with one; the byte swap configuration posted in the document you provided did the trick. Now my LED's are all flashing beatifully and, more importantly, accurately 🙂

   

 

   

Here's what my configuration looked like ultimately:

   

void I2S0() //left channel
{
    //DMA Specifications
    #define DMA0_BYTES_PER_BURST 1
    #define DMA0_REQUEST_PER_BURST 1
    #define DMA0_RX_SRC_BASE (CYDEV_PERIPH_BASE)    //source is I2S peripheral
    #define DMA0_RX_DST_BASE (CYDEV_SRAM_BASE)      //destination is SRAM

   
    DMA0_Chan = DMA0_DmaInitialize(DMA0_BYTES_PER_BURST, DMA0_REQUEST_PER_BURST,
                                 HI16(CYDEV_PERIPH_BASE), HI16(CYDEV_SRAM_BASE));
    DMA0_TD[0] = CyDmaTdAllocate();
   
   
    CyDmaTdSetConfiguration(DMA0_TD[0], 2* LEN, DMA0_TD[0],
                        TD_INC_DST_ADR
                        | DMA0__TD_TERMOUT_EN
                        | CY_DMA_TD_SWAP_EN);
   
    CyDmaTdSetAddress(DMA0_TD[0], LO16((uint32)I2S_1_RX_CH0_F0_PTR), LO16((uint32)L_Buf));
 
    CyDmaChSetInitialTd(DMA0_Chan, DMA0_TD[0]);
   
    CyDmaChEnable(DMA0_Chan, 1);
   
}

   

 

   

I appreciate your advice, and hope this helps anyone else who is confused about configuring their DMA.

   

Regards,

   

Raquel

0 Likes
Bob_Marlowe
Level 10
Level 10
First like given 50 questions asked 10 questions asked

Fine that you got it working and thank you sharing your configuration with us!

   

 

   

Bob

0 Likes