Kinetis K60/K70: configuration of DMA with I2S (RxCh0+RxCh1) on MK60DN512VMD10 and/or MK70FN1M0VMJ12

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

Kinetis K60/K70: configuration of DMA with I2S (RxCh0+RxCh1) on MK60DN512VMD10 and/or MK70FN1M0VMJ12

Jump to solution
4,065 Views
silentlab54
Contributor III

Hi,

I'm studying the Freescale's reference manuals, application notes, example code, but I think the reference manual for my knowledge is poor and I'm not able to understand clearly how to setup the DMA in double buffering to manage a continuous input stream from I2S0_RX (Ch0+Ch1 are acquired simultaneously and independently). Can anyone provide some examples on the necessary setup of the DMA, or some exhaustive documents on how to setup the DMA in double buffering?

Thanks in advance for your answers.

1 Solution
2,381 Views
silentlab54
Contributor III

Hi all,

     the issue is solved for a "bare metal" KDS 2.0 project; in fact, with this DMA code I'm able to acquire correctly the channels Rx0 and Rx1 of I2S1, using major and minor loop and automatic reload of source and destination addresses; this is the code that other users can use as reference.

void DMA_Chan16_Init(void)

{

  SIM_SCGC6 |= SIM_SCGC6_DMAMUX0_MASK|SIM_SCGC6_DMAMUX1_MASK; //0x06; // enable the clock of both DMAMUX0 and DMAMUX1 modules

  SIM_SCGC7 |= SIM_SCGC7_DMA_MASK; //0x02; // enable eDMA peripheral clock

  DMA_CR |= DMA_CR_EMLM_MASK; // Activate the Minor Loop Mode for the DMA: set of the EMLM bit

#ifdef DMA_DEBUG

  DMA_CR |= DMA_CR_EDBG_MASK; // activate debug mode for the DMA (avoids DMA errors while debugging)

#endif

  DMA_DCHPRI16 = 0x00; //set priority of DMA channel16

  DMAMUX1_CHCFG0 = 0x00; // clears DMAmux1 and prepare it for the reconfiguration

  // DMA Source address configuration

  DMA_TCD16_SADDR = (uint32_t)(&I2S1_RDR0); // Source address: I2S1, RX channel 0 (400A_F0A0h)

  DMA_TCD16_SOFF = 0x04; // Offset is 4 because we need to access the I2S1, RX channel 1 (400A_F0A4h)

  DMA_TCD16_ATTR = DMA_ATTR_SSIZE(2)| DMA_ATTR_DSIZE(2); //32-bits transfer, both for source and destination transactions

  DMA_TCD16_NBYTES_MLOFFYES |= DMA_NBYTES_MLOFFNO_SMLOE_MASK; // Source Minor Loop Offset Enable

  DMA_TCD16_NBYTES_MLOFFYES &= ~(DMA_NBYTES_MLOFFYES_MLOFF_MASK);

  DMA_TCD16_NBYTES_MLOFFYES |= DMA_NBYTES_MLOFFYES_MLOFF(-8);

  DMA_TCD16_NBYTES_MLOFFYES &= ~(DMA_NBYTES_MLOFFYES_NBYTES_MASK);

  DMA_TCD16_NBYTES_MLOFFYES |= DMA_NBYTES_MLOFFYES_NBYTES(0x08);

  DMA_TCD16_SLAST = DMA_SLAST_SLAST(-8);

  // DMA destination address configuration

  DMA_TCD16_DADDR = (uint32_t)(&InpData0[0]); // destination address I2S1_RDR0

  DMA_TCD16_DOFF = 0x04; // Destination address increment in bytes (32 bit => 4 bytes)

  // Current Major Iteration Count; decremented each time the minor loop is completed

  DMA_TCD16_CITER_ELINKNO = COUNTER;

  // Starting Major Iteration Count; when the software loads the TCD, this field

  // must be set equal to the corresponding CITER field. Otherwise, a configuration error is reported.

  DMA_TCD16_BITER_ELINKNO = COUNTER;

  // Destination last address adjustment or the memory address for the next

  // transfer control descriptor to be loaded into this channel.

  DMA_TCD16_DLASTSGA = DMA_DLAST_SGA_DLASTSGA(-(COUNTER*8));

  // TCD Control and Status: clear all flags and prepare it for the reconfiguration

  DMA_TCD16_CSR = 0x00;

  // enable DMA channel16 Major Loop Complete interrupt

  DMA_TCD16_CSR |= DMA_CSR_INTMAJOR_MASK;

  // DMA request input signals and this enable request flag must be asserted before a

  // channel’s hardware service request is accepted.

  DMA_ERQ |= DMA_ERQ_ERQ16_MASK; // enable DMA channel16

  //set the priority of DMA channel16 interrupt

  DMAMUX1_CHCFG0 = DMAMUX_CHCFG_ENBL_MASK|DMAMUX_CHCFG_SOURCE(14); // enable DMAmux1 for I2S1

  //enable I2S1 DMA

  I2S1_RCSR|= I2S_RCSR_FRDE_MASK; // enable I2S1 receiver DMA request

  //I2S1_RCSR|= I2S_RCSR_FWDE_MASK; // enable I2S1 receiver Rx FIFO warning DMA request

}

