I2S Zephyr SDOUT not working

I am trying to get I2S communication up and running but am unable to produce a signal on my SDOUT pin. MCLK, LRCLK and SCLK work as intended but trying to send any data does not work. I did notice that the TXD.PTR register is empty. I have tried multiple pins for my SDOUT and have nrf SDK 1.8.0 installed, using VScode. Any help would be appreciated.

#include <zephyr.h>
#include <drivers/gpio.h>
#include <init.h>
#include <nrf.h>
#include <nrfx.h>
#include <nrfx_gpiote.h>
#include <drivers/i2s.h>
#include "nrfx_i2s.h"
#include <errno.h>
#include <string.h>
#include <logging/log.h>

LOG_MODULE_REGISTER(Main);

#define AUDIO_SAMPLE_FREQ (44100)
#define AUDIO_NUM_CHANNELS (1)
#define AUDIO_SAMPLE_BIT_WIDTH (16)
#define AUDIO_SAMPLES_PER_CH_PER_FRAME (128)
#define AUDIO_SAMPLES_PER_FRAME (AUDIO_SAMPLES_PER_CH_PER_FRAME * AUDIO_NUM_CHANNELS)
#define AUDIO_SAMPLE_BYTES (sizeof(uint16_t))
#define AUDIO_FRAME_BUF_BYTES (AUDIO_SAMPLES_PER_FRAME * AUDIO_SAMPLE_BYTES)
#define I2S_PLAY_BUF_COUNT (500)

static const struct device *host_i2s_tx_dev;
static struct k_mem_slab i2s_tx_mem_slab;
static char tx_buffer[AUDIO_FRAME_BUF_BYTES * I2S_PLAY_BUF_COUNT];
static struct i2s_config config;
volatile int ret;

static void init(void)
{
	/*configure tx device*/
	host_i2s_tx_dev = device_get_binding("I2S_0");

	k_mem_slab_init(&i2s_tx_mem_slab, tx_buffer, AUDIO_FRAME_BUF_BYTES, I2S_PLAY_BUF_COUNT);

	/* configure i2s for audio playback */
	config.word_size = AUDIO_SAMPLE_BIT_WIDTH;
	config.channels = AUDIO_NUM_CHANNELS;
	config.format = I2S_FMT_DATA_FORMAT_I2S;
	config.options = (I2S_OPT_BIT_CLK_MASTER | I2S_OPT_FRAME_CLK_MASTER);
	config.frame_clk_freq = AUDIO_SAMPLE_FREQ;
	config.block_size = AUDIO_FRAME_BUF_BYTES;
	config.mem_slab = &i2s_tx_mem_slab;
	config.timeout = -1;
	ret = i2s_configure(host_i2s_tx_dev, I2S_DIR_TX, &config);
	if (ret < 0)
	{
		LOG_INF("tx config:%d", ret);
	}
}

