i2s_trigger failing while setting up - nRF5340 Audio DK

Hey, I'm trying to setup i2s buffers and other related stuff for mic communication, this is my code for i2s

#include <zephyr/kernel.h>
#include <zephyr/drivers/i2s.h>
#include <zephyr/logging/log.h>


/* Audio I2S Configuration Definitions */
#define AUDIO_WORD_SIZE (32) // Number of bits representing one data word.
#define AUDIO_NUM_CHANNELS (2) // Number of words per frame.
#define AUDIO_FORMAT I2S_FMT_DATA_FORMAT_I2S // Data stream format as defined by I2S_FMT_* constants.
#define AUDIO_OPTIONS I2S_OPT_FRAME_CLK_MASTER // Configuration options as defined by I2S_OPT_* constants.
#define AUDIO_SAMPLE_FREQ (44100) // Frame clock (WS) frequency, this is the sampling rate.
#define AUDIO_SAMPLES_PER_CH_PER_FRAME (128) // Samples per channel per frame
#define AUDIO_SAMPLES_PER_FRAME \
    (AUDIO_NUM_CHANNELS * AUDIO_SAMPLES_PER_CH_PER_FRAME) // Samples per frame
#define AUDIO_SAMPLE_BYTES (3) // Sample bytes
#define AUDIO_FRAME_BUF_BYTES \
    (AUDIO_SAMPLES_PER_FRAME * AUDIO_SAMPLE_BYTES) // Size of one RX/TX memory block (buffer) in bytes.
#define I2S_PLAY_BUF_COUNT (50) // I2S buffer count

#define I2S_DEV DT_NODELABEL(i2s0)

// LOG_MODULE_REGISTER(i2s_custom, CONFIG_I2S_LOG_LEVEL);
LOG_MODULE_REGISTER(i2s_custom, LOG_LEVEL_DBG);


/* Device definitions */
static const struct device *host_i2s_rx_dev = DEVICE_DT_GET(DT_NODELABEL(i2s0));
static const struct device *host_i2s_tx_dev = DEVICE_DT_GET(DT_NODELABEL(i2s0));

/* Memory slab definitions */
static struct k_mem_slab i2s_rx_mem_slab;
static struct k_mem_slab i2s_tx_mem_slab;

/* tx and rx buffers */
static char rx_buffers[AUDIO_FRAME_BUF_BYTES * I2S_PLAY_BUF_COUNT];
static char tx_buffer[AUDIO_FRAME_BUF_BYTES * I2S_PLAY_BUF_COUNT];

/* Config definitions */
static struct i2s_config i2s_rx_cfg;
static struct i2s_config i2s_tx_cfg;

const struct device* get_rx_dev(void) {
    return host_i2s_rx_dev;
}

int configure_rx(void) {
    int ret;

    if (!device_is_ready(host_i2s_rx_dev)) {
        LOG_ERR("I2S RX device not ready.");
        return -1;
    }

    LOG_DBG("I2S Device: %s", host_i2s_rx_dev->name);

    /* Allocate memory slab */
    ret = k_mem_slab_init(&i2s_rx_mem_slab, rx_buffers, AUDIO_FRAME_BUF_BYTES, I2S_PLAY_BUF_COUNT);
    if (ret != 0) {
		LOG_ERR("k_mem_slab_init failed with %d error", ret);
        return ret;
	}

    /* Setting up config */
    i2s_rx_cfg.word_size = AUDIO_WORD_SIZE;
	i2s_rx_cfg.channels = AUDIO_NUM_CHANNELS;
	i2s_rx_cfg.format = I2S_FMT_DATA_FORMAT_I2S;
	i2s_rx_cfg.options = AUDIO_OPTIONS;
	i2s_rx_cfg.frame_clk_freq = AUDIO_SAMPLE_FREQ;
	i2s_rx_cfg.block_size = AUDIO_FRAME_BUF_BYTES;
	i2s_rx_cfg.mem_slab = &i2s_rx_mem_slab;
	i2s_rx_cfg.timeout = -1;

    ret = i2s_configure(host_i2s_rx_dev, I2S_DIR_RX, &i2s_rx_cfg);

    if (ret != 0) {
		LOG_ERR("i2s_configure failed with %d error", ret);
        return ret;
	}

    LOG_DBG("Successfully configured RX I2S.");
    return 0;
}

