i2s_read() Error -5 in Using SPH0465 with nRF52832

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 <string.h>
#include <zephyr/drivers/pwm.h>




#define I2S_RX_NODE  DT_NODELABEL(i2s_rx)

#define SAMPLE_FREQUENCY    44100
#define SAMPLE_BIT_WIDTH    24
#define BYTES_PER_SAMPLE    sizeof(int32_t)
#define NUMBER_OF_CHANNELS  1
/* Such block length provides an echo with the delay of 100 ms. */
#define SAMPLES_PER_BLOCK   1//((SAMPLE_FREQUENCY / 30) * NUMBER_OF_CHANNELS) //((SAMPLE_FREQUENCY / 30) * NUMBER_OF_CHANNELS) 
#define INITIAL_BLOCKS      2
#define TIMEOUT             1000

// 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));

#define BITCLK PWM_KHZ(4000U)
#define WORD_S PWM_HZ(62500U)

// // 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


#define BLOCK_SIZE  (BYTES_PER_SAMPLE * SAMPLES_PER_BLOCK)
#define BLOCK_COUNT (INITIAL_BLOCKS + 2)
K_MEM_SLAB_DEFINE_STATIC(mem_slab, BLOCK_SIZE, BLOCK_COUNT, 4);

static int32_t echo_block[SAMPLES_PER_BLOCK];
static volatile bool echo_enabled = false;
static K_SEM_DEFINE(toggle_transfer, 1, 1);

static void process_block_data(void *mem_block, uint32_t number_of_samples)
{
	static bool clear_echo_block;

	if (echo_enabled) {
		for (int i = 0; i < number_of_samples; ++i) {
			int32_t *sample = &((int32_t *)mem_block)[i];
			*sample += echo_block[i];
			echo_block[i] = (*sample) / 2;
		}

		clear_echo_block = true;
	} else if (clear_echo_block) {
		clear_echo_block = false;
		memset(echo_block, 0, sizeof(echo_block));
	}
}

static bool configure_streams(const struct device *i2s_dev_rx,
			      const struct i2s_config *config)
{
	int ret;

	ret = i2s_configure(i2s_dev_rx, I2S_DIR_RX, config);
	if (ret < 0) {
		printk("Failed to configure RX stream: %d\n", ret);
		return false;
	}
	return true;
}


static bool trigger_command(const struct device *i2s_dev_rx,				
			    enum i2s_trigger_cmd cmd)
{
	int ret;
	

	ret = i2s_trigger(i2s_dev_rx, I2S_DIR_RX, cmd);
	if (ret < 0) {
		printk("Failed to trigger command %d on RX: %d\n", cmd, ret);
		return false;
	}
	

	return true;
}

