使用ESP-IDF来驱动INMP441全向麦克风

之前的文章我们讲过了I2S。

I2S是什么通信协议?它如何传输音频数据?它和I2C是什么关系?_i2c接口和i2s-CSDN博客文章浏览阅读836次,点赞12次,收藏14次。这个可以参考ADC来理解,我们的ADC也是有左对齐和右对齐的,假设我们的数据是12位的,而传输数据是按照16bit传输的,这时候12bit填不满16bit,我们就需要选择是将数据左对齐还是右对齐了。我们平时打吃鸡可以听声辨位,最主要的原因就是有左右声道,我们左右耳机的声音其实是不一样的,而我们的大脑可以同时处理这两个声道的声音,从而根据声音来判断敌人的位置。我们刚刚说了WS是声道选择线,而WS同时也是帧同步线,它一个周期就表示一帧,而一帧的数据里包含左声道和右声道的数据,所以WS的频率就等于音频的采样率。_i2c接口和i2shttps://blog.csdn.net/m0_63235356/article/details/145260573?spm=1001.2014.3001.5501https://blog.csdn.net/m0_63235356/article/details/145260573?spm=1001.2014.3001.5501https://blog.csdn.net/m0_63235356/article/details/145260573?spm=1001.2014.3001.5501https://blog.csdn.net/m0_63235356/article/details/145260573?spm=1001.2014.3001.5501对协议不太了解的小伙伴可以先去看看

养兵千日用兵一时,我们就是为的今天用I2S来驱动INMP441这个麦克风模块。

图片

当然了,其他音频相关的模块基本用的也都是I2S,所以我们今天就以INMP441为例,来看看如何使用ESP-IDF的I2S。

因为你现在上网一搜ESP32+INMP441,出来的要么是STM32的,要么是用Arduino来编写ESP32的,再要么就是直接把代码给你的。

所以这可能是全网最详细的关于ESP-IDF使用I2S来驱动INMP441模块的文章了(只要你前置定语加的足够多)

图片

接下来我们来看看INMP441的手册来了解了解它。

因为没有官方的中文手册(至少我没找到),所以我用的搜狗翻译机翻了一下(主要是每个月有免费翻译的额度,不用白不用),虽然挺拉的,但我们还是勉强看看。

关注同名公众号“折途想要敲代码”回复关键词“INMP441”即可免费下载所有相关资料

图片

它这边介绍了一下相关的参数,什么信噪比啦,灵敏度啦我们都不用管,我们要注意的就一个,那就是它输出的是24bit的数据。

图片

接下来是WS的频率,我们知道,WS的频率就是声音的采样率,INMP441的采样率是7.8k~50k,一般来说常见的声音采样率是44.1k,所以INMP441是满足要求的。

图片

INMP441一共是有九个引脚,不过模块就引出了六个,看上面表格也可以看的出来,在模块中八号引脚直接永久拉高表示有效。然后GND只需要引出一个即可。

SCK、SD、WS这仨就是I2S用到的线了。

L/R是低电平的时候表示INMP441采集左声道的数据,高电平就是采集右声道的数据。

因为INMP441只能采集一个声道的数据,所以如果我们要采集立体声道的话需要两个INMP441,一个采集左声道一个采集右声道。

图片

上面是描述INMP441三种工作状态的,之所以没放中文版是因为机翻的有错误,比如说2^18翻译成了218,所以小伙伴们私信我下载的中文手册在看的时候还是要结合原版查看的。

当上电之后,INMP441会过2^18个周期之后再正式进入工作模式,所以我们可以通过延时一段时间(100ms左右,具体看SCK的频率)来跳过这一阶段,不跳也可以,但是采集的数据都会是0。

如果光是上电,但是没有时钟信号,那么就会进入待机模式(姑且这么叫吧),恢复时钟信号的时候需要等待2^14个周期(5ms左右,具体看SCK的频率)。

然后我们不能从待机模式直接过渡到关机模式。

关机模式就是把INMP441的使能引脚拉低,如果要恢复工作模式就把使能引脚拉高,然后等待2^17个周期。

不过我们买的INMP441模块没有把使能引脚引出来,所以我们不需要考虑这个。

