Upgrading from NCSv3.0.0 to NCSv3.2.1, I2S issue

hello,
We have a project that uses sound for distance measurement.

When I upgraded from NCSv3.0.0 to NCSv3.2.1, I encountered a problem where the i2s function stopped working.

The following is the driver code for NCSv3.0.0.Can be used normally.
Main process:  i2s_init()--->Enable the register clock---->enable PA---->k_sleep(250)---->start_i2s()---->handle---->set_next_buf()--->handle---->set_next_buf() .... --->stop_i2s()

Since this sound is an ultrasonic wave, when played directly, there will be a "pop" sound. Therefore, I need to first turn on the i2s clock and reduce this "pop" sound.


#define I2S_DATA_BLOCK_WORDS 192
static void i2s_comp_handler(nrfx_i2s_buffers_t const *released_bufs, uint32_t status)
{
    if (!(status & NRFX_I2S_STATUS_NEXT_BUFFERS_NEEDED)) {
        // printk("i2s No buffer stop : %d\n", status);
        return;
    }

    uint32_t *next_tx_buf = NULL;

    if (released_bufs->p_tx_buffer == m_buffer_tx[0]) {
        next_tx_buf = m_buffer_tx[0];
    } else if (released_bufs->p_tx_buffer == m_buffer_tx[1]) {
        next_tx_buf = m_buffer_tx[1];
    }
    if(!released_bufs->p_tx_buffer) {
        next_tx_buf = m_buffer_tx[1];
    }

    current_buf += 1;
    //Put the data from bpsk_tata into m_buffer_tx[0] or m_buffer_tx[1]
    singleGetAudioDataToTxBuf(bpsk_tata, m_buffer_tx[0], m_buffer_tx[1], current_buf);
    
    const nrfx_i2s_buffers_t next_buffers = {
        .p_rx_buffer = NULL,
        .p_tx_buffer = next_tx_buf,
        .buffer_size = I2S_DATA_BLOCK_WORDS,
    };
    (void)nrfx_i2s_next_buffers_set(&i2s_inst, &next_buffers);
}


    
void audio_i2s_init(void)
{
    int ret;

    ret = pinctrl_apply_state(PINCTRL_DT_DEV_CONFIG_GET(I2S_NL), PINCTRL_STATE_DEFAULT);
    if (ret != 0) {
        printk("Failed to apply pinctrl state: %d\n", ret);
        return;
    }

    IRQ_CONNECT(DT_IRQN(I2S_NL), DT_IRQ(I2S_NL, priority), nrfx_isr, nrfx_i2s_20_irq_handler, 0);
    irq_enable(DT_IRQN(I2S_NL));

    ret = nrfx_i2s_init(&i2s_inst, &cfg, i2s_comp_handler);
    if (ret != 0) {
        printk("Failed to initialize I2S: %d\n", ret);
        return;
    }
    printk("i2s initialize (%d)\r\n",ret);
}

void i2s_codec_thread(void)
{
    audio_i2s_init();
    for (;;) {
        k_sem_take(&i2s_sync_sem, K_FOREVER);
        NRF_I2S20->ENABLE = 1;       // First, turn on the clock stabilization PA
        NRF_I2S20->TASKS_START = 1;
        pf_gpio_write(GPIO_PA, 1);
        k_sleep(K_MSEC(250));
        
        current_buf += 1;
        //Put the data from bpsk_tata into m_buffer_tx[0]
        singleGetAudioDataToTxBuf(bpsk_tata, m_buffer_tx[0], m_buffer_tx[1], current_buf);
        const nrfx_i2s_buffers_t initial_buffers = {
            .p_rx_buffer = NULL,
            .p_tx_buffer = m_buffer_tx[0],
            .buffer_size = (I2S_DATA_BLOCK_WORDS)};
        // Send real data
        ret = nrfx_i2s_start(&i2s_inst, &initial_buffers, 0);
        if (ret != 0) {
            printk("Failed to start I2S: %d\n", ret);
        }
        do 
        {
            __WFE();
            __SEV();
            __WFE();
        } while (current_buf < BLOCKS_TO_TRANSFER);
        nrfx_i2s_stop(&i2s_inst);
        
    }
}

