I2S Memory Allocation and Stop/Start Trigger Failure

Hi there,

I am trying to interface SPH0465 microphone with nRF Dev Kit 52832 with the following configurations of I2S:

config.word_size = 24;
config.channels = 1;
config.format = I2S_FMT_DATA_FORMAT_I2S;
config.options = I2S_OPT_BIT_CLK_SLAVE | I2S_OPT_FRAME_CLK_SLAVE;
config.frame_clk_freq = 44100; // from the datasheet of microphone
config.mem_slab = &mem_slab;
config.block_size = BLOCK_SIZE;
config.timeout = TIMEOUT;

I have followed the code from echo example in Zephyr samples. Here is what it looks like now:

/*
 * Copyright (c) 2021 Nordic Semiconductor ASA
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <zephyr/kernel.h>
#include <zephyr/sys/printk.h>
#include <zephyr/drivers/i2s.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/sys/util.h>
#include <zephyr/sys/printk.h>
#include <inttypes.h>
#include <zephyr/logging/log.h>
#include <zephyr/logging/log_ctrl.h>
#include <string.h>
#include <zephyr/drivers/pwm.h>
#include <zephyr/drivers/uart.h>


LOG_MODULE_REGISTER(MODULE, 3);

#define MSG_SIZE 32
char tx_buf[MSG_SIZE];

static const struct gpio_dt_spec button = GPIO_DT_SPEC_GET_OR(DT_ALIAS(sw1), gpios, {0});
/*
 * Print a null-terminated string character by character to the UART interface
 */
void print_uart(char *buf)
{
	int msg_len = strlen(buf);

	for (int i = 0; i < msg_len; i++) {
		// uart_poll_out(uart_dev, buf[i]);
        printk("%c", buf[i]);
	}
}

///////////////////////////

#define I2S_RX_NODE         DT_NODELABEL(i2s_rx)
#define SAMPLE_FREQUENCY 16000
#define SAMPLE_BIT_WIDTH  24
#define BYTES_PER_SAMPLE 4      
#define NUMBER_OF_CHANNELS 1
// #define SAMPLES_PER_BLOCK ((SAMPLE_FREQUENCY / 200) * NUMBER_OF_CHANNELS)
#define INITIAL_BLOCKS 2
#define TIMEOUT 1000



// size of a block for .3 ms of audio data
// #define BLOCK_SIZE 1024   // 4 samples of 4 bytes each
#define BLOCK_SIZE(_sample_rate, _number_of_channels) (BYTES_PER_SAMPLE * (_sample_rate / 200) * _number_of_channels)

/* Driver will allocate blocks from this slab to receive audio data into them.
 * Application, after getting a given block from the driver and processing its
 * data, needs to free that block.
 */
#define MAX_BLOCK_SIZE   BLOCK_SIZE(SAMPLE_FREQUENCY, 1)
#define BLOCK_COUNT      10 

#define BYTES_PER_MEM_SLAB  BLOCK_SIZE*BLOCK_COUNT // 20 * 220
        
K_MEM_SLAB_DEFINE_STATIC(mem_slab, MAX_BLOCK_SIZE, BLOCK_COUNT, 4);

// PWM DEFINES
static const struct pwm_dt_spec pwm_led0 = PWM_DT_SPEC_GET(DT_ALIAS(pwm_led0));
static const struct pwm_dt_spec pwm_led1 = PWM_DT_SPEC_GET(DT_ALIAS(pwm_led1));


// // frequency for pwm1 module
// // Frequency: 4 MHz -> Period: 250 ns (1 / 4 MHz)
// // Duty cycle: 50% -> Pulse width: 125 ns

#define PERIOD_PWM1_NSEC PWM_NSEC(375U)  // 250 nanoseconds for 4 MHz frequency
#define PULSE_WIDTH_PWM1_NSEC PWM_NSEC(PERIOD_PWM1_NSEC / 2U)  // 50% duty cycle, so pulse width is half the period

// // frequency for pwm0 module
// // Frequency: 62,500 Hz -> Period: 16 µs (1 / 62500)
// // Duty cycle: 50% -> Pulse width: 8 µs

#define PERIOD_PWM0_NSEC  PWM_NSEC(PERIOD_PWM1_NSEC * 64U)  // 16 µs period for pwm1
#define PULSE_WIDTH_PWM0_NSEC  PWM_NSEC(PERIOD_PWM0_NSEC / 2U)  // 50% duty cycle = 8 µs pulse width

K_THREAD_STACK_DEFINE(recording_thread_stack, 20000);

static const struct device *mic = DEVICE_DT_GET(I2S_RX_NODE);

