-
Notifications
You must be signed in to change notification settings - Fork 7.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
XIAO ESP32-C3: I2S takes 250ms before a INMP441 microphone can give accurate readings with I2S. #8207
Comments
Since you are using ESP-IDF's API, I suggest you file an issue in their repo. You can say that you are running on ESP-IDF v4.4.4 |
My wild guess about it is that entering/returning from light sleep changes the source frequency of the I2S peripheral. Maybe (just a guess) if the I2S is set to use a Clock Source that is not APB dependent, it may not be affected by this change.
The new IDF 5.1 I2S driver documentation talk about using APLL... (but remember that Arduino 2.0.9 uses IDF 4.4.4). |
Anyway.... this exactly same issue also happens with UART when returning from Light Sleep. The first bytes read are bad... |
I used It's a shame because the MEMS mic performs so well otherwise. I tried putting a medium-sized ceramic decoupling capacitor at the power pins of the MEMS, but this didn't do anything. If I want to get proper light sleep I'll have to use an analog microphone and sample it with an ISR. |
I guess that those 300ms after returning from light sleep are very important for the project. Can you imagine a process/work around that fits better the needs of the project? |
It works because I can just keep the analog microphone on in a powered state while the CPU sleeps and then immediately start reading again after the processor wakes. This will allow me to microsleep the CPU in the main loop and immediately execute the next loop iteration without a delay. This should massively reduce power consumption. |
I'm not sure that ESP32 ADC will not also take some time to read accurate data... A potential reference: espressif/esp-idf#8287 |
A possible alternative strategy to go low power and get MIC data could be to use the ULP to read the data... but the C3 has no ULP. |
Another way to think about low power, could be run the C3 at 10MHz, no sleep needed. All of it will need experimentation and measures. |
It's not possible to read the I2S under 80 mhz. |
With some work to code it... RMT may do it. Not easy... but it may be possible to decode an I2S MIC signal... |
Sounds complicated. Reading from an analog condenser mic is well known and the components are cheap. I only need it for loudness and some fft. While the mems produced excellent audio, the condenser mic will work well enough. |
Do you know of any repositories that have an I2S implementation in software? |
No. I think that there may be some MCU emulator software that does it, but it may not be applicable within an MCU. |
Can you confirm you are using a Seeed Studio ESP32-C3, I have been told that I2S is not supported as the pins do not exist, but it seems you are mapping them onto pins 2,3,4,5 for i2s |
Confirmed, I am using an ESP32-C3 Xiao. The ESP32 allows you to use many pins for the protocol. |
same here, can you please help with the triage @me-no-dev? Thanks |
Here is a public code repo + pinout of a simple device using IS2 in a test setup displaying the bug. https://github.com/zackees/xiao-inmp441-test This repo is designed to showcase the IS2 bug above. If changes to this code base are required then please fork and issue a pull request. The code that will likely need to be adjusted will be The pinouts, included in the repo, are as follows: #define PIN_I2S_WS GPIO_NUM_7
#define PIN_IS2_SD GPIO_NUM_8
#define PIN_I2S_SCK GPIO_NUM_4
#define PIN_AUDIO_PWR GPIO_NUM_10 With microphone Entering the low power testRun the test software. To enter the low power IS2 test hold the button down for a number of seconds. When the
Notice that the dB readings are high and then settle down. I've thoroughly investigated this for my software error but consistently found that I could alleviate the error by adding a delay(300) after the Building a test deviceThe device can be built with just the microphone and all the other components in the diagram can be ignored. |
Sorry for the wall of text. Bug reproduction can be hard and I wanted to make sure it was as easy as possible with a working codebase that reproduces the issue clearly. |
@zackees @VojtechBartoska given that all code is actually the ESP-IDF API, I will suggest that you contact the IDF team in their repository for help. Not sure which version ESP-IDF you are using, but 2.0.9/2.0.10 use IDF 4.4.4 and 2.0.11 use IDF 4.4.5 |
This is my platform io .ini file:
What is the proper place to file this bug? |
Hi, you can try 3.0.0 alpha2 development release as well, there is new implementation of I2S library. |
Do I read this correctly that this issue will be fixed in the next release? |
Yes, there is new I2S library so you can try it out. Please let us know if it fixes your issue. Thanks |
How do I get this library? link? |
It is the part of https://github.com/espressif/arduino-esp32/releases/tag/3.0.0-alpha3 release. Related Pull Request is here: #8714 |
Thank you. Is this available on platform io? The following platformio.ini file fails to build:
Do you know what the solution is? I'll test this out as soon as I have access. |
I'm trying to test this fix but I have at this point re-write my app into the format used by the idf.py tool first. If anyone else is having trouble installing https://github.com/zackees/idf-install However, it looks like the expressif arduino package is about to land with support for 5.1, which from the changelist includes this fix above. However currently I can't test it out because of a missing Here's the bug report: If this turns out to be an easy fix then I'd love to test out the I2S fix before it goes live. |
Good news, there is an installable platformio version of IDF v5.1 with the I2S fix. get it with this pull request. I verified it my self and now I have to port ledc, I2C etc |
IDF toolchain for platformio just became available thanks to a pull request. Add these to your ini file:
|
Okay so follow up. I installed the unofficial IDF 5.1 libs for platform IO and then went through each device driver and ported it to the the new v5.1 API. LEDC ported fine. But for the life of me, I cannot get the I2S to work with the INMP441 with the new API. It works FINE with the 4.x legacy I2S API. At first I did a straight port. The data signal is delayed by one frame as defined by the Philips standard, which apparently was the default for the legacy API. The straight port did not work. So I spent about another 4-6 hours diving deep, look at the source code and trying every combination I could think of of clock, pin modes, etc. Now that I've gone deep, I have absolutely no idea how the legacy drivers were even working. It appears that every copy and paste of the 4.x I2S api appears wrong. INMP441 works with 16 bit audio in the legacy driver, but it's clear it using 24 bit audio in a 32 bit WS frame, delayed by one clock cycle (Philips mode). I've tried putting the 5.1 api into I2S master mode, slave mode. My INMP441 is set to be right channel only. I saw that in the 4.x lib there was a time where the L/R channels were swapped. So I've tried both but neither work. Bitshift for Philips mode. Left align. Shifting clock values for the MCU. I've exhausted every single thing I could think of and nothing works. BUG?What's really strange is that when i query the info state of the channel like this: err = i2s_channel_init_std_mode(rx_chan, &i2s_std_cfg_rx);
ESP_ERROR_CHECK(err);
i2s_chan_info_t info;
err = i2s_channel_get_info(rx_chan, &info);
ESP_ERROR_CHECK(err);
delay(1000);
Serial.printf("I2S channel info: %d, %d, %d, %d, %d\n", info.id, info.role, info.dir, info.mode, info.pair_chan);
delay(500); I see that So the direction is in TX mode instead of RX mode. Not sure if this is significant. I'm going to try and get a repro case in a public github. For now, I just have this brain dump of the halfway ported I2S Notes: Yes, legacy used role master and i'm using role slave. But I've tried both and neither works. Yes, I've also tried 48kh, 44.1khz, 16khz for audio. Bit depth 16/24/32. Bus bit depth 16/32. I just can't get a signal out of the microphone. However after a while I will start getting garbage readings but they are very very slow. I've tried SLOT stereo/mono, I've tried left and right channels (my mic is configured for #include <iostream>
#include "audio.h"
#include "defs.h"
// #include <driver/i2s.h>
#include <driver/i2s_std.h>
#include "i2s_device.h"
#include "driver/gpio.h"
namespace
{
bool g_is_initialized = false;
static_assert(AUDIO_BIT_RESOLUTION == 16, "Only 16 bit resolution is supported");
static_assert(AUDIO_CHANNELS == 1, "Only 1 channel is supported");
static_assert(sizeof(audio_sample_t) == 2, "audio_sample_t must be 16 bit");
i2s_chan_handle_t rx_chan = NULL;
i2s_chan_handle_t tx_chan = NULL;
//i2s_chan_config_t i2s_chan_cfg_rx = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER); // stores I2S channel values
i2s_chan_config_t i2s_chan_cfg_rx = {
.id = I2S_NUM_0,
.role = I2S_ROLE_SLAVE,
.dma_desc_num = 6,
.dma_frame_num = 240,
.auto_clear = false,
};
i2s_std_config_t i2s_std_cfg_rx = {
//.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(AUDIO_SAMPLE_RATE),
.clk_cfg = {
.sample_rate_hz = 44100ul,
.clk_src = i2s_clock_src_t(I2S_CLK_SRC_PLL_160M),
.mclk_multiple = I2S_MCLK_MULTIPLE_384,
},
//.slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_24BIT, I2S_SLOT_MODE_MONO),
/*
#define I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(bits_per_sample, mono_or_stereo) { \
.data_bit_width = bits_per_sample, \
.slot_bit_width = I2S_SLOT_BIT_WIDTH_AUTO, \
.slot_mode = mono_or_stereo, \
.slot_mask = I2S_STD_SLOT_BOTH, \
.ws_width = bits_per_sample, \
.ws_pol = false, \
.bit_shift = true, \
.left_align = true, \
.big_endian = false, \
.bit_order_lsb = false \
}
*/
.slot_cfg = {
.data_bit_width = I2S_DATA_BIT_WIDTH_24BIT,
.slot_bit_width = I2S_SLOT_BIT_WIDTH_32BIT,
.slot_mode = I2S_SLOT_MODE_MONO,
.slot_mask = I2S_STD_SLOT_RIGHT,
.ws_width = I2S_SLOT_BIT_WIDTH_32BIT,
.ws_pol = false,
.bit_shift = true,
.left_align = true,
.big_endian = false,
.bit_order_lsb = false
},
.gpio_cfg = {
.mclk = GPIO_NUM_1,
.bclk = PIN_I2S_SCK,
.ws = PIN_I2S_WS,
.dout = I2S_GPIO_UNUSED,
.din = PIN_IS2_SD,
.invert_flags = {
.mclk_inv = false,
.bclk_inv = false,
.ws_inv = false,
},
},
};
void init_i2s_pins() {
//i2s_chan_cfg_rx.id = I2S_NUM_AUTO; // I2S_NUM_AUTO, I2S_NUM_0, I2S_NUM_1
//i2s_chan_cfg_rx.role = I2S_ROLE_SLAVE; // I2S controller master role, bclk and lrc signal will be set to output
//i2s_chan_cfg_rx.dma_desc_num = 8; // number of DMA buffer
//i2s_chan_cfg_rx.dma_frame_num = 512; // I2S frame number in one DMA buffer.
//i2s_chan_cfg_rx.auto_clear = true; // i2s will always send zero automatically if no data to send
esp_err_t err = i2s_new_channel(&i2s_chan_cfg_rx, NULL, &rx_chan);
//rx_chan->dir = I2S_DIR_RX;
ESP_ERROR_CHECK(err);
//i2s_std_cfg_rx.slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_MONO);
//i2s_std_cfg_rx.slot_cfg.data_bit_width = I2S_DATA_BIT_WIDTH_16BIT; // Bits per sample
//i2s_std_cfg_rx.slot_cfg.slot_bit_width = I2S_SLOT_BIT_WIDTH_16BIT; // I2S channel slot bit-width equals to data bit-width
//i2s_std_cfg_rx.slot_cfg.slot_mode = I2S_SLOT_MODE_STEREO; // I2S_SLOT_MODE_MONO, I2S_SLOT_MODE_STEREO,
//i2s_std_cfg_rx.slot_cfg.slot_mask = I2S_STD_SLOT_RIGHT; // I2S_STD_SLOT_LEFT, I2S_STD_SLOT_RIGHT
//i2s_std_cfg_rx.slot_cfg.ws_width = I2S_DATA_BIT_WIDTH_16BIT; // WS signal width (i.e. the number of bclk ticks that ws signal is high)
// i2s_std_cfg_rx.slot_cfg.ws_pol = false; // WS signal polarity, set true to enable high lever first
//i2s_std_cfg_rx.slot_cfg.bit_shift = true; // Set to enable bit shift in Philips mode
/*
i2s_std_cfg_rx.gpio_cfg.bclk = I2S_GPIO_UNUSED; // BCLK, Assignment in setPinout()
i2s_std_cfg_rx.gpio_cfg.din = I2S_GPIO_UNUSED; // not used
i2s_std_cfg_rx.gpio_cfg.dout = I2S_GPIO_UNUSED; // DOUT, Assignment in setPinout()
i2s_std_cfg_rx.gpio_cfg.mclk = I2S_GPIO_UNUSED; // MCLK, Assignment in setPinout()
i2s_std_cfg_rx.gpio_cfg.ws = I2S_GPIO_UNUSED; // LRC, Assignment in setPinout()
i2s_std_cfg_rx.gpio_cfg.bclk = PIN_I2S_SCK; // BCLK, Assignment in setPinout()
i2s_std_cfg_rx.gpio_cfg.din = PIN_IS2_SD; // not used
i2s_std_cfg_rx.gpio_cfg.dout = I2S_GPIO_UNUSED; // DOUT, Assignment in setPinout()
i2s_std_cfg_rx.gpio_cfg.mclk = I2S_GPIO_UNUSED; // MCLK, Assignment in setPinout()
i2s_std_cfg_rx.gpio_cfg.ws = PIN_I2S_WS; // LRC, Assignment in setPinout()
i2s_std_cfg_rx.gpio_cfg.invert_flags.mclk_inv = false;
i2s_std_cfg_rx.gpio_cfg.invert_flags.bclk_inv = false;
i2s_std_cfg_rx.gpio_cfg.invert_flags.ws_inv = false;
i2s_std_cfg_rx.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(44100);
*/
//i2s_std_cfg_rx.clk_cfg.sample_rate_hz = 44100;
//i2s_std_cfg_rx.clk_cfg.clk_src = I2S_CLK_SRC_DEFAULT; // Select PLL_F160M as the default source clock
//i2s_std_cfg_rx.clk_cfg.mclk_multiple = I2S_MCLK_MULTIPLE_128; // mclk = sample_rate * 128
//i2s_std_cfg_rx.slot_cfg.slot_mask = I2S_STD_SLOT_LEFT;
//i2s_std_cfg_rx.slot_cfg.bit_shift = 1;
//i2s_std_cfg_rx.slot_cfg.ws_width = 20;
//i2s_std_cfg_rx.slot_cfg.big_endian = 1;
// bit_order_lsbbit_order_lsb
//i2s_std_cfg_rx.slot_cfg.bit_order_lsb = 1;
err = i2s_channel_init_std_mode(rx_chan, &i2s_std_cfg_rx);
ESP_ERROR_CHECK(err);
i2s_chan_info_t info;
err = i2s_channel_get_info(rx_chan, &info);
ESP_ERROR_CHECK(err);
delay(1000);
Serial.printf("I2S channel info: %d, %d, %d, %d, %d\n", info.id, info.role, info.dir, info.mode, info.pair_chan);
delay(500);
ESP_ERROR_CHECK(err);
err = i2s_channel_enable(rx_chan);
ESP_ERROR_CHECK(err);
gpio_num_t gpio_pin = PIN_IS2_SD; // Replace with your GPIO pin
/*
// Configure the pin as an input
gpio_config_t io_conf;
io_conf.intr_type = GPIO_INTR_DISABLE; // Disable GPIO interrupts
io_conf.mode = GPIO_MODE_INPUT; // Set as Input mode
io_conf.pin_bit_mask = (1ULL << gpio_pin); // Bit mask of the pin
io_conf.pull_down_en = GPIO_PULLDOWN_ENABLE; // Enable pull-down resistor
io_conf.pull_up_en = GPIO_PULLUP_DISABLE; // Disable pull-up resistor
*/
// Apply the GPIO configuration
//gpio_config(&io_conf);
err = gpio_pulldown_en(gpio_pin);
ESP_ERROR_CHECK(err);
}
#if 0
const i2s_config_t i2s_config = {
.mode = i2s_mode_t(I2S_MODE_MASTER | I2S_MODE_RX),
.sample_rate = AUDIO_SAMPLE_RATE,
.bits_per_sample = i2s_bits_per_sample_t(AUDIO_BIT_RESOLUTION),
.channel_format = i2s_channel_fmt_t(I2S_CHANNEL_FMT_ONLY_RIGHT),
.communication_format = i2s_comm_format_t(I2S_COMM_FORMAT_STAND_I2S),
.intr_alloc_flags = 0,
.dma_buf_count = AUDIO_DMA_BUFFER_COUNT,
.dma_buf_len = IS2_AUDIO_BUFFER_LEN,
//.use_apll = true
// .tx_desc_auto_clear ?
};
const i2s_pin_config_t pin_config = {
.bck_io_num = PIN_I2S_SCK,
.ws_io_num = PIN_I2S_WS,
.data_out_num = I2S_PIN_NO_CHANGE,
.data_in_num = PIN_IS2_SD};
const i2s_config_t i2s_config = {
.mode = i2s_mode_t(I2S_MODE_MASTER | I2S_MODE_RX), // Receive, not transfer
.sample_rate = 16000, // 16KHz
.bits_per_sample = I2S_BITS_PER_SAMPLE_32BIT, // could only get it to work with 32bits
.channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT, // use right channel
.communication_format = i2s_comm_format_t(I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB),
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, // Interrupt level 1
.dma_buf_count = 4, // number of buffers
.dma_buf_len = 8 // 8 samples per buffer (minimum)
};
#endif
}
void i2s_audio_init()
{
#if 0
i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL);
i2s_set_pin(I2S_NUM, &pin_config);
i2s_zero_dma_buffer(I2S_NUM_0);
// i2s_start(I2S_NUM_0);
#else
//init_i2s_pins();
#endif
}
//esp_err_t i2s_channel_read(i2s_chan_handle_t handle, void *dest, size_t size, size_t *bytes_read, uint32_t timeout_ms)
void i2s_audio_shutdown()
{
// i2s_stop(I2S_NUM_0);
// i2s_driver_uninstall(I2S_NUM_0);
i2s_del_channel(rx_chan);
}
size_t i2s_read_raw_samples(audio_sample_t (&buffer)[IS2_AUDIO_BUFFER_LEN])
{
if (!g_is_initialized)
{
init_i2s_pins();
g_is_initialized = true;
}
#if 0
size_t bytes_read = 0;
i2s_event_t event;
uint32_t current_time = millis();
esp_err_t result = i2s_read(I2S_NUM_0, buffer, sizeof(buffer), &bytes_read, 0);
if (result == ESP_OK)
{
if (bytes_read > 0)
{
// cout << "Bytes read: " << bytes_read << endl;
const size_t count = bytes_read / sizeof(audio_sample_t);
return count;
}
}
return 0;
#else
size_t bytes_read = 0;
esp_err_t err = i2s_channel_read(rx_chan, buffer, sizeof(buffer), &bytes_read, 0);
if (err == ESP_OK)
{
if (bytes_read > 0)
{
// cout << "Bytes read: " << bytes_read << endl;
const size_t count = bytes_read / sizeof(audio_sample_t);
return count;
}
}
if (err != ESP_ERR_TIMEOUT) {
ESP_ERROR_CHECK(err);
}
return 0;
#endif
} |
Okay, I've gone ahead and created a minimal repo that reproduces this bug using the alpha release 3.0.0 5.1 libraries that were unofficially released. For the life of me, I cannot get the INMP441 to work in the new v5.1 drivers, but I could make them work in the v4.4 drivers. The legacy i2s drivers work with the new v5.1 alpha release 3.0.0, fyi. |
Okay, here's the code to get IS2 to work in v5.1 for the INMP441 mems microphone that is so popular. The trick was here:
There's very little documentation on this The INMP441 only seemed to work when I put it in 16 bit mode, which I assume means that the lower 16 bits of the 32 bit word select frame are truncated. Which is the reason why the legacy i2s drivers were able to work out of spec with the INMP441 datasheet. A humble suggestion is to create a macro designed for the INMP441 and document in the C header code that the I think my problem here was that the Here's the code for the INMP441 /*
Uses the new idf 5.1 i2s driver..
Not thread safe.
*/
#include <iostream>
#include <Arduino.h>
#include "defs.h"
#include <driver/i2s_std.h>
#include "i2s_device.h"
#include "driver/gpio.h"
namespace
{
enum {
INMP441_BIT_RESOLUTION = 24,
INMP441_CHANNELS = 1,
INMP441_FULL_FRAME_SIZE = 32
};
static_assert(AUDIO_BIT_RESOLUTION == 16, "Only 16 bit resolution is outputted by the microphone");
static_assert(AUDIO_CHANNELS == 1, "Only 1 channel is supported");
static_assert(sizeof(audio_sample_t) == 2, "audio_sample_t must be 16 bit");
struct I2SContext
{
i2s_chan_handle_t rx_chan;
i2s_chan_config_t i2s_chan_cfg_rx;
i2s_std_config_t i2s_std_cfg_rx;
};
I2SContext make_inmp441_context() {
I2SContext ctx;
i2s_chan_handle_t rx_chan = NULL;
i2s_chan_config_t i2s_chan_cfg_rx = {
.id = I2S_NUM_0,
.role = I2S_ROLE_MASTER,
.dma_desc_num = AUDIO_DMA_BUFFER_COUNT,
.dma_frame_num = IS2_AUDIO_BUFFER_LEN,
.auto_clear = false,
};
i2s_std_config_t rx_std_cfg = {
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(AUDIO_CHANNEL_SAMPLE_RATE),
.slot_cfg = {
.data_bit_width = I2S_DATA_BIT_WIDTH_16BIT,
.slot_bit_width = I2S_SLOT_BIT_WIDTH_32BIT,
.slot_mode = I2S_SLOT_MODE_MONO,
.slot_mask = I2S_STD_SLOT_RIGHT,
.ws_width = 32,
.ws_pol = false,
.bit_shift = true,
.left_align = true,
.big_endian = false,
.bit_order_lsb = false,
},
.gpio_cfg = {
.mclk = I2S_GPIO_UNUSED,
.bclk = PIN_I2S_SCK,
.ws = PIN_I2S_WS,
.dout = I2S_GPIO_UNUSED,
.din = PIN_IS2_SD,
.invert_flags = {
.mclk_inv = false,
.bclk_inv = true,
.ws_inv = false,
},
},
};
ctx = {rx_chan, i2s_chan_cfg_rx, rx_std_cfg};
return ctx;
}
I2SContext s_i2s_context = make_inmp441_context();
void init_i2s_pins()
{
//s_i2s_context = get_i2s_context();
esp_err_t err = i2s_new_channel(&s_i2s_context.i2s_chan_cfg_rx, NULL, &s_i2s_context.rx_chan);
ESP_ERROR_CHECK(err);
err = i2s_channel_init_std_mode(s_i2s_context.rx_chan, &s_i2s_context.i2s_std_cfg_rx);
ESP_ERROR_CHECK(err);
i2s_chan_info_t info;
err = i2s_channel_get_info(s_i2s_context.rx_chan, &info);
ESP_ERROR_CHECK(err);
err = i2s_channel_enable(s_i2s_context.rx_chan);
ESP_ERROR_CHECK(err);
// Set the pulldown resistor on the SD pin
gpio_set_pull_mode(PIN_IS2_SD, GPIO_PULLDOWN_ONLY);
}
} // namespace
void i2s_audio_init()
{
pinMode(PIN_AUDIO_PWR, OUTPUT);
digitalWrite(PIN_AUDIO_PWR, HIGH); // Power on the IS2 microphone.
init_i2s_pins();
}
// esp_err_t i2s_channel_read(i2s_chan_handle_t handle, void *dest, size_t size, size_t *bytes_read, uint32_t timeout_ms)
void i2s_audio_shutdown()
{
// i2s_stop(I2S_NUM_0);
// i2s_driver_uninstall(I2S_NUM_0);
i2s_del_channel(s_i2s_context.rx_chan);
}
void i2s_audio_enter_light_sleep()
{
// digitalWrite(PIN_AUDIO_PWR, LOW); // Power off the IS2 microphone.
// hold pin engaged
digitalWrite(PIN_AUDIO_PWR, HIGH); // Power on the IS2 microphone.
gpio_hold_en(PIN_AUDIO_PWR);
// i2s_stop(I2S_NUM_0);
// i2s_driver_uninstall(I2S_NUM_0);
}
void i2s_audio_exit_light_sleep()
{
// digitalWrite(PIN_AUDIO_PWR, HIGH); // Power on the IS2 microphone.
// i2s_start(I2S_NUM_0);
// i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL);
}
size_t i2s_read_samples(audio_sample_t (&buffer)[IS2_AUDIO_BUFFER_LEN])
{
size_t bytes_read = 0;
esp_err_t err = i2s_channel_read(s_i2s_context.rx_chan, buffer, sizeof(buffer), &bytes_read, 0);
const size_t count = bytes_read / sizeof(audio_sample_t);
ASSERT(bytes_read / sizeof(audio_sample_t) <= IS2_AUDIO_BUFFER_LEN, "Buffer overflow!");
if (err == ESP_OK)
{
if (bytes_read > 0)
{
return count;
}
}
if (err != ESP_ERR_TIMEOUT)
{
ESP_ERROR_CHECK(err);
}
return 0;
}
|
Update4: Oscilloscope time!
Should the ESP32 keep the clock/ws pin alive and feed into the DMA buffer? Or at least strobe them and discard the data. I'ml going to look into feeding in a clock signal during sleep and see if this solves the issue. |
So it looks like DMA is NOT enabled during light sleep. Which is the reason I2S stops. My next trick will be to try and attach an LEDC driver in low bit mode and try to strobe the clk signal to keep the INMP441 from going into sleep mode. Here is some code that will configure a pin to use LEDC during light sleep: |
So confirmed working via the oscilloscope in 5.1 alpha 3.0.0. Closing this bug. It's the INMP441 microphone that is the culprit, which might be auto-sleeping when the I2S halts it's clock during light sleep. A potential work-around is to use ledc on the RTC clock to create a clock signal that keeps the INMP441 microphone from going to sleep. I'm having difficulty for I2S and LEDC to share a pin so I think I'm just going to tie the pins together and see how it goes. |
Okay update: I've mostly got it working.There are two issue here as I went deep into this issue.
Yes, I'm locking the APB frequency on setup(). I suspect still that it's the INMP441 that's causing the issue, but I can't rule out the esp32 driver yet. Next steps:
Other notes:Wow, it's really hard to get that LEDC driver to operate in light sleep mode! The documentation is missing the following requirements:
I've posted my working code sample here I've also filed a bug report to update the LEDC manual to include these requirements, which can be found here: |
Confirmed working in esp32 idf 5.1 alpha 3.0.0
The INMP441 mems mic is the culprit. The working theory is that when the I2S clock stops the microphone goes into a power down mode and then generates noise during power up when I2S resumes. See final comment for possible work around using LEDC to generate a pseudo clock to keep the device awake.
Example:
Board
XIAO ESP32-C3 with battery
Device Description
https://www.amazon.com/dp/B0B94JZ2YF?psc=1&ref=ppx_yo2ov_dt_b_product_details
I'm using Platform io. Here is my ini file
I have a INMP441 mems mic:
https://www.amazon.com/AITRIP-Omnidirectional-Microphone-Precision-Interface/dp/B092HWW4RS/ref=sr_1_3?keywords=INMP441&sr=8-3
For the life of me I cannot get the IS2 peripheral to power back on quickly. I have to wait about 1/3rd of a second before the microphone stabilizes. I've tried so many different things: changing the clock signal. Changing the board frequency. Nothing seems to work to make the microphone better.
Hardware Configuration
Version
v2.0.9
IDE Name
PlatformIO
Operating System
Max OS 13 on M1
Flash frequency
default
PSRAM enabled
no
Upload speed
115200
Description
I2S has to wait a long time before it stabilizes after a light sleep. I have to throw away the next 12 buffers of 1024, 44100 hz audio data in mono format. I thought it was the microphone but it appears to be the IS2 bus.
Sketch
Debug Message
Other Steps to Reproduce
No response
I have checked existing issues, online documentation and the Troubleshooting Guide
The text was updated successfully, but these errors were encountered: