Controlling I2S TX sample rate with DMA on K22F

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

Controlling I2S TX sample rate with DMA on K22F

1,294 Views
scottm
Senior Contributor II

What's the correct way to control the sample rate on the I2S peripheral when using DMA transfers?  I'm driving a TI DAC and have audio sources of various sample rates.

Until now, I've been feeding samples to the I2S data register in interrupt mode, with a PIT generating an interrupt every sample time and the I2S clock running at a few MHz.  This works fine.

To reduce CPU utilization, I need to use DMA mode.  Using Processor Expert, the only way I see to control the sample rate is to set the bit clock rate.  Unfortunately the bit clock divider doesn't seem to have sufficient resolution to get the required accuracy across all source sample rates so I can't use that.  (PE also doesn't support runtime configuration of the bit rate, but that's not a big deal.)

It seems like the way to do this ought to be to set the DMA channel up in periodic trigger mode with the PIT gating transfer requests, but PE won't do less than two words per transfer and says the hardware doesn't support a TX FIFO watermark of 1, and shows 2-8 to be the valid settings.  That doesn't seem to match the reference manual - the manual just says the RFW field of I2Sx_RCR1 is a 3-bit field and says nothing about an offset, so I'd assume it takes 0-7.

Before I waste more time ripping out the PE I2S component to try it myself, can someone tell me how this is supposed to work?

Thanks,

Scott

Labels (1)
0 Kudos
10 Replies

795 Views
jingpan
NXP TechSupport
NXP TechSupport

Hi Scott,

I think you'd better not require DMA transfer by timer. I2S/SAI module can require DMA transfer when fifo empty by itself.

There is a demo in the K22 SDK. Please look into \SDK_2.2_TWR-K22F120M\boards\twrk22f120m\driver_examples\sai\edma_transfer. It works in SAI mode. But I think it's good for you to refer to.

0 Kudos

795 Views
scottm
Senior Contributor II

Yeah, my concern was just that I didn't think I'd get the resolution I needed from the bit clock module, but I think it'll be OK. The system needs to play audio sources of arbitrary bit rates and doesn't have a lot of CPU time to spare for resampling, so it's preferable to be able to set the DAC rate accurately to match the source.

The PE component has no run-time clock configuration, but I was able to do that through the PDD and it's working adequately now.  If the system is ever expanded to use a second I2S DAC, that's going to complicate things more.

What I'd really like is a one-shot mode for each I2S channel, so I could perform a single DMA transfer to the transmit data register, and have the sample sent out at a high bit rate for the external DAC to latch until the next sample.  I suppose I could probably accomplish that with the SPI module, if the DAC's frame sync signal was compatible with the PCS timing.  Short of that, I'll probably just have to plan to up-sample everything.  It's kind of a pain dealing with multiple audio channels when one might be playing a 44.1 ksps WAV file and another is playing an 8 ksps VoIP stream.

Scott

0 Kudos

795 Views
egoodii
Senior Contributor III

It's not clear how a single I2S bus is linking DACs running different MCLK rates (each as fixed multiples of their sample-rate), but for changeable rates, even 'locking' to a rate from a different fixed source, I use the Silicon Labs Si5351A.  Makes any precise, stable clock you need with jitter low enough for MCLK requirements.

The AC97 standard made 'variabe I2S stuffing' possible.  The K22F I2S module has 'slight' support for AC97 format, but I don't see any 'clean' way to work with the AC97 'control' slot (first) and with that per-frame 'occupied slots' control.

0 Kudos

795 Views
scottm
Senior Contributor II

I have the advantage that I'm mostly dealing with audio over narrow band radios, so the bandwidth is limited.  I don't need high fidelity audio.

I think the single I2S channel with its internal clock generation will be OK for now.  I really wish the second internal DAC was available on a non-BGA package for this MCU - I'd be able to eliminate I2S entirely and have consistent handling of the audio output channels.  As it is, the DAC is the only thing I'd need out of the BGA package and switching from a 64LQFP would add way too much to the board cost.

Scott

0 Kudos

795 Views
egoodii
Senior Contributor III

