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 <string.h> #include <zephyr/drivers/pwm.h> #define I2S_RX_NODE DT_NODELABEL(i2s_rx) #define SAMPLE_FREQUENCY 44100 #define SAMPLE_BIT_WIDTH 24 #define BYTES_PER_SAMPLE sizeof(int32_t) #define NUMBER_OF_CHANNELS 1 /* Such block length provides an echo with the delay of 100 ms. */ #define SAMPLES_PER_BLOCK 1//((SAMPLE_FREQUENCY / 30) * NUMBER_OF_CHANNELS) //((SAMPLE_FREQUENCY / 30) * NUMBER_OF_CHANNELS) #define INITIAL_BLOCKS 2 #define TIMEOUT 1000 // 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(4000U) #define WORD_S PWM_HZ(62500U) // // 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 #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 int32_t echo_block[SAMPLES_PER_BLOCK]; static volatile bool echo_enabled = false; static K_SEM_DEFINE(toggle_transfer, 1, 1); static void process_block_data(void *mem_block, uint32_t number_of_samples) { static bool clear_echo_block; if (echo_enabled) { for (int i = 0; i < number_of_samples; ++i) { int32_t *sample = &((int32_t *)mem_block)[i]; *sample += echo_block[i]; echo_block[i] = (*sample) / 2; } clear_echo_block = true; } else if (clear_echo_block) { clear_echo_block = false; memset(echo_block, 0, sizeof(echo_block)); } } 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; } static bool trigger_command(const struct device *i2s_dev_rx, enum i2s_trigger_cmd cmd) { int ret; ret = i2s_trigger(i2s_dev_rx, I2S_DIR_RX, cmd); if (ret < 0) { printk("Failed to trigger command %d on RX: %d\n", cmd, ret); return false; } return true; } int main(void) { uint32_t bclk_period; uint32_t ws_period; int ret1; int ret2; printk("Starting main thread\n\r"); 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; } bclk_period = BITCLK; while (pwm_set_dt(&pwm_led0, PERIOD_PWM0_NSEC, PULSE_WIDTH_PWM0_NSEC )) { bclk_period /= 2U; printk("half bclk"); } ws_period = WORD_S; while (pwm_set_dt(&pwm_led1, PERIOD_PWM1_NSEC, PULSE_WIDTH_PWM1_NSEC )) { ws_period /= 2U; printk("half ws"); } ws_period = WORD_S; bclk_period = BITCLK; const struct device *const i2s_dev_rx = DEVICE_DT_GET(I2S_RX_NODE); // const struct device *const i2s_dev_tx = DEVICE_DT_GET(I2S_TX_NODE); struct i2s_config config; if (!device_is_ready(i2s_dev_rx)) { printk("%s is not ready\n", i2s_dev_rx->name); return 0; } // if (i2s_dev_rx != i2s_dev_tx && !device_is_ready(i2s_dev_tx)) { // printk("%s is not ready\n", i2s_dev_tx->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_SLAVE | I2S_OPT_FRAME_CLK_SLAVE; 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, i2s_dev_tx, &config)) { if (!configure_streams(i2s_dev_rx, &config)) { return 0; } printk("Streams and I2S configured\n"); for (;;) { k_sem_take(&toggle_transfer, K_FOREVER); if (!trigger_command(i2s_dev_rx, I2S_TRIGGER_START)) { return 0; } printk("Streams started\n"); while (k_sem_take(&toggle_transfer, K_NO_WAIT) != 0) { void *mem_block; uint32_t block_size; int ret; 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: %d\n", value); process_block_data(mem_block, SAMPLES_PER_BLOCK); } if (!trigger_command(i2s_dev_rx, I2S_TRIGGER_DROP)) { return 0; } printk("Streams stopped\n"); } while (1) { ret1 = pwm_set_dt(&pwm_led0, PERIOD_PWM0_NSEC, PULSE_WIDTH_PWM0_NSEC ); if (ret1) { printk("Error %d: failed to set pulse width\n", ret1); return 0; } ret2 =pwm_set_dt(&pwm_led1, PERIOD_PWM1_NSEC, PULSE_WIDTH_PWM1_NSEC ); if (ret2) { printk("Error %d: failed to set pulse width\n", ret2); 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:
*** Booting nRF Connect SDK v2.7.99-cs1-23a9baf2aed6 *** *** Using Zephyr OS v3.6.99-28b6861211d2 *** Starting main thread [00:00:00.492,156] <dbg> pwm_nrfx: pwm_nrfx_set_cycles: channel 0, pulse 192, period 384, prescaler: 0. [00:00:00.502,441] <dbg> pwm_nrfx: pwm_nrfx_set_cycles: channel 0, pulse 2, period 6, prescaler: 0. Streams and I2S configured Streams started Received audio data: 2621440 Received audio data: 825280 [00:00:00.620,788] <err> i2s_nrfx: Failed to allocate next RX buffer: -12 Received audio data: 890048 Received audio data: 688832 Failed to read data: -5 Streams stopped
Any advice on how to allocate the next RX buffer location will be appreciated.
Umer
Update: I was able to resolve the re-allocation error by rewriting the software main.c in the following way. The worker thread now keeps freeing the space after reading the data from I2S microphone.
/* * 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/logging/log.h> #include <zephyr/logging/log_ctrl.h> #include <string.h> #include <zephyr/drivers/pwm.h> LOG_MODULE_REGISTER(MODULE, 3); #define I2S_RX_NODE DT_NODELABEL(i2s_rx) #define SAMPLE_FREQUENCY 44100 #define SAMPLE_BIT_WIDTH 24 #define BYTES_PER_SAMPLE sizeof(int32_t) #define NUMBER_OF_CHANNELS 1 // #define SAMPLES_PER_BLOCK ((SAMPLE_FREQUENCY / 200) * NUMBER_OF_CHANNELS) #define INITIAL_BLOCKS 2 #define TIMEOUT 2000 // size of a block for .3 ms of audio data #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 20 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(4000U) // #define WORD_S PWM_HZ(62500U) // // 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 = SAMPLE_BIT_WIDTH; config.channels = NUMBER_OF_CHANNELS; config.format = 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); 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; // 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); } k_sem_take(&enable_recording, K_FOREVER); } 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) { 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 (!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; } // enabling PWM signals for both modules 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){ LOG_INF("Start recording again\r\n"); start_recording(); k_sleep(K_MSEC(5000)); LOG_INF("Stop recording again\r\n"); stop_recording(); k_sleep(K_MSEC(5000)); } }
The issue I face now is still an err=-5 but it states Unable to Stop Trigger for I2S bus and Unable to configure trigger start for I2S bus, after receiving one batch of data.
*** Booting nRF Connect SDK v2.7.99-cs1-23a9baf2aed6 *** *** Using Zephyr OS v3.6.99-28b6861211d2 *** [00:00:00.270,050] <inf> MODULE: Starting main thread [00:00:00.376,037] <inf> MODULE: Recording module initialized [00:00:00.382,507] <dbg> pwm_nrfx: pwm_nrfx_set_cycles: channel 0, pulse 192, period 384, prescaler: 0. [00:00:00.392,791] <dbg> pwm_nrfx: pwm_nrfx_set_cycles: channel 0, pulse 2, period 6, prescaler: 0. [00:00:00.402,679] <inf> MODULE: Start recording again [00:00:00.408,721] <inf> MODULE: Worker thread started [00:00:00.414,550] <inf> MODULE: raw rx: 0 - Received 880 bytes => 220 samples [00:00:00.422,760] <inf> MODULE: raw rx: -391232 - Received 880 bytes => 220 samples [00:00:00.431,488] <inf> MODULE: raw rx: -65024 - Received 880 bytes => 220 samples [00:00:00.440,155] <inf> MODULE: raw rx: 272768 - Received 880 bytes => 220 samples [00:00:00.461,608] <err> i2s_nrfx: No room in RX queue [00:00:00.482,696] <err> i2s_nrfx: No room in RX queue [00:00:00.448,822] <inf> MODULE: raw rx: 536893704 - Received 880 bytes => 220 samples [00:00:00.499,938] <inf> MODULE: raw rx: 610176 - Received 880 bytes => 220 samples [00:00:00.508,575] <inf> MODULE: raw rx: 536893704 - Received 880 bytes => 220 samples [00:00:00.517,456] <inf> MODULE: raw rx: 536893704 - Received 880 bytes => 220 samples [00:00:00.526,336] <inf> MODULE: raw rx: 793344 - Received 880 bytes => 220 samples [00:00:05.408,752] <inf> MODULE: Stop recording again [00:00:05.414,642] <inf> MODULE: Stopping recording [00:00:05.420,166] <err> MODULE: Unable to stop trigger for I2S bus (-5) [00:00:10.427,947] <inf> MODULE: Start recording again [00:00:10.433,929] <err> MODULE: Unable to configure trigger start for I2S bus (-5) [00:00:15.442,657] <inf> MODULE: Stop recording again [00:00:15.448,547] <inf> MODULE: Stopping recording [00:00:15.454,071] <err> MODULE: Unable to stop trigger for I2S bus (-5)