I2s MEMS microphone help (sph0645)
I2s MEMS microphone help (sph0645)
Hello, I would like to get help for my project. I submitted an issue on ESP-IDF github to find an answer, so I try here aswell.
I don't want to duplicate the description of my problem, so I just link my github issue ticket here.
https://github.com/espressif/esp-idf/issues/14415
I hope someone can help me out, or atleast start me somewhere where I can find a solution.
I don't want to duplicate the description of my problem, so I just link my github issue ticket here.
https://github.com/espressif/esp-idf/issues/14415
I hope someone can help me out, or atleast start me somewhere where I can find a solution.
-
- Posts: 1735
- Joined: Mon Oct 17, 2022 7:38 pm
- Location: Europe, Germany
Re: I2s MEMS microphone help (sph0645)
Github issues are actually meant to report (suspected) bugs in e.g. the IDF. For questions like this, i.e. how to interpret I2S data you receive, this forum would be the more appropriate place.
For reference, in the issue there is some example output:
Try interpreting your buffer as int32 (const int32_t* samples = (const int32_t*)r_buf;) and see if that gives you what you expect.
For reference, in the issue there is some example output:
This looks like plausible little-endian int32 sample values with 24 (or, at least 17) significant bits.Code: Select all
Read Task: i2s read 2048 bytes [0] 0 [1] 80 [2] 6 [3] f3 [4] 0 [5] 80 [6] f8 [7] f2
Try interpreting your buffer as int32 (const int32_t* samples = (const int32_t*)r_buf;) and see if that gives you what you expect.
Re: I2s MEMS microphone help (sph0645)
Thanks for your reply .
I was not sure where to got with this problem for the first place. But I understand that I should keep posting here for advice.
I try to implement your code into mine and I keep you updated.
I was not sure where to got with this problem for the first place. But I understand that I should keep posting here for advice.
I try to implement your code into mine and I keep you updated.
Re: I2s MEMS microphone help (sph0645)
I managed to continue my code, he it is:
But I don't know how to proceed further.
How can I decode the I2s communication ?
And do I have to use two's complement transformation on my trimmed_sample ?
Also here is an output of my code:
Reading the datasheet of my mic, it seems okey.
(https://www.knowles.com/docs/default-so ... asheet.pdf)
Figure 7 shows, that from the 18th bit the data stream should be only zeros, and that's the case here.
Code: Select all
#include <stdint.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/i2s_std.h"
#include "driver/gpio.h"
#include "esp_check.h"
#include "sdkconfig.h"
#define BUFFER_SIZE 2048
static i2s_chan_handle_t rx_chan; // I2S RX channel handler
static void i2s_read_task(void *args)
{
uint8_t *r_buf = (uint8_t *)calloc(1, BUFFER_SIZE);
assert(r_buf); // Check if the r_buf allocation was successful
size_t r_bytes = 0;
// Enable the RX channel
ESP_ERROR_CHECK(i2s_channel_enable(rx_chan));
while (1)
{
// Read I2S data
if (i2s_channel_read(rx_chan, r_buf, BUFFER_SIZE, &r_bytes, 1000) == ESP_OK)
{
printf("Read Task: i2s read %zu bytes\n-----------------------------------\n", r_bytes);
// Print the buffer as samples
const uint32_t *samples = (const uint32_t *)r_buf;
int num_samples = r_bytes / sizeof(uint32_t);
// Print the 32-bit samples and their 18-bit versions
for (int i = 0; i < num_samples; i++)
{
uint32_t sample = samples[i];
// Print the original 32-bit sample bit by bit
printf("Original 32-bit Sample: ");
for (int bit = 31; bit >= 0; bit--)
{
printf("%ld", (sample >> bit) & 1);
}
printf("\n");
// Trim the last 14 bits
uint32_t trimmed_sample = (sample >> 14) & 0x3FFFF; // Trim the last 14 bits and keep the lower 18 bits
// Print the trimmed 18-bit sample bit by bit
printf("Trimmed 18-bit Sample: ");
for (int bit = 17; bit >= 0; bit--)
{
printf("%ld", (trimmed_sample >> bit) & 1);
}
printf("\n");
}
}
else
{
printf("Read Task: i2s read failed\n");
}
vTaskDelay(pdMS_TO_TICKS(200));
}
free(r_buf);
vTaskDelete(NULL);
}
static void i2s_init_sph0645lm4h(void)
{
// I2S channel configuration
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER);
ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, NULL, &rx_chan));
// I2S standard configuration
i2s_std_config_t std_cfg = {
.clk_cfg = {
.sample_rate_hz = 48000,
.clk_src = I2S_CLK_SRC_DEFAULT,
.mclk_multiple = I2S_MCLK_MULTIPLE_256,
},
// Philips or standard configuration
//.slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_32BIT, I2S_SLOT_MODE_MONO),
.slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_32BIT, I2S_SLOT_MODE_MONO),
.gpio_cfg = {
.mclk = I2S_GPIO_UNUSED,
.bclk = GPIO_NUM_4,
.ws = GPIO_NUM_5,
.dout = I2S_GPIO_UNUSED, // Only read, no write
.din = GPIO_NUM_19,
.invert_flags = {
.mclk_inv = false,
.bclk_inv = false,
.ws_inv = false,
},
},
};
std_cfg.slot_cfg.slot_mask = I2S_STD_SLOT_LEFT;
ESP_ERROR_CHECK(i2s_channel_init_std_mode(rx_chan, &std_cfg));
}
void app_main(void)
{
// I2S initialization
i2s_init_sph0645lm4h();
// Task creation
xTaskCreate(i2s_read_task, "i2s_read_task", 4096, NULL, 5, NULL);
}
How can I decode the I2s communication ?
And do I have to use two's complement transformation on my trimmed_sample ?
Also here is an output of my code:
Code: Select all
Original 32-bit Sample:111101010011000100 00000000000000
Trimmed 18-bit Sample: 111101010011000100
Original 32-bit Sample:111101010010111000 00000000000000
Trimmed 18-bit Sample: 111101010010111000
(https://www.knowles.com/docs/default-so ... asheet.pdf)
Figure 7 shows, that from the 18th bit the data stream should be only zeros, and that's the case here.
Re: I2s MEMS microphone help (sph0645)
So I continued further (my comments are waiting for approval so it feels like I am talking to myself).
I started reading about DAC features. In the Digital To Analog Converter (DAC) part of ESP IDF docs it says :
I started reading about DAC features. In the Digital To Analog Converter (DAC) part of ESP IDF docs it says :
Also in the programming guide there is a section that says :On ESP32, the DAC digital controller can be connected internally to the I2S0 and use its DMA for continuous conversion. Although the DAC only needs 8-bit data for conversion, it has to be the left-shifted 8 bits (i.e., the high 8 bits in a 16-bit slot) to satisfy the I2S communication format. By default, the driver helps to expand the data to 16-bit wide automatically. To expand manually, please disable CONFIG_DAC_DMA_AUTO_16BIT_ALIGN in the menuconfig.
It feels like I can transfer data from I2s to ADC, so easly but I don't know how. Beacuse the example code is not going in detail how sadly. For example where can I set the pins, or which pins-s should I use ?Short example configuring I2S to use internal DAC for analog output:Code: Select all
#include "driver/i2s.h" #include "freertos/queue.h" static const int i2s_num = 0; // i2s port number static const i2s_config_t i2s_config = { .mode = I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN, .sample_rate = 44100, .bits_per_sample = 16, /* the DAC module will only take the 8bits from MSB */ .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, .communication_format = I2S_COMM_FORMAT_I2S_MSB, .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, // high interrupt priority .dma_buf_count = 8, .dma_buf_len = 64 }; ... i2s_driver_install(i2s_num, &i2s_config, 0, NULL); //install and start i2s driver i2s_set_pin(i2s_num, NULL); //for internal DAC, this will enable both of the internal channels //You can call i2s_set_dac_mode to set built-in DAC output mode. //i2s_set_dac_mode(I2S_DAC_CHANNEL_BOTH_EN); i2s_set_sample_rates(i2s_num, 22050); //set sample rates i2s_driver_uninstall(i2s_num); //stop & destroy i2s driver
-
- Posts: 1735
- Joined: Mon Oct 17, 2022 7:38 pm
- Location: Europe, Germany
Re: I2s MEMS microphone help (sph0645)
I think the DAC only needs the I2S peripheral to control the DMA transfer.
You'll have to convert the signed 32-bit data received via I2S to unsigned 8 bit for the DAC, e.g. uint8_t dacSample = (uint8_t)((sample32 >> 24) + 128).
You'll have to convert the signed 32-bit data received via I2S to unsigned 8 bit for the DAC, e.g. uint8_t dacSample = (uint8_t)((sample32 >> 24) + 128).
Re: I2s MEMS microphone help (sph0645)
I reckon I am going to lose some resolution, but the DAC is the bottleneck here.
I try to implement your idea.
I try to implement your idea.
Re: I2s MEMS microphone help (sph0645)
So I added your idea
But it is not working as I can tell.
As you can tell, the trimmed stream is changing, but the DACSample is not.
I should figure out something easier. How can I decode the I2s data stream to something usefull ? I have the data stream, but that's it.
Code: Select all
#include <stdint.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/i2s_std.h"
#include "driver/gpio.h"
#include "esp_check.h"
#include "sdkconfig.h"
#define BUFFER_SIZE 2048
static i2s_chan_handle_t rx_chan; // I2S RX channel handler
static void i2s_read_task(void *args)
{
uint8_t *r_buf = (uint8_t *)calloc(1, BUFFER_SIZE);
assert(r_buf); // Check if the r_buf allocation was successful
size_t r_bytes = 0;
// Enable the RX channel
ESP_ERROR_CHECK(i2s_channel_enable(rx_chan));
while (1)
{
// Read I2S data
if (i2s_channel_read(rx_chan, r_buf, BUFFER_SIZE, &r_bytes, 1000) == ESP_OK)
{
printf("Read Task: i2s read %zu bytes\n-----------------------------------\n", r_bytes);
// Print the buffer as samples
const uint32_t *samples = (const uint32_t *)r_buf;
int num_samples = r_bytes / sizeof(uint32_t);
// Print the 32-bit samples and their 18-bit versions
for (int i = 0; i < num_samples; i++)
{
uint32_t sample = samples[i];
// Print the original 32-bit sample bit by bit
// printf("Original 32-bit Sample: ");
// for (int bit = 31; bit >= 0; bit--)
// {
// printf("%ld", (sample >> bit) & 1);
// }
// printf("\n");
// Trim the last 14 bits
uint32_t trimmed_sample = (sample >> 14) & 0x3FFFF; // Trim the last 14 bits and keep the lower 18 bits
uint8_t dacSample = (uint8_t)((sample >> 24) + 128);
// Print the trimmed 18-bit sample bit by bit
printf("Trimmed 18-bit Sample: ");
for (int bit = 17; bit >= 0; bit--)
{
printf("%ld", (trimmed_sample >> bit) & 1);
}
printf("\n");
printf("DACSample 8-bit Sample: ");
for (int bit = 7; bit >= 0; bit--) // A cycle from 0 to 7
{
printf("%d", (dacSample >> bit) & 1);
}
printf("\n");
}
}
else
{
printf("Read Task: i2s read failed\n");
}
vTaskDelay(pdMS_TO_TICKS(200));
}
free(r_buf);
vTaskDelete(NULL);
}
static void i2s_init_sph0645lm4h(void)
{
// I2S channel configuration
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER);
ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, NULL, &rx_chan));
// I2S standard configuration
i2s_std_config_t std_cfg = {
.clk_cfg = {
.sample_rate_hz = 48000,
.clk_src = I2S_CLK_SRC_DEFAULT,
.mclk_multiple = I2S_MCLK_MULTIPLE_256,
},
// Philips or standard configuration
//.slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_32BIT, I2S_SLOT_MODE_MONO),
.slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_32BIT, I2S_SLOT_MODE_MONO),
.gpio_cfg = {
.mclk = I2S_GPIO_UNUSED,
.bclk = GPIO_NUM_4,
.ws = GPIO_NUM_5,
.dout = I2S_GPIO_UNUSED, // Only read, no write
.din = GPIO_NUM_19,
.invert_flags = {
.mclk_inv = false,
.bclk_inv = false,
.ws_inv = false,
},
},
};
std_cfg.slot_cfg.slot_mask = I2S_STD_SLOT_LEFT;
ESP_ERROR_CHECK(i2s_channel_init_std_mode(rx_chan, &std_cfg));
}
void app_main(void)
{
// I2S initialization
i2s_init_sph0645lm4h();
// Task creation
xTaskCreate(i2s_read_task, "i2s_read_task", 4096, NULL, 5, NULL);
}
Code: Select all
Trimmed 18-bit Sample: 111001011100010100
DACSample 8-bit Sample: 01100101
Trimmed 18-bit Sample: 111001011011111000
DACSample 8-bit Sample: 01100101
Trimmed 18-bit Sample: 111001011100000000
DACSample 8-bit Sample: 01100101
Trimmed 18-bit Sample: 111001011100100000
DACSample 8-bit Sample: 01100101
I should figure out something easier. How can I decode the I2s data stream to something usefull ? I have the data stream, but that's it.
-
- Posts: 1735
- Joined: Mon Oct 17, 2022 7:38 pm
- Location: Europe, Germany
Re: I2s MEMS microphone help (sph0645)
I don't know what 'useful' would be to you here.How can I decode the I2s data stream to something usefull ?
But: The signed 32-bit sample values you get via I2S are the numerical representations of the air pressure the microphone picks up. 0 means no sound pressure, 2^31-1 is maximum possible positive pressure (amplitude) and -2^31 the maximum negative.
Incident sound will make the pressure, and with it the sample values, oscillate up and down around 0.
Because presumably the sound you're recording has much lower frequencies than half the sample rate, the difference from one sample to the next will be relatively small.
For demonstration, you could e.g. find and print the maximum sample value over a number of samples. Loud sound should give a larger max value than quiet sounds or silence.
Down-converting the resolution from 18 to 8 bits naturally causes loss of information; very small variations between sample values will be lost and similar 18-bit sample values will give the same 8-bit value. In fact, 1024 adjacent 18-bit values will be mapped to the same 8-bit value.
Re: I2s MEMS microphone help (sph0645)
find and print the maximum sample value over a number of samples
I try to do something similiar.
I will have to get a better understanding in digital audio and I2s to know what I wanto to do with this raw datastream. I am going to come back with other dumb questions, if I understand more in this topic .
Thanks for every comment you made so far, it is very helpful.
Who is online
Users browsing this forum: No registered users and 150 guests