Zephyr I2S nrfx_i2s.c Assertion Fail and "Cannot write in state: 4" Error During Matter Example Modification

Hi all,

I'm modifying the Matter Light Bulb example from Nordic's nRF Connect SDK to integrate I2S audio output on the nRF5340 DK. The goal is to play a continuous beep waveform through an external I2S DAC (UDA1334A).

I've implemented a Zephyr thread that handles I2S audio streaming using memory slabs. The configuration uses a 16 kHz sample rate, 16-bit stereo format, and I stream a looped sine waveform buffer.

The issue arises when I trigger I2S playback from within a Matter event callback (not from a button callback). Specifically, I’m using the CHIP Tool on pi device to commission and control the device over Matter. Once I trigger the playback (e.g., via an On/Off command), the following error occurs:

E: Cannot write in state: 4
I2S write failed: -5
ASSERTION FAIL @ nrfx_i2s.c:510
E: r0/a1:  0x00000004  r1/a2:  0x000001fe  r2/a3:  0x00000001
E: r3/a4:  0x00000004 r12/ip:  0x00000000 r14/lr:  0x00033315
E:  xpsr:  0x09000000
E: Faulting instruction address (r15/pc): 0x0007bce0
E: >>> ZEPHYR FATAL ERROR 4: Kernel panic on CPU 0
E: Current thread: 0x2000ff70 (main)
E: Halting system

This happens during a call to i2s_buf_write(). From what I understand, state 4 corresponds to NRFX_I2S_STATE_RUNNING, which should be valid for writes. But the assertion and kernel panic suggest otherwise.

Implementation details:
Zephyr version: [insert version]

nRF Connect SDK: 2.9.0

Board: nRF5340 DK

I2S settings: 16 kHz, 16-bit stereo, master mode

Threading: I2S audio playback is managed in a separate Zephyr thread

Triggering: I2S is triggered from the Matter event callback (not a button interrupt)

Commissioning: Done using CHIP Tool

I use i2s_buf_write() . I also use semaphores and thread signaling to start/stop playback cleanly.

Questions:
1. What are the valid I2S state transitions when writing with i2s_buf_write()?

2. Are there timing/threading concerns with calling I2S APIs from within a Matter callback context?

3. What can cause the nrfx I2S driver to assert in nrfx_i2s.c:510 while in NRFX_I2S_STATE_RUNNING?

4. Are there recommended best practices for triggering I2S audio playback in Zephyr from a Matter application?

5. Could the Matter stack or scheduler delay thread execution in a way that leads to I2S state issues?

Any insight into the correct way to safely stream I2S audio within a Matter-enabled Zephyr application (especially from event callbacks) would be greatly appreciated.