View solution in original post

13 Replies
2,382 Views
silentlab54
Contributor III

Hi all,

     the issue is solved for a "bare metal" KDS 2.0 project; in fact, with this DMA code I'm able to acquire correctly the channels Rx0 and Rx1 of I2S1, using major and minor loop and automatic reload of source and destination addresses; this is the code that other users can use as reference.

void DMA_Chan16_Init(void)

{

  SIM_SCGC6 |= SIM_SCGC6_DMAMUX0_MASK|SIM_SCGC6_DMAMUX1_MASK; //0x06; // enable the clock of both DMAMUX0 and DMAMUX1 modules

  SIM_SCGC7 |= SIM_SCGC7_DMA_MASK; //0x02; // enable eDMA peripheral clock

  DMA_CR |= DMA_CR_EMLM_MASK; // Activate the Minor Loop Mode for the DMA: set of the EMLM bit

#ifdef DMA_DEBUG

  DMA_CR |= DMA_CR_EDBG_MASK; // activate debug mode for the DMA (avoids DMA errors while debugging)

#endif

  DMA_DCHPRI16 = 0x00; //set priority of DMA channel16

  DMAMUX1_CHCFG0 = 0x00; // clears DMAmux1 and prepare it for the reconfiguration

  // DMA Source address configuration

  DMA_TCD16_SADDR = (uint32_t)(&I2S1_RDR0); // Source address: I2S1, RX channel 0 (400A_F0A0h)

  DMA_TCD16_SOFF = 0x04; // Offset is 4 because we need to access the I2S1, RX channel 1 (400A_F0A4h)

  DMA_TCD16_ATTR = DMA_ATTR_SSIZE(2)| DMA_ATTR_DSIZE(2); //32-bits transfer, both for source and destination transactions

  DMA_TCD16_NBYTES_MLOFFYES |= DMA_NBYTES_MLOFFNO_SMLOE_MASK; // Source Minor Loop Offset Enable

  DMA_TCD16_NBYTES_MLOFFYES &= ~(DMA_NBYTES_MLOFFYES_MLOFF_MASK);

  DMA_TCD16_NBYTES_MLOFFYES |= DMA_NBYTES_MLOFFYES_MLOFF(-8);

  DMA_TCD16_NBYTES_MLOFFYES &= ~(DMA_NBYTES_MLOFFYES_NBYTES_MASK);

  DMA_TCD16_NBYTES_MLOFFYES |= DMA_NBYTES_MLOFFYES_NBYTES(0x08);

  DMA_TCD16_SLAST = DMA_SLAST_SLAST(-8);

  // DMA destination address configuration

  DMA_TCD16_DADDR = (uint32_t)(&InpData0[0]); // destination address I2S1_RDR0

  DMA_TCD16_DOFF = 0x04; // Destination address increment in bytes (32 bit => 4 bytes)

  // Current Major Iteration Count; decremented each time the minor loop is completed

  DMA_TCD16_CITER_ELINKNO = COUNTER;

  // Starting Major Iteration Count; when the software loads the TCD, this field

  // must be set equal to the corresponding CITER field. Otherwise, a configuration error is reported.

  DMA_TCD16_BITER_ELINKNO = COUNTER;

  // Destination last address adjustment or the memory address for the next

  // transfer control descriptor to be loaded into this channel.

  DMA_TCD16_DLASTSGA = DMA_DLAST_SGA_DLASTSGA(-(COUNTER*8));

  // TCD Control and Status: clear all flags and prepare it for the reconfiguration

  DMA_TCD16_CSR = 0x00;

  // enable DMA channel16 Major Loop Complete interrupt

  DMA_TCD16_CSR |= DMA_CSR_INTMAJOR_MASK;

  // DMA request input signals and this enable request flag must be asserted before a

  // channel’s hardware service request is accepted.

  DMA_ERQ |= DMA_ERQ_ERQ16_MASK; // enable DMA channel16

  //set the priority of DMA channel16 interrupt

  DMAMUX1_CHCFG0 = DMAMUX_CHCFG_ENBL_MASK|DMAMUX_CHCFG_SOURCE(14); // enable DMAmux1 for I2S1

  //enable I2S1 DMA

  I2S1_RCSR|= I2S_RCSR_FRDE_MASK; // enable I2S1 receiver DMA request

  //I2S1_RCSR|= I2S_RCSR_FWDE_MASK; // enable I2S1 receiver Rx FIFO warning DMA request

}

