I2S problems with Seeed Studio Xiao nRF52840

When I run my code on NCS 2.6.0-rc2 and upload it to my xiao ble board, the board disappears from the COM port. It can only be seen again if I replace my I2S code with something that does not have I2S. I can post my code here if needed. The I2S will be connected to SPH0645 MEMS microphone. I am aware of the swidth problem of nrf52840 which is why my code also consist of PWMs that will be my dummy clocks. Thank you!

This is my main.c code.

/*
 * Copyright (c) 2016 Intel Corporation
 * Copyright (c) 2020 Nordic Semiconductor ASA
 *
 * SPDX-License-Identifier: Apache-2.0
 */

/**
 * @file Sample app to demonstrate PWM.
 */

#include <zephyr/kernel.h>
#include <zephyr/sys/printk.h>
#include <zephyr/device.h>

#include <zephyr/drivers/pwm.h>
#include <zephyr/drivers/i2s.h>
#include <zephyr/drivers/gpio.h>

#include <string.h>
#include <zephyr/logging/log.h>
#include <zephyr/logging/log_ctrl.h>

#define MODULE main

// I2S DEFINES
LOG_MODULE_REGISTER(MODULE, 3);

#define MIC_CTRL  2
#define I2S_RX_NODE         DT_NODELABEL(i2s_rx)
#define SAMPLE_FREQUENCY 44100
#define SAMPLE_BIT_WIDTH 32
#define BYTES_PER_SAMPLE sizeof(int32_t)
#define NUMBER_OF_CHANNELS 2
#define SAMPLES_PER_BLOCK ((SAMPLE_FREQUENCY / 10) * NUMBER_OF_CHANNELS)
#define INITIAL_BLOCKS 2
#define TIMEOUT 1000

/* Size of a block for 100 ms of audio data. */
#define BLOCK_SIZE(_sample_rate, _number_of_channels) \
    (BYTES_PER_SAMPLE * (_sample_rate / 10) * _number_of_channels)

/* Driver will allocate blocks from this slab to receive audio data into them.
 * Application, after getting a given block from the driver and processing its
 * data, needs to free that block.
 */
#define MAX_BLOCK_SIZE   BLOCK_SIZE(SAMPLE_FREQUENCY, 2)
#define BLOCK_COUNT      5
K_MEM_SLAB_DEFINE_STATIC(mem_slab, MAX_BLOCK_SIZE, BLOCK_COUNT, 4);

// PWM DEFINES
static const struct pwm_dt_spec pwm_led0 = PWM_DT_SPEC_GET(DT_ALIAS(pwm_led0));
static const struct pwm_dt_spec pwm_led1 = PWM_DT_SPEC_GET(DT_ALIAS(pwm_led1));

#define BITCLK PWM_KHZ(4096U)
#define WORD_S PWM_KHZ(64U)
//#define WORD_S PWM_HZ(1U)

// LOG_MODULE_REGISTER(Recording, LOG_LEVEL_DBG);

K_THREAD_STACK_DEFINE(recording_thread_stack, 20000);

static const struct device *mic = DEVICE_DT_GET(I2S_RX_NODE);

static K_SEM_DEFINE(enable_recording, 1, 1);
static bool recording_active=false;

static struct k_thread recording_thread;
static k_tid_t worker_thread_id ;

void mic_worker_thread(void *p1, void *p2, void *p3) ;
int ret;

