nRF9161 DK and an I2S microphone

I'm trying to convert some code from ESP32 to nRF, and I'm having problems with the microphone input. Mind you, I'm a beginner with nRF, so I'm sure I'm missing something!

I have this in the prj.conf file:

CONFIG_I2S=y
CONFIG_I2S_NRFX=y


and this nrf9161dk_nrf9161_ns.overlay:
&pinctrl {
    i2s0_default: i2s0_default {
        group1 {
            psels = 
                <NRF_PSEL(I2S_SCK_M, 0, 21)>,  /* BCLK */
                <NRF_PSEL(I2S_SDIN, 0, 22)>,   /* DOUT*/
                <NRF_PSEL(I2S_LRCK_M, 0, 23)>; /* LRCL */
        };
    };
};

&i2s0 {
    compatible = "nordic,nrf-i2s";
    status = "okay";
    pinctrl-0 = <&i2s0_default>;
    pinctrl-names = "default";
};

I can see this in the GPIO overview in VS Code, and I made sure those pins weren't in use. My SPH0645s BCLK is connected to P0.21, DOUT is connected to P0.22, LRCL is connected to P0.23. 

Here's the code, mic_input.c:

#define SAMPLE_RATE 48000
#define BLOCK_TIME_MS 125
#define SAMPLES_PER_BLOCK (SAMPLE_RATE * BLOCK_TIME_MS / 1000)
#define BLOCK_SIZE (SAMPLES_PER_BLOCK * sizeof(int16_t))

K_MEM_SLAB_DEFINE(i2s_rx_slabs, BLOCK_SIZE, 2, 4);
__aligned(4) static int16_t raw_buf[SAMPLE_RATE / 8];

static void mic_thread(void *, void *, void *)
{
    printf("Mic thread started!\n");
    printf("Block size: %d\n", BLOCK_SIZE);
    printf("Buffer size: %d\n", sizeof(raw_buf));

    // Finn I2S-enhet
    i2s_dev = DEVICE_DT_GET(I2S_DEV_NODE);
    __ASSERT(device_is_ready(i2s_dev), "I2S device not ready");
    __ASSERT(i2s_dev, "I2S device not found");

    // Konfigurer I2S
    struct i2s_config cfg = {
        .word_size      = 16,
        .channels       = 1,
        .format         = I2S_FMT_DATA_FORMAT_I2S,
        .options        = I2S_OPT_BIT_CLK_MASTER | I2S_OPT_FRAME_CLK_MASTER,
        .frame_clk_freq = 48000,
        .block_size     = 12000, // matcher slab blokk
        .mem_slab       = &i2s_rx_slabs,
        .timeout        = 2000,
    };

    //Configure I2S RX
    int ret = i2s_configure(i2s_dev, I2S_DIR_RX, &cfg);
    if(ret < 0) {
        LOG_ERR("I2S configure failed: %d", ret);
        return;
    }
    printf("i2S configured!\n");

    // Reset og gjør klar buffere
    ret = i2s_trigger(i2s_dev, I2S_DIR_RX, I2S_TRIGGER_PREPARE);
    if (ret < 0) {
        LOG_ERR("I2S trigger prepare failed: %d", ret);
        return;
    }
    printf("i2S prepared!\n");
    
    //Trigger I2S RX
    ret = i2s_trigger(i2s_dev, I2S_DIR_RX, I2S_TRIGGER_START);
    if (ret < 0) {
        printk("I2S trigger start failed: %d\n", ret);
        return;
    }
    printf("i2S triggered!\n");

    k_sleep(K_MSEC(1));   // Vent litt så clocks starter

    printf("Staring I2S RX...\n");
    
    void *mem_block;
    size_t size;
    
    while (true) {
        ret = i2s_read(i2s_dev, &mem_block, &size);
    
        if (ret == 0) {
            // Vi fikk en gyldig buffer!
            memcpy(raw_buf, mem_block, size);
    
            // Nå kan du bruke raw_buf fritt
            printk("First sample: %d\n", ((int32_t *)raw_buf)[0]);
    
            // Release buffer etter bruk!
            //i2s_release(i2s_dev, I2S_DIR_RX);
        }
        else if (ret == -EAGAIN) {
            printk("No audio available, waiting...\n");
            k_sleep(K_MSEC(10));
        }
        else {
            printk("i2s_read() error: %d\n", ret);
            k_sleep(K_MSEC(100));
        }
    }

}

K_THREAD_DEFINE(mic_thread_instance, 2048, mic_thread, NULL, NULL, NULL, 7, 0, 0);

... but I only get:

Mic thread started!
Block size: 12000
Buffer size: 12000
i2S configured!
*** Booting nRF Connect SDK v3.0.0-3bfc46578e42 ***
*** Using Zephyr OS v4.0.99-3e0ce7636fa6 ***
[00:00:00.318,511] <inf> i2s_nrfx: I2S MCK frequency: 1523809, actual PCM rate: 47619
[00:00:00.319,976] <err> mic: I2S trigger prepare failed: -5

Where do I start?