2,382 Views
scottthompson
Contributor II

Thank you!  I am using all four hardware lines on both I2S0 and I2S1 and this interleaving fixed the priority issues and garbage buffers I was experiencing.  I really appreciate you taking the time to post your results as I had the exact same issue.  Cheers, Scott.

0 Kudos
2,382 Views
xiangjun_rong
NXP TechSupport
NXP TechSupport

Hi, all,

The customer created a SR:1-3694320171, the issue has been solved.

BR

XiangJun Rong

0 Kudos
2,382 Views
xiangjun_rong
NXP TechSupport
NXP TechSupport

Hi, Friend,

For the double buffering mode, I suppose it is your scheme, you create two buffer, use DMA to transfer multiple data of two channels to one buffer which I call buffer0, for example, the buffer0 is 1024 words, DMA transfers 512 data of left channel and 512 data of right channel to the buffer0, the data in left channel and right channel are interleaved. after 1024 data have been transferred, an interrupt is generated, in the ISR, core set up  new parameter to DMA and restart DMA so that the DMA can transfer data to another buffer(buffer1), while core handle the data in buffer0. After buffer1 transfer is over, DMA interrupt is generated, core load buffer0 parameter to DMA and restart DMA, then core handle the data in buffer1. I think it is okay.

I think the AN4520 is helpful, pls download it from the website:


http://tinyurl.com/munnh6d

BR

XiangJun Rong

0 Kudos
2,382 Views
silentlab54
Contributor III

Hi xiangjun.rong,

I'm not using the "default" I2S scheme. My configuration is based on using only the I2S receiver (I need to acquire RX Ch0 and RX Ch1 simultaneously and independently), every channel configured in double buffering, every frame has 16 samples (every frame has a width of 32 bit) but I need to have in the FIFO only the first frame (the one covered by the active-high phase of the frame sync: actually I'm using the FIFO mask register to do that). I'm using ProcessorExpert and I'm not able to create the structure with the two buffers because a lot of configurable fields/methods/events are locked.

1) I've tried to activate the half-done interrupt, but I haven't understand how ProcessorExpert manages the destination buffers (buffers that I can't change because the setup field is locked).

2) Are you suggesting to me that I have to create two DMA descriptors to be loaded alternatively?

3) Is it right to say that every I2S channel has its independent FIFO?

Thanks in advance for your answers.

0 Kudos
2,382 Views
xiangjun_rong
NXP TechSupport
NXP TechSupport

Hi, friends,

Regarding your question, I am still confused, please clarify it. firstly, the K63 has two receiving pins: I2S0_RXD0 and I2S0_RXD1, do you use the two pin to receive data simultaneously or use just one of them to receive data?

Secondly, based on I2S protocol, one I2S frame includes just two channel data(or slots), for example, High logic of I2S0_RX_FS is the left channel data(slot0), Low logic of I2S0_RX_FS is the right channel data(slot1), do you use the mode? It seems that you use TDM mode rather than I2S mode, one frame includes 16 data(16 slots), is it right?

