Hi there,
I am trying to interface SPH0465 microphone with nRF Dev Kit 52832 with the following configurations of I2S:
config.word_size = 24; config.channels = 1; config.format = I2S_FMT_DATA_FORMAT_I2S; config.options = I2S_OPT_BIT_CLK_SLAVE | I2S_OPT_FRAME_CLK_SLAVE; config.frame_clk_freq = 44100; // from the datasheet of microphone config.mem_slab = &mem_slab; config.block_size = BLOCK_SIZE; config.timeout = TIMEOUT;
I have followed the code from echo example in Zephyr samples. Here is what it looks like now:
/*
* 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 <zephyr/sys/util.h>
#include <zephyr/sys/printk.h>
#include <inttypes.h>
#include <zephyr/logging/log.h>
#include <zephyr/logging/log_ctrl.h>
#include <string.h>
#include <zephyr/drivers/pwm.h>
#include <zephyr/drivers/uart.h>
LOG_MODULE_REGISTER(MODULE, 3);
#define MSG_SIZE 32
char tx_buf[MSG_SIZE];
static const struct gpio_dt_spec button = GPIO_DT_SPEC_GET_OR(DT_ALIAS(sw1), gpios, {0});
/*
* Print a null-terminated string character by character to the UART interface
*/
void print_uart(char *buf)
{
int msg_len = strlen(buf);
for (int i = 0; i < msg_len; i++) {
// uart_poll_out(uart_dev, buf[i]);
printk("%c", buf[i]);
}
}
///////////////////////////
#define I2S_RX_NODE DT_NODELABEL(i2s_rx)
#define SAMPLE_FREQUENCY 16000
#define SAMPLE_BIT_WIDTH 24
#define BYTES_PER_SAMPLE 4
#define NUMBER_OF_CHANNELS 1
// #define SAMPLES_PER_BLOCK ((SAMPLE_FREQUENCY / 200) * NUMBER_OF_CHANNELS)
#define INITIAL_BLOCKS 2
#define TIMEOUT 1000
// size of a block for .3 ms of audio data
// #define BLOCK_SIZE 1024 // 4 samples of 4 bytes each
#define BLOCK_SIZE(_sample_rate, _number_of_channels) (BYTES_PER_SAMPLE * (_sample_rate / 200) * _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, 1)
#define BLOCK_COUNT 10
#define BYTES_PER_MEM_SLAB BLOCK_SIZE*BLOCK_COUNT // 20 * 220
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));
// // frequency for pwm1 module
// // Frequency: 4 MHz -> Period: 250 ns (1 / 4 MHz)
// // Duty cycle: 50% -> Pulse width: 125 ns
#define PERIOD_PWM1_NSEC PWM_NSEC(375U) // 250 nanoseconds for 4 MHz frequency
#define PULSE_WIDTH_PWM1_NSEC PWM_NSEC(PERIOD_PWM1_NSEC / 2U) // 50% duty cycle, so pulse width is half the period
// // frequency for pwm0 module
// // Frequency: 62,500 Hz -> Period: 16 µs (1 / 62500)
// // Duty cycle: 50% -> Pulse width: 8 µs
#define PERIOD_PWM0_NSEC PWM_NSEC(PERIOD_PWM1_NSEC * 64U) // 16 µs period for pwm1
#define PULSE_WIDTH_PWM0_NSEC PWM_NSEC(PERIOD_PWM0_NSEC / 2U) // 50% duty cycle = 8 µs pulse width
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;
// configuration of the i2s module and trigger prepare of the receiving part
void recording_init() {
if (!device_is_ready(mic)) {
LOG_INF("Microphone device is not supported : %s", mic->name);
return;
}
struct i2s_config config;
config.word_size = 24; // it is okay if 24 bits are sampled. rest of the bits are high Z anyways.
config.channels = NUMBER_OF_CHANNELS;
config.format = I2S_FMT_DATA_FORMAT_LEFT_JUSTIFIED;//I2S_FMT_DATA_FORMAT_I2S;
config.options = I2S_OPT_BIT_CLK_SLAVE | I2S_OPT_FRAME_CLK_SLAVE;
config.frame_clk_freq = SAMPLE_FREQUENCY;
config.mem_slab = &mem_slab;
config.block_size = BLOCK_SIZE(SAMPLE_FREQUENCY, NUMBER_OF_CHANNELS);
config.timeout = TIMEOUT;
int err = i2s_configure(mic, I2S_DIR_RX, &config);
if (err < 0) {
LOG_ERR("Failed to configure Microphone (%d)", err);
return;
}
// void *rx_buffer;
// if (k_mem_slab_alloc(&mem_slab, &rx_buffer, K_NO_WAIT) == 0) {
// // Queue the first block for I2S to use
// err = i2s_read(mic, &rx_buffer, NULL);
// if (err < 0) {
// LOG_ERR("Failed to queue initial block (%d)", err);
// k_mem_slab_free(&mem_slab, &rx_buffer);
// }
// } else {
// LOG_ERR("Failed to allocate initial memory block");
// }
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);
LOG_INF("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);
}
// trigger for receiving
void start_recording() {
recording_active = true;
LOG_INF("Started recording");
// 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);
}
// triggering to stop recording
void stop_recording() {
recording_active = false;
LOG_INF("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);
}
LOG_INF("Completed the Stop Trigger\n");
k_sem_take(&enable_recording, K_FOREVER);
}
void print_rx_buffer(const void *buffer, size_t size) {
if (buffer == NULL) {
printf("Buffer is NULL\n");
return;
}
// Cast the buffer to a uint32_t pointer for easy access if you know it holds 32-bit data
const uint32_t *data = (const uint32_t *)buffer;
size_t num_elements = size / sizeof(uint32_t);
printf("Buffer contents (%zu bytes, %zu samples):\n", size, num_elements);
for (size_t i = 0; i < num_elements; i++) {
// printf("0x%08X ", data[i]);
snprintf(tx_buf, num_elements, "0x%08X ", data[i]);
print_uart(tx_buf);
// Print a new line every 4 elements for better readability
// if ((i + 1) % 4 == 0) {
// printf("\n");
// }
}
// Print a final newline if the last line did not end with one
// if (num_elements % 4 != 0) {
// printf("\n");
// printf("-----------------------------------------------");
// }
}
void* rx_buffer;
size_t bytes_read;
void mic_worker_thread(void *p1, void *p2, void *p3) {
LOG_INF("Worker thread started");
void* rx_buffer;
size_t bytes_read;
while (k_sem_take(&enable_recording, K_FOREVER) == 0) { // remains active as long as recording is ongoing
// LOG_INF("Arrived after starting recording \n");
bytes_read=0;
int ret=0;
if (k_mem_slab_alloc(&mem_slab, &rx_buffer, K_MSEC(500)) == 0) {
ret = i2s_read(mic, &rx_buffer, &bytes_read);
if (ret < 0) {
if ( ret != -5) {
LOG_INF("Worker thread error (%d)\r\n", ret);
}
} else {
LOG_INF(" 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);
}
LOG_INF("Worker thread ended");
}
int main(void)
{
int ret1;
int ret2;
LOG_INF("Starting main thread\n\r");
recording_init();
if (!gpio_is_ready_dt(&button)) {
printk("Error: button device %s is not ready\n",
button.port->name);
return 0;
}
ret = gpio_pin_configure_dt(&button, GPIO_INPUT);
if (ret != 0) {
printk("Error %d: failed to configure %s pin %d\n",
ret, button.port->name, button.pin);
return 0;
}
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;
}
ret1 = pwm_set_dt(&pwm_led0, PERIOD_PWM0_NSEC, PERIOD_PWM0_NSEC / 2U);
if (ret1) {
printk("Error %d: failed to set pulse width\n", ret1);
return 0;
}
ret2 = pwm_set_dt(&pwm_led1, PERIOD_PWM1_NSEC, PERIOD_PWM1_NSEC / 2U);
if (ret2) {
printk("Error %d: failed to set pulse width\n", ret2);
return 0;
}
while(1){
if(gpio_pin_get_dt(&button)>0)
{
start_recording();
k_sleep(K_MSEC(10000));
stop_recording();
LOG_INF("Done Stopping\r\n");
}
}
return 0;
}
And here is the overlay file I am using. This has definition for two PWM modules 0 and 1 with output channel 0 for each on pins 17 and 13, respectively. The I2S module is interfaced with SDIN - 0.26, SCK - 0.31, LRCK - 0.30:
/*
* Copyright (c) 2021 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
&pinctrl {
pwm0_custom: pwm0_custom {
group1 {
psels = <NRF_PSEL(PWM_OUT0, 0, 17)>;
nordic,invert;
};
};
pwm0_csleep: pwm0_csleep {
group1 {
psels = <NRF_PSEL(PWM_OUT0, 0, 17)>;
low-power-enable;
};
};
pwm1_custom: pwm1_custom {
group1 {
psels = <NRF_PSEL(PWM_OUT0, 0, 13)>;
nordic,invert;
};
};
pwm1_csleep: pwm1_csleep {
group1 {
psels = <NRF_PSEL(PWM_OUT0, 0, 13)>;
low-power-enable;
};
};
i2s0_default_alt: i2s0_default_alt {
group1 {
psels = <NRF_PSEL(I2S_SDIN, 0, 26)>,
<NRF_PSEL(I2S_SCK_S, 0, 31)>,
<NRF_PSEL(I2S_LRCK_S, 0, 30)>;
};
};
i2s0_sleep: i2s0_sleep {
group1 {
psels = <NRF_PSEL(I2S_SDIN, 0, 26)>,
<NRF_PSEL(I2S_SCK_S, 0, 31)>,
<NRF_PSEL(I2S_LRCK_S, 0, 30)>;
low-power-enable;
};
};
};
i2s_rx: &i2s0 {
status = "okay";
pinctrl-0 = <&i2s0_default_alt>;
pinctrl-names = "default";
};
&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>;
pwms = <&pwm0 0 PWM_NSEC(250) PWM_POLARITY_NORMAL>;
};
pwm_led1: pwm_led_1 {
// pwms = <&pwm1 0 PWM_MSEC(20) PWM_POLARITY_NORMAL>;
pwms = <&pwm1 0 PWM_NSEC(250) PWM_POLARITY_NORMAL>;
};
};
aliases {
pwm-led0 = &pwm_led0;
pwm-led1 = &pwm_led1;
};
};
There are no compilation errors but as I run the program, here is the output log:
[00:00:05.747,283] <27>[0m<inf> MODULE: Started recording<27>[0m<\r><\n> [00:00:05.752,777] <27>[0m<inf> MODULE: Worker thread started<27>[0m<\r><\n> [00:00:05.758,636] <27>[0m<inf> MODULE: raw rx: -3276800 - Rec[00:00:05.764,312] <27>[1;31m<err> i2s_nrfx: No room in RX queue<27>[0m<\r><\n> [00:00:05.770,416] <27>[1;31m<err> i2s_nrfx: No room in RX queue<27>[0m<\r><\n> eived 320 bytes => 80 samples <27>[0m<\r><\n> [00:00:05.779,663] <27>[0m<inf> MODULE: raw rx: 8134048 - Received 320 bytes => 80 samples <27>[0m<\r><\n> [00:00:05.788,330] <27>[0m<inf> MODULE: raw rx: 8135328 - Received 320 bytes => 80 samples <27>[0m<\r><\n> [00:00:05.796,997] <27>[0m<inf> MODULE: raw rx: 8136288 - Received 320 bytes => 80 samples <27>[0m<\r><\n> [00:00:05.820,739] <27>[0m<inf> MODULE: raw rx: 8137952 - Received 320 bytes => 80 samples <27>[0m<\r><\n> [00:00:15.752,807] <27>[0m<inf> MODULE: Stopping recording<27>[0m<\r><\n> [00:00:15.758,361] <27>[1;31m<err> MODULE: Unable to stop trigger for I2S bus (-5)<27>[0m<\r><\n> [00:00:15.766,052] <27>[0m<inf> MODULE: Completed the Stop Trigger<\n><27>[0m<\r><\n> [00:00:15.772,430] <27>[0m<inf> MODULE: Done Stopping<\r>
My questions are as follows:
- Why does i2s_read face "No Room in RX queue" right after the thread starts recording but no such error appears afterwards as there is plenty of space in memory slab?
- Why does i2s_trigger fail at stopping the recording?
Any advice on how to allocate the next RX buffer location will be appreciated.
Umer