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?

  • Bumping this thread since there's no reply for the past 6 days. 

    Can someone please look into this on priority?

  • Hello,

    Thank you for your extreme patience with this.

    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?

    Error 5 is IO error, which could mean that the communication with the device is not going as expected.
    Do you have access to a logic analyzer, so that you may confirm that communication on the lines directly?

    Where do you get your interpretation of the error number 5?

    Best regards,
    Karl

  • Where do you get your interpretation of the error number 5?

    It's mentioned in the definition of i2s_trigger function that the EIO error corresponds to that description.

    Do you have access to a logic analyzer, so that you may confirm that communication on the lines directly?

    I did not get you, can you please elaborate how I'm supposed to do that?

  • Hello,

    SohamGhugare said:
    It's mentioned in the definition of i2s_trigger function that the EIO error corresponds to that description.

    You are correct.
    Your issue here is likely the state in which the trigger is issued. The I2S START trigger can only be used in READY state.
    Could you write out your I2S states throughout the program, so that we may verify that they are set as expected?
    The I2S_TRIGGER_START command requires that some data is already queued, have you already populated these buffers before issuing the tx command?

    SohamGhugare said:
    I did not get you, can you please elaborate how I'm supposed to do that?

    Do you have access to a logic analyzer or oscilloscope so that you may scope the I2S lines, to verify what is being sent on them?
    This question will become relevant again if you should encounter any issues with the audio once we get the transfers started.

    Best regards,
    Karl

  • have you already populated these buffers before issuing the tx command?

    Oh right, that was the issue, I was executing the START Trigger before populating the buffers. I fixed that but now I can only send data once.

    So I made a write_audio handler which collects the audio data sent over ble and writes it to I2S, here's the relevant files:

    i2s.c

    #include <zephyr/kernel.h>
    #include <zephyr/drivers/i2s.h>
    #include <zephyr/logging/log.h>
    
    #include "i2s.h"
    
    /* Audio I2S Configuration Definitions */
    #define SAMPLE_FREQUENCY    44100
    #define SAMPLE_BIT_WIDTH    16
    #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 SAMPLES_PER_BLOCK 	16
    #define INITIAL_BLOCKS      2
    #define TIMEOUT             1000
    
    #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);
    
    #define I2S_DEV DT_NODELABEL(i2s_dev)
    
    static int16_t echo_block[SAMPLES_PER_BLOCK];
    static volatile bool echo_enabled = true;
    static K_SEM_DEFINE(toggle_transfer, 1, 1);
    
    /* Device definitions */
    const struct device *const i2s_dev_rx = DEVICE_DT_GET(DT_NODELABEL(i2s_dev));
    const struct device *const i2s_dev_tx = DEVICE_DT_GET(DT_NODELABEL(i2s_dev));
    
    
    // LOG_MODULE_REGISTER(i2s_custom, CONFIG_I2S_LOG_LEVEL);
    LOG_MODULE_REGISTER(i2s_custom, LOG_LEVEL_DBG);
    
    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) {
    			LOG_ERR("Failed to configure streams: %d\n", ret);
    			return false;
    		}
    	}
    
    	ret = i2s_configure(i2s_dev_rx, I2S_DIR_RX, config);
    	if (ret < 0) {
    		LOG_ERR("Failed to configure RX stream: %d\n", ret);
    		return false;
    	}
    
    	ret = i2s_configure(i2s_dev_tx, I2S_DIR_TX, config);
    	if (ret < 0) {
    		LOG_ERR("Failed to configure TX stream: %d\n", ret);
    		return false;
    	}
    
    	return true;
    }
    
    static bool prepare_transfer(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) {
    			LOG_ERR("Failed to allocate TX block %d: %d\n", i, ret);
    			return false;
    		}
    
    		memset(mem_block, 0, BLOCK_SIZE);
    
    		ret = i2s_write(i2s_dev_tx, mem_block, BLOCK_SIZE);
    		if (ret < 0) {
    			LOG_ERR("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) {
    			LOG_ERR("Failed to trigger command %d: %d\n", cmd, ret);
    			return false;
    		}
    	}
    
    	ret = i2s_trigger(i2s_dev_rx, I2S_DIR_RX, cmd);
    	if (ret < 0) {
    		LOG_ERR("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) {
    		LOG_ERR("Failed to trigger command %d on TX: %d\n", cmd, ret);
    		return false;
    	}
    
    	return true;
    }
    
    int i2s_write_audio_data(const uint8_t *data, size_t len) {
    	LOG_INF("Writing audio to i2s...");
    
        int ret;
    
    	// if(!prepare_transfer(i2s_dev_tx)){
    	// 	LOG_ERR("Failed to prepare transfer...");
    	// 	return -1;
    	// }
    
        void *mem_block;
        ret = k_mem_slab_alloc(&mem_slab, &mem_block, K_NO_WAIT);
        if (ret < 0) {
            LOG_ERR("Failed to allocate memory slab for I2S write: %d\n", ret);
            return ret;
        }
    
        memcpy(mem_block, data, len);
    	LOG_DBG("mem allocated...");
    
    
        ret = i2s_write(i2s_dev_tx, mem_block, len);
        if (ret < 0) {
            LOG_ERR("Failed to write audio data to I2S: %d\n", ret);
            k_mem_slab_free(&mem_slab, &mem_block);
            return ret;
        }
    
    	LOG_DBG("i2s_write done...");
    
    	ret = i2s_trigger(i2s_dev_tx, I2S_DIR_TX, I2S_TRIGGER_START);
    	if(ret!=0){
    		LOG_ERR("i2s_trigger (START) failed with error %d", ret);
    		return ret;
    	}
    
    	// ret = i2s_trigger(i2s_dev_tx, I2S_DIR_TX, I2S_TRIGGER_DROP);
    	// if(ret!=0){
    	// 	LOG_ERR("i2s_trigger (DROP) failed with error %d", ret);
    	// 	return ret;
    	// }
    
    	return 0;
    }
    
    int audio_i2s_setup(void) {
        int err;
    
    	LOG_INF("Setting up I2S...");
    
        
    	struct i2s_config config;
    
    	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_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;
    	}
    
    	LOG_INF("I2S configured successfully");
    
        return 0;
    
    }
    

    ble.c

    #include <zephyr/kernel.h>
    #include <zephyr/device.h>
    #include <zephyr/logging/log.h>
    #include <zephyr/bluetooth/bluetooth.h>
    #include <zephyr/bluetooth/conn.h>
    #include <zephyr/bluetooth/hci.h>
    #include <zephyr/bluetooth/gap.h>
    #include <zephyr/bluetooth/gatt.h>
    #include <zephyr/bluetooth/uuid.h>
    #include <zephyr/bluetooth/addr.h>
    #include <zephyr/drivers/i2s.h>
    
    
    #include "../gpio/gpio.h"
    #include "../audio/i2s.h"
    
    LOG_MODULE_REGISTER(ble_custom, CONFIG_BLE_LOG_LEVEL);
    
    #define AUDIO_UUID_VAL BT_UUID_128_ENCODE(0x00001234, 0x1234, 0xabcd, 0x1234, 0x785feabcd123)
    #define AUDIO_CHAR_UUID_VAL BT_UUID_128_ENCODE(0x00001235, 0x1234, 0xabcd, 0x1234, 0x785feabcd123)
    #define AUDIO_UUID BT_UUID_DECLARE_128(AUDIO_UUID_VAL)
    #define AUDIO_CHAR_UUID BT_UUID_DECLARE_128(AUDIO_CHAR_UUID_VAL)
    
    
    /* Audio characteristic value */
    // static uint8_t audio_value[251]; 
    static uint8_t audio_value[32]; // TODO: Change Buffer Length
    
    
    /* Write data handler for GATT service */
    static ssize_t write_audio(struct bt_conn *conn, const struct bt_gatt_attr *attr, const void *buf, uint16_t len, uint16_t offset, uint8_t flags)
    {
        LOG_INF("Received audio data, len: %d", len);
    
        // Process the received audio data
        memcpy(audio_value, buf, len);
    
        int err;
        // Send the audio data to the I2S interface
        err = i2s_write_audio_data(audio_value, len);
        if(err!=0){
            LOG_ERR("i2s_write_audio_data failed with error %d", err);
            return -1;
        }
        LOG_INF("Sent audio data to I2S, len: %d", len);
    
        return len;
    }
    
    
    BT_GATT_SERVICE_DEFINE(audio_svc,
        BT_GATT_PRIMARY_SERVICE(AUDIO_UUID),
        BT_GATT_CHARACTERISTIC(AUDIO_CHAR_UUID,
                               BT_GATT_CHRC_WRITE,
                               BT_GATT_PERM_WRITE,
                               NULL, write_audio, NULL),
    );
    
    
    
    /* GATT Operation Callbacks */
    static void connected(struct bt_conn *conn, uint8_t err)
    {
        if (err) {
            LOG_ERR("Failed to connect (err %u)", err);
            return;
        }
        LOG_INF("Connected");
    
        /* Request Data Length Extension (DLE) */
        struct bt_conn_le_data_len_param data_len_params = {
            .tx_max_len = 251,
            .tx_max_time = 2120,
        };
    
        int data_len_err = bt_conn_le_data_len_update(conn, &data_len_params);
        if (data_len_err) {
            LOG_ERR("Data length update failed (err %d)", data_len_err);
        } else {
            LOG_INF("Data length update requested");
        }
    
    
        set_green_led();
    }
    
    static void disconnected(struct bt_conn *conn, uint8_t reason)
    {
        LOG_INF("Disconnected (reason %u)", reason);
        set_red_led();
        k_msleep(1000);
        set_blue_led();
    }
    
    static void le_data_length_updated(struct bt_conn *conn, const struct bt_conn_le_data_len_info *info)
    {
        LOG_INF("Data length updated: TX (%u) RX (%u)", info->tx_max_len, info->rx_max_len);
    }
    
    struct bt_conn_cb connection_callbacks = {
        .connected = connected,
        .disconnected = disconnected,
        .le_data_len_updated = le_data_length_updated,
    };
    
    int ble_setup(void) {
        LOG_INF("Setting up BLE...");
    
        bt_conn_cb_register(&connection_callbacks);
    
        int err;
        err = bt_enable(NULL);
        if (err) {
            LOG_ERR("Bluetooth init failed (err %d)", err);
            return err;
        }
        LOG_INF("Bluetooth initialized");
    
        err = bt_le_adv_start(BT_LE_ADV_CONN, NULL, 0, NULL, 0);
        if (err) {
            LOG_ERR("Advertising failed to start (err %d)", err);
            return err;
        }
        LOG_INF("Advertising successfully started");
    
        set_blue_led();
    
    	LOG_INF("BLE configured successfully");
    
        return 0;
    
    }

    The end result what I want to achieve is being able to stream audio data from my mobile phone to the speakers connected. The data flow is as follows:

    BLE -> I2S -> CS47L63 Codec -> Headphone Out

    however now when I connect to the board via BLE and send some data, it writes to I2S successfully once and then throws this error

    <err> i2s_nrfx: Next buffers not supplied on time

    Where am I going wrong now?

    PS: I changed the buffer size for testing, it was initially 17k bytes

Related