ESP32 I2S音頻總線學習筆記(二):I2S讀取INMP441音頻數據

簡介

在這個系列的上一篇文章中,我們介紹了ESP32 I2S音頻總線的相關知識,簡要了解了什么是I2S總線、它的通信格式,以及相關的底層API函數。沒有看過上篇文章的可以點擊文章進行回顧:

ESP32 I2S音頻總線學習筆記(一):初識I2S通信與配置基礎

這篇文章將介紹一個小案例——ESP32驅動INMP441讀取音頻數據,它是關于如何使用I2S讀取數據的一個應用,主要是將ESP32讀取到的音頻數據發送到串口上并實時顯示波形,這個我們可以通過串口繪圖儀來實現。在這之前先來看一下INMP441這個模塊吧

INMP441全向麥克風模塊

在這里插入圖片描述

NMP441是一款高性能、低功耗、數字輸出,帶底部端口的全向MEMS麥克風。該完整的INMP441解決方案由一個MEMS傳感器、信號調節模塊、數字混合濾波器、電源管理和行業標準的24位I2S接口組成。I2S接口允許INMP441直接連接到數字處理器,如DSP和微控制器,無需額外的音頻解碼器。NMP441具有高信噪比,是一種適用于近場應用的理想選擇。

產品特性:

具有高精度24位數據的數字I2S接口
高信噪比為61 dBA
高靈敏度-26 dBFS
從60 Hz到15 kHz的穩定頻率響應
低功耗:低電流消耗1.4 mA
高PSR:-75 dBFS

功能框圖

INMP441模塊使用到的芯片內置ADC,用于將采集到的模擬信號轉換成數字信號,上面還有濾波器和硬件控制、電源相關的引腳。
在這里插入圖片描述

引腳定義

VDD輸入電源,1.8V至3.3V
GND電源地
SCKI2S接口的串行數據時鐘
WS用于I2S接口的串行數據字選擇
SDI2S接口的串行數據輸出
L/R左/右聲道選擇

其中L/R是 左/右聲道選擇。設置為低電平時,麥克風在I2S幀的左聲道輸出信號。設置為高電平時,麥克風在右聲道輸出信號。

安裝I2S驅動

上篇文章我們在介紹I2S底層API函數提到,在使用I2S通信時需要加載I2S驅動,不知道小伙伴們還記不記得。這個加載I2S驅動的函數就是:esp_err_t i2s_driver_install(i2s_port_t i2s_num, const i2s_config_t *i2s_config, int queue_size, void *i2s_queue),里面有4個參數需要配置,在上次都有提到每個參數的意義。 其中比較復雜的是i2s_config這個結構體變量,我們需要對結構體的每個參數進行配置,如下:

typedef struct {i2s_mode_t              mode;                       /*< 設置 I2S 的工作模式 */uint32_t                sample_rate;                /*!< 設置音頻采樣率 */i2s_bits_per_sample_t   bits_per_sample;            /*!< 設置采樣位數 */i2s_channel_fmt_t       channel_format;             /*!< 設置數據通道格式.*/i2s_comm_format_t       communication_format;       /*!< 設置I2C數據傳輸格式 */int                     intr_alloc_flags;           /*!< 設置中斷相關標志位*/int                     dma_buf_count;  dma緩存個數,            int                     dma_buf_len;                
} i2s_driver_config_t;typedef i2s_driver_config_t i2s_config_t;

除了后面幾個整型變量,每個結構體成員其實是一個枚舉類型

I2S工作模式mode

