I2S: configuring clock details with Zephyr

Hello,

I have developed an NCS/Zephyr-based application that is able to stream out .WAV files over the I2S peripheral.

I have a dev board using a MAX98357A audio amplifier. This amplifier is able to interpret the I2S output from my nRF52840 and it plays my .WAV file normally out of a speaker.

Here is the I2S configuration I'm using to configure the Zephyr I2S driver:

    my_i2s_config.word_size = SAMPLE_BIT_WIDTH;			// 16
	my_i2s_config.channels = NUMBER_OF_CHANNELS;		// 1
	my_i2s_config.format = I2S_FMT_DATA_FORMAT_I2S;
	my_i2s_config.options = I2S_OPT_BIT_CLK_MASTER | I2S_OPT_FRAME_CLK_MASTER;
	my_i2s_config.frame_clk_freq = SAMPLE_FREQUENCY;	// 16000
	my_i2s_config.mem_slab = &mem_slab;
	my_i2s_config.block_size = BLOCK_SIZE;
	my_i2s_config.timeout = TIMEOUT;

With these settings, I am observing these I2S output clocks on my oscilloscope:
BCLK = 510kHz
LRCLK = 15.9kHz
BCLK to LRCLK Ratio: 32 

Now, I want to integrate a more advanced audio amplifier: the TI TAS2563. However, this audio amplifier does not support BCLK to LRCLK ratio of 32. It requires a minimum ratio of 64.

I still want my audio sampling frequency to be 16kHz, so I need to increase the BCLK speed up to ~1.024MHz.

I know that the previous NRFX drivers had registers for configuring the MCK_SETUP and MCK/LRCK ratio. I had a previous project (before NCS/Zephyr) where we used a MCK_SETUP value of NRF_I2S_MCK_32MDIV21 and a ratio value of NRF_I2S_RATIO_96X to get 16kHz streaming working. If I'm remembering the math correctly, this would give:

32MHz / 21 = 1.524MHz I2S peripheral clock
1.524MHz / 96 BCLKs/LRCLK = 15.9kHz LRCLK (close enough for 16kHz sampling)

Unfortunately, I do not see the options in the Zephyr driver to achieve this level of configuration over the I2S peripheral. 

Is there a way to configure the I2S peripheral from Zephyr such that I have a 16kHz LRCLK and a BCLK to LRCLK ratio of 64 or higher?

  • Hi Chris

    The Zephyr I2S driver is quite generic and will not be able to cover all the configuration options of the I2S peripheral in the nRF devices. 

    The developers behind the LE Audio project ended up using the nrfx_i2s driver rather than the Zephyr driver for just this reason, and you might want to do the same in order to get full control over the I2S peripheral. 

    I can take a closer look at the driver implementation tomorrow and see if I can spot a way to get the configuration you need, but I wouldn't be surprised if this can only be handled by the nrfx driver. 

    Best regards
    Torbjørn

  • Understood. I wondered if that might be the case. I appreciate you taking a look to see if this is possible with the Zephyr driver. I look forward to hearing back from you on this, thank you!

  • Hi Chris

    Looking at the code it seems the radio is automatically configured, trying to find a configuration that gives the least amount of offset from the requested settings without giving you exact control of all the parameters. 

    As an experiment, could you try to change this line in i2s_nrfx.c to start the loop at 2 rather than 0? 
    https://github.com/nrfconnect/sdk-zephyr/blob/main/drivers/i2s/i2s_nrfx.c#L92

    for (r = 2; r < ARRAY_SIZE(ratios); ++r) {

    Then it should hopefully select a ratio of 64 rather than 32...

    Best regards
    Torbjørn

  • Apologies for the lateness of this reply.

    I have spent some time rewriting my audio thread to use the nrfx I2S driver, instead of the Zephyr I2S driver. I now have a version of my application that is successfully clocking out data over I2S again, and I can adjust the clock speeds by changing inputs to the nrfx_i2s_config_t struct.

    However, I am still having trouble controlling the BCLK to LRCLK ratio, specifically.
    I have tried a variety of inputs, adjusting the mck_setup, ratio, and sample_width parameters. However I am still unable to achieve a BCLK to LRCLK ratio of 64. It seems that no matter what ratio I apply (48X/64X/96X/192X) the actual ratio I observe is 32 BCLKs per LRCLK.

    I also dug through the sample_width parameter definitions and tried out I2S_CONFIG_SWIDTH_SWIDTH_16BitIn32 (#defined as 0x6), which did allow me to hit a ratio of 48 BCLKs per LRCLK. I tried I2S_CONFIG_SWIDTH_SWIDTH_8BitIn32 (#define as 0x5), but that again gives me an observed ratio of 32.


    Here is a table I created of the I2S config parameters I have tested, and the output clock speeds I have observed on my oscilloscope:

    My target I2S parameters:
    -16kHz sampling
    -Mono audio
    -16 bits/sample
    -BCLK to LRCLK ratio of 64, 96, or 128 (so, BCLK freq of 1.024MHz/1.536MHz/2.048MHz)

    Is there a process behind the scenes that is calculating clock speeds automatically?
    Why isn't the nrfx_i2s_config_t.ratio parameter being respected?
    What other parameters can I change to hit 64 BCLKs per LRCLK?

  • Hi  , I have not tried this yet, but I am very hesitant to modify any of the code within the SDK itself. I don't want to have to fork the SDK and maintain these customizations outside of my own software repositories.

Related