Data shifting I2S

Hi,I have been using NRF5340 Audio DK  and I able to get  data using SPH0645 using i2s_read function and able to transmit data to amplifier TAS2562 using i2s_write function .But the issue is I am reading a data of 32 bit of each sample ,need to right shift by 16 bit and then I need to pass to amplifier the shifted data i2s_write(),but if i prob the sdout pin i able to see data of 18 bit in oscilloscope(actually mic has 18 bit data precision).I want to know how to shift data and pass to write.I took from sample i2s echo.In case if we need to use function i2s_buf_read/write help me in implementing as no example show how to use it ,wheather it uses memory slab or not as I tried and could'nt achieve to get 16 bit as output.To mention I am using single i2s_rxtx.


 &pinctrl {
	i2c0_default_alt: i2c0_default_alt {
		group1 {
			psels = <NRF_PSEL(TWIM_SDA, 1, 2)>,
			<NRF_PSEL(TWIM_SCL, 1, 3)>;
		};
	};

	i2c0_sleep_alt: i2c0_sleep_alt {
		group1 {
			psels = <NRF_PSEL(TWIM_SDA, 1, 2)>,
			<NRF_PSEL(TWIM_SCL, 1, 3)>;
			low-power-enable;
		};
	};

	i2s0_default_alt: i2s0_default_alt {
		group1 {
			psels =<NRF_PSEL(I2S_SCK_M, 0, 26)>,
			<NRF_PSEL(I2S_LRCK_M, 0, 25)>,
			<NRF_PSEL(I2S_SDOUT, 0, 6)>,
			<NRF_PSEL(I2S_SDIN, 0, 7)>;
		};
	};
	i2s0_sleep_alt: i2s0_sleep_alt {
		group1 {
			psels =  <NRF_PSEL(I2S_SCK_M, 0, 26)>,
			<NRF_PSEL(I2S_LRCK_M, 0, 25)>,
			<NRF_PSEL(I2S_SDOUT, 0, 6)>,
			<NRF_PSEL(I2S_SDIN, 0, 7)>;
		};
	};
};

&i2c1 {
	status = "okay";
	pinctrl-0 = <&i2c0_default_alt>;
	pinctrl-1 = <&i2c0_sleep_alt>;
	pinctrl-names = "default", "sleep";
};

&clock {
	hfclkaudio-frequency = <11289600>;
};

i2s_rxtx: &i2s0 {
	status = "okay";
	pinctrl-0 = <&i2s0_default_alt>;
	pinctrl-1 = <&i2s0_sleep_alt>;
	pinctrl-names = "default","sleep";
	clock-source = "ACLK";
};
#include <zephyr/kernel.h>
//#include "codec.h"
#include <zephyr/sys/printk.h>
#include <zephyr/drivers/i2s.h>
#include <zephyr/drivers/i2c.h>
#include <zephyr/drivers/gpio.h>
#include <string.h>
#include "tas.h"


#if DT_NODE_EXISTS(DT_NODELABEL(i2s_rxtx))
#define I2S_RX_NODE  DT_NODELABEL(i2s_rxtx)
#define I2S_TX_NODE  I2S_RX_NODE
#else
#define I2S_RX_NODE  DT_NODELABEL(i2s_rx)
#define I2S_TX_NODE  DT_NODELABEL(i2s_tx)
#endif
#define I2C1_NODE DT_NODELABEL(tas2563)


#define SAMPLE_FREQUENCY    16000
#define SAMPLE_BIT_WIDTH    32
#define BYTES_PER_SAMPLE    sizeof(int32_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

#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 const struct i2c_dt_spec dev_i2c = I2C_DT_SPEC_GET(I2C1_NODE);


static K_SEM_DEFINE(toggle_transfer, 1, 1);
volatile int16_t bellMicData, ancMicData, filterData, ancData=0;
int32_t process_block[SAMPLES_PER_BLOCK];
const struct device *const i2s_dev_tx = DEVICE_DT_GET(I2S_TX_NODE);

static void i2cInit()
{
	if (!device_is_ready(dev_i2c.bus))
	{
		printk("I2C bus %s is not ready!\n\r", dev_i2c.bus->name);
		return;
	}
	else
	{
		printk("I2C bus %s is ready!\n\r", dev_i2c.bus->name);
	}
}
static void process_block_data(void *mem_block, uint32_t number_of_samples)
{

		for (int i = 0; i < number_of_samples; ++i) {
			int32_t *sample = &((int32_t *)mem_block)[i];
			mem_block= (*sample)>>16 ;
			
		}
			
	}

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

		memset(mem_block, 0, 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);
	struct i2s_config config;

	printk("I2S echo sample\n");