typedef enum {I2S_MODE_MASTER       = (0x1 << 0),       /*!< Master mode*/I2S_MODE_SLAVE        = (0x1 << 1),       /*!< Slave mode*/I2S_MODE_TX           = (0x1 << 2),       /*!< TX mode*/I2S_MODE_RX           = (0x1 << 3),       /*!< RX mode*/
#if SOC_I2S_SUPPORTS_DAC//built-in DAC functions are only supported on I2S0 for ESP32 chip.I2S_MODE_DAC_BUILT_IN = (0x1 << 4),       /*!< Output I2S data to built-in DAC, no matter the data format is 16bit or 32 bit, the DAC module will only take the 8bits from MSB*/
#endif // SOC_I2S_SUPPORTS_DAC
#if SOC_I2S_SUPPORTS_ADCI2S_MODE_ADC_BUILT_IN = (0x1 << 5),       /*!< Input I2S data from built-in ADC, each data can be 12-bit width at most*/
#endif // SOC_I2S_SUPPORTS_ADC// PDM functions are only supported on I2S0 (all chips).I2S_MODE_PDM          = (0x1 << 6),       /*!< I2S PDM mode*/
} i2s_mode_t;

音頻采樣率bits_per_sample

typedef enum {I2S_BITS_PER_SAMPLE_8BIT    = 8,            /*!< data bit-width: 8 */I2S_BITS_PER_SAMPLE_16BIT   = 16,           /*!< data bit-width: 16 */I2S_BITS_PER_SAMPLE_24BIT   = 24,           /*!< data bit-width: 24 */I2S_BITS_PER_SAMPLE_32BIT   = 32,           /*!< data bit-width: 32 */
} i2s_bits_per_sample_t;

通道格式channel_format

typedef enum {I2S_CHANNEL_FMT_RIGHT_LEFT,         /*!< Separated left and right channel */I2S_CHANNEL_FMT_ALL_RIGHT,          /*!< Load right channel data in both two channels */I2S_CHANNEL_FMT_ALL_LEFT,           /*!< Load left channel data in both two channels */I2S_CHANNEL_FMT_ONLY_RIGHT,         /*!< Only load data in right channel (mono mode) */I2S_CHANNEL_FMT_ONLY_LEFT,          /*!< Only load data in left channel (mono mode) */
#if SOC_I2S_SUPPORTS_TDM// Multiple channels are available with TDM featureI2S_CHANNEL_FMT_MULTIPLE,           /*!< More than two channels are used */
#endif
}  i2s_channel_fmt_t;

通信傳輸格式communication_format

typedef enum {I2S_COMM_FORMAT_STAND_I2S        = 0X01, /*!< I2S communication I2S Philips standard, data launch at second BCK*/I2S_COMM_FORMAT_STAND_MSB        = 0X02, /*!< I2S communication MSB alignment standard, data launch at first BCK*/I2S_COMM_FORMAT_STAND_PCM_SHORT  = 0x04, /*!< PCM Short standard, also known as DSP mode. The period of synchronization signal (WS) is 1 bck cycle.*/I2S_COMM_FORMAT_STAND_PCM_LONG   = 0x0C, /*!< PCM Long standard. The period of synchronization signal (WS) is channel_bit*bck cycles.*/I2S_COMM_FORMAT_STAND_MAX,               /*!< standard max*///old definition will be removed in the future.I2S_COMM_FORMAT_I2S       __attribute__((deprecated)) = 0x01, /*!< I2S communication format I2S, correspond to `I2S_COMM_FORMAT_STAND_I2S`*/I2S_COMM_FORMAT_I2S_MSB   __attribute__((deprecated)) = 0x01, /*!< I2S format MSB, (I2S_COMM_FORMAT_I2S |I2S_COMM_FORMAT_I2S_MSB) correspond to `I2S_COMM_FORMAT_STAND_I2S`*/I2S_COMM_FORMAT_I2S_LSB   __attribute__((deprecated)) = 0x02, /*!< I2S format LSB, (I2S_COMM_FORMAT_I2S |I2S_COMM_FORMAT_I2S_LSB) correspond to `I2S_COMM_FORMAT_STAND_MSB`*/I2S_COMM_FORMAT_PCM       __attribute__((deprecated)) = 0x04, /*!< I2S communication format PCM, correspond to `I2S_COMM_FORMAT_STAND_PCM_SHORT`*/I2S_COMM_FORMAT_PCM_SHORT __attribute__((deprecated)) = 0x04, /*!< PCM Short, (I2S_COMM_FORMAT_PCM | I2S_COMM_FORMAT_PCM_SHORT) correspond to `I2S_COMM_FORMAT_STAND_PCM_SHORT`*/I2S_COMM_FORMAT_PCM_LONG  __attribute__((deprecated)) = 0x08, /*!< PCM Long, (I2S_COMM_FORMAT_PCM | I2S_COMM_FORMAT_PCM_LONG) correspond to `I2S_COMM_FORMAT_STAND_PCM_LONG`*/
} i2s_comm_format_t;

