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 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
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”?