但如果我们是自己画个板子要用到INMP441这个芯片的话,就可以把使能引脚接到主控芯片上了。

还有一点要注意,那就是不要在没有给INMP441提供VDD的时候给它时钟信号,否则有可能会损坏INMP441。

图片

最后看看上面截的图,是机翻的,勉强看看。

我们要抓的重点就是三个红框框出来的。

首先INMP441发出的格式是24位二进制补码,所以我们拿到手的数据还需要转换一下格式。

第二点是SD数据线需要加一个100k的下拉电阻,关于这一点,拿INMP441芯片焊在自己板子上的小伙伴要注意一下,用模块的话就不需要管这一点了,因为模块上已经集成了一个104的电阻。

最后一个就是如果我们要采集双声道数据,那么使用两个INMP441连线就要参考第三个红框这样。

一个INMP441的L/R接低电平,另一个接高电平,这样每采集一帧数据,WS低电平的时候负责左声道的INMP441发送数据,WS高电平的时候负责右声道的INMP441发送数据,所以两个INMP441共用时钟线和数据线也是没问题的。

以上就是关于INMP441的部分了,剩下是一些跟音频参数相关的,我看不懂,感兴趣的小伙伴自己去看手册。

接下来我们来看看如何使用ESP-IDF的I2S。

I2S - ESP32-S3 - — ESP-IDF 编程指南 release-v5.1 文档https://docs.espressif.com/projects/esp-idf/zh_CN/release-v5.1/esp32s3/api-reference/peripherals/i2s.html#_CPPv419i2s_tdm_slot_mask_thttps://docs.espressif.com/projects/esp-idf/zh_CN/release-v5.1/esp32s3/api-reference/peripherals/i2s.html#_CPPv419i2s_tdm_slot_mask_thttps://docs.espressif.com/projects/esp-idf/zh_CN/release-v5.1/esp32s3/api-reference/peripherals/i2s.html#_CPPv419i2s_tdm_slot_mask_thttps://docs.espressif.com/projects/esp-idf/zh_CN/release-v5.1/esp32s3/api-reference/peripherals/i2s.html#_CPPv419i2s_tdm_slot_mask_t一定要记得选对芯片型号呀呀呀呀呀!!!

图片

上面这个是ESP32S3的。

下面这个是ESP32的(ESP32不仅表示ESP32这类芯片,同时ESP32也是一个具体型号的芯片,这边指的是这个特定型号的芯片)

图片

不同型号的芯片,对于I2S的处理是不一样的。

你们猜我为什么单独拎出来说。

我昨晚折腾了一晚上,然后发现芯片型号没选对,数据处理错误导致串口绘图的数据极其离谱

关于I2S的代码,官方的编程指南里直接就有简单的代码,我们直接复制下来改改就能用,不过我还是像往常一样把相关函数给大家一一讲解一遍吧。

图片

我们看看上面这个状态图,我们要接收来自INMP441的数据,一共需要四步。

第一步是new一个通道,第二步初始化这个通道,第三步使能这个通道,第四步就是读取数据了。

所以我们只需要看上面四个函数就行。

第一步new一个通道。

图片

第一个参数是配置用的结构体。

第二个参数是用来发送数据的通道句柄指针,属于传出参数。

第三个参数是用来发送数据的通道句柄指针,属于传出参数。

如果我们只需要接收数据而不需要发送数据,那么第二个参数可以直接给NULL表示我们不需要配置用来发送数据的通道句柄。

接着可以看看示例代码是怎么处理的。

i2s_chan_handle_t rx_handle;
/* 通过辅助宏获取默认的通道配置 
* 这个辅助宏在 'i2s_common.h' 中定义,由所有 I2S 通信模式共享 
* 它可以帮助指定 I2S 角色和端口 ID */
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER);
/* 分配新的 TX 通道并获取该通道的句柄 */
i2s_new_channel(&chan_cfg, NULL, &rx_handle);

​​​后两个参数不用说,我们看看第一个参数。

示例代码是使用了一个宏I2S_CHANNEL_DEFAULT_CONFIG来获取到第一个参数。

我们来看看这个宏返回了什么样的东西。

图片

宏也有两个参数,用来配置在了返回的结构体的第一和第二个成员变量上。