//	i2cInit();
//	playbackInit();


	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 = 32;
	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;

			size_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 );
			ret = i2s_write(i2s_dev_tx,mem_block, block_size);
			if (ret < 0) {
				printk("Failed to read data: %d\n", ret);
			
			}

			k_mem_slab_free(&mem_slab, mem_block);

		}

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

		printk("Streams stopped\n");
	}
}

Parents
  • You say that your bitlength is 18 bit. What does it look like when it enters the application? 

    I see that you are doing some bit shifting in your handler. Does it work? What do you see? And in each of your samples (32bit, I guess). Where is the data? Is it occupying the 18 least significant bits (lower bits), or the 18 most significant bits(upper bits)?

    Best regards,

    Edvin

  • Data will be in 18 MSB,so I am shifting 16 bits to get 16 bit of each mic sample

    Does it work? What do you see?

    Data is not shifting .How can I achieve and pass to amplifier of only one mic data of 16000 samples(I will use second mic data for ANC )So I need output of only one mic .How can I do that

  • These pointer bit-handling is always a bit tricky. Particularly with void * buffers. I added this to one of our samples (I used the central_uart from ncs\nrf\samples\bluetooth\central_uart, since I recently used it for something else). I added these functions, and called it from the start of main:

    void print_blob(int *int_blob, int16_t num_elements)
    {
        for (int i=0; i<num_elements; i++) {
            LOG_INF("blob [%d] = 0x%08x", i, int_blob[i]);
        }
    }
    
    void process_blob(void *blob, int16_t num_elements)
    {
        uint32_t *int_blob = blob;
        for (int i=0; i<num_elements;i++){
            int_blob[i] = int_blob[i]>>16;
        }
    }
    
    void test_function(void)
    {
        LOG_INF("test_blob");
        void *blob;
        blob = k_malloc(3*sizeof(uint32_t));
        if (blob) {
            uint32_t *int_blob = (uint32_t*)blob;
            LOG_INF("k_malloc success");
            int_blob[0] = 0x11111111;
            int_blob[1] = 0xFFFFFFFF;
            int_blob[2] = 0x0000FFFF;
            LOG_INF("pre processing********");
            print_blob(blob, 3);
            process_blob(blob,3);
            LOG_INF("post processing*******");
            print_blob(blob, 3);
    
            //blob = int_blob;
            LOG_INF("blob = %p", blob);
            LOG_INF("int_blob = %p", int_blob);
        } 
        else {
            LOG_ERR("k_malloc fail");
        }
        
    }
    
    int main(void)
    {
        test_function();
    

    Note that I casted the pointer to a uint32_t pointer before doing modifications to it. As long as it is a pointer that is set to the same address, the content of my "blob" void pointer is also changed.

    This produced the following log:

    [00:00:00.000,640] <inf> central_uart: test_blob
    [00:00:00.000,701] <inf> central_uart: k_malloc success
    [00:00:00.000,701] <inf> central_uart: pre processing********
    [00:00:00.000,732] <inf> central_uart: blob [0] = 0x11111111
    [00:00:00.000,732] <inf> central_uart: blob [1] = 0xffffffff
    [00:00:00.000,762] <inf> central_uart: blob [2] = 0x0000ffff
    [00:00:00.000,762] <inf> central_uart: post processing*******
    [00:00:00.000,793] <inf> central_uart: blob [0] = 0x00001111
    [00:00:00.000,793] <inf> central_uart: blob [1] = 0x0000ffff
    [00:00:00.000,793] <inf> central_uart: blob [2] = 0x00000000
    [00:00:00.000,823] <inf> central_uart: blob = 0x20008f48
    [00:00:00.000,915] <wrn> cbprintf_package: (unsigned) char * used for %p argument. It's recommended to cast it to void * because it may cause misbehavior in certain configurations. String:"int_blob = %p" argument:0
    [00:00:00.000,915] <inf> central_uart: int_blob = 0x20008f48
    

    Try something similar for your processing function.

    Best regards,

    Edvin

  • Thanks for that Edvin,this definitely helps and works for me but I have tried with i2s_buff_r/w function which solved my issue in shifting and writing to i2s_buff_write with a minimal delay I can able to listen audio  in Amplifier .Can this delay be reduced ?if yes ,suggest me the way and now I need to transfer shifted data over BLE.In order to achieve that ,guide me the way.My sampling rate is 16000.I saw the audio sample is incorporated with LC3 codec but not understanding to achieve blewith my audio data.

    Thanks

    Kashyap

  • I suggest that you study the sample application, NCS\nrf\applications\nrf5340_audio, as this is a complex subject. Also see the documentation for this application.

    It is not standard BLE, but BLE Audio, which is fairly new. The LC3 codec is automatically used, unless you specify something else. 

    Regarding the delay, I don't know any way to do this more efficiently, if it is a possibility to use another sample bit width than 18, or a different microphone.

    Best regards,

    Edvin

Reply Children
Related