static K_SEM_DEFINE(enable_recording, 1, 1);
static bool recording_active=false;

static struct k_thread recording_thread;
static k_tid_t worker_thread_id ;

void mic_worker_thread(void *p1, void *p2, void *p3) ;
int ret;



// configuration of the i2s module and trigger prepare of the receiving part
void recording_init() {
    if (!device_is_ready(mic)) {
        LOG_INF("Microphone device is not supported : %s", mic->name);
        return;
    }
	struct i2s_config config;
	config.word_size = 24; // it is okay if 24 bits are sampled. rest of the bits are high Z anyways. 
	config.channels = NUMBER_OF_CHANNELS;
	config.format = I2S_FMT_DATA_FORMAT_LEFT_JUSTIFIED;//I2S_FMT_DATA_FORMAT_I2S;
	config.options = I2S_OPT_BIT_CLK_SLAVE | I2S_OPT_FRAME_CLK_SLAVE;
	config.frame_clk_freq = SAMPLE_FREQUENCY;
	config.mem_slab = &mem_slab;
	config.block_size = BLOCK_SIZE(SAMPLE_FREQUENCY, NUMBER_OF_CHANNELS);
	config.timeout = TIMEOUT;                                                                              

	int err = i2s_configure(mic, I2S_DIR_RX, &config);

    if (err < 0) {
        LOG_ERR("Failed to configure Microphone (%d)", err);
        return;
    }

    // void *rx_buffer;
    // if (k_mem_slab_alloc(&mem_slab, &rx_buffer, K_NO_WAIT) == 0) {
    //     // Queue the first block for I2S to use
    //     err = i2s_read(mic, &rx_buffer, NULL);
    //     if (err < 0) {
    //         LOG_ERR("Failed to queue initial block (%d)", err);
    //         k_mem_slab_free(&mem_slab, &rx_buffer);
    //     }
    // } else {
    //     LOG_ERR("Failed to allocate initial memory block");
    // }

    i2s_trigger(mic, I2S_DIR_RX, I2S_TRIGGER_PREPARE);
    k_sleep(K_MSEC(100));
    if (err < 0) {
        LOG_ERR("Failed to initialize Microphone (%d)", err);
        return;
    }
    k_sem_take(&enable_recording, K_FOREVER);

    LOG_INF("Recording module initialized");
    worker_thread_id = k_thread_create(&recording_thread, recording_thread_stack,
                    K_THREAD_STACK_SIZEOF(recording_thread_stack),
                    mic_worker_thread,
                    NULL, NULL, NULL,
                    4, 0, K_NO_WAIT);
}

// trigger for receiving 
void start_recording() {
    recording_active = true;
    LOG_INF("Started recording");
    // Initialize file for recording.
    int ret = i2s_trigger(mic, I2S_DIR_RX, I2S_TRIGGER_START);
    if (ret) {
        LOG_ERR("Unable to configure trigger start for I2S bus (%d)", ret);
        return;
    }
    k_sem_give(&enable_recording);    
}

// triggering to stop recording
void stop_recording() {
    recording_active = false;
    LOG_INF("Stopping recording");
    int ret = i2s_trigger(mic, I2S_DIR_RX, I2S_TRIGGER_STOP);
    if (ret) {
        LOG_ERR("Unable to stop trigger for I2S bus (%d)", ret);
    }
    LOG_INF("Completed the Stop Trigger\n");
    k_sem_take(&enable_recording, K_FOREVER);
}

void print_rx_buffer(const void *buffer, size_t size) {
    if (buffer == NULL) {
        printf("Buffer is NULL\n");
        return;
    }

    // Cast the buffer to a uint32_t pointer for easy access if you know it holds 32-bit data
    const uint32_t *data = (const uint32_t *)buffer;
    size_t num_elements = size / sizeof(uint32_t);

    printf("Buffer contents (%zu bytes, %zu samples):\n", size, num_elements);
    for (size_t i = 0; i < num_elements; i++) {
        // printf("0x%08X ", data[i]);
        snprintf(tx_buf, num_elements, "0x%08X ", data[i]);
        print_uart(tx_buf);
        // Print a new line every 4 elements for better readability
        // if ((i + 1) % 4 == 0) {
        //     printf("\n");
        // }
    }
    // Print a final newline if the last line did not end with one
    // if (num_elements % 4 != 0) {
    //     printf("\n");
    //     printf("-----------------------------------------------");
    // }
}
void* rx_buffer;
size_t bytes_read;

