Unable to interface ES8388 with nrf5340DK.

HI, Developers.

  i am trying to interface ES8388 codec with nrf5340DK. but unfortunately i can't understand how to interface with this board. The ES8388 is successfully interface with i2c protocol and i able to read and write register. but in case of i2s i can't understand why it is not working, here IRQ- EVENT is worked !.  

Here i Already tried 1] SAMPLE/DRIVER/I2S/ECHO example, 2] NCS_I2S_nrfx_driver example,  3] the test example I2S-speed and I2S-api test. 4] LOOPBACK .

  1. now in ECHO example i got this error : 

[00:00:01.960,235] <err> i2s_nrfx: Failed to allocate next RX buffer: -12
Failed to read data: -11

 here is project file :  echo_ESP.zip

       2. Now in NCS_I2S_nrfx_driver by siguhe example :  i got print sound is " 0,0,0,0,0,0,0,0,0"  and also i find the in 

data_handler() function p_rx_buffer is null.  

 

if (p_released)
	{
		if (p_released->p_rx_buffer != NULL)
		{
			data_ready_flag = true; //This is used in print_sound()
		}
	}
	

>>> one more thing in this project is suggested its configure as Non-Secure peripherals. so they edited nrf/subsys/spm/Kconfig, inside the menu  "Configure Non-Secure peripherals": 

config SPM_NRF_I2S0_NS
	bool "I2S0 is Non-Secure"
	default y

and  nrf/subsys/spm/spm.c: 

#ifdef NRF_I2S0
        PERIPH("NRF_I2S0", NRF_I2S0, CONFIG_SPM_NRF_I2S0_NS),
#endif
  but this file is no longer available in current NCS version.

    3.  Now in i2s test api  example : i got this errors: 

 4] . now in loopback example : i got 

 

 here the received sample right /left  data is always got ffff/ffff  and mismatch with send data !. 

 here is the project file : loopback.zip 

 

Finally I am a current beginner in this embedded system. So I apologize if I have made many mistakes in this project. 

if i make mistake in interfacing peripheral or i misunderstand processes of interfacing peripheral. so please right processes of interfacing unknown peripheral.
and also give me a tutorial or reference of it So that I can correct my mistakes well.

Thank you.

Parents Reply
  • one more thing in this project is suggested its configure as Non-Secure peripherals. so they edited nrf/subsys/spm/Kconfig, inside the menu  "Configure Non-Secure peripherals": 

    Where did you find these old projects? Can you link to the DevZone case or similar where they are from? Regardless though, the SPM has been deprecated for quite some time now so these projects must be old, so you will have quite a few API changes you need to take into account. I would also recommend that you do not make a non-secure build for now. While that may be sensible in many applications, I would not add another layer of complexity at this moment.