void recording_init() {
    if (!device_is_ready(mic)) {
        printk("Microphone device is not supported : %s", mic->name);
        return;
    }

    struct i2s_config config = {
        .word_size= SAMPLE_BIT_WIDTH,
        .channels = NUMBER_OF_CHANNELS,
        .format = I2S_FMT_DATA_FORMAT_I2S,
        .options = I2S_OPT_BIT_CLK_SLAVE | I2S_OPT_FRAME_CLK_SLAVE,
        .frame_clk_freq = SAMPLE_FREQUENCY, /* Sampling rate */
        .mem_slab = &mem_slab,/* Memory slab to store rx/tx data */
        .block_size = MAX_BLOCK_SIZE,/* size of memory buffer in bytes */
        .timeout = TIMEOUT, /* Number of milliseconds to wait in case Tx queue is full or RX queue is empty, or 0, or SYS_FOREVER_MS */
    };

	int err = i2s_configure(mic, I2S_DIR_RX, &config);
    i2s_trigger(mic, I2S_DIR_RX, I2S_TRIGGER_PREPARE);
    k_sleep(K_MSEC(100));
    if (err < 0) {
        LOG_ERR("Failed to initialize Microphone (%d)", err);
        return;
    }
    k_sem_take(&enable_recording, K_FOREVER);

    printk("Recording module initialized");
    worker_thread_id = k_thread_create(&recording_thread, recording_thread_stack,
                    K_THREAD_STACK_SIZEOF(recording_thread_stack),
                    mic_worker_thread,
                    NULL, NULL, NULL,
                    4, 0, K_NO_WAIT);
}

void start_recording() {
    recording_active = true;
    // Initialize file for recording.
    int ret = i2s_trigger(mic, I2S_DIR_RX, I2S_TRIGGER_START);
    if (ret) {
        LOG_ERR("Unable to configure trigger start for I2S bus (%d)", ret);
        return;
    }
    k_sem_give(&enable_recording);
}

void stop_recording() {
    recording_active = false;
    printk("Stopping recording");
    int ret = i2s_trigger(mic, I2S_DIR_RX, I2S_TRIGGER_STOP);
    if (ret) {
        LOG_ERR("Unable to stop trigger for I2S bus (%d)", ret);
    }
    k_sem_take(&enable_recording, K_FOREVER);
}

void mic_worker_thread(void *p1, void *p2, void *p3) {
    printk("Worker thread started");
    void* rx_buffer;
    size_t bytes_read;
    while (k_sem_take(&enable_recording, K_FOREVER) == 0) {
            bytes_read=0;
            int ret=0;
            if  (k_mem_slab_alloc(&mem_slab, &rx_buffer, K_MSEC(200)) == 0) {
                ret = i2s_read(mic, &rx_buffer, &bytes_read);
                if (ret < 0) {
                    if ( ret != -5) {
                        printk("Worker thread error (%d)\r\n", ret);
                    }
                } else {
                    printk(" raw rx: %d - Received %d bytes => %d samples ", ((uint32_t *)rx_buffer)[0], bytes_read, bytes_read / sizeof(uint32_t));
                }
                k_mem_slab_free(&mem_slab, &rx_buffer);
            }
        k_sem_give(&enable_recording);

    }
    printk("Worker thread ended");
}

int main(void)
{
	uint32_t bclk_period;
	uint32_t ws_period;
	int ret1;
	int ret2;

	printk("Starting main thread\n\r");
  	recording_init();
  	const struct device *mic_ctrl_dev = device_get_binding("GPIO_0");
    gpio_pin_configure(mic_ctrl_dev,MIC_CTRL,GPIO_OUTPUT);
   
   	int pin = gpio_pin_set(mic_ctrl_dev,MIC_CTRL,1);
   	printk("%d", pin);

	printk("PWM-based blinky\n");

	if (!pwm_is_ready_dt(&pwm_led0)) {
		printk("Error: PWM device %s is not ready\n",
		       pwm_led0.dev->name);
		return 0;
	}

	if (!pwm_is_ready_dt(&pwm_led1)) {
		printk("Error: PWM device %s is not ready\n",
		       pwm_led0.dev->name);
		return 0;
	}

	/*
	 * In case the default MAX_PERIOD value cannot be set for
	 * some PWM hardware, decrease its value until it can.
	 *
	 * Keep its value at least MIN_PERIOD * 4 to make sure
	 * the sample changes frequency at least once.
	 */

	bclk_period = BITCLK;
	while (pwm_set_dt(&pwm_led0, bclk_period, bclk_period / 2U)) {
		bclk_period /= 2U;
		printk("half bclk");
	}

	ws_period = WORD_S;
	while (pwm_set_dt(&pwm_led1, ws_period, ws_period / 2U)) {
		ws_period /= 2U;
		printk("half ws");
	}


	ws_period = WORD_S;
	bclk_period = BITCLK;
	while (1) {
		ret1 = pwm_set_dt(&pwm_led0, ws_period, ws_period / 2U);
		if (ret1) {
			printk("Error %d: failed to set pulse width\n", ret1);
			return 0;
		}

		ret2 = pwm_set_dt(&pwm_led1, bclk_period, bclk_period / 2U);
		if (ret2) {
			printk("Error %d: failed to set pulse width\n", ret2);
			return 0;
		}

		printk("Start recording again\r\n");
		start_recording();
		k_sleep(K_MSEC(2000));
		printk("Stop recording again\r\n");
		stop_recording();
		k_sleep(K_MSEC(2000));
	}
	return 0;
}