If you use TDM mode, for example the frame order is slot0/slot1/slot2/.../slot15, every slot data will be saved into I2S receiving FIFO, the FIFO deepth is 8 at most, for a ned frame, this is the FIFO data order: slot0 data, slot 1 data, slot2 data, slot3 data..., it is impossible to have an independent FIFO for each slot, all slot use the same FIFO to save data. If you use the FIFO and DMA transfer data, for example, you set the DMA deepth as 8, the DMA can read 8 data for once I2S to DMA triggering, so you have set the minor loop counter as 8 for the DMA_TCDn_NBYTES_MLNO register. in order to simplify the code at test stage, I suggest you do not use FIFO  mode for DMA transfer by setting the FIFO size as 1, in the case, set DMA_TCDn_NBYTES_MLNO as 1, pls have a try.

I suppose you set the FIFI size with a value above 1, but do not use Minor loop, which leads to only reading one data.

Hope it can shed a light, but i do not know if it can solve your issue.

BR

Xiangjun Rong

0 Kudos
2,382 Views
silentlab54
Contributor III

Hi,

firstly, the K63 has two receiving pins: I2S0_RXD0 and I2S0_RXD1, do you use the two pin to receive data simultaneously or use just one of them to receive data?

I'm using K60 (MK60DN512VMD10) on silicon Rev.2, or K70 (MK70FN1M0VMJ12), not K63; I'm using I2S0_RXD0 and I2S0_RXD1 to receive data simultaneously (all the receive pins)

Secondly, based on I2S protocol, one I2S frame includes just two channel data(or slots), for example, High logic of I2S0_RX_FS is the left channel data(slot0), Low logic of I2S0_RX_FS is the right channel data(slot1), do you use the mode? It seems that you use TDM mode rather than I2S mode, one frame includes 16 data(16 slots), is it right?

I'm NOT using the I2S protocol, I'm using the peripheral as a synchronous serial interface (normal mode); I'm not processing audio data, but I'm driving an ADC converter with the peripheral clock and Frame Sync and I'm receiving to converted output on I2S0_RXD0 and I2S0_RXD1 (I need the data from the two pins simultaneously); one frame includes 16 data, but I'm interested only on the FIRST data (data0) from each channel, because the A/D converter outputs the converted value (for each channel) on the first data after the Fsync pulse (one bit active high Fsync).

If you use TDM mode, for example the frame order is slot0/slot1/slot2/.../slot15, every slot data will be saved into I2S receiving FIFO, the FIFO depth is 8 at most, for a ned frame, this is the FIFO data order: slot0 data, slot 1 data, slot2 data, slot3 data..., it is impossible to have an independent FIFO for each slot, all slot use the same FIFO to save data.

Freescale's Document Number K70P256M150SF3RM, chapter 59.4.5.1 says that "Data alignment: Each transmit and receive channel includes a FIFO of size 8 × 32-bit.": it is possibile to have independent FIFO per each pin and I'm not using the TDM mode.

If you use the FIFO and DMA transfer data, for example, you set the DMA depth as 8, the DMA can read 8 data for once I2S to DMA triggering, so you have set the minor loop counter as 8 for the DMA_TCDn_NBYTES_MLNO register. in order to simplify the code at test stage, I suggest you do not use FIFO  mode for DMA transfer by setting the FIFO size as 1, in the case, set DMA_TCDn_NBYTES_MLNO as 1, pls have a try.

I suppose you set the FIFI size with a value above 1, but do not use Minor loop, which leads to only reading one data.

Hope it can shed a light, but i do not know if it can solve your issue.

I'll have a try; I add some details about the DMA (the priorities of the two channels are: I2S0_RXD0 = high priority; I2S0_RXD1 = low priority):

  • if I set up and acquire via DMA only I2S0_RXD0 (with a single buffer), I find in memory the correct data;
  • if I set up and acquire via DMA only I2S0_RXD1 (with a single buffer), I find in memory the correct data;
  • if I set up and acquire via DMA both I2S0_RXD0 and I2S0_RXD1 (each with its single channel buffer), I find in memory wrong data in the I2S0_RXD1 buffer.


If I change the priorities of the two channels (I2S0_RXD1 = high priority; I2S0_RXD0 = Low priority) I obtain the opposite situation:

  • if I set up and acquire via DMA both I2S0_RXD0 and I2S0_RXD1 (each with its single channel buffer), I find in memory wrong data in the I2S0_RXD0 buffer.


Maybe can be a DMA arbitration problem? How can I do two reliable peripheral DMA transfers? Why the DMA isn't able to acquire in a reliable way both channels? If a single RX channel acquisition is OK, I think DMA configuration is also OK (almost for single-buffering).