You still haven't told me much about the DAC you are 'adding' on the I2S bus, but it sounds like it is only ONE, so I don't see why we can't just let the K22F set clocks and frame rates that support whatever rate is needed for the current audio stream.  The I2S0_MDR 'digital clock generator' is crude at best, but you tell me your audio requirements are 'not stellar' so the inherent jitter in the divide-function (up to half input clock) sounds like a noise-floor-increase you can tolerate.  I have to make a lot of assumptions here 'in a vacuum', but here is an example:

Let SAI MCLK divide-source be a 100MHz system clock.  This supplies 200M edges to the divider.  Let us further presume your DAC needs a x256 MCLK, and that this currently active audio stream needs a 44.1KHz sample rate.  Thus MCLK is 11.2896MHz, and the 'ideal' divider for that is 17.715419501133786848072562358277 (multiply by .056448).  The MDR divider runs as a simple 'ratio', and the way to find two integers that supply the 'closest possible ratio' to the desired exact divider, while fitting within the bit-count limits of the divider hardware (in this case numerator 1 to 256, denominator 1 to 4096), is called the 'best rational approximation'.  If you need to support 'a large number' of possible clock rates, and so compute these values at run-time, I can supply a web-supplied routine for that.  Else, you might just have a table of pre-computed results (say from www.bee-man.us math fraction_approximation.htm).  For this case, a 'good pair' is 123/2179 (0.056447911886186324001835704451583).

This nets the proper MCLK for the sample rate, and the appropriate 256 divider in the K22 OR the DAC will set the exact frame rate, and no sample-rate-fiddling is required

0 Kudos

795 Views
scottm
Senior Contributor II

I've got it working now, thanks.  I've got a handful of pre-computed ratios and for the rest it runs a ratio approximation function.

Scott

0 Kudos

795 Views
scottm
Senior Contributor II

Hi Earl,

It's a TI DAC7311.  It's just 16 bits per frame.  With a 120 MHz clock, all of the bit rates I've checked so far are within 0.1% of the target value, so I think that's going to be fine.  For voice operation, jitter isn't going to be a problem.  In data modes, the bit rate can be chosen for the best fit since the waveforms are generated on the fly.

Thanks for the fraction calculator - I'll add that bookmark to my toolbox.

When I get further into the VoIP portion of this project I'll need to deal with matching sample rates for streaming audio with mismatched clocks.  I'm not sure yet if I'm going to want to try tweaking MCLK on the fly or if I'll just drop or double the occasional sample.  I'll tackle that when I get to it.

Thanks,

Scott

0 Kudos

795 Views
egoodii
Senior Contributor III

I see! So not an audio DAC at all, just a serial DAC.  So a programmed MCLK will just divide out to the right frame rate---sounds great!

From 120MHz(x2) 147/3125 comes to EXACTLY 256x44.1KHz, and of course 32/625 to 256x48KHz --- nice enough!

And to close out the heading on this thread, you won't NEED to control the I2S rate by DMA or any other means; just set the DMA for continuous stream satisfaction at what is now the 'exact enough' sample rate, matching the I2S frame rate!

Good luck!

0 Kudos

795 Views
scottm
Senior Contributor II

My original intent was to use a 2-channel I2S analog front end, but the need for independent bit rates made that impractical.

It seems to be mostly OK right now.  The I2S DAC sounds a bit raspier than the internal DAC in voice playback, but I haven't figured out yet where that's coming from.  That'll be easier to figure out once I've fixed my DDS tone generation.  Switching to DMA broke that and I need to handle double buffering properly.

Thanks,

Scott

0 Kudos

795 Views
scottm
Senior Contributor II

After some more experimentation I've found that it's possible to use the PIT to trigger a DMA channel to feed the I2S data register, but it doesn't do much good because the FIFO underrun error keeps it from taking any more data until the error bit is cleared.  Short of maybe chaining another DMA channel to write to the interrupt status register, I don't see any way to get around that.

On the other hand, it looks like maybe the bit clock divider is accurate enough after all. I only saw the 8-bit divider in TCR2, and missed the divider in MDR.  I'll give that a try later.

Scott

0 Kudos