Parents
  • Hi,

    I do not immediately see the problem, but you get -5 (-EIO) returned from i2s_trigger(). Can you debug and step down until you see more precisely where the error comes from?

  • Hei Einar, thanks for getting back to me.

    I get this error:
    Failed to read memory at 0x40028000

    ... but I'm not sure that's what I'm looking for. Stepping in gets me here in the end:

    Do I understand this correctly?
    When I run...

        ret = i2s_trigger(i2s_dev, I2S_DIR_RX, I2S_TRIGGER_PREPARE);

    ... cmd_allowed will be set to FALSE because the state error is not equal to I2S_STATE_ERROR...?
    Should it be?

  • Hi,

    Moskus said:
    A small update: Using an external clock (like the working ESP32) gives data as we expect!

    I see, do you mean just clocking the microphone with that, or did you test with the nRF in slave mode using this clock?

    Moskus said:
    It seems that the buffer is zeros because the microphone needs an oversampling of 64: WS must be BCLK/64. It seems like the I2S implementation on the nRF9161 isn't capable of that.

    Can you elaborate on that? From Master clock (MCK) and CONFIG.RATIO that should be OK? Is there any need to use the PWM?

    Regarding the PWM, the "Incompatible period" comes from the PWM frequency having to be the same on all channels on the same PWM instance.

    For the secure fault, I wonder if you are using overlapping peripheral instances, and it is allready used by secure mode firmware (TF-M). But there is no overlap with PWM instances and other peripherals (see Instatiation), so that may not be the case. I see the error happened at 0x2000ce80 (check again if you modifi the firmware). What do you see there with addr2line?

  • I see, do you mean just clocking the microphone with that, or did you test with the nRF in slave mode using this clock?

    This is setting the nRF to slave mode, and using the BCLK and LRCL from an ESP32.

    Can you elaborate on that? From Master clock (MCK) and CONFIG.RATIO that should be OK? Is there any need to use the PWM?

    I'd love it if it's possible to avoid PWM, but it seems that I can't get the ratio to anything else than 48 when I2S is master in zephyr. There's no support for 32 bit (if my googling is to be believed). The microphone requires 64 ratio. (My filters require 48 kHz sampling rate, but I'm sure I can redefine them if needed).

    Even if the first issue can be solved, I'm not sure if the 32 problem can be...? In slave mode 32 bit issue apparently isn't a problem.

    For the secure fault, I wonder if you are using overlapping peripheral instances, and it is allready used by secure mode firmware (TF-M). But there is no overlap with PWM instances and other peripherals (see Instatiation), so that may not be the case. I see the error happened at 0x2000ce80 (check again if you modifi the firmware). What do you see there with addr2line?

    I'm new to nordic, my project is pretty standard. I just tried to add two different pwm instances, nothing else fancy going on (to my somewhat limited knowledge Wink).

    addr2line outputs:

    C:/ncs/v3.0.0/zephyr/kernel/init.c:54


    ... which tells us:

    struct k_thread z_main_thread;

  • Yet another small update, for the pwm approach (even if I'd like to avoid it). As suspected, I did something stupid in the .overlay file. This works just fine:

    &pinctrl {
        /* PWM0.ch0 → BCLK on P0.24 */
        pwm0_ch0: pwm0_bclk {
            group1 { psels = <NRF_PSEL(PWM_OUT0, 0, 24)>; };
        };
    
        /* PWM1.ch0 → LRCLK on P0.24 */
        pwm1_lr: pwm1_lr {
                group1 { psels = <NRF_PSEL(PWM_OUT0, 0 , 25)>; };
        };
        /* I2S-slave: SDIN=22, BCLK=21, LRCLK=23 */
        i2s0_default: i2s0_default {
            group1 {
                psels = <NRF_PSEL(I2S_SDIN,   0, 22)>,
                        <NRF_PSEL(I2S_SCK_S,  0, 21)>,
                        <NRF_PSEL(I2S_LRCK_S, 0, 23)>;
            };
        };
    };
    
    &pwm0 {
            status = "okay";
            pinctrl-names = "default","sleep";
            pinctrl-0 = <&pwm0_ch0>;
            pinctrl-1 = <&pwm0_ch0>;
    };
    &pwm1 {
            status = "okay";
            pinctrl-names = "default","sleep";
            pinctrl-0 = <&pwm1_lr>;
            pinctrl-1 = <&pwm1_lr>;
    };
    
    &i2s0 {
        status = "okay";
        pinctrl-names = "default";
        pinctrl-0 = <&i2s0_default>;
    };
    


    The problem was here:

        pwm1_lr: pwm1_lr {
                group1 { psels = <NRF_PSEL(PWM_OUT0, 0 , 25)>; };
        };

    where I for some reason had "pwm_out0, 1, 25".

  • Smells, that nRF52 24-bit I2S Microphone Audio Recording Utility applies for the nRF91 as well.

    Thank you very much for this link! I will absolutely look into this! Blush
    It actually looks like what I'm trying to do. 

Reply Children
No Data
Related