void mic_worker_thread(void *p1, void *p2, void *p3) {
    LOG_INF("Worker thread started");
    void* rx_buffer;
    size_t bytes_read;
    while (k_sem_take(&enable_recording, K_FOREVER) == 0) { // remains active as long as recording is ongoing
            // LOG_INF("Arrived after starting recording \n");
            bytes_read=0;
            int ret=0;
            if  (k_mem_slab_alloc(&mem_slab, &rx_buffer, K_MSEC(500)) == 0) {
                
                ret = i2s_read(mic, &rx_buffer, &bytes_read);
                if (ret < 0) {
                    if ( ret != -5) {
                        LOG_INF("Worker thread error (%d)\r\n", ret);
                    }
                } else {
                    LOG_INF(" raw rx: %d - Received %d bytes => %d samples ", ((uint32_t *)rx_buffer)[0], bytes_read, bytes_read / sizeof(uint32_t));
                }
                k_mem_slab_free(&mem_slab, &rx_buffer);
            }
        k_sem_give(&enable_recording);

    }
    LOG_INF("Worker thread ended");
}

int main(void)
{
	
	int ret1;
	int ret2;

	LOG_INF("Starting main thread\n\r");
  	recording_init();

    if (!gpio_is_ready_dt(&button)) {
		printk("Error: button device %s is not ready\n",
		       button.port->name);
		return 0;
	}

	ret = gpio_pin_configure_dt(&button, GPIO_INPUT);
	if (ret != 0) {        
		printk("Error %d: failed to configure %s pin %d\n",
		       ret, button.port->name, button.pin);
		return 0;
	}

	if (!pwm_is_ready_dt(&pwm_led0)) {
		printk("Error: PWM device %s is not ready\n",
		       pwm_led0.dev->name);
		return 0;   
	}

	if (!pwm_is_ready_dt(&pwm_led1)) {
		printk("Error: PWM device %s is not ready\n",
		       pwm_led0.dev->name);
		return 0;
	}

    
    ret1 = pwm_set_dt(&pwm_led0, PERIOD_PWM0_NSEC, PERIOD_PWM0_NSEC / 2U);
    if (ret1) {
        printk("Error %d: failed to set pulse width\n", ret1);
        return 0;
    }

    ret2 = pwm_set_dt(&pwm_led1, PERIOD_PWM1_NSEC, PERIOD_PWM1_NSEC / 2U);
    if (ret2) {
        printk("Error %d: failed to set pulse width\n", ret2);
        return 0;
    }

	while(1){

		
		if(gpio_pin_get_dt(&button)>0)
        {
            
		    start_recording();
            k_sleep(K_MSEC(10000));
            stop_recording();
		    LOG_INF("Done Stopping\r\n");
            
        }
        				        		
	}

    return 0;
} 

And here is the overlay file I am using. This has definition for two PWM modules 0 and 1 with output channel 0 for each on pins 17 and 13, respectively. The I2S module is interfaced with SDIN - 0.26, SCK - 0.31, LRCK - 0.30:

/*
 * Copyright (c) 2021 Nordic Semiconductor ASA
 *
 * SPDX-License-Identifier: Apache-2.0
 */

 &pinctrl {

	pwm0_custom: pwm0_custom {
        group1 {
            psels = <NRF_PSEL(PWM_OUT0, 0, 17)>;
            nordic,invert;
        };
    };

    pwm0_csleep: pwm0_csleep {
        group1 {
            psels = <NRF_PSEL(PWM_OUT0, 0, 17)>;
            low-power-enable;
        };
    };
	pwm1_custom: pwm1_custom {
        group1 {
            psels = <NRF_PSEL(PWM_OUT0, 0, 13)>;
            nordic,invert;
        };
    };

    pwm1_csleep: pwm1_csleep {
        group1 {
            psels = <NRF_PSEL(PWM_OUT0, 0, 13)>;
            low-power-enable;
        };
    };
	i2s0_default_alt: i2s0_default_alt {
		group1 {
			psels = <NRF_PSEL(I2S_SDIN, 0, 26)>,
                    <NRF_PSEL(I2S_SCK_S, 0, 31)>,
                    <NRF_PSEL(I2S_LRCK_S, 0, 30)>;            
		};
	};
	i2s0_sleep: i2s0_sleep {
		group1 {
			psels = <NRF_PSEL(I2S_SDIN, 0, 26)>,
                    <NRF_PSEL(I2S_SCK_S, 0, 31)>,
                    <NRF_PSEL(I2S_LRCK_S, 0, 30)>;
			low-power-enable;
		};
	};
};


i2s_rx: &i2s0 {
	status = "okay";
	pinctrl-0 = <&i2s0_default_alt>;
	pinctrl-names = "default";
};