Parents
  • Hi,

    I'll have to get back to you on this next week. I hope that is fine.

    Regards,

    Elfving

  • Hi,

    I have some updates to share. I've resolved the system halt issues by making a few modifications to my I2S driver. Now, I'm able to successfully play the beep sound triggered by both Matter events and button events.

    However, the beep sound abruptly stops at a certain point, with an error indicating that "Next buffers not supplied on time." This issue occurs consistently, regardless of whether the sound is triggered by a Matter event or a button press.

    I've attached the C file of my I2S driver for your reference, along with the code snippet where we invoke the thread responsible for handling the beep function.

    #include <string.h>
    #include <nrf.h>
    #include <zephyr/kernel.h>
    #include <zephyr/device.h>
    #include <zephyr/drivers/i2s.h>
    #include <zephyr/drivers/gpio.h>
    #include <math.h>
    #include "beep_player.h"
    #include <stdlib.h>
    
    #define BUFFER_SIZE         512  // Bytes per buffer
    #define FRAME_CLOCK_CYCLES  32   // Depends on I2S configuration
    #define SAMPLE_RATE         16000
    #define FREQUENCY_HZ        440
    #define AMPLITUDE           10000
    
    #define NUM_SAMPLES 32
    #define BLOCK_SIZE (2 * sizeof(data_frame))  // 2x data_frame size
    #define NUM_BLOCKS 5
    #define M_PI 3.14159265f
    //thread
    #define BEEP_THREAD_STACK_SIZE 1024
    #define BEEP_THREAD_PRIORITY   5
    K_THREAD_STACK_DEFINE(beep_stack, BEEP_THREAD_STACK_SIZE);
    static struct k_thread beep_thread_data;
    static k_tid_t beep_tid = NULL;
    static bool running = false;
    static struct k_sem sem_exit;
    
    static int16_t data_frame[NUM_SAMPLES] = {
        6392,  12539,  18204,  23169,  27244,  30272,  32137,  32767,  32137,
        30272, 27244, 23169, 18204, 12539, 6392, 0, -6393, -12540,
        -18205, -23170, -27245, -30273, -32138, -32767, -32138, -30273, -27245,
        -23170, -18205, -12540, -6393, -1
    };
    
    static K_MEM_SLAB_DEFINE(mem_slab, BLOCK_SIZE, NUM_BLOCKS, NUM_SAMPLES);
    static const struct device *i2s_dev;
    static void *mem_blocks;
    void beep_init(void) {
        int ret;
        i2s_dev = DEVICE_DT_GET(DT_NODELABEL(i2s_rxtx));
        if (!device_is_ready(i2s_dev)) {
            printk("I2S device not ready\n");
            return;
        }
    struct i2s_config cfg = {
            .word_size = 16,
            .channels = 2,
            .format = I2S_FMT_DATA_FORMAT_I2S,
            .options = I2S_OPT_BIT_CLK_MASTER | I2S_OPT_FRAME_CLK_MASTER,
            .frame_clk_freq = 44100,
            .mem_slab = &mem_slab,
            .block_size = BLOCK_SIZE,
            .timeout = 0 
        };
    
        ret = i2s_configure(i2s_dev, I2S_DIR_TX, &cfg);
        if (ret != 0) {
            printk("I2S config failed: %d\n", ret);
            k_sem_give(&sem_exit);
            return;
        }
    
        k_sem_init(&sem_exit, 0, 1);
    }
    
    // Starts audio transmission and handles data streaming
    void beep_start(void *p1, void *p2, void *p3) 
    {
        int ret;
        
        ret = k_mem_slab_alloc(&mem_slab, &mem_blocks, K_FOREVER);
        if (ret != 0) {
            printk("Memory allocation failed: %d\n", ret);
        }
    
         for (size_t i = 0; i < BLOCK_SIZE/sizeof(uint16_t); i++) {
                ((uint16_t *)mem_blocks)[i] = data_frame[i % NUM_SAMPLES];
            }
    
        ret = i2s_buf_write(i2s_dev, mem_blocks, BLOCK_SIZE);
        ret = i2s_trigger(i2s_dev, I2S_DIR_TX, I2S_TRIGGER_START);
        if (ret != 0) {
            printk("I2S start failed: %d\n", ret);
            k_sem_give(&sem_exit);
            return;
        }
        running = true;
    
        while (running) 
        {
            
            // printk("Test");
            ret = i2s_buf_write(i2s_dev, mem_blocks, BLOCK_SIZE);
           
            if (ret != 0) {
                printk("I2S write failed: %d\n", ret);
                // break;
                ret = i2s_trigger(i2s_dev, I2S_DIR_TX, I2S_TRIGGER_PREPARE);
                ret = i2s_trigger(i2s_dev, I2S_DIR_TX, I2S_TRIGGER_START);
            }
            // k_mem_slab_free(&mem_slab, (void *)mem_blocks);
        }
        printk("\n Exit here \n");
         k_sem_give(&sem_exit);
    
    }
    
    void beep_start_thread(void)
    {
        
        if (running) {
            printk("Beep already running");
            return;
        }
    
        if (!device_is_ready(i2s_dev)) {
            printk("I2S not ready\n");
            return;
        }
        //Thread creating
        beep_tid = k_thread_create(&beep_thread_data,
            beep_stack,
            K_THREAD_STACK_SIZEOF(beep_stack),
            beep_start,
            NULL, NULL, NULL,
            BEEP_THREAD_PRIORITY,
            0,
            K_NO_WAIT);
    }
    
    
    void beep_stop_thread(void)
    {
        int ret;
        if (!running) {
            return;
        }
        running = false;
        
        // Wait with timeout for thread to clean up
        if(beep_tid){
        if (k_sem_take(&sem_exit, K_MSEC(1000)) == 0) {
            printk("Thread exit\n");
            k_thread_abort(beep_tid);
        }
        beep_tid = NULL;
        // First try PREPARE (to pause)
        ret = i2s_trigger(i2s_dev, I2S_DIR_TX, I2S_TRIGGER_PREPARE);
        if(ret != 0) {
            printk("\n I2S prepare failed: %d\n", ret);
        }
        // ret = i2s_trigger(i2s_dev, I2S_DIR_TX, I2S_TRIGGER_STOP);
        // if (ret != 0) {
        //     printk("I2S Stop failed: %d\n", ret);
        // }
        ret = i2s_trigger(i2s_dev, I2S_DIR_TX, I2S_TRIGGER_DROP);
        if (ret != 0) {
            printk("I2S drop failed: %d\n", ret);
        }
        k_mem_slab_free(&mem_slab, (void *)mem_blocks);
        }
    }

    #if defined(CONFIG_PWM)
    void AppTask::ActionInitiated(Nrf::PWMDevice::Action_t action, int32_t actor)
    {
    	if (action == Nrf::PWMDevice::ON_ACTION) {
    		LOG_INF("Turn On Action has been initiated");
    		beep_on = true;
    		k_work_submit(&beep_work);
    	} else if (action == Nrf::PWMDevice::OFF_ACTION) {
    		LOG_INF("Turn Off Action has been initiated");
    		beep_on = false;
    		k_work_submit(&beep_work);
    	} else if (action == Nrf::PWMDevice::LEVEL_ACTION) {
    		LOG_INF("Level Action has been initiated");
    	}
    }