配置的分别是使用的I2S资源编号和ESP32S3在I2S总线上扮演的角色。

示例代码中的参数表示自动分配I2S资源以及我们ESP32S3属于主机。

ESP32S3一共有两个I2S资源,I2S0和I2S1,一般我们就用示例代码中的参数,让它自动分配一个就行。

剩下三个参数都是配置DMA的,因为I2S的数据是用DMA来搬运的,不过我们不用管DMA,因为底层都帮我们封装好了。

三个参数的含义依次是使用的DMA的编号,DMA里I2S的数据帧数量,是否自动清理DMA的发送缓冲区。

如果我们I2S采集的数据是24bit的,那么dma_frame_num这个得配置为3的倍数,因为24bit是3Byte,不过它默认配置的240就是3的倍数,所以我们不用管。

DMA缓冲区大小不能超过4092(没错,就是4092),所以这个数据帧数量*3(INMP441一帧数据是24bit,也就是3Byte)不能超过4092。

在这个前提下,我们可以把这个参数配置的越大越好,因为缓冲区大小越大,容纳的数据越多,触发中断的次数也就越少,效率也就越高。

按照我个人的理解就是我们到时候读取数据的时候,一次性读取的数量不要超过这个数,但是实际测试下来好像超过了也没什么问题。

图片

接下来我们进入第二步,初始化这个通道。

图片

参数一传入要配置的通道句柄,我们就把我们刚刚new完的接收句柄传进去,然后参数二是配置用的结构体指针。

我们看看这个结构体。

图片

这个结构体的成员有三个,也都是结构体,从上到下是配置时钟的,配置插槽的(???),配置相关GPIO口的。

我们再次一一查看。

图片

配置时钟的结构体有三个成员,依次是采样率,时钟源,以及MCLK的倍数。

采样率就按照我们的需求来,时钟源就用默认的,这个MCLK在我们之前I2S那篇文章没提到,其实MCLK就是作为整个音频系统的参考时钟,确保所有设备同步工作。

我们使用采集24bit的数据的话,需要把这个MCLK配置为3的倍数,所以我们没得选只能用I2S_MCLK_MULTIPLE_384这个参数。

图片

第二个插槽配置的结构体的成员比较多,不过我们不需要一一去配置。

图片

示例代码里用了一个宏来直接帮我们获取一个配置好的这个结构体。

.slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_24BIT, I2S_SLOT_MODE_MONO),

然后我们看看这个宏帮我们配置的怎么样了。

图片

然后关于这些参数都是什么意思,我懒得一个个去讲了,我们直接对着下面机翻的图来看吧。

图片

我直接说我们要改的地方,第一个是slot_mask,这边默认是配了个both,实际上我们只用一个INMP441的话只采集一个声道的数据,所以我们这边要改成仅左声道或者是仅右声道。

第二个要改的是bit_order_lsb,是否低位先行,默认是false,也就是高位先行;这边我们改成true,也就是低位先行。

INMP441里面写了它是高位先行,那为什么我们这边要配置为低位先行呢?答案是我也不知道,我只知道配位高位先行的话得到的数据绝对值都非常大,配为低位先行的话会好一些(起码我说话的时候绘制的波形会跟着变动)。

关于这一点,我自己也有很多疑问,所以这边不一定是正确的。

数据不对也有可能是因为我自己数据没处理好,欢迎大家对我后面贴出的代码提出修正的建议。

图片

最后是配置GPIO口的结构体,我们直接按照我们的接线去配置就行。

其中MCLK和DOUT我们不需要,因为我们没有MCLK且只接收数据不发送数据。

初始化完通道,接下来就是使能。

图片

只需要把句柄传进去就行。

最后是读取数据。

图片

参数一是句柄。

参数二是存放我们读取到的数据的地址,是传出参数。

参数三是我们要读取的数据长度,单位字节。

参数四是我们实际读取到的数据长度,是传出参数。

参数五是阻塞时间,会阻塞到我们真的读取到了参数三那么多个数据,可以直接给个portMAX_DELAY

然后我们要注意的是,我们要读取的数据长度得是3的倍数,因为INMP441的数据是24bit的,所以我们需要按照顺序把三个byte拼接起来,由于数据类型没有24bit的,所以我们只能使用32bit的数据类型。