知道了每個參數的含義以及知道它可以配置哪些參數,就可以調用i2s_driver_install這個函數了。
這里我們舉一個安裝I2S驅動的例子,就比較容易理解了。同時配置的時候,我們把它放在一個函數里面,起名為i2s_install( )。

 // 安裝I2S驅動void i2s_install(){// 配置I2S接收i2s_config_t i2s_config = {.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX),.sample_rate = SAMPLE_RATE,.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,  .channel_format = I2S_CHANNEL_FMT_ONLY_LEFT,.communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB),.intr_alloc_flags = 0,.dma_buf_count = 16,.dma_buf_len = bufferLen,.use_apll = false      };if (ESP_OK != i2s_driver_install(I2S_PORT, &i2s_config, 0, NULL)) {Serial.println("Install I2S driver failed");return;}  
}

配置I2S引腳

I2S通信最重要的三個信號是位時鐘BCK、字時鐘WS、數據引腳SD,因此我們需要對其引腳進行配置,設置I2S引腳的函數是
esp_err_t i2s_set_pin(i2s_port_t i2s_num, const i2s_pin_config_t *pin),第一個參數傳入I2S端口,填I2S_NUM_0或I2S_NUM_1, 第二個參數是結構體如下:

typedef struct {int mck_io_num;     /*!< MCK in out pin. Note that ESP32 supports setting MCK on GPIO0/GPIO1/GPIO3 only*/int bck_io_num;     /*!< BCK in out pin*/int ws_io_num;      /*!< WS in out pin*/int data_out_num;   /*!< DATA out pin*/int data_in_num;    /*!< DATA in pin*/
} i2s_pin_config_t;

其中mck_io_num; bck_io_num,ws_io_num等都是整型變量,data_out_num如果我們沒有用到就傳入-1,在driver/i2s.h頭文件定義了

#define I2S_PIN_NO_CHANGE (-1) /*!< Use in i2s_pin_config_t for pins which should not be changed */

假設我們要配置的引腳位時鐘BCK、字時鐘WS、數據引腳SD分別是D13, D12, D14,同樣把要配置的參數寫入一個函數i2s_setpin()里面,配置I2S引腳示例如下:

// 配置I2S引腳
#define I2S_MIC_WS 12
#define I2S_MIC_SD 14
#define I2S_MIC_BCK 13void i2s_setpin(){i2s_pin_config_t pin_config = {};pin_config.bck_io_num = I2S_MIC_BCK;pin_config.ws_io_num = I2S_MIC_WS;pin_config.data_out_num = I2S_PIN_NO_CHANGE;pin_config.data_in_num = I2S_MIC_SD;if (ESP_OK != i2s_set_pin(I2S_PORT, &pin_config)) {Serial.println("I2S set pin failed");return;}}

安裝完I2S驅動和配置好I2S引腳后,我們只要在setup()函數里面調用這兩個函數就可以了。

void setup() {Serial.begin(115200);Serial.println("Setup I2S ...");delay(1000);i2s_install();i2s_setpin();delay(500);  
}

讀取I2S數據

