This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Glitch in analog audio output when using I2S module

Hello.

I'm using the NRF52832 with I2S module connected to TLV320DAC from TI. I've configured the DAC and I2S module successfully. I'm getting audio output (simple sine wave read from a table) but there is an impulse glitch in the audio signal. The glitch appears to occur at the end of every I2S buffer send. When I set the I2S TX buffer size to 250 samples, I see a glitch in the audio at the corresponding time interval. For example, 250 buffer size at 41666 sample rate, causes a glitch in the audio every 0.006 seconds. When I increase the I2S buffer size, the glitch period increases correspondingly.

I've checked my sine table interpolation algorithm. It looks good. I believe the problem is with the NRF52832 and not the DAC. The I2S buffer size is the only variable which seems to affect the audio glitch. Oh, I also tried using I2S slave mode where I program the DAC to send the I2S clocks to the NRF chip. That did not change the glitch though.

I recorded the audio output in Audacity. Take a look at the waveform and frequency spectrum...

\

  • Here's another possible clue. When I set the buffer size to 64 samples the glitch occurs frequently. When the sine wave increases, the glitch is in the negative direction. When the sine wave decreases, the glitch is in the positive direction. I don't know what that means but may be useful.

  • Another observation. It seems like the last sample the buffer is carried over to the next buffer. The sample value at the glitch appears to be the value which should be at the previous glitch location. I still don't know if this is a bug in my code or possibly the I2S driver.

  • After some experiments, I found that you must use a double buffered scheme. This is not mentioned in the documentation and not too obvious from the single I2S example. The two sample buffers need to be passed in one when starting I2S and the other inside handler, the first iteration.

  • Looks like it's trying to play a sample back in time (old sample)? Maybe you are not updating the buffers fast enough (or not using two buffers)?

    Can you try to call nrf_drv_i2s_start(m_buffer_tx[0]) and then immediately nrf_drv_i2s_next_buffers_set(m_buffer_tx[1]) to prepare next buffer already? (This should however already be done by data_handler()) .Then in data_handler() you would alternate between m_buffer_tx[0] and [1] when calling nrf_drv_i2s_next_buffers_set(), just making sure you are not always using m_buffer_tx[1] and/or updating m_buffer_tx[1] when that is being played

    Kenneth

  • I'm seeing the same symptoms with a nRF9160, measured right at the SDOUT pin. I also found the problem did not go away with changes to the buffer size or its contents. Even with a buffer of all zeros, padded to protect against overruns, a glitch would occur between buffers.

    Unlike the OP, I found that double buffering my source data only made the problem half as frequent! That led me to try some variations, eventually resulting in this discovery:

    • Whenever the original buffer (the one passed to nrfx_i2s_start) is reused by passing it to nrfx_i2s_next_buffers_set, the glitch occurs!

    So instead of double buffering, I had to use a throw-away buffer for the start, and could then happily keep using the same buffer in every update after that.

    The relevant code is as follows:

    ISR_DIRECT_DECLARE(i2s_isr_handler)
    {
      nrfx_i2s_irq_handler();
      ISR_DIRECT_PM(); /* PM done after servicing interrupt for best latency */
    
      return 1; /* We should check if scheduling decision should be made */
    }

    void init(void)
    {
      IRQ_DIRECT_CONNECT(I2S_IRQn, 0, i2s_isr_handler, 0);
    
      nrfx_i2s_config_t config = NRFX_I2S_DEFAULT_CONFIG(I2S_SCK_PIN,                       \
                                                         I2S_WS_PIN,                        \
                                                         NRFX_I2S_PIN_NOT_USED, /* MCK */   \
                                                         I2S_SD_PIN,            /* SDOUT */ \
                                                         NRFX_I2S_PIN_NOT_USED  /* SDIN */);
      config.mck_setup = NRF_I2S_MCK_32MDIV23; //32 MHz / 23 = 1.391304 MHz
      config.channels = NRF_I2S_CHANNELS_STEREO;
      
      nrfx_i2s_init(&config, data_handler);
      nrfx_i2s_start(&gI2SBuffers0, I2S_DATA_BLOCK_WORDS, 0);
    }
    static void data_handler(nrfx_i2s_buffers_t const* pReleased, uint32_t status)
    {
      if(status | NRFX_I2S_STATUS_NEXT_BUFFERS_NEEDED)
        nrfx_i2s_next_buffers_set(&gI2SBuffers1);
    }
Related