而且INMP441传输的是补码,而实际上音频数据是有正负的,所以我们还需要把补码中是负数的拎出来处理一下(因为正数的补码和原码是一样的,所以不用管)。

处理的方式就是判断一下这个24bit的数据的最高位(也就是符号位)是否为1,为1就是负数,负数就要处理。

处理方式就是把有int32_t的高八位置1,这样就可以了,因为芯片存储数据本身用的也是补码,所以我们只要把24bit和32bit之间差的那8个bit补上1就是,如果是正数的话就补0,但是默认就是0,所以正数不用补。

如果上面一段看不懂的小伙伴可以直接用我下面的示例代码,或者去复习一下计算机原理这门课。

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/i2s_std.h"
#include "driver/gpio.h"
#define INMP_SD     1
#define INMP_SCK    2
#define INMP_WS     3
// 每次读取多少帧数据
#define DATA_LEN    64
i2s_chan_handle_t rx_handle;
void I2S_Init(void){
    // 通过辅助宏获取默认的通道配置它可以帮助指定 I2S 角色和端口 ID
    i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER);
    chan_cfg.dma_frame_num = 1024;
    // 分配新的 TX 通道并获取该通道的句柄
    i2s_new_channel(&chan_cfg, NULL, &rx_handle);
    // 初始化或更新声道和时钟配置
    i2s_std_config_t std_cfg = {
        .clk_cfg = {
            .clk_src = I2C_CLK_SRC_DEFAULT, // 默认时钟
            .mclk_multiple = I2S_MCLK_MULTIPLE_384,
            .sample_rate_hz = 44100         // 44.1k采集率
        },
        .slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_24BIT, I2S_SLOT_MODE_MONO),
        .gpio_cfg = {
            .mclk = I2S_GPIO_UNUSED,        // 不需要
            .dout = I2S_GPIO_UNUSED,        // 不需要
            .bclk = INMP_SCK,
            .ws = INMP_WS,
            .din = INMP_SD,
            .invert_flags = {               // 都不需要
                .mclk_inv = false,
                .bclk_inv = false,
                .ws_inv = false,
            },
        },
    };
    std_cfg.slot_cfg.slot_mask = I2S_STD_SLOT_LEFT; // 修改为左声道
    std_cfg.slot_cfg.bit_order_lsb = true;          // 低位先行,这边我不确定,但采集的数据确实受环境声音的改变而改变,高位先行却没有
    /* 初始化通道 */
    i2s_channel_init_std_mode(rx_handle, &std_cfg);
    /* 在读取数据之前,先启动 RX 通道 */
    i2s_channel_enable(rx_handle);
}
void app_main(void){
    I2S_Init();
    uint8_t* read_data_buff = (uint8_t*)malloc(sizeof(uint8_t) * 3 * DATA_LEN);
    size_t len = 0;
    for(;;){
        i2s_channel_read(rx_handle, read_data_buff, DATA_LEN * 3, &len, portMAX_DELAY);
        for(uint16_t i = 0; i < len; i += 3){
            int32_t real_data = (read_data_buff[i] << 16) | (read_data_buff[i + 1] << 8) | (read_data_buff[i + 2]); // 拼接3个byte
            if(real_data & 0x00800000)    real_data |= 0xFF000000;      // 如果是负数,那么由24bit无符号转有符号32bit,需要在前8个bit补上1,也就是FF
            printf("%ld\r\n",real_data);
        }
    }
}

上面代码把采集到的数据直接打印出来了,我们可以使用SerialPlot这个软件来串口绘图。

图片

先配置串口,一般不用动,串口都是这么配置的。

然后把数据类型改为ASCII,默认是Binary的,但是我们使用printf打印的是ASCII类型。

图片

接着修改下面这些参数来调整xy轴来达到自己想要的效果。

图片

下面是我没发出声音的时候的波形。

图片

下面是我唱歌的时候的波形。

图片

估计是数据没有处理好,没有人家那样很漂亮的声波形状。

