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...

\

Parents
  • 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);
    }
Reply
  • 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);
    }
Children
No Data
Related