上面只是對I2S進行了一下初始化,要通過INMP441讀取i2s數據,我們只需要調用esp_err_t i2s_read(i2s_port_t i2s_num, void *dest, size_t size, size_t *bytes_read, TickType_t ticks_to_wait);這個函數。因為前面在初始化I2S時的量化位數是16位,所以每個采樣點的數據大小為2字節,我們將讀取到的數據放入一個緩存區數組sBuffer,將數組長度bufferLen定義為64,確保每次從I2S接口讀取時能獲得足夠的音頻數據。 如果讀取成功2s_read這個函數會返回一個ESP_OK,成功后就進入數據處理部分。這里我們將讀取到的音頻數據求均值然后可以用串口繪圖儀觀察它的數據波形,代碼如下:

void loop() {size_t bytesIn = 0;esp_err_t result = i2s_read(I2S_PORT, &sBuffer, bufferLen, &bytesIn, portMAX_DELAY);if (result == ESP_OK){int samples_read = bytesIn / 2;if (samples_read > 0) {float mean = 0;for (int i = 0; i < samples_read; ++i) {mean += (sBuffer[i]);}mean /= samples_read;Serial.println(mean);delay(50);}}
}

完整代碼

#include "driver/i2s.h"
#define SAMPLE_RATE (44100)#define I2S_MIC_WS 12
#define I2S_MIC_SD 14
#define I2S_MIC_BCK 13
#define I2S_PORT I2S_NUM_0
#define bufferLen 64int16_t sBuffer[bufferLen];// 安裝I2S驅動void i2s_install(){// 配置I2S接收i2s_config_t i2s_config = {.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX),.sample_rate = SAMPLE_RATE,.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,  .channel_format = I2S_CHANNEL_FMT_ONLY_LEFT,.communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB),.intr_alloc_flags = 0,.dma_buf_count = 16,.dma_buf_len = bufferLen,.use_apll = false      };if (ESP_OK != i2s_driver_install(I2S_PORT, &i2s_config, 0, NULL)) {Serial.println("Install I2S driver failed");return;}  
}// 配置I2S引腳void i2s_setpin(){i2s_pin_config_t pin_config = {};pin_config.bck_io_num = I2S_MIC_BCK;pin_config.ws_io_num = I2S_MIC_WS;pin_config.data_out_num = I2S_PIN_NO_CHANGE;pin_config.data_in_num = I2S_MIC_SD;if (ESP_OK != i2s_set_pin(I2S_PORT, &pin_config)) {Serial.println("I2S set pin failed");return;}}void setup() {Serial.begin(115200);Serial.println("Setup I2S ...");delay(1000);i2s_install();i2s_setpin();i2s_start(I2S_PORT);delay(500);  
}void loop() {size_t bytesIn = 0;esp_err_t result = i2s_read(I2S_PORT, &sBuffer, bufferLen, &bytesIn, portMAX_DELAY);if (result == ESP_OK){int samples_read = bytesIn / 2;if (samples_read > 0) {float mean = 0;for (int i = 0; i < samples_read; ++i) {mean += (sBuffer[i]);}mean /= samples_read;Serial.println(mean);delay(50);}}}

接線圖

寫完代碼后就可以開始接線了,接線圖如下圖所示:供電這里接3.3V,L/R接地。
在這里插入圖片描述

實驗效果

一開始接收到的是外界的聲音,波形是雜亂無章的。后面用嘴吹氣,波形會跟著吹氣變化,不吹氣波形是平緩不變的,后面大概吹了幾次,可以看到波形變化,如下圖:

在這里插入圖片描述

總結

本篇文章介紹了通過ESP32的I2S通信實時讀取INMP441麥克風模塊的音頻數據,并通過串口繪圖儀顯示音頻數據波形。后面我們還會介紹使用INMP441采集音頻并實時播放音頻,感興趣的可以先關注收藏一下!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/67221.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/67221.shtml
英文地址,請注明出處:http://en.pswp.cn/web/67221.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

EasyExcel使用詳解

文章目錄 EasyExcel使用詳解一、引言二、環境準備與基礎配置1、添加依賴2、定義實體類 三、Excel 讀取詳解1、基礎讀取2、自定義監聽器3、多 Sheet 處理 四、Excel 寫入詳解1、基礎寫入2、動態列與復雜表頭3、樣式與模板填充 五、總結 EasyExcel使用詳解 一、引言 EasyExcel 是…