<think>好的,我现在要处理用户关于INMP441麦克风的技术规格、数据表和应用场景的查询。首先,我需要确认用户的需求是什么。用户提到想了解INMP441的技术规格、数据表和应用场景,可能是在进行硬件选型或者设计音频采集系统时需要这些信息。 首先,我需要回忆INMP441的基本信息。INMP441是英飞凌公司生产的一款MEMS麦克风,属于数字输出型,具有全向指向性。技术规格方面,应该包括灵敏度、信噪比、频率响应、工作电压、接口类型等参数。数据表通常可以在制造商官网找到,但用户可能需要直接获取关键信息。应用场景可能涉及智能手机、智能家居、录音设备等需要高质量音频采集的场合。 接下来,我需要确保回答符合系统级指令。所有数学表达式要用$...$,独立公式用$$...$$,但这个问题可能不需要太多数学内容。需要检查是否所有行内公式都正确使用了$符号。然后,回答要用中文,结构清晰,分点说明。生成相关问题部分要自然,与用户的问题相关。 需要验证技术参数的正确性。例如,工作电压是否为1.8V至3.3V,信噪比是否为64dB,输出接口是否为I2S。这些数据需要准确,否则会误导用户。同时,应用场景部分要具体,比如智能音箱、会议系统等,可能需要参考数据表中的典型应用案例。 用户可能还关心如何正确使用麦克风,比如电路设计中的注意事项,是否需要外部元件,电源噪声的影响等。这些问题可以作为生成的相关问题提出,帮助用户进一步了解。 最后,确保引用了提供的参考内容。用户提供的引用[1]提到了工作电压、电流和声学环境的影响,这些内容需要整合到回答中,特别是在技术规格和注意事项部分。例如,在技术规格里提到工作电压范围,注意事项中强调电气参数和声学环境的重要性,并引用[^1]。 现在,我需要组织这些信息,确保结构清晰,分点明确,语言流畅。同时,生成的相关问题要针对用户可能的后续疑问,比如如何设计电路、与其他麦克风的比较、软件处理需求等。 总结步骤: 1. 分点回答技术规格、数据表、应用场景。 2. 确保每个部分包含关键参数和详细信息。 3. 注意事项部分引用提供的参考内容。 4. 生成相关问题,覆盖设计、比较、软件支持等方面。 5. 检查LaTeX格式是否正确,但可能这里用不上太多数学公式。 6. 保持回答专业但易懂,适合不同层次的读者。</think>### INMP441 麦克风技术规格及信息 #### 一、技术规格 1. **基本参数** - **类型**: 数字输出MEMS麦克风 - **指向性**: 全向 - **灵敏度**: $-26 \pm 1 \, \text{dB FS}$(以$94 \, \text{dB SPL}$为参考) - **信噪比 (SNR)**: $64 \, \text{dB}$ - **频率响应**: $50 \, \text{Hz} \sim 16 \, \text{kHz}$ - **动态范围**: $120 \, \text{dB}$ - **工作电压**: $1.8 \sim 3.3 \, \text{V}$ - **功耗**: $1.4 \, \text{mA}$(典型值) - **接口**: I²S 数字输出,支持PDM格式 2. **电气特性** - 支持宽电压范围,适合低功耗设备。 - 内置模数转换器(ADC),可直接输出数字信号。 #### 二、数据表要点 - **封装尺寸**: $3.76 \times 4.72 \times 1.0 \, \text{mm}$(微型化设计,适用于紧凑空间)。 - **时钟频率**: 支持$1 \sim 4 \, \text{MHz}$(取决于模式)。 - **抗射频干扰 (RFI)**: 通过内部屏蔽设计优化抗干扰性能。 - **温度范围**: $-40^\circ \text{C} \sim +85^\circ \text{C}$(工业级适用性)。 #### 三、应用场景 1. **消费电子**: 智能手机、平板电脑、TWS耳机等音频输入设备。 2. **智能家居**: 语音助手(如智能音箱)、家庭安防系统。 3. **专业录音**: 会议系统、便携式录音笔、高保真音频采集。 4. **物联网 (IoT)**: 低功耗传感器节点中的语音唤醒功能。 #### 四、注意事项 - **电源稳定性**: 需确保工作电压稳定,避免电压波动导致信号失真。 - **声学设计**: 建议搭配声学密封结构,减少环境噪声干扰。 - **PCB布局**: 麦克风应远离高频信号线,降低电磁干扰。 --- ###
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值