Adafruit I2S MEMS Microphone interface with nRF5340 DK

Hi,

I am trying to connect and acquire the signal from the Adafruit I2S MEMS Microphone using I2s communication with my board (nRF5340 DK). Because I am new in this controller, I was looking to use a sample program as a starting point, specifically the I2S ECHO SAMPLE (https://docs.zephyrproject.org/latest/samples/drivers/i2s/echo/README.html).

What should I change in this code for making it work? I am aware my board doesn't have the audio CODEC (WM8731), therefore when running it fails. Is there maybe another sample more adjusted for my needs?

Thanks.

Parents
  • Hi

    The I2S Echo sample is probably the best starting point, but I would recommend having a look at the nRF5340 Audio application, specifically how it sets up an audio stream with I2S and how it deals with the audio codec:

    https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/nrf/applications/nrf5340_audio/README.html

    Let me know if you get stuck on anything specific.

    Best regards,

    Einar 

  • Hello,

    Sorry for the late response, I tried many different ways but still I can't make it work.

    I checked the nRF5340 Audio example, and came to the conclusion there is no need for the audio codec in my case, since I don't have to process any data, I just have to collect it from the I2S interface.

    I modified the I2S echo sample, my goal is just to print the audio data from the I2S MEMS microphone (SPH0645):

    /*
     * Copyright (c) 2021 Nordic Semiconductor ASA
     *
     * SPDX-License-Identifier: Apache-2.0
     */
    
    #include <zephyr/kernel.h>
    #include <zephyr/sys/printk.h>
    #include <zephyr/drivers/i2s.h>
    #include <zephyr/drivers/gpio.h>
    #include <string.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 SAMPLE_FREQUENCY    44100 //-> 44100kHz
    #define SAMPLE_BIT_WIDTH    24 
    #define BYTES_PER_SAMPLE    sizeof(int32_t)//32 bits
    #define NUMBER_OF_CHANNELS  1 //1
    /* 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 K_SEM_DEFINE(toggle_transfer, 1, 1);
    
    static bool configure_streams(const struct device *i2s_dev_rx,
    			      const struct i2s_config *config)
    {
    	int ret;
    	ret = i2s_configure(i2s_dev_rx, I2S_DIR_RX, config);
    	if (ret < 0) {
    		printk("Failed to configure RX stream: %d\n", ret);
    		return false;
    	}
    	return true;
    }
    
    int main(void)
    {
    	int ret;
    	const struct device *const i2s_dev_rx = DEVICE_DT_GET(I2S_RX_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;
    	}
    	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, &config)) {
    		return 0;
    	}
    
    	printk("Streams started\n");
    	for (;;) {
    		k_sem_take(&toggle_transfer, K_FOREVER); //Bloquea hasta que esta disponible
    		ret = i2s_trigger(i2s_dev_rx, I2S_DIR_RX, I2S_TRIGGER_START);
    		if (ret != 0) {
    			printk("i2s_trigger failed with %d error\n", ret);
    			return 0;
    		}
    		while (k_sem_take(&toggle_transfer, K_NO_WAIT) != 0) {
    			void *mem_block;
    			uint32_t block_size;
    			ret = i2s_read(i2s_dev_rx, &mem_block, &block_size);
    			if (ret < 0) {
    				printk("Failed to read data: %d\n", ret);
    				break;
    			}
    			unsigned int* value_ptr = (unsigned int*)mem_block;
        		unsigned int value = *value_ptr;
       			printk("Received audio data: %u\n", value);
    		}
    	}
    	return 0;
    }

    My overlay file is the following one: 

    /*
     * Copyright (c) 2021 Nordic Semiconductor ASA
     *
     * SPDX-License-Identifier: Apache-2.0
     */
    
     &pinctrl {
    	i2s0_default_alt: i2s0_default_alt {
    		group1 {
    			psels = <NRF_PSEL(I2S_SCK_M, 0, 7)>,
    				<NRF_PSEL(I2S_LRCK_M, 0, 12)>,
    				//<NRF_PSEL(I2S_SDOUT, 1, 13)>,
    				<NRF_PSEL(I2S_SDIN, 1, 6)>;
    		};
    	};
    };
    
    &clock {
    	hfclkaudio-frequency = <11289600>;
    };
    
    i2s_rxtx: &i2s0 {
    	status = "okay";
    	pinctrl-0 = <&i2s0_default_alt>;
    	pinctrl-names = "default";
    	clock-source = "ACLK";
    };
    &arduino_adc {
    	io-channel-map = <0 &adc 0>, <1 &adc 1>, <2 &adc 2>, <3 &adc 3>, <4 &adc 4>, <5 &adc 5>;
    };
    
    &arduino_header {
    	gpio-map = <0 0 &gpio0 4 0>,
    			   <1 0 &gpio0 5 0>,
    			   <2 0 &gpio0 6 0>,
    			   <3 0 &gpio0 7 0>,
    			   <4 0 &gpio0 25 0>,
    			   <5 0 &gpio0 26 0>,
    			   <6 0 &gpio1 0 0>,
    			   <7 0 &gpio1 1 0>,
    			   <8 0 &gpio1 4 0>,
    			   <9 0 &gpio1 5 0>,
    			   <10 0 &gpio1 6 0>,
    			   <11 0 &gpio1 7 0>,
    			   <12 0 &gpio1 8 0>,
    			   <13 0 &gpio1 9 0>,
    			   <14 0 &gpio1 10 0>,
    			   <15 0 &gpio1 11 0>,
    			   <16 0 &gpio1 12 0>,
    			   <17 0 &gpio1 13 0>,
    			   <18 0 &gpio1 14 0>,
    			   <19 0 &gpio1 15 0>,
    			   <20 0 &gpio1 2 0>,
    			   <21 0 &gpio1 3 0>;
    };
    

    And in my prj.conf I only have this line: 

    CONFIG_I2S=y
    I expected a continous output with the values of what the MEMS microphone detects, but all I get is this:
    *** Booting Zephyr OS build v3.3.99-ncs1 ***
    I2S echo sample
    Streams started
    Received audio data: 0
    Received audio data: 0
    Received audio data: 0
    Received audio data: 0
    Failed to read data: -5


    The microphone works well, it worked in another controller. I don't know what I am doing wrong, it looks like it doesn't detect the microphone...
    Thank you in advance. 
     
Reply
  • Hello,

    Sorry for the late response, I tried many different ways but still I can't make it work.

    I checked the nRF5340 Audio example, and came to the conclusion there is no need for the audio codec in my case, since I don't have to process any data, I just have to collect it from the I2S interface.

    I modified the I2S echo sample, my goal is just to print the audio data from the I2S MEMS microphone (SPH0645):

    /*
     * Copyright (c) 2021 Nordic Semiconductor ASA
     *
     * SPDX-License-Identifier: Apache-2.0
     */
    
    #include <zephyr/kernel.h>
    #include <zephyr/sys/printk.h>
    #include <zephyr/drivers/i2s.h>
    #include <zephyr/drivers/gpio.h>
    #include <string.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 SAMPLE_FREQUENCY    44100 //-> 44100kHz
    #define SAMPLE_BIT_WIDTH    24 
    #define BYTES_PER_SAMPLE    sizeof(int32_t)//32 bits
    #define NUMBER_OF_CHANNELS  1 //1
    /* 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 K_SEM_DEFINE(toggle_transfer, 1, 1);
    
    static bool configure_streams(const struct device *i2s_dev_rx,
    			      const struct i2s_config *config)
    {
    	int ret;
    	ret = i2s_configure(i2s_dev_rx, I2S_DIR_RX, config);
    	if (ret < 0) {
    		printk("Failed to configure RX stream: %d\n", ret);
    		return false;
    	}
    	return true;
    }
    
    int main(void)
    {
    	int ret;
    	const struct device *const i2s_dev_rx = DEVICE_DT_GET(I2S_RX_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;
    	}
    	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, &config)) {
    		return 0;
    	}
    
    	printk("Streams started\n");
    	for (;;) {
    		k_sem_take(&toggle_transfer, K_FOREVER); //Bloquea hasta que esta disponible
    		ret = i2s_trigger(i2s_dev_rx, I2S_DIR_RX, I2S_TRIGGER_START);
    		if (ret != 0) {
    			printk("i2s_trigger failed with %d error\n", ret);
    			return 0;
    		}
    		while (k_sem_take(&toggle_transfer, K_NO_WAIT) != 0) {
    			void *mem_block;
    			uint32_t block_size;
    			ret = i2s_read(i2s_dev_rx, &mem_block, &block_size);
    			if (ret < 0) {
    				printk("Failed to read data: %d\n", ret);
    				break;
    			}
    			unsigned int* value_ptr = (unsigned int*)mem_block;
        		unsigned int value = *value_ptr;
       			printk("Received audio data: %u\n", value);
    		}
    	}
    	return 0;
    }

    My overlay file is the following one: 

    /*
     * Copyright (c) 2021 Nordic Semiconductor ASA
     *
     * SPDX-License-Identifier: Apache-2.0
     */
    
     &pinctrl {
    	i2s0_default_alt: i2s0_default_alt {
    		group1 {
    			psels = <NRF_PSEL(I2S_SCK_M, 0, 7)>,
    				<NRF_PSEL(I2S_LRCK_M, 0, 12)>,
    				//<NRF_PSEL(I2S_SDOUT, 1, 13)>,
    				<NRF_PSEL(I2S_SDIN, 1, 6)>;
    		};
    	};
    };
    
    &clock {
    	hfclkaudio-frequency = <11289600>;
    };
    
    i2s_rxtx: &i2s0 {
    	status = "okay";
    	pinctrl-0 = <&i2s0_default_alt>;
    	pinctrl-names = "default";
    	clock-source = "ACLK";
    };
    &arduino_adc {
    	io-channel-map = <0 &adc 0>, <1 &adc 1>, <2 &adc 2>, <3 &adc 3>, <4 &adc 4>, <5 &adc 5>;
    };
    
    &arduino_header {
    	gpio-map = <0 0 &gpio0 4 0>,
    			   <1 0 &gpio0 5 0>,
    			   <2 0 &gpio0 6 0>,
    			   <3 0 &gpio0 7 0>,
    			   <4 0 &gpio0 25 0>,
    			   <5 0 &gpio0 26 0>,
    			   <6 0 &gpio1 0 0>,
    			   <7 0 &gpio1 1 0>,
    			   <8 0 &gpio1 4 0>,
    			   <9 0 &gpio1 5 0>,
    			   <10 0 &gpio1 6 0>,
    			   <11 0 &gpio1 7 0>,
    			   <12 0 &gpio1 8 0>,
    			   <13 0 &gpio1 9 0>,
    			   <14 0 &gpio1 10 0>,
    			   <15 0 &gpio1 11 0>,
    			   <16 0 &gpio1 12 0>,
    			   <17 0 &gpio1 13 0>,
    			   <18 0 &gpio1 14 0>,
    			   <19 0 &gpio1 15 0>,
    			   <20 0 &gpio1 2 0>,
    			   <21 0 &gpio1 3 0>;
    };
    

    And in my prj.conf I only have this line: 

    CONFIG_I2S=y
    I expected a continous output with the values of what the MEMS microphone detects, but all I get is this:
    *** Booting Zephyr OS build v3.3.99-ncs1 ***
    I2S echo sample
    Streams started
    Received audio data: 0
    Received audio data: 0
    Received audio data: 0
    Received audio data: 0
    Failed to read data: -5


    The microphone works well, it worked in another controller. I don't know what I am doing wrong, it looks like it doesn't detect the microphone...
    Thank you in advance. 
     
Children
Related