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

Parents
  • 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 

  • Hi Daniel

    Sorry for the slow response, many of us where out in Easter holiday last week. 

    Thanks for providing links to the blog you followed, now I understand better what you are trying to do. 

    I agree that this method should be viable, but this is not something we have tested internally. 

    Do you make all the external connections as described in the readme of the github repo, to wire the two PWM signals to the I2S SCK and LRCLK pins? 

    It seems you don't configure the SCK and LRCLK I2S pins in your overlay. Are you sure that these pins are set correctly? 

    Again a scope trace would be useful, to verify that the signals going into the nRF I2S lines is correct. 

    Best regards
    Torbjørn

  • Hello, I adjusted my pinctrl in the overlay to configure the SCK and LRCLK for slave mode. I also connected wire lines to follow this 

     

    &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)>, <NRF_PSEL(I2S_SCK_S, 1, 15)>, <NRF_PSEL(I2S_LRCK_M, 1, 14)>;
    		};
    	};
    	i2s0_sleep: i2s0_sleep {
    		group1 {
    			psels = <NRF_PSEL(I2S_SDIN, 0, 29)>, <NRF_PSEL(I2S_SCK_S, 1, 15)>, <NRF_PSEL(I2S_LRCK_M, 1, 14)>;
    			low-power-enable;
    		};
    	};
    };

    I'll try to use an oscilloscope ASAP to check for signals. BTW if it matters, the most common errors I get is on the i2s_read() part where it outputs -5 and sometimes -11.

    Last question, how can I extract the audio data from the i2s memory to send it to another device using BLE? Thank you for your response!

  • Btw, if you are asking me to probe the PWM signals using oscilloscope I already have results on that. It showed correct frequency and good 50 duty cycle. The Frequency of CH1 signal is 4MHz if zoomed in. It just shows 5MHz because of zoom out.

  • Hello,

    Just taking over this case, can you just recap what is the current question(s)?

    Looking at the retval in i2s.h the return codes are:

     * @retval 0 If successful.
     * @retval -EIO The interface is in NOT_READY or ERROR state and there are no
     *         more data blocks in the RX queue.
     * @retval -EBUSY Returned without waiting.
     * @retval -EAGAIN Waiting period timed out.

    Kenneth

  • How does those return values translate to the numbers printed. Usual errors are -5 and -11 which is not listed there. I think my other questions were answered except for this:

    **Last question, how can I extract the audio data from the i2s memory to send it to another device using BLE?**

Reply Children
Related