int configure_tx(void) {
    int ret;

    if (!device_is_ready(host_i2s_tx_dev)) {
        LOG_ERR("I2S TX device not ready.");
        return -1;
    }

    LOG_DBG("I2S Device: %s", host_i2s_tx_dev->name);

    /* Allocate memory slab */
    ret = k_mem_slab_init(&i2s_tx_mem_slab, tx_buffer, AUDIO_FRAME_BUF_BYTES, I2S_PLAY_BUF_COUNT);
    if (ret != 0) {
		LOG_ERR("k_mem_slab_init failed with %d error", ret);
        return ret;
	}

    /* Setting up config */
    i2s_tx_cfg.word_size = AUDIO_WORD_SIZE;
	i2s_tx_cfg.channels = AUDIO_NUM_CHANNELS;
	i2s_tx_cfg.format = I2S_FMT_DATA_FORMAT_I2S;
	i2s_tx_cfg.options = AUDIO_OPTIONS;
	i2s_tx_cfg.frame_clk_freq = AUDIO_SAMPLE_FREQ;
	i2s_tx_cfg.block_size = AUDIO_FRAME_BUF_BYTES;
	i2s_tx_cfg.mem_slab = &i2s_tx_mem_slab;
	i2s_tx_cfg.timeout = -1;

    ret = i2s_configure(host_i2s_tx_dev, I2S_DIR_TX, &i2s_tx_cfg);

    if (ret != 0) {
		LOG_ERR("i2s_configure failed with %d error", ret);
        return ret;
	}

    LOG_DBG("Successfully configured TX I2S.");
    return 0;
}

int audio_i2s_setup(void) {
    int err;

	LOG_INF("Setting up I2S...");

    err = configure_rx();
    if (err != 0) {
		LOG_ERR("Failed to setup rx i2s: err %d", err);
        return err;
	}

    err = configure_tx();
    if (err != 0) {
		LOG_ERR("Failed to setup tx i2s: err %d", err);
        return err;
	}

    /* start i2s rx driver */
	err = i2s_trigger(host_i2s_rx_dev, I2S_DIR_RX, I2S_TRIGGER_START);
	if (err != 0) {
		LOG_ERR("i2s_trigger failed with %d error\n", err);
		return err;
	}

	/* start i2s tx driver */
	err = i2s_trigger(host_i2s_tx_dev, I2S_DIR_TX, I2S_TRIGGER_START);
	if (err != 0) {
		LOG_ERR("i2s_trigger failed with %d error\n", err);
		return err;
	}

    /* receive data */
	void *rx_mem_block, *tx_mem_block;
	size_t size;

	while (true) {
		k_mem_slab_alloc(&i2s_tx_mem_slab, &tx_mem_block, K_NO_WAIT);
		i2s_read(host_i2s_rx_dev, &rx_mem_block, &size);
		memcpy(tx_mem_block, rx_mem_block, size);
		i2s_write(host_i2s_tx_dev, tx_mem_block, size);
		k_mem_slab_free(&i2s_rx_mem_slab, rx_mem_block);
	}

	LOG_INF("I2S configured successfully");


    return 0;

}

There are no errors while building or flashing, however while running i2s_trigger, I'm getting this error

<err> i2s_custom: i2s_trigger failed with -5 error

This supposedly means that  The trigger cannot be executed in the current state or a DMA channel cannot be allocated.