objection的簡單使用

objection環境配置 下載以下版本 objection1.11.0 frida-tools9.2.4 python3.8.8 報錯的話看這里: 在這個網站下載frida-tools14.2.18的離線包Links for frida 然后放入C:\Users\Asus\frida-14.2.18-py3.8-win-amd64.egg文件夾下 執行poetry add frida-tools14.2.18 ob…

危機13小時:追蹤一場GitHub投毒事件

事件概要 自北京時間 2024.12.4 晚間6點起&#xff0c; GitHub 上不斷出現“幽靈倉庫”&#xff0c;倉庫中沒有任何代碼&#xff0c;只有誘導性的病毒文件。當天&#xff0c;他們成為了 GitHub 上 star 增速最快的倉庫。超過 180 個虛假僵尸賬戶正在傳播病毒&#xff0c;等待不…

【Redis】緩存+分布式鎖

目錄 緩存 Redis最主要的使用場景就是作為緩存 緩存的更新策略&#xff1a; 1.定期生成 2.實時生成 面試重點&#xff1a; 緩存預熱&#xff08;Cache preheating&#xff09;&#xff1a; 緩存穿透&#xff08;Cache penetration&#xff09; 緩存雪崩 (Cache avalan…

記錄 | MaxKB創建本地AI智能問答系統

目錄 前言一、重建MaxKBStep1 復制路徑Step2 刪除MaxKBStep3 創建數據存儲文件夾Step4 重建 二、創建知識庫Step1 新建知識庫Step2 下載測試所用的txtStep3 上傳本地文檔Step4 選擇模型補充智譜的API Key如何獲取 Step5 查看是否成功 三、創建應用Step1 新建應用Step2 配置AI助…

C#新語法

目錄 頂級語句&#xff08;C#9.0&#xff09; using 全局using指令&#xff08;C#10.0&#xff09; using資源管理問題 using聲明&#xff08;C#8.0&#xff09; using聲明陷阱 錯誤寫法 正確寫法 文件范圍的命名空間聲明&#xff08;C#10.0&#xff09; 可空引用類型…

基于dlib/face recognition人臉識別推拉流實現

目錄 一.環境搭建 二.推拉流代碼 三.人臉檢測推拉流 一.環境搭建 1.下載RTSP服務器MediaMTX與FFmpeg FFmpeg是一款功能強大的開源多媒體處理工具,而MediaMTX則是一個輕量級的流媒體服務器。兩者結合,可以實現將本地視頻或者實時攝像頭畫面推送到RTSP流,從而實現視頻…

Couchbase UI: Analytics

Couchbase 的 Analytics 頁面是用于執行分析查詢的部分&#xff0c;允許用戶對存儲在 Couchbase 中的數據進行復雜的分析和聚合。Analytics 提供了 SQL-like 查詢語言&#xff08;N1QL for Analytics&#xff09;&#xff0c;使得用戶能夠輕松地執行數據分析任務。以下是關于 C…

梯度下降優化算法-Adam

Adam&#xff08;Adaptive Moment Estimation&#xff09;是一種結合了動量法&#xff08;Momentum&#xff09;和 RMSProp 的自適應學習率優化算法。它通過計算梯度的一階矩&#xff08;均值&#xff09;和二階矩&#xff08;未中心化的方差&#xff09;來調整每個參數的學習率…

論文筆記(六十三)Understanding Diffusion Models: A Unified Perspective(六)(完結)

Understanding Diffusion Models: A Unified Perspective&#xff08;六&#xff09;&#xff08;完結&#xff09; 文章概括指導&#xff08;Guidance&#xff09;分類器指導無分類器引導&#xff08;Classifier-Free Guidance&#xff09; 總結 文章概括 引用&#xff1a; …

【PySide6快速入門】信號與槽的使用