int main(void)
{
	uint32_t bclk_period;
	uint32_t ws_period;
	int ret1;
	int ret2;

	printk("Starting main thread\n\r");

	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;
	}


	bclk_period = BITCLK;	
	
	while (pwm_set_dt(&pwm_led0, PERIOD_PWM0_NSEC, PULSE_WIDTH_PWM0_NSEC )) {
		bclk_period /= 2U;
		printk("half bclk");
	}

	ws_period = WORD_S;	
	while (pwm_set_dt(&pwm_led1, PERIOD_PWM1_NSEC, PULSE_WIDTH_PWM1_NSEC )) {
		ws_period /= 2U;
		printk("half ws");
	}
	ws_period = WORD_S;
	bclk_period = BITCLK;

	const struct device *const i2s_dev_rx = DEVICE_DT_GET(I2S_RX_NODE);
	// const struct device *const i2s_dev_tx = DEVICE_DT_GET(I2S_TX_NODE);
	struct i2s_config config;

	if (!device_is_ready(i2s_dev_rx)) {
		printk("%s is not ready\n", i2s_dev_rx->name);
		return 0;
	}

	// if (i2s_dev_rx != i2s_dev_tx && !device_is_ready(i2s_dev_tx)) {
	// 	printk("%s is not ready\n", i2s_dev_tx->name);
	// 	return 0;
	// }

	config.word_size = SAMPLE_BIT_WIDTH;
	config.channels = NUMBER_OF_CHANNELS;
	config.format = 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;
	config.timeout = TIMEOUT;

	// if (!configure_streams(i2s_dev_rx, i2s_dev_tx, &config)) {
        if (!configure_streams(i2s_dev_rx,  &config)) {
		return 0;
	}
        printk("Streams and I2S configured\n");
	for (;;) {
		k_sem_take(&toggle_transfer, K_FOREVER);

		
                if (!trigger_command(i2s_dev_rx,
				     I2S_TRIGGER_START)) {                                        
			return 0;
		}

		printk("Streams started\n");

		while (k_sem_take(&toggle_transfer, K_NO_WAIT) != 0) {
			void *mem_block;
			uint32_t block_size;
			int ret;

			ret = i2s_read(i2s_dev_rx, &mem_block, &block_size);
			if (ret < 0) {
				printk("Failed to read data: %d\n", ret);
				break;
			}
			unsigned int* value_ptr = (unsigned int*)mem_block;
    		        unsigned int value = *value_ptr;
   			printk("Received audio data: %d\n", value);

			process_block_data(mem_block, SAMPLES_PER_BLOCK);
		}

		
                if (!trigger_command(i2s_dev_rx,
				     I2S_TRIGGER_DROP)) {
			return 0;
		}

		printk("Streams stopped\n");
	}

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

		ret2 =pwm_set_dt(&pwm_led1, PERIOD_PWM1_NSEC, PULSE_WIDTH_PWM1_NSEC );
		if (ret2) {
			printk("Error %d: failed to set pulse width\n", ret2);
			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:

*** Booting nRF Connect SDK v2.7.99-cs1-23a9baf2aed6 ***
*** Using Zephyr OS v3.6.99-28b6861211d2 ***
Starting main thread
[00:00:00.492,156] <dbg> pwm_nrfx: pwm_nrfx_set_cycles: channel 0, pulse 192, period 384, prescaler: 0.
[00:00:00.502,441] <dbg> pwm_nrfx: pwm_nrfx_set_cycles: channel 0, pulse 2, period 6, prescaler: 0.
Streams and I2S configured
Streams started
Received audio data: 2621440
Received audio data: 825280
[00:00:00.620,788] <err> i2s_nrfx: Failed to allocate next RX buffer: -12
Received audio data: 890048
Received audio data: 688832
Failed to read data: -5
Streams stopped

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

Umer

Update: I was able to resolve the re-allocation error by rewriting the software main.c in the following way. The worker thread now keeps freeing the space after reading the data from I2S microphone.

/*
 * 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/logging/log.h>
#include <zephyr/logging/log_ctrl.h>
#include <string.h>
#include <zephyr/drivers/pwm.h>

LOG_MODULE_REGISTER(MODULE, 3);


#define I2S_RX_NODE         DT_NODELABEL(i2s_rx)
#define SAMPLE_FREQUENCY 44100
#define SAMPLE_BIT_WIDTH 24
#define BYTES_PER_SAMPLE sizeof(int32_t)
#define NUMBER_OF_CHANNELS 1
// #define SAMPLES_PER_BLOCK ((SAMPLE_FREQUENCY / 200) * NUMBER_OF_CHANNELS)
#define INITIAL_BLOCKS 2
#define TIMEOUT 2000

// size of a block for .3 ms of audio data
#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      20

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

// #define BITCLK PWM_KHZ(4000U)
// #define WORD_S PWM_HZ(62500U)

// // 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 = SAMPLE_BIT_WIDTH;
	config.channels = NUMBER_OF_CHANNELS;
	config.format = 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);
    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;
    // 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);
    }
    k_sem_take(&enable_recording, K_FOREVER);
}


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) {
            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 (!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;
	}

	// enabling PWM signals for both modules

		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){
		
		
		LOG_INF("Start recording again\r\n");
		start_recording();
		k_sleep(K_MSEC(5000));
		LOG_INF("Stop recording again\r\n");
		stop_recording();
		k_sleep(K_MSEC(5000));
		}
}

The issue I face now is still an err=-5 but it states Unable to Stop Trigger for I2S bus and Unable to configure trigger start for I2S bus, after receiving one batch of data. 

*** Booting nRF Connect SDK v2.7.99-cs1-23a9baf2aed6 ***
*** Using Zephyr OS v3.6.99-28b6861211d2 ***
[00:00:00.270,050] <inf> MODULE: Starting main thread

[00:00:00.376,037] <inf> MODULE: Recording module initialized
[00:00:00.382,507] <dbg> pwm_nrfx: pwm_nrfx_set_cycles: channel 0, pulse 192, period 384, prescaler: 0.
[00:00:00.392,791] <dbg> pwm_nrfx: pwm_nrfx_set_cycles: channel 0, pulse 2, period 6, prescaler: 0.
[00:00:00.402,679] <inf> MODULE: Start recording again

[00:00:00.408,721] <inf> MODULE: Worker thread started
[00:00:00.414,550] <inf> MODULE:  raw rx: 0 - Received 880 bytes => 220 samples 
[00:00:00.422,760] <inf> MODULE:  raw rx: -391232 - Received 880 bytes => 220 samples 
[00:00:00.431,488] <inf> MODULE:  raw rx: -65024 - Received 880 bytes => 220 samples 
[00:00:00.440,155] <inf> MODULE:  raw rx: 272768 - Received 880 bytes => 220 samples 
[00:00:00.461,608] <err> i2s_nrfx: No room in RX queue
[00:00:00.482,696] <err> i2s_nrfx: No room in RX queue
[00:00:00.448,822] <inf> MODULE:  raw rx: 536893704 - Received 880 bytes => 220 samples 
[00:00:00.499,938] <inf> MODULE:  raw rx: 610176 - Received 880 bytes => 220 samples 
[00:00:00.508,575] <inf> MODULE:  raw rx: 536893704 - Received 880 bytes => 220 samples 
[00:00:00.517,456] <inf> MODULE:  raw rx: 536893704 - Received 880 bytes => 220 samples 
[00:00:00.526,336] <inf> MODULE:  raw rx: 793344 - Received 880 bytes => 220 samples 
[00:00:05.408,752] <inf> MODULE: Stop recording again

[00:00:05.414,642] <inf> MODULE: Stopping recording
[00:00:05.420,166] <err> MODULE: Unable to stop trigger for I2S bus (-5)
[00:00:10.427,947] <inf> MODULE: Start recording again

[00:00:10.433,929] <err> MODULE: Unable to configure trigger start for I2S bus (-5)
[00:00:15.442,657] <inf> MODULE: Stop recording again

[00:00:15.448,547] <inf> MODULE: Stopping recording
[00:00:15.454,071] <err> MODULE: Unable to stop trigger for I2S bus (-5)

Parents Reply Children
Related