What could be the possible reason for this, even tho the rx and tx i2s instances are being configured properly?

  • Hello,

    Great! I am glad to hear that you're now able to send data once.
    The next thing then for us to look into is to play continuously. To do this you will need to supply new buffers to the I2S peripheral as you go. You can use the audio system implementation from the nRF5340 LE Audio reference application as a reference for how to implement this in your project. In the audio system start function you can see how additional buffers are prepared using the audio_i2s_set_next_buf function, and then again as part of the BLK complete handler function.

    Please add this into your application as well, and see if this then resolves your issue so that you may play continuously.

    Best regards,
    Karl

  • Hey,

    Sorry for the delayed reply. I am working with Zephyr I2S API and the functions you are pointing at are irrelevant as they are use nrfx_i2s. I have gone through the buffer filling part and there's a lot of complicated stuff going on there.

  • Hi Soham,

    We only have Nrf5340_Audio application as a sample in NCS.

    Even if you use the zephyr api, it would connect to underlying nordic drivers.

    Did you modify your application as suggested by Karl? Are you getting a different error (as previously you got: <err> i2s_nrfx: Next buffers not supplied on time)?

  • Hey,

    Yes I modified my application, this is the complete code.

    #include <zephyr/kernel.h>
    #include <zephyr/sys/printk.h>
    #include <zephyr/drivers/i2s.h>
    #include <zephyr/drivers/gpio.h>
    #include <string.h>
    
    
    #define I2S_RX_NODE  DT_NODELABEL(i2s0)
    #define I2S_TX_NODE  DT_NODELABEL(i2s0)
    
    #define SAMPLE_FREQUENCY    44100
    #define SAMPLE_BIT_WIDTH    16
    #define BYTES_PER_SAMPLE    sizeof(int16_t)
    #define NUMBER_OF_CHANNELS  2
    #define TONE_FREQUENCY      440 
    /* 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
    
    #define M_PI 3.14159
    
    #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 sine_wave[SAMPLES_PER_BLOCK];
    
    static int16_t echo_block[SAMPLES_PER_BLOCK];
    static volatile bool echo_enabled = true;
    static K_SEM_DEFINE(toggle_transfer, 1, 1);
    
    static void generate_sine_wave(void)
    {
        for (int i = 0; i < SAMPLES_PER_BLOCK / NUMBER_OF_CHANNELS; ++i) {
            double sample = sin(2.0 * M_PI * TONE_FREQUENCY * i / SAMPLE_FREQUENCY);
            int16_t sample_value = (int16_t)(sample * INT16_MAX);
            for (int j = 0; j < NUMBER_OF_CHANNELS; ++j) {
                sine_wave[i * NUMBER_OF_CHANNELS + j] = sample_value;
            }
        }
    }
    
    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 device *i2s_dev_tx,
    			      const struct i2s_config *config)
    {
    	int ret;
    
    	if (i2s_dev_rx == i2s_dev_tx) {
    		ret = i2s_configure(i2s_dev_rx, I2S_DIR_BOTH, config);
    		if (ret == 0) {
    			return true;
    		}
    		/* -ENOSYS means that the RX and TX streams need to be
    		 * configured separately.
    		 */
    		if (ret != -ENOSYS) {
    			printk("Failed to configure streams: %d\n", ret);
    			return false;
    		}
    	}
    
    	ret = i2s_configure(i2s_dev_rx, I2S_DIR_RX, config);
    	if (ret < 0) {
    		printk("Failed to configure RX stream: %d\n", ret);
    		return false;
    	}
    
    	ret = i2s_configure(i2s_dev_tx, I2S_DIR_TX, config);
    	if (ret < 0) {
    		printk("Failed to configure TX stream: %d\n", ret);
    		return false;
    	}
    
    	return true;
    }
    
    static bool prepare_transfer(const struct device *i2s_dev_rx,
    			     const struct device *i2s_dev_tx)
    {
    	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;
    		}
    
    		memcpy(mem_block, sine_wave, BLOCK_SIZE);
    
    		ret = i2s_write(i2s_dev_tx, mem_block, BLOCK_SIZE);
    		if (ret < 0) {
    			printk("Failed to write block %d: %d\n", i, ret);
    			return false;
    		}
    	}
    
    	return true;
    }
    
    static bool trigger_command(const struct device *i2s_dev_rx,
    			    const struct device *i2s_dev_tx,
    			    enum i2s_trigger_cmd cmd)
    {
    	int ret;
    
    	if (i2s_dev_rx == i2s_dev_tx) {
    		ret = i2s_trigger(i2s_dev_rx, I2S_DIR_BOTH, cmd);
    		if (ret == 0) {
    			return true;
    		}
    		/* -ENOSYS means that commands for the RX and TX streams need
    		 * to be triggered separately.
    		 */
    		if (ret != -ENOSYS) {
    			printk("Failed to trigger command %d: %d\n", cmd, ret);
    			return false;
    		}
    	}
    
    	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;
    	}
    
    	ret = i2s_trigger(i2s_dev_tx, I2S_DIR_TX, cmd);
    	if (ret < 0) {
    		printk("Failed to trigger command %d on TX: %d\n", cmd, ret);
    		return false;
    	}
    
    	return true;
    }
    
    int main(void)
    {
    	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;
    
    	printk("I2S echo sample\n");
    
    	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;
    	}
    
    	generate_sine_wave();
    
    	config.word_size = SAMPLE_BIT_WIDTH;
    	config.channels = NUMBER_OF_CHANNELS;
    	config.format = I2S_FMT_DATA_FORMAT_I2S;
    	config.options = I2S_OPT_BIT_CLK_MASTER | I2S_OPT_FRAME_CLK_MASTER;
    	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)) {
    		return 0;
    	}
    
    	for (;;) {
    		k_sem_take(&toggle_transfer, K_FOREVER);
    
    		if (!prepare_transfer(i2s_dev_rx, i2s_dev_tx)) {
    			return 0;
    		}
    
    		if (!trigger_command(i2s_dev_rx, i2s_dev_tx,
    				     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;
    			} else if(ret == 0) {
    				printk("Successfully read data (%d)...\n", block_size);
    			}
    			
    			
    
    			process_block_data(mem_block, SAMPLES_PER_BLOCK);
    			memcpy(mem_block, sine_wave, BLOCK_SIZE);
    
    
    			ret = i2s_write(i2s_dev_tx, mem_block, block_size);
    			if (ret < 0) {
    				printk("Failed to write data: %d\n", ret);
    				break;
    			} else if(ret == 0) {
    				printk("Successfully wrote data (%d)\n\n", block_size);
    			}
    
    		}
    
    		if (!trigger_command(i2s_dev_rx, i2s_dev_tx,
    				     I2S_TRIGGER_DROP)) {
    			return 0;
    		}
    
    		printk("Streams stopped\n");
    	}
    }
    

    This code is supposed to generate a sine wave tone and play it to the speakers connected to the I2S pins.

    No build or flash errors, after running it just spams

    Successfully read data (17640)

    Successfully wrote data (17640)

    which im assuming means its able to read and write data from I2S, but I ain't getting any output from the speakers. 

Related