Children
  • okay understand.  but whats about sample I2S-ECHO ?. and  loopback example ? 

  • The I2S echo sample should work out of the box on the nRF5340 DK with NCS 2.4.2 with WM8731, but I do not have an I2S device so I have not been able to do it myself. If it does not for you, can you explain which NCS version  you are using, how you are testing (including physical setup) and which changes to the sample did you do for your test setup (includign using ES8388)?

    Regarding loopback, it seems you have something based on zephyr/samples/drivers/audio/dmic/? That should also work on the DK out of the box and does so on my desk. Have you tested that?

  • Currently i am using ncs 2.4.0.  And i connected ES8388  to nrf5340:

    I2C connection :  nrf pin no. 25,26 respectively as DATA pin and CLK pin with codec. 

    I2S connection :  nrf pin no. 15,12,13,14,16 respectively as SCK , LRCK , DOUT, DIN and MCK with codec.

    > The power pin AVDD and DVDD with NRF pin VDD( 2.9 v). but i also tried with external power supply.

    > GND common with GND.

    > The mic is SPW 2438 and its DC(data pin of mic ) pin attached with MIC pin in codec. The Vin and         GND respectively with nrf VDD and GND pin.


    As per ECHO sample i replace library of WM8731  with ES8388.  i already attached in project file. 

    > the I2C address of codec is 0x10 so i replace in overlay file. 

    &pinctrl {
    	i2c0_default_alt: i2c0_default_alt {
    		group1 {
    			psels = <NRF_PSEL(TWIM_SDA, 0, 25)>,
    				<NRF_PSEL(TWIM_SCL, 0, 26)>;
    				bias-pull-up;
    		};
    	};
    
    	i2c0_sleep_alt: i2c0_sleep_alt {
    		group1 {
    			psels = <NRF_PSEL(TWIM_SDA, 0, 25)>,
    				<NRF_PSEL(TWIM_SCL, 0, 26)>;
    			low-power-enable;
    		};
    	};
    
    	i2s0_default_alt: i2s0_default_alt {
    		group1 {
    			psels = <NRF_PSEL(I2S_SCK_M, 1, 15)>,
    				<NRF_PSEL(I2S_LRCK_M, 1, 12)>,
    				<NRF_PSEL(I2S_SDOUT, 1, 13)>,
    				<NRF_PSEL(I2S_SDIN, 1, 14)>;	
    		};
    	};
    };
    
    &i2c1 {
    	status = "okay";
    	pinctrl-0 = <&i2c0_default_alt>;
    	pinctrl-1 = <&i2c0_sleep_alt>;
    	pinctrl-names = "default", "sleep";
    
    	wm8731: wm8731@10 {
    		compatible = "wolfson,wm8731";
    		reg = <0x10>;
    	};
    };
    
    &clock {
    	hfclkaudio-frequency = <11289600>;
    };
    
    i2s_rxtx: &i2s0 {
    	status = "okay";
    	pinctrl-0 = <&i2s0_default_alt>;
    	pinctrl-names = "default";
    	clock-source = "ACLK";
    };
    

    > The yaml file is remain same and node label remain same.

    > Now I2C is working perfectly and i am able to read and write register of codec.


    Here Codec has 4 mode;

    1] only ADC, 2] only DAC, 3] ADC and DAC both , 4] LIN-IN (bypass mode) .

    > The bypass mode is working perfectly. when i set ES8388 as LININ mode. 

       

    >> But in case of I2S  need to set  "DAC and  ADC  both " . now the register of codec is write perfectly as data sheet of code. 

    >>  The format  codec is I2S normal.

    >>  data length or sample width is 16 bit . 

    >> sample frequency is 8000.

    Now whole code is already i attached above as echo_esp.zip 

    > The code is compile with no any error. 

    >> terminal is : 

    ERROR : Streams started , Failed to read data: -5


    NOW IN LOOP-BACK  EXAMPLE : 

    It is based on nrfx driver , here i am trying  transmitting some data and  receive that data after comparing both and check its same or not: the whole code is below:

    And the physical connection remain same:  also whole project file attached as loopback.zip.

    #include <zephyr/kernel.h>
    #include <nrfx_i2s.h>
    #include <zephyr/sys/printk.h>
    #include <zephyr/logging/log.h>
    
    #include "codec.h"
    
    LOG_MODULE_REGISTER(i2s_zephyr_loopback, LOG_LEVEL_INF);
    
    
    #define I2S_DATA_BLOCK_WORDS    512
    static uint32_t m_buffer_rx[2][I2S_DATA_BLOCK_WORDS];
    static uint32_t m_buffer_tx[2][I2S_DATA_BLOCK_WORDS];
    
    // Delay time between consecutive I2S transfers performed in the main loop
    // (in milliseconds).
    #define PAUSE_TIME          500
    // Number of blocks of data to be contained in each transfer.
    #define BLOCKS_TO_TRANSFER  20
    
    static uint8_t volatile m_blocks_transferred     = 0;
    static uint8_t          m_zero_samples_to_ignore = 0;
    static uint16_t         m_sample_value_to_send;
    static uint16_t         m_sample_value_expected;
    static bool             m_error_encountered;
    
    static uint32_t       * volatile mp_block_to_fill  = NULL;
    static uint32_t const * volatile mp_block_to_check = NULL;
    
    
    static void prepare_tx_data(uint32_t * p_block)
    {
        // These variables will be both zero only at the very beginning of each
        // transfer, so we use them as the indication that the re-initialization
        // should be performed.
        if (m_blocks_transferred == 0 && m_zero_samples_to_ignore == 0)
        {
            // Number of initial samples (actually pairs of L/R samples) with zero
            // values that should be ignored - see the comment in 'check_samples'.
            m_zero_samples_to_ignore = 2;
            m_sample_value_to_send   = 0xCAFE;
            m_sample_value_expected  = 0xCAFE;
            m_error_encountered      = false;
        }
    
        // [each data word contains two 16-bit samples]
        uint16_t i;
        for (i = 0; i < I2S_DATA_BLOCK_WORDS; ++i)
        {
            uint16_t sample_l = m_sample_value_to_send - 1;
            uint16_t sample_r = m_sample_value_to_send + 1;
            ++m_sample_value_to_send;
    
            uint32_t * p_word = &p_block[i];
            ((uint16_t *)p_word)[0] = sample_l;
            ((uint16_t *)p_word)[1] = sample_r;
        }
    }
    
    
    static bool check_samples(uint32_t const * p_block)
    {
        // [each data word contains two 16-bit samples]
        uint16_t i;
        for (i = 0; i < I2S_DATA_BLOCK_WORDS; ++i)
        {
            uint32_t const * p_word = &p_block[i];
            uint16_t actual_sample_l = ((uint16_t const *)p_word)[0];
            uint16_t actual_sample_r = ((uint16_t const *)p_word)[1];
    
            // Normally a couple of initial samples sent by the I2S peripheral
            // will have zero values, because it starts to output the clock
            // before the actual data is fetched by EasyDMA. As we are dealing
            // with streaming the initial zero samples can be simply ignored.
            if (m_zero_samples_to_ignore > 0 &&
                actual_sample_l == 0 &&
                actual_sample_r == 0)
            {
                --m_zero_samples_to_ignore;
            }
            else
            {
                m_zero_samples_to_ignore = 0;
    
                uint16_t expected_sample_l = m_sample_value_expected - 1;
                uint16_t expected_sample_r = m_sample_value_expected + 1;
                ++m_sample_value_expected;
    
                if (actual_sample_l != expected_sample_l ||
                    actual_sample_r != expected_sample_r)
                {
                    LOG_INF("%3u: %04x/%04x, expected: %04x/%04x (i: %u)",
                        m_blocks_transferred, actual_sample_l, actual_sample_r,
                        expected_sample_l, expected_sample_r, i);
                    return false;
                }
            }
        }
        LOG_INF("%3u: OK", m_blocks_transferred);
        return true;
    }
    
    
    static void check_rx_data(uint32_t const * p_block)
    {
        ++m_blocks_transferred;
    
        if (!m_error_encountered)
        {
            m_error_encountered = !check_samples(p_block);
        }
    
        if (m_error_encountered)
        {
            //bsp_board_led_off(LED_OK);
            //bsp_board_led_invert(LED_ERROR);
        }
        else
        {
            //bsp_board_led_off(LED_ERROR);
            //bsp_board_led_invert(LED_OK);
        }
    }
    
    
    static void data_handler(nrfx_i2s_buffers_t const * p_released,
                             uint32_t                      status)
    {
        // 'nrf_drv_i2s_next_buffers_set' is called directly from the handler
        // each time next buffers are requested, so data corruption is not
        // expected.
        //TODO assert(p_released);
    
        // When the handler is called after the transfer has been stopped
        // (no next buffers are needed, only the used buffers are to be
        // released), there is nothing to do.
        if (!(status & NRFX_I2S_STATUS_NEXT_BUFFERS_NEEDED))
        {
            return;
        }
    
        // First call of this handler occurs right after the transfer is started.
        // No data has been transferred yet at this point, so there is nothing to
        // check. Only the buffers for the next part of the transfer should be
        // provided.
        if (!p_released->p_rx_buffer)
        {
            nrfx_i2s_buffers_t const next_buffers = {
                .p_rx_buffer = m_buffer_rx[1],
                .p_tx_buffer = m_buffer_tx[1],
            };
            nrfx_i2s_next_buffers_set(&next_buffers);
    
            mp_block_to_fill = m_buffer_tx[1];
        }
        else
        {
            mp_block_to_check = p_released->p_rx_buffer;
            // The driver has just finished accessing the buffers pointed by
            // 'p_released'. They can be used for the next part of the transfer
            // that will be scheduled now.
            nrfx_i2s_next_buffers_set(p_released);
    
            // The pointer needs to be typecasted here, so that it is possible to
            // modify the content it is pointing to (it is marked in the structure
            // as pointing to constant data because the driver is not supposed to
            // modify the provided data).
            mp_block_to_fill = (uint32_t *)p_released->p_tx_buffer;
        }
    }
    
    ISR_DIRECT_DECLARE(i2s_isr_handler) 
    {
      nrfx_i2s_irq_handler();
      ISR_DIRECT_PM();
    
      return 1; 
    }
    
    void loopback_init()
    {
        nrfx_i2s_config_t config = NRFX_I2S_DEFAULT_CONFIG(15, 12, 16, 13, 14);
        // In Master mode the MCK frequency and the MCK/LRCK ratio should be
        // set properly in order to achieve desired audio sample rate (which
        // is equivalent to the LRCK frequency).
        // For the following settings we'll get the LRCK frequency equal to
        // 15873 Hz (the closest one to 16 kHz that is possible to achieve).
        config.sdin_pin  = 14;
        config.sdout_pin = 13;
        config.mck_setup = 0x66666000;
        config.ratio     = NRF_I2S_RATIO_128X;
        config.channels  = NRF_I2S_CHANNELS_STEREO;
    
        IRQ_DIRECT_CONNECT(I2S0_IRQn, 0,i2s_isr_handler, 0);
    
        int err_code = nrfx_i2s_init(&config, data_handler);
    
        LOG_INF("loopback_int done...\n");
    }
    
    void loopback_transfer()
    {
        LOG_INF("staring loopback_transfer...\n");
    
        m_blocks_transferred = 0;
        mp_block_to_fill  = NULL;
        mp_block_to_check = NULL;
    
        prepare_tx_data(m_buffer_tx[0]);
        LOG_INF("after prepare_tx_data...\n");
    
        nrfx_i2s_buffers_t const initial_buffers = {
            .p_tx_buffer = m_buffer_tx[0],
            .p_rx_buffer = m_buffer_rx[0],
        };
        int err_code = nrfx_i2s_start(&initial_buffers, I2S_DATA_BLOCK_WORDS, 0);
        LOG_INF("err_code = %d...\n", err_code);
    
        //APP_ERROR_CHECK(err_code);
    
        LOG_INF("after nrfx_i2s_start...\n");
    
        do {
            // Wait for an event.
            //__WFE();
            // Clear the event register.
            //__SEV();
            //__WFE();
    
            if (mp_block_to_fill)
            {
                prepare_tx_data(mp_block_to_fill);
                mp_block_to_fill = NULL;
            }
            if (mp_block_to_check)
            {
                check_rx_data(mp_block_to_check);
                mp_block_to_check = NULL;
            }
        } while (m_blocks_transferred < BLOCKS_TO_TRANSFER);
    
        nrfx_i2s_stop();
    }
    
    static void config_es8388(){
    	es8388_dac_output_t output = DAC_OUTPUT_LOUT1 | DAC_OUTPUT_LOUT2 | DAC_OUTPUT_ROUT1 | DAC_OUTPUT_ROUT2;
    
    	es8388_adc_input_t input = ADC_INPUT_LINPUT1_RINPUT1;
    
    	es8388_init(output, input);
    
    	es_bits_length_t bits_length = BIT_LENGTH_16BITS;
    	es_module_t module = ES_MODULE_ADC_DAC;
    	es_format_t fmt = I2S_NORMAL;
    
    	es8388_config_i2s(bits_length, ES_MODULE_ADC_DAC, fmt);
    	es8388_set_voice_volume(70);
    	es8388_start(module);
    	//es8388_read_all();
    
    	printk("ES8388 CONFIG : \n");
    }
    
    
    void main()
    {
    	config_es8388();
    
    	loopback_init();
    	loopback_transfer();
    
    }

    The  terminal screenshot: 

      

    the board is continuously booting :: 

    ERROR: 

    ASSERTION FAIL [p_initial_buffers->p_rx_buffer != ((void *)0) || p_initial_buffers->p_tx_buffer != ((void *)0)] @ WEST_TOPDIR/modules/hal/nor1
    [00:00:00.260,253] <err> os: r0/a1: 0x00000004 r1/a2: 0x00000119 r2/a3: 0x00000020
    [00:00:00.260,284] <err> os: r3/a4: 0x20000218 r12/ip: 0x200036d0 r14/lr: 0x000074e7
    [00:00:00.260,284] <err> os: xpsr: 0x69000000
    [00:00:00.260,314] <err> os: Faulting instruction address (r15/pc): 0x0000ae58
    [00:00:00.260,345] <err> os: >>> ZEPHYR FATAL ERROR 4: Kernel panic on CPU 0
    [00:00:00.260,406] <err> os: Current thread: 0x20000788 (unknown)

    >> And if you have any code of working or test code or example ES8388 with nrf board, then share with me. 

    Regarding loopback, it seems you have something based on zephyr/samples/drivers/audio/dmic/? That should also work on the DK out of the box and does so on my desk. Have you tested that?

    yeah i will tested dimic its working but in case of codec its work on analog mic. and i am trying fully functional codec. DMIC  is not need in my case. 

    the line  out of the box and on your desk what does that mean ? 

  • Hi Einar,  i accidentally closed my ticket,  do you have any suggestion of my question or solution about it ?

  • Hi,

    Ajaysinh said:
    And if you have any code of working or test code or example ES8388 with nrf board, then share with me. 

    I am a bit confused about the applications. Is there a direct link between the ECHO sample and the loopback sample that I am overlooking? If not, it would be better to split this in separate cases to avoid confusion.

    Ajaysinh said:
    ERROR : Streams started , Failed to read data: -5

    This (-5 = -EIO) comes from i2s_nrfx_read() in zephyr/drivers/i2s/i2s_nrfx.c, due to -ENOMSG returned from k_msgq_get(). So this is either a timeout waiting for I2S data, or the internal state is I2S_STATE_ERROR. Can you check with a debugger which is the case?

    Ajaysinh said:
    the board is continuously booting :: 

    It is booting because this assert in the implementation of nrfx_i2s_start() in nrfx_i2s.c is failing:

        NRFX_ASSERT(p_initial_buffers->p_rx_buffer != NULL ||
                    p_initial_buffers->p_tx_buffer != NULL);

    So there is something wrong with the buffers you provide. Looking at your code I see this:

    		void *buffer;
    		uint32_t size;
    		int ret;
    
    		ret = dmic_read(dmic_dev, 0, &buffer, &size, READ_TIMEOUT);
    		if (ret < 0) {
    			LOG_ERR("%d - read failed: %d", i, ret);
    			return ret;
    		}

    You are declaring a pointer (buffer) to nowhere, and using it directly. So the assert is expected, and you need to fix your buffer handling.

    Ajaysinh said:
    And if you have any code of working or test code or example ES8388 with nrf board, then share with me. 

    Unfortunately no, I don't have any code for ES8388 (nor the device itself).

    Ajaysinh said:
    the line  out of the box and on your desk what does that mean ? 

    "out of the box" refers to using the example from the SDK without any modifications at all, and "on my desk" refers to my testing on my desk.

Related