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

I2S handler always called an extra time

We have an I2S implementation that I based on the example code, but using the nrfx driver.  Audio plays fine, but after I call nrfx_i2s_stop, I first see that no next buffers are needed - same code as in the example, if (!(status & NRFX_I2S_STATUS_NEXT_BUFFERS_NEEDED)) - then I get another call into the handler, triggering the ASSERT(p_released) guard at the top of the handler because there is nothing to have been released.  I am not seeing how this can be the case since clearly the driver knows that I stopped it - I don't match against the next buffers needed flag.  What am I missing?

Here is my handler:

static void i2s_data_handler(nrfx_i2s_buffers_t const * p_released, uint32_t status)
{
    // 'nrfx_i2s_next_buffers_set' is called directly from the handler
    // each time next buffers are requested, so data corruption is not
    // expected.
    ASSERT(p_released);

    // When the handler is called after the transfer has been stopped
    // (no next buffers are needed, only the used buffers are to be
    // released), there is nothing to do.
    if (!(status & NRFX_I2S_STATUS_NEXT_BUFFERS_NEEDED))
    {
        m_state = AUDIO_COMPLETE; // Tells main loop that we should execute post-audio code
		return;
    }

    // First call of this handler occurs right after the transfer is started.
    // No data has been transferred yet at this point, so there is nothing to
    // check. Only the buffers for the next part of the transfer should be
    // provided.
    if (!p_released->p_tx_buffer)
    {
        nrfx_err_t err_code;
        nrfx_i2s_buffers_t const next_buffers = {
            .p_tx_buffer = (uint32_t *)m_file_drv.file_buffer[m_file_drv.current_buffer],
            .p_rx_buffer = NULL,
        };
        
        err_code = nrfx_i2s_next_buffers_set(&next_buffers);
        APP_ERROR_CHECK(err_code);

        err_code = file_driver_get_next_file_page(&m_file_drv);
        if (err_code == FILE_DRIVER_EOF)
        {
            m_state = AUDIO_ENDED; // Tells main loop audio has ended and we can stop I2S
        }
    }
    else
    {
        // The driver has just finished accessing the buffers pointed by
        // 'p_released'. They can be used for the next part of the transfer
        // that will be scheduled now.
        nrfx_err_t err_code;

        if (m_state == AUDIO_DRIVER_PLAYING)
        {
            err_code = nrfx_i2s_next_buffers_set(p_released);
            APP_ERROR_CHECK(err_code);

            err_code = file_driver_get_next_file_page(&m_file_drv);
            if (err_code == FILE_DRIVER_EOF)
            {
                m_state = AUDIO_ENDED; // Tells main loop audio has ended and we can stop I2S
            }
        }
    }
}

and main.c looks like:

while(1)
{
    ...
    if (m_state == AUDIO_ENDED)
    {
        nrfx_i2s_stop();
    }
    if (m_state == AUDIO_COMPLETE)
    {
        // Post audio tasks - these run before ASSERT is thrown
        ...
    }
    ...
}

**EDIT** I thought I should add that I have tried many different things to get this working, but I will mention one of the most obvious.  I have tried changing the final else block into

else
    {
        // The driver has just finished accessing the buffers pointed by
        // 'p_released'. They can be used for the next part of the transfer
        // that will be scheduled now.
        nrfx_err_t err_code;

        if (m_state == AUDIO_DRIVER_PLAYING)
        {
            err_code = file_driver_get_next_file_page(&m_file_drv);
            if (err_code == FILE_DRIVER_EOF)
            {
                m_state = AUDIO_ENDED; // Tells main loop audio has ended and we can stop I2S
                return;
            }
            
            err_code = nrfx_i2s_next_buffers_set(p_released);
            APP_ERROR_CHECK(err_code);
        }
    }

thinking that setting another set of buffers when I was about to stop might have caused the issue, but it still results in the ASSERT being thrown.  I should also add that the buffers for the audio are in their own RAM region to avoid bus collisions (something that we had happen here).

Related