and my overlay code

&pwm0 {
    status = "okay";
    pinctrl-0 = <&pwm0_custom>;
    pinctrl-1 = <&pwm0_csleep>;
    pinctrl-names = "default", "sleep";
};

&pwm1 {
    status = "okay";
    pinctrl-0 = <&pwm1_custom>;
    pinctrl-1 = <&pwm1_csleep>;
    pinctrl-names = "default", "sleep";
};

/{
    pwmleds {
        compatible = "pwm-leds";
        pwm_led0: pwm_led_0 {
            pwms = <&pwm0 0 PWM_MSEC(20) PWM_POLARITY_NORMAL>;
        };
		pwm_led1: pwm_led_1 {
            pwms = <&pwm1 0 PWM_MSEC(20) PWM_POLARITY_NORMAL>;
        };
    };
	aliases {
		pwm-led0 = &pwm_led0;
		pwm-led1 = &pwm_led1;
	
	};
};


&pinctrl {
    pwm0_custom: pwm0_custom {
        group1 {
            psels = <NRF_PSEL(PWM_OUT0, 0, 3)>;
            nordic,invert;
        };
    };

    pwm0_csleep: pwm0_csleep {
        group1 {
            psels = <NRF_PSEL(PWM_OUT0, 0, 3)>;
            low-power-enable;
        };
    };
	pwm1_custom: pwm1_custom {
        group1 {
            psels = <NRF_PSEL(PWM_OUT0, 0, 28)>;
            nordic,invert;
        };
    };

    pwm1_csleep: pwm1_csleep {
        group1 {
            psels = <NRF_PSEL(PWM_OUT0, 0, 28)>;
            low-power-enable;
        };
    };
	i2s0_default_alt: i2s0_default_alt {
		group1 {
			psels = <NRF_PSEL(I2S_SDIN, 0, 29)>;
		};
	};
	i2s0_sleep: i2s0_sleep {
		group1 {
			psels = <NRF_PSEL(I2S_SDIN, 0, 29)>;             
			low-power-enable;
		};
	};
};

i2s_rx: &i2s0 {
	status = "okay";
	pinctrl-0 = <&i2s0_default_alt>;
	pinctrl-names = "default";
};

Hi there,
I don’t see any Comm port or Serial config? are You are using the Log output port with no setup?
HTH
GL :slight_smile: PJ

This is my prj.conf

CONFIG_STDOUT_CONSOLE=y
CONFIG_PRINTK=y
CONFIG_PWM=y
CONFIG_I2S=y
CONFIG_LOG=y
CONFIG_LOG_PRINTK=y
CONFIG_LOG_MODE_IMMEDIATE=y
CONFIG_PWM_LOG_LEVEL_DBG=y

the board disappears here when I upload the I2S code
image

Replying for follow up. Thank you!

Hi @Daniel_Vicho, I am trying to figure something similar out myself, but I am a couple steps behind.

I am quite new to working with microcontrollers and hardware in general. If possible, could you send a picture of how you connected the SPH0645LM4H MEMS mic and the XIAO nRF52840 microcontroller?

And what do you mean by the “swidth problem of nrf52840”?

Hi there,
Ah’ so that’s good info , How is it connected. The Comport disapearing sounds like it thinks its a straping pin or You are overwriting some bootloader area.
What is the tools configuration menu file system settings?
HTH
GL :wink: PJ
Your close so , try to attach a image also. :v:

maybe a short on ground or 5v could kill the com port on the computer side… fyi