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