Reply
  • Hi,

    I have some updates to share. I've resolved the system halt issues by making a few modifications to my I2S driver. Now, I'm able to successfully play the beep sound triggered by both Matter events and button events.

    However, the beep sound abruptly stops at a certain point, with an error indicating that "Next buffers not supplied on time." This issue occurs consistently, regardless of whether the sound is triggered by a Matter event or a button press.

    I've attached the C file of my I2S driver for your reference, along with the code snippet where we invoke the thread responsible for handling the beep function.

    #include <string.h>
    #include <nrf.h>
    #include <zephyr/kernel.h>
    #include <zephyr/device.h>
    #include <zephyr/drivers/i2s.h>
    #include <zephyr/drivers/gpio.h>
    #include <math.h>
    #include "beep_player.h"
    #include <stdlib.h>
    
    #define BUFFER_SIZE         512  // Bytes per buffer
    #define FRAME_CLOCK_CYCLES  32   // Depends on I2S configuration
    #define SAMPLE_RATE         16000
    #define FREQUENCY_HZ        440
    #define AMPLITUDE           10000
    
    #define NUM_SAMPLES 32
    #define BLOCK_SIZE (2 * sizeof(data_frame))  // 2x data_frame size
    #define NUM_BLOCKS 5
    #define M_PI 3.14159265f
    //thread
    #define BEEP_THREAD_STACK_SIZE 1024
    #define BEEP_THREAD_PRIORITY   5
    K_THREAD_STACK_DEFINE(beep_stack, BEEP_THREAD_STACK_SIZE);
    static struct k_thread beep_thread_data;
    static k_tid_t beep_tid = NULL;
    static bool running = false;
    static struct k_sem sem_exit;
    
    static int16_t data_frame[NUM_SAMPLES] = {
        6392,  12539,  18204,  23169,  27244,  30272,  32137,  32767,  32137,
        30272, 27244, 23169, 18204, 12539, 6392, 0, -6393, -12540,
        -18205, -23170, -27245, -30273, -32138, -32767, -32138, -30273, -27245,
        -23170, -18205, -12540, -6393, -1
    };
    
    static K_MEM_SLAB_DEFINE(mem_slab, BLOCK_SIZE, NUM_BLOCKS, NUM_SAMPLES);
    static const struct device *i2s_dev;
    static void *mem_blocks;
    void beep_init(void) {
        int ret;
        i2s_dev = DEVICE_DT_GET(DT_NODELABEL(i2s_rxtx));
        if (!device_is_ready(i2s_dev)) {
            printk("I2S device not ready\n");
            return;
        }
    struct i2s_config cfg = {
            .word_size = 16,
            .channels = 2,
            .format = I2S_FMT_DATA_FORMAT_I2S,
            .options = I2S_OPT_BIT_CLK_MASTER | I2S_OPT_FRAME_CLK_MASTER,
            .frame_clk_freq = 44100,
            .mem_slab = &mem_slab,
            .block_size = BLOCK_SIZE,
            .timeout = 0 
        };
    
        ret = i2s_configure(i2s_dev, I2S_DIR_TX, &cfg);
        if (ret != 0) {
            printk("I2S config failed: %d\n", ret);
            k_sem_give(&sem_exit);
            return;
        }
    
        k_sem_init(&sem_exit, 0, 1);
    }
    
    // Starts audio transmission and handles data streaming
    void beep_start(void *p1, void *p2, void *p3) 
    {
        int ret;
        
        ret = k_mem_slab_alloc(&mem_slab, &mem_blocks, K_FOREVER);
        if (ret != 0) {
            printk("Memory allocation failed: %d\n", ret);
        }
    
         for (size_t i = 0; i < BLOCK_SIZE/sizeof(uint16_t); i++) {
                ((uint16_t *)mem_blocks)[i] = data_frame[i % NUM_SAMPLES];
            }
    
        ret = i2s_buf_write(i2s_dev, mem_blocks, BLOCK_SIZE);
        ret = i2s_trigger(i2s_dev, I2S_DIR_TX, I2S_TRIGGER_START);
        if (ret != 0) {
            printk("I2S start failed: %d\n", ret);
            k_sem_give(&sem_exit);
            return;
        }
        running = true;
    
        while (running) 
        {
            
            // printk("Test");
            ret = i2s_buf_write(i2s_dev, mem_blocks, BLOCK_SIZE);
           
            if (ret != 0) {
                printk("I2S write failed: %d\n", ret);
                // break;
                ret = i2s_trigger(i2s_dev, I2S_DIR_TX, I2S_TRIGGER_PREPARE);
                ret = i2s_trigger(i2s_dev, I2S_DIR_TX, I2S_TRIGGER_START);
            }
            // k_mem_slab_free(&mem_slab, (void *)mem_blocks);
        }
        printk("\n Exit here \n");
         k_sem_give(&sem_exit);
    
    }
    
    void beep_start_thread(void)
    {
        
        if (running) {
            printk("Beep already running");
            return;
        }
    
        if (!device_is_ready(i2s_dev)) {
            printk("I2S not ready\n");
            return;
        }
        //Thread creating
        beep_tid = k_thread_create(&beep_thread_data,
            beep_stack,
            K_THREAD_STACK_SIZEOF(beep_stack),
            beep_start,
            NULL, NULL, NULL,
            BEEP_THREAD_PRIORITY,
            0,
            K_NO_WAIT);
    }
    
    
    void beep_stop_thread(void)
    {
        int ret;
        if (!running) {
            return;
        }
        running = false;
        
        // Wait with timeout for thread to clean up
        if(beep_tid){
        if (k_sem_take(&sem_exit, K_MSEC(1000)) == 0) {
            printk("Thread exit\n");
            k_thread_abort(beep_tid);
        }
        beep_tid = NULL;
        // First try PREPARE (to pause)
        ret = i2s_trigger(i2s_dev, I2S_DIR_TX, I2S_TRIGGER_PREPARE);
        if(ret != 0) {
            printk("\n I2S prepare failed: %d\n", ret);
        }
        // ret = i2s_trigger(i2s_dev, I2S_DIR_TX, I2S_TRIGGER_STOP);
        // if (ret != 0) {
        //     printk("I2S Stop failed: %d\n", ret);
        // }
        ret = i2s_trigger(i2s_dev, I2S_DIR_TX, I2S_TRIGGER_DROP);
        if (ret != 0) {
            printk("I2S drop failed: %d\n", ret);
        }
        k_mem_slab_free(&mem_slab, (void *)mem_blocks);
        }
    }

    #if defined(CONFIG_PWM)
    void AppTask::ActionInitiated(Nrf::PWMDevice::Action_t action, int32_t actor)
    {
    	if (action == Nrf::PWMDevice::ON_ACTION) {
    		LOG_INF("Turn On Action has been initiated");
    		beep_on = true;
    		k_work_submit(&beep_work);
    	} else if (action == Nrf::PWMDevice::OFF_ACTION) {
    		LOG_INF("Turn Off Action has been initiated");
    		beep_on = false;
    		k_work_submit(&beep_work);
    	} else if (action == Nrf::PWMDevice::LEVEL_ACTION) {
    		LOG_INF("Level Action has been initiated");
    	}
    }

Children
No Data
Related