&pwm0 {
    status = "okay";
    pinctrl-0 = <&pwm0_custom>;
    pinctrl-1 = <&pwm0_csleep>;
    pinctrl-names = "default", "sleep";
};

&pwm1 {
    status = "okay";
    pinctrl-0 = <&pwm1_custom>;
    pinctrl-1 = <&pwm1_csleep>;
    pinctrl-names = "default", "sleep";
};

/{
    pwmleds {
        compatible = "pwm-leds";
        pwm_led0: pwm_led_0 {
            // pwms = <&pwm0 0 PWM_MSEC(20) PWM_POLARITY_NORMAL>;
            pwms = <&pwm0 0 PWM_NSEC(250) PWM_POLARITY_NORMAL>;
        };
		pwm_led1: pwm_led_1 {
            // pwms = <&pwm1 0 PWM_MSEC(20) PWM_POLARITY_NORMAL>;
            pwms = <&pwm1 0 PWM_NSEC(250) PWM_POLARITY_NORMAL>;
        };
    };
	aliases {
		pwm-led0 = &pwm_led0;
		pwm-led1 = &pwm_led1;

	};
};

There are no compilation errors but as I run the program, here is the output log:

[00:00:05.747,283] <27>[0m<inf> MODULE: Started recording<27>[0m<\r><\n>
[00:00:05.752,777] <27>[0m<inf> MODULE: Worker thread started<27>[0m<\r><\n>
[00:00:05.758,636] <27>[0m<inf> MODULE:  raw rx: -3276800 - Rec[00:00:05.764,312] <27>[1;31m<err> i2s_nrfx: No room in RX queue<27>[0m<\r><\n>
[00:00:05.770,416] <27>[1;31m<err> i2s_nrfx: No room in RX queue<27>[0m<\r><\n>
eived 320 bytes => 80 samples <27>[0m<\r><\n>
[00:00:05.779,663] <27>[0m<inf> MODULE:  raw rx: 8134048 - Received 320 bytes => 80 samples <27>[0m<\r><\n>
[00:00:05.788,330] <27>[0m<inf> MODULE:  raw rx: 8135328 - Received 320 bytes => 80 samples <27>[0m<\r><\n>
[00:00:05.796,997] <27>[0m<inf> MODULE:  raw rx: 8136288 - Received 320 bytes => 80 samples <27>[0m<\r><\n>
[00:00:05.820,739] <27>[0m<inf> MODULE:  raw rx: 8137952 - Received 320 bytes => 80 samples <27>[0m<\r><\n>
[00:00:15.752,807] <27>[0m<inf> MODULE: Stopping recording<27>[0m<\r><\n>
[00:00:15.758,361] <27>[1;31m<err> MODULE: Unable to stop trigger for I2S bus (-5)<27>[0m<\r><\n>
[00:00:15.766,052] <27>[0m<inf> MODULE: Completed the Stop Trigger<\n><27>[0m<\r><\n>
[00:00:15.772,430] <27>[0m<inf> MODULE: Done Stopping<\r>

My questions are as follows:

- Why does i2s_read face "No Room in RX queue" right after the thread starts recording but no such error appears afterwards as there is plenty of space in memory slab?

- Why does i2s_trigger fail at stopping the recording?

Any advice on how to allocate the next RX buffer location will be appreciated. 

Umer

Parents
  • Hi,

    Does the issue with "No Room in RX queue" orinted (from i2s_nrfx.c I assume) happen only once, and after that there is no issue, and you receive data as it look slike in the log? Honestly I am not able to see why you would get this, but perhaps some stepts are performed out of order as you get data subdsequently? Did you do any debugging to understand more about why this happened? What did you find out?

    Regarding stopping I see  you get -5 returned from i2s_trigger(mic, I2S_DIR_RX, I2S_TRIGGER_STOP);. . Triggering stop can only be doen when the internal state is RUNNING, and if the state is something else, you will get -EIO (-5). Looking at the code and log it should be though, so I am not able to say much here either. I suggest debugging to see what is happening in the driver.

  • Thanks for the reply. 

    Yes, No Room in RX Queue only comes in the start as soon as I2S_TRIGGER_START is run and it doesnt appear afterwards. 

    In explanation for all enums for I2S_TRIGGER there are references to i2s_state values but it is not very helpful since these states are only visible internally. 

    I have only recently discovered i2s_nrfx.c so will spend time debugging it there.

  • Further Updates on debugging i2s_nrfx.c: 

    I have modified my application code to remove the semaphores as there is only a single operation of reading i2s data. I have also removed the memory allocation and memory freeing from the application code as it was redudandant on top of i2s_nrfx.c performing the same operations on the memory slab. Here is my application code:

    /*
     * Author: Umer Huzaifa
     * Last Tested: 12/13/2024
     * 
     * This script achieves the following:
     * - Configures two PWM channels for I2S slave configuration
     * - Sets up I2S configuration for receiving microphone data at a given frequency
     * - Provides functionality to display the received data using the print_rx_buffer function
     */
    
    /*
     * Copyright (c) 2021 Nordic Semiconductor ASA
     *
     * SPDX-License-Identifier: Apache-2.0
     */
    
    #include <zephyr/kernel.h>
    #include <zephyr/sys/printk.h>
    #include <zephyr/drivers/i2s.h>
    #include <zephyr/drivers/gpio.h>
    #include <zephyr/sys/util.h>
    #include <zephyr/sys/printk.h>
    #include <inttypes.h>
    #include <zephyr/logging/log.h>
    #include <zephyr/logging/log_ctrl.h>
    #include <string.h>
    #include <zephyr/drivers/pwm.h>
    #include <zephyr/drivers/uart.h>
    
    
    LOG_MODULE_REGISTER(MODULE, 3);
    
    #define MSG_SIZE 32
    char tx_buf[MSG_SIZE];
    
    
    /*
     * Print a null-terminated string character by character to the UART interface
     */
    void print_uart(char *buf)
    {
    	int msg_len = strlen(buf);
    
    	for (int i = 0; i < msg_len; i++) {
    		// uart_poll_out(uart_dev, buf[i]);
            printk("%c", buf[i]);
    	}
    }
    
    ///////////////////////////
    
    #define I2S_RX_NODE         DT_NODELABEL(i2s_rx)
    #define SAMPLE_FREQUENCY 16000
    #define SAMPLE_BIT_WIDTH  24
    #define BYTES_PER_SAMPLE 4      
    #define NUMBER_OF_CHANNELS 1
    #define INITIAL_BLOCKS 2
    #define TIMEOUT 1000
    
    
    
    // size of a block for 50 ms of audio data
    // for sample frequency of 16 kHz, 1 block will have 80 samples representing 5 ms of audio data
    #define BLOCK_SIZE(_sample_rate, _number_of_channels) (BYTES_PER_SAMPLE * (_sample_rate / 20) * _number_of_channels)
    
    /* Driver will allocate blocks from this slab to receive audio data into them.
     * Application, after getting a given block from the driver and processing its
     * data, needs to free that block.
     */
    #define MAX_BLOCK_SIZE   40 //BLOCK_SIZE(SAMPLE_FREQUENCY, 1)
    #define BLOCK_COUNT      12//8//8 // a total of 8 blocks will be allocated i.e. 8 * 50 ms = 400 ms of audio data 
                                // 12 means: 12 * 50 ms = 600 ms of audio data
    
    #define BYTES_PER_MEM_SLAB  BLOCK_SIZE*BLOCK_COUNT // 80 samples * 12 = 320 bytes * 12 = 6400 bytes
            
    K_MEM_SLAB_DEFINE_STATIC(mem_slab, BLOCK_SIZE(SAMPLE_FREQUENCY, 1), BLOCK_COUNT, 4);
    
    // PWM DEFINES
    static const struct pwm_dt_spec pwm_led0 = PWM_DT_SPEC_GET(DT_ALIAS(pwm_led0));
    static const struct pwm_dt_spec pwm_led1 = PWM_DT_SPEC_GET(DT_ALIAS(pwm_led1));
    
    
    // // frequency for pwm1 module
    // // Frequency: 4 MHz -> Period: 250 ns (1 / 4 MHz)
    // // Duty cycle: 50% -> Pulse width: 125 ns
    
    #define PERIOD_PWM1_NSEC PWM_NSEC(500U)  // 980 nanoseconds for 1.024 MHz frequency --> 16 KhZ LRCK sampling frequency
    #define PULSE_WIDTH_PWM1_NSEC PWM_NSEC(PERIOD_PWM1_NSEC / 2U)  // 50% duty cycle, so pulse width is half the period
    
    // // frequency for pwm0 module
    // // Frequency: 62,500 Hz -> Period: 16 µs (1 / 62500)
    // // Duty cycle: 50% -> Pulse width: 8 µs
    
    #define PERIOD_PWM0_NSEC  PWM_NSEC(PERIOD_PWM1_NSEC * 64U)  // 16 µs period for pwm1
    #define PULSE_WIDTH_PWM0_NSEC  PWM_NSEC(PERIOD_PWM0_NSEC / 2U)  // 50% duty cycle = 8 µs pulse width
    
    K_THREAD_STACK_DEFINE(recording_thread_stack, 2048);
    
    static const struct device *mic = DEVICE_DT_GET(I2S_RX_NODE);
    
    // static K_SEM_DEFINE(enable_recording, 1, 1);
    static bool recording_active=false;
    
    static struct k_thread recording_thread;
    static k_tid_t worker_thread_id ;
    
    void mic_worker_thread(void *p1, void *p2, void *p3) ;
    int ret;
    
    
    
    // configuration of the i2s module and trigger prepare of the receiving part
    void recording_init() {
        if (!device_is_ready(mic)) {
            LOG_INF("Microphone device is not supported : %s  \r\n", mic->name);
            return;
        }
    
        struct i2s_config config = {
            .word_size = 24,
            .channels = NUMBER_OF_CHANNELS,
            .format = I2S_FMT_DATA_FORMAT_LEFT_JUSTIFIED,
            .options = I2S_OPT_BIT_CLK_SLAVE | I2S_OPT_FRAME_CLK_SLAVE,
            .frame_clk_freq = SAMPLE_FREQUENCY,
            .mem_slab = &mem_slab,
            .block_size = BLOCK_SIZE(SAMPLE_FREQUENCY, NUMBER_OF_CHANNELS),
            .timeout = TIMEOUT,
        };
    
        int err = i2s_configure(mic, I2S_DIR_RX, &config);
        if (err < 0) {
            LOG_ERR("Failed to configure Microphone (%d) \r\n", err);
            return;
        }
    
         void *rx_buffer;
        if (k_mem_slab_alloc(&mem_slab, &rx_buffer, K_MSEC(500)) == 0) {
        // Allocate a buffer before triggering the I2S start
        }
        i2s_trigger(mic, I2S_DIR_RX, I2S_TRIGGER_PREPARE);
        k_sleep(K_MSEC(100));
        if (err < 0) {
            LOG_ERR("Failed to initialize Microphone (%d) \r\n", err);
            return;
        }
    
        worker_thread_id = k_thread_create(&recording_thread, recording_thread_stack,
                        K_THREAD_STACK_SIZEOF(recording_thread_stack),
                        mic_worker_thread,
                        NULL, NULL, NULL,
                        1, 0, K_NO_WAIT);
    
        LOG_INF("Recording module initialized \r\n");
    }
    
    // the data here is in Little Endian format
    void sendBufferData(const void *buffer, size_t size, size_t block_number) {
        if (buffer == NULL) {
            printf("Buffer is NULL \r\n");
            return;
        }
    
        // Cast the buffer to a uint32_t pointer for easier access to 32-bit chunks
        const uint32_t *data = (const uint32_t *)buffer;
        size_t num_elements = size / sizeof(uint32_t);
    
        // printf("Block %zu contents (%zu bytes, %zu samples):\n", block_number, size, num_elements);
    
        // Iterate through the buffer and print each element on a new line
            
        for (size_t i = 0; i < num_elements; i++) {
            // printf("Sample %zu: 0x%08X\n", i, data[i]);
            printf("%08X",data[i]);
        }
    
        // printf("-----------------------------------------------\n");
    }
    
    // Trigger for receiving
    void start_recording() {
        recording_active = true;
        LOG_INF("Started recording \r\n");
    
        int ret = i2s_trigger(mic, I2S_DIR_RX, I2S_TRIGGER_START);
        if (ret) {
            LOG_ERR("Unable to start I2S RX trigger (%d) \r\n", ret);
            return;
        }
        
        LOG_INF("Recording started successfully \r\n");
    }
    
    
    // Restarting the recording feature
    // no inf message for successful recording start here
    
    void continue_recording() {
        recording_active = true;    
    
        // Start the I2S RX interface
        int ret = i2s_trigger(mic, I2S_DIR_RX, I2S_TRIGGER_START);
        if (ret) {
            LOG_ERR("Unable to start I2S RX trigger (%d)  \r\n", ret);
            return;
        }
        
    }
    
    void mic_worker_thread(void *p1, void *p2, void *p3) {
        LOG_INF("Worker thread started  \r\n");
        void *rx_buffer;
        size_t bytes_read;
        size_t block_count = 0;
    
        while (true) {  // Continuous loop for seamless operation
            
    
            bytes_read = 0;
    
            // Attempt to read directly from the I2S subsystem
            int ret = i2s_read(mic, &rx_buffer, &bytes_read);
    
            if (ret < 0) {
               if (ret == -ENOMEM) {
                    LOG_WRN("Memory allocation failed, retrying...  \r\n");
                    k_msleep(5);  // Short delay to allow buffer recycling
                    continue;
                }
                else if (ret != -EAGAIN) {  // Ignore buffer-empty errors
                    // LOG_ERR("I2S read error: %d  \r\n", ret);
                    LOG_DBG("I2S read error: %d  \r\n", ret);
                    continue;
                }
                
            }
    
            else if (bytes_read > 0) {  // that is, ret=0 and there is some data
                    block_count++;
                    LOG_INF("Received %d bytes, %d samples, starting at %p \r\n",
                            bytes_read, bytes_read / sizeof(uint32_t), rx_buffer);
                }
    
            // Avoid freeing or re-initializing the buffer in the application
            // The I2S driver will handle this.
        
        }
    
        LOG_INF("Worker thread ended \r\n");
    }
    
    
    
    
    int main(void)
    {
    	
    	int ret1;
    	int ret2;
    
    	LOG_INF("Starting main thread \n\r");
      	recording_init();
    
        
    	
    	if (!pwm_is_ready_dt(&pwm_led0)) {
    		printk("Error: PWM device %s is not ready \r\n",
    		       pwm_led0.dev->name);
    		return 0;   
    	}
    
    	if (!pwm_is_ready_dt(&pwm_led1)) {
    		printk("Error: PWM device %s is not ready \r\n",
    		       pwm_led0.dev->name);
    		return 0;
    	}
        
        ret1 = pwm_set_dt(&pwm_led0, PERIOD_PWM0_NSEC, PERIOD_PWM0_NSEC / 2U);
        if (ret1) {
            printk("Error %d: failed to set pulse width \r\n", ret1);
            return 0;
        }
    
        ret2 = pwm_set_dt(&pwm_led1, PERIOD_PWM1_NSEC, PERIOD_PWM1_NSEC / 2U);
        if (ret2) {
            printk("Error %d: failed to set pulse width \r\n", ret2);
            return 0;
        }
    
        start_recording();
    	while(1){
            
    		k_sleep(K_MSEC(100));				        		
    	}
    
        return 0;
    } 

    After making changes in the application code, my logs are much cleaner now but the application stops processing further data after it encounters a "Failed to allocate next RX buffer"

    *** Booting nRF Connect SDK v2.7.0-5cb85570ca43 ***<\r><\n>
    *** Using Zephyr OS v3.6.99-100befc70c74 ***<\r><\n>
    [00:00:00.269,744] <27>[0m<inf> MODULE: Starting main thread <\n><\r><27>[0m<\r><\n>
    [00:00:00.375,885] <27>[0m<inf> MODULE: Recording module initialized <\r><\n>
    <27>[0m<\r><\n>
    [00:00:00.382,659] <27>[0m<dbg> pwm_nrfx: pwm_nrfx_set_cycles: channel 0, pulse 256, period 512, prescaler: 0.<27>[0m<\r><\n>
    [00:00:00.393,066] <27>[0m<dbg> pwm_nrfx: pwm_nrfx_set_cycles: channel 0, pulse 4, period 8, prescaler: 0.<27>[0m<\r><\n>
    [00:00:00.403,015] <27>[0m<inf> MODULE: Started recording <\r><\n>
    <27>[0m<\r><\n>
    [00:00:00.408,813] <27>[0m<inf> i2s_nrfx: Next buffers: 0/0x20006040<27>[0m<\r><\n>
    [00:00:00.415,313] <27>[0m<inf> MODULE: Recording started successfully <\r><\n>
    <27>[0m<\r><\n>
    [00:00:00.422,302] <27>[0m<inf> MODULE: Worker thread started  <\r><\n>
    <27>[0m<\r><\n>
    [00:00:00.434,478] <27>[0m<inf> i2s_nrfx: Queued RX 0x20006cc0<27>[0m<\r><\n>
    [00:00:00.440,429] <27>[0m<inf> i2s_nrfx: Next buffers: 0/0x200053c0<27>[0m<\r><\n>
    [00:00:00.446,929] <27>[0m<inf> i2s_nrfx: Released RX 0x20006cc0<27>[0m<\r><\n>
    [00:00:00.460,144] <27>[0m<inf> i2s_nrfx: Queued RX 0x20006040<27>[0m<\r><\n>
    [00:00:00.481,079] <27>[0m<inf> i2s_nrfx: Next buffers: 0/0x20004740<27>[0m<\r><\n>
    [00:00:00.487,548] <27>[0m<inf> i2s_nrfx: Queued RX 0x200053c0<27>[0m<\r><\n>
    [00:00:00.493,469] <27>[0m<inf> i2s_nrfx: Next buffers: 0/0x20003ac0<27>[0m<\r><\n>
    [00:00:00.453,063] <27>[0m<inf> MODULE: Received 3200 bytes, 800 samples, starting at 0x20006cc0[00:00:00.511,505] <27>[0m<inf> i2s_nrfx: Queued RX 0x20004740<27>[0m<\r><\n>
    [00:00:00.517,395] <27>[0m<inf> i2s_nrfx: Next buffers: 0/0x20002e40<27>[0m<\r><\n>
     <\r><\n>
    <27>[0m<\r><\n>
    [00:00:00.524,688] <27>[0m<inf> i2s_nrfx: Released RX 0x20006040<27>[0m<\r><\n>
    [00:00:00.530,792] <27>[0m<inf> MODULE: Received 3200 bytes, 800 samples[00:00:00.537,200] <27>[0m<inf> i2s_nrfx: Queued RX 0x20003ac0<27>[0m<\r><\n>
    [00:00:00.543,090] <27>[0m<inf> i2s_nrfx: Next buffers: 0/0x200021c0<27>[0m<\r><\n>
    , starting at 0x20006040 <\r><\n>
    <27>[0m<\r><\n>
    [00:00:00.552,429] <27>[0m<inf> i2s_nrfx: Released RX 0x200053c0<27>[0m<\r><\n>
    [00:00:00.558,532] <27>[0m<inf> MODULE: Received[00:00:00.562,866] <27>[0m<inf> i2s_nrfx: Queued RX 0x20002e40<27>[0m<\r><\n>
    [00:00:00.568,786] <27>[1;31m<err> i2s_nrfx: Failed to allocate next RX buffer: -12<27>[0m<\r><\n>
    [00:00:00.576,599] <27>[0m<inf> i2s_nrfx: Queued RX 0x200021c0<27>[0m<\r><\n>
     3200 bytes, 800 samples, starting at 0x200053c0 <\r><\n>
    <27>[0m<\r><\n>
    [00:00:00.587,463] <27>[0m<inf> i2s_nrfx: Released RX 0x20004740<27>[0m<\r><\n>
    [00:00:00.593,566] <27>[0m<inf> MODULE: Received 3200 bytes, 800 samples, starting at 0x20004740 <\r><\n>
    <27>[0m<\r><\n>
    [00:00:00.602,844] <27>[0m<inf> i2s_nrfx: Released RX 0x20003ac0<27>[0m<\r><\n>
    [00:00:00.608,947] <27>[0m<inf> MODULE: Received 3200 bytes, 800 samples, starting at 0x20003ac0 <\r><\n>
    <27>[0m<\r><\n>
    [00:00:00.618,225] <27>[0m<inf> i2s_nrfx: Released RX 0x20002e40<27>[0m<\r><\n>
    [00:00:00.624,359] <27>[0m<inf> MODULE: Received 3200 bytes, 800 samples, starting at 0x20002e40 <\r><\n>
    <27>[0m<\r><\n>
    [00:00:00.633,605] <27>[0m<inf> i2s_nrfx: Released RX 0x200021c0<27>[0m<\r><\n>
    [00:00:00.639,709] <27>[0m<inf> MODULE: Received 3200 bytes, 800 samples, starting at 0x200021c0 <\r><\n>
    <27>[0m

    The error in i2s_nrfx.c leads to an "I2S_ERROR_STATE" and in this state, unless you retrigger the I2S start, the next buffers start coming in again. Was it intended to be this way at the driver level?

    Best.

  • Update: I have been able to resolve this issue.

    The problem in this case was the small queue size in the RX queue of i2s_nrfx driver, and the fact that the memory block needs to be freed after every i2s_read operation. 

    The former can be fixed with the following configuration statement in prj.conf:

    CONFIG_I2S_NRFX_RX_BLOCK_COUNT=10

    and the latter is done by using the following function call after every successful i2s_read.

    k_mem_slab_free(&mem_slab, mem_block);

Reply
  • Update: I have been able to resolve this issue.

    The problem in this case was the small queue size in the RX queue of i2s_nrfx driver, and the fact that the memory block needs to be freed after every i2s_read operation. 

    The former can be fixed with the following configuration statement in prj.conf:

    CONFIG_I2S_NRFX_RX_BLOCK_COUNT=10

    and the latter is done by using the following function call after every successful i2s_read.

    k_mem_slab_free(&mem_slab, mem_block);

Children
No Data
Related