When upgrading to NCSv3.2.1,Performing this operation will cause the system to crash.
The cause of the system crash:call (void)nrfx_i2s_next_buffers_set(&i2s_inst, &next_buffers); 

ASSERTION FAIL [p_cb->state == NRFX_DRV_STATE_POWERED_ON] @ WEST_TOPDIR/modules/hal/nordic/nrfx/drivers/src/nrfx_i2s.c:385

At this moment p_cb->state=1

I tried to stop manipulating the registers. Instead, I played a silent segment first and then switched to the actual data. This way, the system no longer crashed. However, this had an impact on the accuracy of my distance measurement.

I hope that the NCSv3.2.1 version can solve my problem.

Look forward to your reply!
Thanks

Parents
  • Hi, 

    From your code, it appears that you are mixing nrfx_* API calls with direct register access. This usage is not supported, and we do not test such scenarios because it is not possible to predict how users may manipulate the peripheral outside the nrfx_* API context.

    The crash likely occurs because an interrupt is triggered between nrfx_i2s_init and nrfx_i2s_start. In that window, nrfx_i2s_next_buffers_set is called from the callback context. However, this function checks the driver state and expects it to be POWERED_ON, which is only set in nrfx_i2s_start.

    This behavior is a direct consequence of accessing the peripheral through registers instead of exclusively using the I2S driver API.

    -Amanda H.

Reply
  • Hi, 

    From your code, it appears that you are mixing nrfx_* API calls with direct register access. This usage is not supported, and we do not test such scenarios because it is not possible to predict how users may manipulate the peripheral outside the nrfx_* API context.

    The crash likely occurs because an interrupt is triggered between nrfx_i2s_init and nrfx_i2s_start. In that window, nrfx_i2s_next_buffers_set is called from the callback context. However, this function checks the driver state and expects it to be POWERED_ON, which is only set in nrfx_i2s_start.

    This behavior is a direct consequence of accessing the peripheral through registers instead of exclusively using the I2S driver API.

    -Amanda H.