void main(void)
{
	init();

	uint16_t buf[128];
	volatile size_t size = sizeof(buf);

	int i = 0;
	for (i = 0; i < size; i++)
	{
		if (i <= size / 4)
		{
			buf[i] = i;
		}
		else if (i > size / 4)
		{
			buf[i] = size / 2 - i;
		}
	}

	void *tx_mem_block;

	ret = i2s_trigger(host_i2s_tx_dev, I2S_DIR_TX, I2S_TRIGGER_START);

	while (1)
	{
		ret = k_mem_slab_alloc(&i2s_tx_mem_slab, &tx_mem_block, K_NO_WAIT);
		memcpy(tx_mem_block, &buf, size);
		ret = i2s_write(host_i2s_tx_dev, tx_mem_block, size);
		k_mem_slab_free(&i2s_tx_mem_slab, &tx_mem_block);
	}
}
# CONFIG_MULTITHREADING=n
# CONFIG_KERNEL_MEM_POOL=n
# CONFIG_NUM_PREEMPT_PRIORITIES=0
# CONFIG_SYS_CLOCK_EXISTS=n
# CONFIG_ARM_MPU=n
# CONFIG_SIZE_OPTIMIZATIONS=y
CONFIG_I2S=y
CONFIG_NRFX_I2S=y
# CONFIG_WATCHDOG=n
CONFIG_GPIO=y
# CONFIG_PINMUX=n
# CONFIG_SPI=n
# CONFIG_SERIAL=n
# CONFIG_FLASH=n
# CONFIG_PM=n
# CONFIG_DYNAMIC_INTERRUPTS=n
# CONFIG_IRQ_OFFLOAD=n
# CONFIG_THREAD_STACK_INFO=n
# CONFIG_THREAD_CUSTOM_DATA=n
# CONFIG_BOOT_BANNER=n
# CONFIG_BOOT_DELAY=0
# CONFIG_CONSOLE=n
# CONFIG_UART_CONSOLE=n
# CONFIG_STDOUT_CONSOLE=n
# CONFIG_EARLY_CONSOLE=n
CONFIG_BOARD_ENABLE_CPUNET=n
CONFIG_NRF_RTC_TIMER=n
CONFIG_CORTEX_M_SYSTICK=y
CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC=64000000
CONFIG_SYS_CLOCK_TICKS_PER_SEC=1000000
CONFIG_LOG=y
CONFIG_USE_SEGGER_RTT=y
CONFIG_LOG_BACKEND_RTT=y

  • Yes I use nrf connect for VS code, and I just recently tried it in an unmodified SDK. It runs without any errors for me but the DOUT line remains low while the clocks function.

  • Hello,

    So I was able to change the project so that I was able to compile without your custom files, and so that it ran on my nRF53DK "without errors". 

    Did you check the return value from i2s_write()? (It didn't print until I added "k_sleep(K_SECONDS(1))" to the end of your while loop (for the log to be processed), but in my case it returns -5 and prints "Cannot write in state: 4",  where 4 refers to I2S_STATE_ERROR.

    I haven't gotten to play around with I2S in NCS that much. Did you test the sample found in NCS\zephyr\samples\drivers\i2s?
    Best regards,
    Edvin
  • Hi, 

    Yes I did take a look at the sample projects there but unfortunately they did not work on my board. Not sure if something is being configured incorrectly or where the issue is stemming from. I had a delay at first but then it gave me the I2S_STATE_ERROR until I removed it. The state that I sent my code in ran for me with all returns of 0.

  • Can you please try to replace your while loop with this:

        ret = 0;
    	while (!ret)
    	{
    		ret = k_mem_slab_alloc(&i2s_tx_mem_slab, &tx_mem_block, K_NO_WAIT);
    		if (ret < 0)
    		{
    			LOG_INF("mem slab alloc: %d", ret);
    		}
    
    		memcpy(tx_mem_block, buf, size);
    
    	    ret = i2s_trigger(host_i2s_tx_dev, I2S_DIR_TX, I2S_TRIGGER_START);
    	    if (ret < 0)
    	    {
                LOG_INF("trigger: %d", ret);
            }
    
    		ret = i2s_write(host_i2s_tx_dev, tx_mem_block, size);
    		if (ret < 0)
    		{
    			LOG_INF("write: %d", ret);
    		}
    
    		k_mem_slab_free(&i2s_tx_mem_slab, &tx_mem_block);
    	}
        while (true)
        {
            LOG_INF("done");
            k_sleep(K_SECONDS(1));
        }
    }

    Is the last loop ever reached?

  • I did some more testing, removing the break and testing with the logic analyzer. This is the main.c I ended up using:

    /*
     * Copyright (c) 2020 Nordic Semiconductor ASA
     *
     * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
     */
    
    #include <zephyr.h>
    #include <drivers/gpio.h>
    #include <init.h>
    #include <nrf.h>
    #include <nrfx.h>
    #include <nrfx_gpiote.h>
    #include <drivers/i2s.h>
    #include "nrfx_i2s.h"
    #include <errno.h>
    #include <string.h>
    #include <logging/log.h>
    #include <dk_buttons_and_leds.h>
    
    LOG_MODULE_REGISTER(Main);
    
    #define AUDIO_SAMPLE_FREQ (44100)
    #define AUDIO_NUM_CHANNELS (1)
    #define AUDIO_SAMPLE_BIT_WIDTH (16)
    #define AUDIO_SAMPLES_PER_CH_PER_FRAME (128)
    #define AUDIO_SAMPLES_PER_FRAME (AUDIO_SAMPLES_PER_CH_PER_FRAME * AUDIO_NUM_CHANNELS)
    #define AUDIO_SAMPLE_BYTES (sizeof(uint16_t))
    #define AUDIO_FRAME_BUF_BYTES (AUDIO_SAMPLES_PER_FRAME * AUDIO_SAMPLE_BYTES)
    #define I2S_PLAY_BUF_COUNT (500)
    
    #define RUN_STATUS_LED DK_LED1
    #define TEST_LED1 DK_LED1
    #define TEST_LED2 DK_LED2
    #define TEST_LED3 DK_LED3
    #define TEST_LED4 DK_LED4
    #define TEST_COUNT  10
    
    static const struct device *host_i2s_tx_dev;
    static struct k_mem_slab i2s_tx_mem_slab;
    static char tx_buffer[AUDIO_FRAME_BUF_BYTES * I2S_PLAY_BUF_COUNT];
    static struct i2s_config config;
    volatile int ret;
    
    
    
    static void init(void)
    {
    	host_i2s_tx_dev = device_get_binding("I2S_0");
    
    	k_mem_slab_init(&i2s_tx_mem_slab, tx_buffer, AUDIO_FRAME_BUF_BYTES, I2S_PLAY_BUF_COUNT);
    	//if (ret < 0)
    	{
    		LOG_INF("mem slab init: %d", ret);
    	}
    
    	config.word_size = AUDIO_SAMPLE_BIT_WIDTH;
    	config.channels = AUDIO_NUM_CHANNELS;
    	//config.format = I2S_FMT_DATA_FORMAT_I2S;
        config.format = I2S_FMT_DATA_FORMAT_LEFT_JUSTIFIED;
    	config.options = (I2S_OPT_BIT_CLK_MASTER | I2S_OPT_FRAME_CLK_MASTER);
    	config.frame_clk_freq = AUDIO_SAMPLE_FREQ;
    	config.block_size = AUDIO_FRAME_BUF_BYTES;
    	config.mem_slab = &i2s_tx_mem_slab;
    	config.timeout = 1000;
    
    	ret = i2s_configure(host_i2s_tx_dev, I2S_DIR_TX, &config);
    	//if (ret < 0)
    	{
    		LOG_INF("tx config: %d", ret);
    	}
    }
    
    void main(void)
    
    {
        uint32_t counter = 0;
    	init();
    
        LOG_INF("I2S sample started");
    
    
    
    	uint16_t buf[128];
    	size_t size = sizeof(buf);
    
    	int i;
    	for (i = 0; i < size; i++)
    	{
    		if (i <= size / 4)
    		{
    			buf[i] = i;
    		}
    		else if (i > size / 4)
    		{
    			buf[i] = size / 2 - i;
    		}
    	}
        LOG_INF("0x%04x", buf[0]);
        LOG_INF("0x%04x", buf[1]);
        LOG_INF("0x%04x", buf[2]);
        LOG_INF("0x%04x", buf[3]);
        LOG_INF("0x%04x", buf[4]);
    
    	void *tx_mem_block;
    
    
    
        ret = 0;
        
    	while ((ret == 0) && (counter < TEST_COUNT))
    	{
    
           	ret = i2s_trigger(host_i2s_tx_dev, I2S_DIR_TX, I2S_TRIGGER_START);
            if (ret < 0)
            {
                LOG_INF("trigger: %d", ret);
                break;
            }
    
    
    		ret = k_mem_slab_alloc(&i2s_tx_mem_slab, &tx_mem_block, K_NO_WAIT);
    		if (ret < 0)
    		{
    			LOG_INF("mem slab alloc: %d", ret);
                break;
    		}
    
    
    		memcpy(tx_mem_block, buf, size);
    
            ret = i2s_write(host_i2s_tx_dev, tx_mem_block, size);
    		if (ret != 0)
    		{
    			LOG_INF("write: %d", ret);
                break;
    		}
    		
    		k_mem_slab_free(&i2s_tx_mem_slab, &tx_mem_block);
            counter++;
            LOG_INF("counter %d", counter);
    	}
        while (true)
        {
            k_sleep(K_SECONDS(1));
            LOG_INF("done, counter = %d", counter);
        }
    }

    I am not familiar with I2S, and how to use it, but running it on an nRF5340DK, I get data on the DOUT pin:

    The reason I changed 

    config.format = I2S_FMT_DATA_FORMAT_I2S
    to
    config.format = I2S_FMT_DATA_FORMAT_LEFT_JUSTIFIED;
    was just so that I could see that the data was correct in the logic analyzer. There was data on the DOUT pin before I changed it as well.
Related