I2S configuration error when running code.

Good day! My code is adapted from the i2s echo example. I removed all the code regarding the button click and codec because it is not needed in my application. Below is the code. When i run the code, i get errors on configuring the Rx stream. 

/*
 * 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    62500
#define SAMPLE_BIT_WIDTH    32
#define BYTES_PER_SAMPLE    sizeof(int16_t)
#define NUMBER_OF_CHANNELS  2
/* Such block length provides an echo with the delay of 100 ms. */
#define SAMPLES_PER_BLOCK   ((SAMPLE_FREQUENCY / 10) * 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)

#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 int16_t echo_block[SAMPLES_PER_BLOCK];
static volatile bool echo_enabled = true;
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) {
			int16_t *sample = &((int16_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 prepare_transfer(const struct device *i2s_dev_rx)
{
	int ret;

	for (int i = 0; i < INITIAL_BLOCKS; ++i) {
		void *mem_block;

		ret = k_mem_slab_alloc(&mem_slab, &mem_block, K_NO_WAIT);
		if (ret < 0) {
			printk("Failed to allocate TX block %d: %d\n", i, ret);
			return false;
		}

		memset(mem_block, 0, BLOCK_SIZE);
	}

	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");
	printk("PWM-based blinky\n");

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

	/*
	 * In case the default MAX_PERIOD value cannot be set for
	 * some PWM hardware, decrease its value until it can.
	 *
	 * Keep its value at least MIN_PERIOD * 4 to make sure
	 * the sample changes frequency at least once.
	 */

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

	ws_period = WORD_S;
	while (pwm_set_dt(&pwm_led1, ws_period, ws_period / 2U)) {
		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);
	struct i2s_config config;

	printk("I2S echo sample\n");


	if (!device_is_ready(i2s_dev_rx)) {
		printk("%s is not ready\n", i2s_dev_rx->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, &config)) {
		return 0;
	}

	for (;;) {
		k_sem_take(&toggle_transfer, K_FOREVER);

		if (!prepare_transfer(i2s_dev_rx)) {
			return 0;
		}

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

			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, bclk_period, bclk_period / 2U);
		if (ret1) {
			printk("Error %d: failed to set pulse width\n", ret1);
			return 0;
		}

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

overlay

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

&pinctrl {

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

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

    pwm1_csleep: pwm1_csleep {
        group1 {
            psels = <NRF_PSEL(PWM_OUT0, 0, 28)>;
            low-power-enable;
        };
    };
	i2s0_default_alt: i2s0_default_alt {
		group1 {
			psels = <NRF_PSEL(I2S_SDIN, 0, 29)>;
		};
	};
	i2s0_sleep: i2s0_sleep {
		group1 {
			psels = <NRF_PSEL(I2S_SDIN, 0, 29)>;             
			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>;
        };
		pwm_led1: pwm_led_1 {
            pwms = <&pwm1 0 PWM_MSEC(20) PWM_POLARITY_NORMAL>;
        };
    };
	aliases {
		pwm-led0 = &pwm_led0;
		pwm-led1 = &pwm_led1;
	
	};
};

  • Update. I managed to start the rx streams by changing the swidth to 24 but I am stuck on i2s_read() error code -11. I think this is similar with the problem from this one. What does error -11 mean? I can give a picture of my pin connections if needed.

    I also want to ask if I still need to assign pins for MCK and LRCLK if I am going to use I2S in slave mode. I am currently using PWM signals to emulate the MCK and LRCLK. These PWM signals are the one connected to the SPH0645 microphone. Thank you!

  • Hi,

    Qwelectric said:
    Update. I managed to start the rx streams by changing the swidth to 24 but I am stuck on i2s_read() error code -11. I think this is similar with the problem from this one. What does error -11 mean? I can give a picture of my pin connections if needed.

    This means that the Waiting period timed out. Most likely there is no response from the other side. If you probe the signals, do you see the expected output?

    regards

    Jared 

  • By signals do you mean the PWM for the external clocks? If yes, I have tested it on an oscilloscope and the signals had the expected output. If by signals you mean the I2S output itself, I haven't tried that on an oscilloscope yet. Anyway for the follow up question regarding the SCK and LRCLK, do I still need to assign pins for them if I want to use the I2S as a slave? 

    Another follow-up. Since the microphone wants 32bit SWIDTH (which is why I am using slave mode), what do I enter in this line '#define SAMPLE_BIT_WIDTH    xx'? I tried running it with 32 but it won't start streams with that. 

    Thank you for your response. I hope you can be patient with me because I'm just a beginner

  • Good day! Just want to follow up with these concerns

  • Hi Daniel

    Jared is a bit busy at the moment, and I agreed to help out with this one. 

    By signals I assume he meant SCK, LRCLK, SDOUT and SDIN.

    Qwelectric said:
    Anyway for the follow up question regarding the SCK and LRCLK, do I still need to assign pins for them if I want to use the I2S as a slave? 

    You most certainly do. In slave mode these pins will be inputs rather than outputs, but without them no data will be sent or received, which could explain why you see the EAGAIN (-11) error when trying to read I2S data. 

    In master mode the nRF will output data whether or not anything is connected, but in slave mode this is not the case. 

    If you want the nRF device to be the slave it means the SPH0645 has to be the master. Are you sure that this is supported in this device? 

    I would start by checking that the SPH is actually outputting data on the SCK, LRCLK and SDOUT pins. Once you have confirmed this you can try to connect these pins to the nRF device, and verify whether or not you can see the data received on the nRF side. 

    Best regards
    Torbjørn

Related