Children
  • hi,


    Thank you for your reply.

    I know that I used the register and the API together.Due to the nature of the project, I had no choice but to do so, and the first version has already been verified by NCSv3.0.0.

    I need a method that starts the I2S clock but does not send any data before calling the function nrfx_i2s_start().

    Look forward to your reply!
    Thanks

  • yuanFang said:
    I need a method that starts the I2S clock but does not send any data before calling the function nrfx_i2s_start().

    You can use nrfx_i2s_start() and provide a NULL TX buffer and a "dummy" RX buffer. This will start the actual transaction (SCK and LRCK clocks) and fill the RX buffer, but the data in the RX buffer can be ignored.

  • hi,

    Thank you for your reply.

    Could you please provide an example based on my description?

    1. One of the devices only has the function of playing (TX) but not receiving (RX).
    2. In the NCSv3.0.0 version, the actual data sent was "int16_t bpsk_tata[1521]", and the buffer_size for each I2S transmission was 192.
    3. According to what you said, when TX_buffer is NULL, what should be the size of buffer_size? What should be done in the i2s_comp_handler?
    4. When the delay of 250ms is over, will the actual data be directly switched in the i2s_comp_handler(), or will it be sent first by calling i2s_stop() and then I2s_start()?

    The distance measurement is conducted once every 1 second. Each playback and reception must be synchronized, and the error should be within 100 microseconds. Therefore, I need to ensure that the timing when sending dummy data and then switching to real data is fixed.


    Thank you very much for your kind assistance.

    Look forward to your reply!
    Thanks

  • Hi,

    I tried some other approaches, but the results were not as good as those of ncsv3.0.0. I wonder if any of you have better methods.

    I also came up with an idea: to separate the NCSv3.0.0 I2s driver file and place it on NCSv3.2.1. During the compilation process, specify the NCSv3.0.0 I2s driver file. Will this method work?

    Look forward to your reply!
    Thanks

  • hi,
    I tried your method, and the code is as follows:

    static void i2s_comp_handler(nrfx_i2s_buffers_t const *released_bufs, uint32_t status)
    {
        if (!(status & NRFX_I2S_STATUS_NEXT_BUFFERS_NEEDED)) {
            // printk("i2s No buffer stop : %d\n", status);
            return;
        }
    
    
    if (!start_real_data) {
        // printk("i2s No start_real_data stop : %d\n", status);
        return;
    }
    
        if (released_bufs->p_tx_buffer == m_buffer_tx[0]/*!(current_buf % 2)*/) {
            current_buf += 1;
            singleGetAudioDataToTxBuf(bpsk_tata, m_buffer_tx[0], m_buffer_tx[1], current_buf);
            const nrfx_i2s_buffers_t next_buffers1 = {
                .p_rx_buffer = NULL,
                .p_tx_buffer = m_buffer_tx[0],
                .buffer_size = I2S_DATA_BLOCK_WORDS};
            nrfx_i2s_next_buffers_set(&i2s_inst, &next_buffers1);
            // printk("i2s event handler 1 :%d %d %d %d\n",released_bufs->p_tx_buffer[0],m_buffer_tx[0][0], m_buffer_tx[0][1],m_buffer_tx[0][191]);
        } else if (released_bufs->p_tx_buffer == m_buffer_tx[1] /*(current_buf % 2)*/) {
            current_buf += 1;
            singleGetAudioDataToTxBuf(bpsk_tata, m_buffer_tx[0], m_buffer_tx[1], current_buf);
            const nrfx_i2s_buffers_t next_buffers2 = {
                .p_rx_buffer = NULL,
                .p_tx_buffer = m_buffer_tx[1],
                .buffer_size = I2S_DATA_BLOCK_WORDS};
            nrfx_i2s_next_buffers_set(&i2s_inst, &next_buffers2);
            // printk("i2s event handler 2 :%d %d %d %d %d %d\n",released_bufs->p_tx_buffer[0],m_buffer_tx[1][0], m_buffer_tx[1][135],m_buffer_tx[1][136],m_buffer_tx[1][190],m_buffer_tx[1][191]);
        }
        if(!released_bufs->p_tx_buffer) {
            current_buf += 1;
            singleGetAudioDataToTxBuf(bpsk_tata, m_buffer_tx[0], m_buffer_tx[1], current_buf);
            const nrfx_i2s_buffers_t next_buffers2 = {
                .p_rx_buffer = NULL,
                .p_tx_buffer = m_buffer_tx[1],
                .buffer_size = I2S_DATA_BLOCK_WORDS};
            nrfx_i2s_next_buffers_set(&i2s_inst, &next_buffers2);
            // printk("i2s event handler 0 :%d %d %d %d\n",released_bufs->p_tx_buffer[0],m_buffer_tx[1][0], m_buffer_tx[1][1],m_buffer_tx[1][191]);
        }
    }
    static uint32_t m_buffer_rx[2][96] = {0};
    
    void es8311Play()
    {
        current_buf = 0;
        current_index = 0;
        buf_end_flag = 0;
    
        start_real_data = false;
    
        const nrfx_i2s_buffers_t initial_buffers_test = {
            .p_rx_buffer = m_buffer_rx[0],
            .p_tx_buffer = NULL,
            .buffer_size = 1};
        int ret = nrfx_i2s_start(&i2s_inst, &initial_buffers_test, 0);
        if (ret != 0) {
            printk("Failed to start I2S111111: %d\n", ret);
        }
    
        pf_gpio_write(GPIO_PA_GPIO, 1);
        k_sleep(K_MSEC(250));
        start_real_data = true;
        nrfx_i2s_stop(&i2s_inst);
    
        es8311_i2s_start();
    
        do 
        {
            __WFE();
            __SEV();
            __WFE();
        } while (current_buf < BLOCKS_TO_TRANSFER);
        nrfx_i2s_stop(&i2s_inst);
    }

    1. 

    nrfx_i2s_start()

    p_tx_buffer = NULL,  and a "dummy" RX buffer,size = 1

    2. Turn on the PA
    3. delay 250ms
    4. 
    nrfx_i2s_stop(&i2s_inst);

    5. nrfx_i2s_start() ,Start sending the actual data.

    After running for a period of time, a problem of system freeze was discovered.

    ASSERTION FAIL [p_buffers->p_rx buffer I= ((void *)0)] @WEST_TOPDIR/modules/hal/nor di c/nr fx/drivers/sro/nr fx_i2s. c:422

    Look forward to your reply!
    Thanks

Related