I've tested this configuration on the Freescale Evaluation Boards TWR-K60D100M (chip MK60DN512VMD10 Silicon Rev. 2.x) and TWR-K70F120M (chip MK70FN1M0VMJ12) with the same (bad) results.

Thanks in advance for your answers.

0 Kudos
2,382 Views
xiangjun_rong
NXP TechSupport
NXP TechSupport

Hi, Friends,

Thank you for the explanation, I see you use K60/K70 and use both RXD0/RXD1 pins to receive data, especially, you use Normal mode rather than network mode based on SSI module concept. But the SAI module of both K60 and K70 only supports network mode instead of Normal mode, which means that the data in all 16 slots will be written into I2S0_RDRn register, it is upon firmware to pick up the first data which is only valid data as one out of  16 data.

Because you want to get data from I2S0_RXD0 and I2S0_RDX1 pins, the receiving data register for I2S0_RXD0 is I2S0_RDR0, whose address is 0x4002_F0A0, the receiving data register for I2S0_RXD1 pin is I2S0_RDR1, whose address is 0x4002_F0A4. It is called that each channel pin has independent FIFO, for each DMA receiving triggering, user can read two data from both I2S0_RDR0 and I2S0_RDR1 register. Therefore, user should use Minor loop for DMA transfer.

I recommend a soluution, pls check and test yourself if it is possible and have a try.

One frame includes 16 slots, but only first slot data is valid for both I2S0_RDR0 and I2S0_RDR1, the other are useless, but have to be received. I suggest you set up the DMA so that all 32 words can be transferred to memory by DMA automatically, then generate an interrupt, in the ISR, you pick up the valid two data, save them to another memory location.

Based on the above idea, you can set up the minor loop as 8 bytes so that two words from I2S0_RDR0 and I2S0_RDR1 can be transferred to memory pointed by destination address of DMA for each I2S receiving DMA request, then you can use modulo address or offset adresss so that the sources can be restored to I2S0_RDR0 address. Set up the major loop count as 16*4, when all one frame data has been transferred, the major loop count reaches up to zero, an DMA interrupt is generated, in the ISR, you can pick the valid data to another memory.

You can use WSF in I2Sx_RCSR to know the first frame.

Hope it can help you.

BR

XiangJun Rong

0 Kudos
2,382 Views
silentlab54
Contributor III

Hi,

But the SAI module of both K60 and K70 only supports network mode instead of Normal mode, which means that the data in all 16 slots will be written into I2S0_RDRn register, it is upon firmware to pick up the first data which is only valid data as one out of  16 data.

I'm not sure this information is completely true because if I acquire only one channel and If I set the register I2Sx_RMR (Chapter 59.3.18, K70 Sub-Family Reference Manual, Rev. 2, Dec 2011) I obtain only my desired values; this fact let me think that the FIFO is configured correctly and accepts only the masked data and the DMA is well configured to do the correct transfers.

In the context I've described, I think the solution you gave to me is not optimized and before trying it I want to be sure of the peripheral correct mode of operation; I've also tried the dual channel mode using two different I2S modules (I2S0Ch0 + I2S1Ch0) with only one channel (and setting the I2S1_RCR2[SYNC] bit field) and this solution works, but it's a waste of hardware resources..

Thanks in advance for you support.

0 Kudos
2,382 Views
xiangjun_rong
NXP TechSupport
NXP TechSupport

Hi,

I am sorry, you are right, setting the I2S0_RMR register can disable the unused slot to be  received. currently, what is your problem? I suppose that DMA can not transfer correct data from both I2S0_RDR0 and I2S0_RDR1 to memory, I think the DMA configuration has problem, can you paste the DMA configuration code so that we can review it?

BR

Xiangjun Rong

0 Kudos
2,382 Views
silentlab54
Contributor III

Hi,

Have you seen my code? Can you help me with the problem?

Anyone is using both the I2S0 channels?

Thanks in advance for your support.

0 Kudos
2,382 Views
silentlab54
Contributor III

Hi,

I think the DMA configuration has problem, can you paste the DMA configuration code so that we can review it?

BR

Xiangjun Rong

Sure! The code is generated by Freescale's Processor Expert (it don't let so much customization about the setup of the DMA channels).

Thanks for your help.

0 Kudos
2,382 Views
xiangjun_rong
NXP TechSupport
NXP TechSupport
0 Kudos