文章目錄 前言什么是信號與槽信號與槽的功能最簡單的信號與槽控件連接信號與信號的連接總結 前言 在 PySide6 中&#xff0c;信號與槽機制是核心概念之一&#xff0c;它是 Qt 庫中事件通信的基礎。通過信號與槽&#xff0c;開發者能夠實現不同組件之間的解耦&#xff0c;從而使…

GOGOGO 枚舉

含義&#xff1a;一種類似于類的一種結構 作用&#xff1a;是Java提供的一個數據類型&#xff0c;可以設置值是固定的 【當某一個數據類型受自身限制的時候&#xff0c;使用枚舉】 語法格式&#xff1a; public enum 枚舉名{…… }有哪些成員&#xff1f; A、對象 public …

AWTK 骨骼動畫控件發布

Spine 是一款廣泛使用的 2D 骨骼動畫工具&#xff0c;專為游戲開發和動態圖形設計設計。它通過基于骨骼的動畫系統&#xff0c;幫助開發者創建流暢、高效的角色動畫。本項目是基于 Spine 實現的 AWTK 骨骼動畫控件。 代碼&#xff1a;https://gitee.com/zlgopen/awtk-widget-s…

[免費]基于Python的Django博客系統【論文+源碼+SQL腳本】

大家好&#xff0c;我是java1234_小鋒老師&#xff0c;看到一個不錯的基于Python的Django博客系統&#xff0c;分享下哈。 項目視頻演示 【免費】基于Python的Django博客系統 Python畢業設計_嗶哩嗶哩_bilibili 項目介紹 隨著互聯網技術的飛速發展&#xff0c;信息的傳播與…

如何將電腦桌面默認的C盤設置到D盤?詳細操作步驟!

將電腦桌面默認的C盤設置到D盤的詳細操作步驟&#xff01; 本博文介紹如何將電腦桌面&#xff08;默認為C盤&#xff09;設置在D盤下。 首先&#xff0c;在D盤建立文件夾Desktop&#xff0c;完整的路徑為D:\Desktop。winR&#xff0c;輸入Regedit命令。&#xff08;或者單擊【…

C++ 寫一個簡單的加減法計算器

************* C topic&#xff1a;結構 ************* Structure is a very intersting issue. I really dont like concepts as it is boring. I would like to cases instead. If I want to learn something, donot hesitate to make shits. Like building a house. Wh…

excel如何查找一個表的數據在另外一個表是否存在

比如“Sheet1”有“張三”、“李四”“王五”三個人的數據&#xff0c;“Sheet2”只有“張三”、“李四”的數據。我們通過修改“Sheet1”的“民族”或者其他空的列&#xff0c;修改為“Sheet2”的某一列。這樣修改后篩選這個修改的列為空的或者為出錯的&#xff0c;就能找到兩…

MySQL 基礎學習(2): INSERT 操作

在這篇文章中&#xff0c;我們將專注于 MySQL 中的 INSERT 操作&#xff0c;深入了解如何高效地向表中插入數據&#xff0c;并探索插入操作中的一些常見錯誤與解決方案。 一、基礎 INSERT 語法 在 MySQL 中&#xff0c;INSERT 操作用于向表中插入新記錄&#xff0c;基本語法如…

CVE-2023-38831 漏洞復現:win10 壓縮包掛馬攻擊剖析

目錄 前言 漏洞介紹 漏洞原理 產生條件 影響范圍 防御措施 復現步驟 環境準備 具體操作 前言 在網絡安全這片沒有硝煙的戰場上&#xff0c;新型漏洞如同隱匿的暗箭&#xff0c;時刻威脅著我們的數字生活。其中&#xff0c;CVE - 2023 - 38831 這個關聯 Win10 壓縮包掛…

論文閱讀(二):理解概率圖模型的兩個要點:關于推理和學習的知識

1.論文鏈接&#xff1a;Essentials to Understand Probabilistic Graphical Models: A Tutorial about Inference and Learning 摘要&#xff1a; 本章的目的是為沒有概率圖形模型背景或沒有深入背景的科學家提供一個高級教程。對于更熟悉這些模型的讀者&#xff0c;本章將作為…