MicroPython 開發ESP32應用教程 之 I2S、INMP441音頻錄制、MAX98357A音頻播放、SD卡讀寫

本課程我們講解Micropython for ESP32 的i2s及其應用,比如INMP441音頻錄制、MAX98357A音頻播放等,還有SD卡的讀寫。

一、硬件準備

1、支持micropython的ESP32S3開發板
2、INMP441數字全向麥克風模塊
3、MAX98357A音頻播放模塊
4、SD卡模塊
5、面包板及連接線若干

連接方式:
? ? ? ? ? ? ? ? ?

inmp441MAX98357AESP32S3
SDIO13
WSIO12
SCKIO11
L/R接地
SD接VCC
GAIN接地
DINIO37
BCLKIO38
LRCIO39
SD卡模塊ESP32S3
SCKIO4
MOSIIO5
MISOIO16
CSIO17

? ? ? ?

二、i2s介紹?

一)、I2S協議基礎

I2S(Inter-IC Sound)是一種同步串行通信協議,專為數字音頻設備設計,支持單向/雙向音頻數據傳輸。其物理層包含三條信號線:

  • ?SCK?(串行時鐘):同步數據傳輸速率
  • ?WS?(字選擇):區分左右聲道或定義采樣率
  • ?SD?(串行數據):傳輸實際音頻數據流?
二)、MicroPython I2S類特性

? ? ? ? A.? 僅支持主設備操作模式,可控制SCK和WS信號的生成,適用于連接麥克風、
?????????????DAC等從設備?

? ? ? ? B.?支持ESP32、STM32、RP2等主流微控制器平臺,通過統一接口簡化跨硬件開發?

三)、核心功能實現
  1. ?音頻輸入/輸出?

    • ?錄音?:從麥克風模塊獲取PCM音頻數據
    • ?播放?:向DAC或音頻解碼器發送音頻流?27。
  2. ?參數靈活配置?
    初始化時可設置關鍵參數:

    i2s = I2S(id,  # 硬件實例編號(如I2S.NUM0)sck=Pin(11), ws=Pin(12), sd=Pin(13),  # 引腳映射mode=I2S.RX,  # 模式(RX/TX)bits=16,      # 采樣位深format=I2S.MONO,  # 聲道格式 MONO為單聲道,STEREO為立體聲rate=16000,   # 采樣率ibuf=8092)   # 輸入緩沖區大小?:ml-citation{ref="4,7" data="citationList"}
    

  3. ?中斷與DMA支持?
    支持異步數據讀寫,通過DMA減少CPU占用率,提升實時性?

?

?四)、典型應用場景
  1. ?音頻播放器?
    播放WAV/MP3文件(需解碼庫支持)?。

  2. ?語音采集系統?
    連接INMP441等數字麥克風實現環境音錄制?。

  3. ?實時語音處理?
    結合神經網絡進行關鍵詞識別或聲紋分析?

三、MicroPython SD卡介紹?

一)、SD卡初始化與掛載

硬件接口配置?
使用SPI模式連接SD卡(需4線:CLK/MOSI/MISO/CS),典型ESP32配置示例:

from sdcard import SDCard
import os, time, gcspi = SPI(2,baudrate=80000000,polarity=0,phase=0,sck=Pin(4),mosi=Pin(5),miso=Pin(16))
sd = SDCard(spi,Pin(17,Pin.OUT))
二)、文件操作API

????????基礎文件讀寫?
????????使用標準文件操作接口:

?

def test_sd():os.mount(sd,'/sd')# 重新查詢系統文件目錄print('掛載SD后的系統目錄:{}'.format(os.listdir()))with open("/sd/test.txt", "w") as f:f.write(str("Hello MicroPython!"))# 從sd卡目錄下讀取hello.txt文件內容with open("/sd/test.txt", "r") as f:# 打印讀取的內容data = f.read()print (data)

四、inmp4411錄制音頻

通過前面的講解,這一小節的內容需要掌握的知識點我們都已經掌握,直接上代碼:

audiofilename = '/sd/rec.pcm'
def record_audio(filename=audiofilename, duration=5, sample_rate=16000):
#     # 硬件診斷print("初始化I2S...")try:i2s = I2S(0,sck=Pin(11), ws=Pin(12), sd=Pin(13),mode=I2S.RX,bits=16,format=I2S.MONO,rate=sample_rate,ibuf=4096)except Exception as e:print("I2S初始化失敗:", e)return# 計算數據量bytes_per_second = sample_rate * 2  # 16bit=2字節total_bytes = bytes_per_second * duration
#    header = createWavHeader(sample_rate, 16, 1, total_bytes)# 錄音循環try:with open(audiofilename, 'wb') as f:
#            f.write(header)start_time = time.ticks_ms()bytes_written = 0buffer = bytearray(2048)  # 小緩沖區減少內存壓力while bytes_written < total_bytes:read = i2s.readinto(buffer)if read == 0:print("警告:未讀取到數據")continuef.write(buffer[:read])bytes_written += readgc.collect()# 實時進度elapsed = time.ticks_diff(time.ticks_ms(), start_time) / 1000print(f"進度: {bytes_written/total_bytes*100:.1f}%, 時間: {elapsed:.1f}s")except OSError as e:print("文件寫入錯誤:", e)finally:i2s.deinit()
#        print("錄音結束,文件大小:", os.stat(audiofilename)[6], "字節")print("錄音結束,文件大小:", bytes_written, "字節")

但這里需要說明一下的是,我們剛開始開發的時候,錄制的音頻文件中的數據全是0,也就是說沒有聲音,噪音都沒有,檢查連接線、換IO口等等,各種折騰,但問題依然存在,后來因為出了其它的錯誤,就暫停了,具體可以參考:MicroPython 開發ESP32應用教程 之 WIFI、BLE共用常見問題處理及中斷處理函數注意事項

上文中提到的問題處理完后,我們繼續折騰音頻錄制及播放的功能,奇怪的事情發生了,連接好各功能模塊后,測試,居然好了,懷疑是上文中提到的電源的問題,但把外接電源移除,測試沒有問題。

也就是說,到現在,我們還是不知道之前為什么有問題?現在為什么好了?只能懷疑電源不穩?

五、MAX98357A音頻播放

這個也沒什么好講,直接上代碼吧

audiofilename = '/sd/rec.pcm'audio_out = I2S(1, sck=Pin(38), ws=Pin(39), sd=Pin(37), mode=I2S.TX, bits=16, format=I2S.MONO, rate=16000, ibuf=20000)
def play_audio(filename='/sd/rec.wav', duration=5, sample_rate=16000):        #    audio_out.volume(80)with open(audiofilename,'rb') as f:# 跳過文件的開頭的44個字節,直到數據段的第1個字節
#        pos = f.seek(44) # 用于減少while循環中堆分配的內存視圖wav_samples = bytearray(1024)wav_samples_mv = memoryview(wav_samples)print("開始播放音頻...")#并將其寫入I2S DACwhile True:try:num_read = f.readinto(wav_samples_mv)# WAV文件結束if num_read == 0: break# 直到所有樣本都寫入I2S外圍設備num_written = 0while num_written < num_read:num_written += audio_out.write(wav_samples_mv[num_written:num_read])except Exception as ret:print("產生異常...", ret)

六、完整代碼
?

該代碼簡單修改可保存為WAV格式文件,可以用我們常見的音頻播放軟件播放。

from machine import I2S, Pin,SPI
from sdcard import SDCard
import os, time, gcspi = SPI(2,baudrate=20000000,polarity=0,phase=0,sck=Pin(4),mosi=Pin(5),miso=Pin(16))
sd = SDCard(spi,Pin(17,Pin.OUT))audiofilename = '/sd/rec.pcm'
def createWavHeader(sampleRate, bitsPerSample, num_channels, datasize):    riff_size = datasize + 36 - 8  # 修正RIFF塊大小header = bytes("RIFF", 'ascii')header += riff_size.to_bytes(4, 'little')header += bytes("WAVE", 'ascii')header += bytes("fmt ", 'ascii')header += (16).to_bytes(4, 'little')          # fmt塊大小header += (1).to_bytes(2, 'little')            # PCM格式header += num_channels.to_bytes(2, 'little')   # 聲道數header += sampleRate.to_bytes(4, 'little')     # 采樣率header += (sampleRate * num_channels * bitsPerSample // 8).to_bytes(4, 'little')  # 字節率header += (num_channels * bitsPerSample // 8).to_bytes(2, 'little')  # 塊對齊header += bitsPerSample.to_bytes(2, 'little')  # 位深header += bytes("data", 'ascii')header += datasize.to_bytes(4, 'little')       # 數據塊大小return headerdef record_audio(filename=audiofilename, duration=5, sample_rate=16000):
#     # 硬件診斷print("初始化I2S...")try:i2s = I2S(0,sck=Pin(11), ws=Pin(12), sd=Pin(13),mode=I2S.RX,bits=16,format=I2S.MONO,rate=sample_rate,ibuf=4096)except Exception as e:print("I2S初始化失敗:", e)return# 計算數據量bytes_per_second = sample_rate * 2  # 16bit=2字節total_bytes = bytes_per_second * duration
#    header = createWavHeader(sample_rate, 16, 1, total_bytes)# 錄音循環try:with open(audiofilename, 'wb') as f:
#            f.write(header)start_time = time.ticks_ms()bytes_written = 0buffer = bytearray(1024)  # 小緩沖區減少內存壓力while bytes_written < total_bytes:read = i2s.readinto(buffer)if read == 0:print("警告:未讀取到數據")continuef.write(buffer[:read])bytes_written += readgc.collect()# 實時進度elapsed = time.ticks_diff(time.ticks_ms(), start_time) / 1000print(f"進度: {bytes_written/total_bytes*100:.1f}%, 時間: {elapsed:.1f}s")except OSError as e:print("文件寫入錯誤:", e)finally:i2s.deinit()
#        print("錄音結束,文件大小:", os.stat(audiofilename)[6], "字節")print("錄音結束,文件大小:", bytes_written, "字節")audio_out = I2S(1, sck=Pin(38), ws=Pin(39), sd=Pin(37), mode=I2S.TX, bits=16, format=I2S.MONO, rate=16000, ibuf=20000)
def play_audio(filename='/sd/rec.wav', duration=5, sample_rate=16000):        #    audio_out.volume(80)with open(audiofilename,'rb') as f:# 跳過文件的開頭的44個字節,直到數據段的第1個字節
#        pos = f.seek(44) # 用于減少while循環中堆分配的內存視圖wav_samples = bytearray(1024)wav_samples_mv = memoryview(wav_samples)print("開始播放音頻...")#并將其寫入I2S DACwhile True:try:num_read = f.readinto(wav_samples_mv)# WAV文件結束if num_read == 0: break# 直到所有樣本都寫入I2S外圍設備num_written = 0while num_written < num_read:num_written += audio_out.write(wav_samples_mv[num_written:num_read])except Exception as ret:print("產生異常...", ret)if __name__ == "__main__":try:os.mount(sd,'/sd')   record_audio(duration=5)play_audio()except Exception as e:print("異常:",e)
# 測試'''import time
from machine import I2S, Pin
import math# I2S配置
i2s = I2S(0,sck=Pin(22), ws=Pin(23), sd=Pin(21),mode=I2S.RX,bits=16,rate=16000,channel_format=I2S.ONLY_LEFT)# 參數配置
SILENCE_THRESHOLD = 0.02  # 需根據環境噪聲校準
CHECK_INTERVAL = 0.1      # 檢測間隔(秒)
SILENCE_DURATION = 1.0    # 目標靜默時長buffer = bytearray(1024)  # 512個16位樣本
last_sound_time = time.time()while True:i2s.readinto(buffer)  # 讀取I2S數據?:ml-citation{ref="6" data="citationList"}# 計算當前塊RMS值sum_sq = 0for i in range(0, len(buffer), 2):sample = int.from_bytes(buffer[i:i+2], 'little', True)sum_sq += (sample / 32768) ** 2  # 16位有符號轉浮點?:ml-citation{ref="6" data="citationList"}rms = math.sqrt(sum_sq / 512)# 更新最后有聲時間戳if rms > SILENCE_THRESHOLD:last_sound_time = time.time()# 判斷靜默持續時間if (time.time() - last_sound_time) >= SILENCE_DURATION:print("檢測到持續靜默")# 觸發后續處理
'''

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

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

相關文章

UE5 物理模擬 與 觸發檢測

文章目錄 碰撞條件開啟模擬關閉模擬 多層級的MeshUE的BUG 觸發觸發條件 碰撞 條件 1必須有網格體組件 2網格體組件必須有網格&#xff0c;沒有網格雖然可以開啟物理模擬&#xff0c;但是不會有任何效果 注意開啟的模擬的網格體組件會計算自己和所有子網格的mesh范圍 3只有網格…

微信小程序 - swiper輪播圖

官方文檔&#xff1a;https://developers.weixin.qq.com/miniprogram/dev/component/swiper.html <swiper indicator-color"ivory" indicator-active-color"#d43c33" indicator-dots autoplay><swiper-item><image src"/images/banner…

深入探究C#官方MCP:開啟AI集成新時代

一、引言 在當今數字化時代&#xff0c;.NET 開發領域不斷演進&#xff0c;而 C# 官方 MCP&#xff08;Model Context Protocol&#xff0c;模型上下文協議&#xff09;的出現&#xff0c;無疑為開發者們帶來了全新的機遇與挑戰。隨著人工智能技術的迅猛發展&#xff0c;將 AI…

二分查找法

使用二分查找法的前提&#xff1a;&#xff08;1&#xff09;數組為有序數組. &#xff08;2&#xff09;數組中無重復元素. 二分的兩種寫法&#xff1a; 方法一&#xff1a;[left&#xff0c;right] class Solution { public:int search(vector<int>& nums, int …

HarmonyOS:頁面滾動時標題懸浮、背景漸變

一、需求場景 進入到app首頁或者分頁列表首頁時&#xff0c;隨著頁面滾動&#xff0c;分類tab要求固定懸浮在頂部。進入到app首頁、者分頁列表首頁、商品詳情頁時&#xff0c;頁面滾動時&#xff0c;頂部導航欄&#xff08;菜單、標題&#xff09;背景漸變。 二、相關技術知識點…

鯤鵬+昇騰部署集群管理軟件GPUStack,兩臺服務器搭建雙節點集群【實戰詳細踩坑篇】

前期說明 配置&#xff1a;2臺鯤鵬32C2 2Atlas300I duo&#xff0c;之前看網上文檔&#xff0c;目前GPUstack只支持910B芯片&#xff0c;想嘗試一下能不能310P也部署試試&#xff0c;畢竟華為的集群軟件要收費。 系統&#xff1a;openEuler22.03-LTS 驅動&#xff1a;24.1.rc…

React中 點擊事件寫法 的注意(this、箭頭函數)

目錄 ?1、錯誤寫法?&#xff1a;onClick{this.acceptAlls()} ?2、正確寫法?&#xff1a;onClick{this.acceptAlls}&#xff08;不帶括號&#xff09; 總結 方案1&#xff1a;構造函數綁定 方案2&#xff1a;箭頭函數包裝方法&#xff08;更簡潔&#xff09; 方案3&am…

【路由交換方向IE認證】BGP選路原則之Weight屬性

文章目錄 一、路由器BGP路由的處理過程控制平面和轉發平面選路工具 二、BGP的選路順序選路的前提選路順序 三、Wight屬性選路原則規則9與規則11的潛移默化使用Weight值進行選路直接更改Weight值進行選路配合使用route-map進行選路 四、BGP鄰居建立配置 一、路由器BGP路由的處理…

Missashe考研日記-day20

Missashe考研日記-day20 1 高數 學習時間&#xff1a;2h30min學習內容&#xff1a; 今天當然是刷題啦&#xff0c;做不等式的證明板塊的真題&#xff0c;證明題懂的都懂&#xff0c;難起來是真的一點思路都沒有&#xff0c;這個板塊還沒做完&#xff0c;做完再總結題型。 2…

了解JVM

一.JVM概述 1.JVM的作用 ?把字節碼編譯為機器碼去執行,負責把字節碼裝載到虛擬機中 ?現在的 JVM 不僅可以執行 java 字節碼文件,還可以執行其他語言編譯后的字節碼文件,是一個跨語言平臺 2.JVM的組成部分 類加載器&#xff08;ClassLoader&#xff09;運行時數據區&#x…

LeetCode LCR157 套餐內商品的排列順序

生成字符串的全部排列&#xff08;去重&#xff09;&#xff1a;從問題到解決方案的完整解析 問題背景 在編程和算法設計中&#xff0c;生成字符串的所有排列是一個經典問題。它不僅出現在算法競賽中&#xff0c;也在實際開發中有著廣泛的應用&#xff0c;比如生成所有可能的…

pgsql:關聯查詢union(并集)、except(差集)、intersect(交集)

pgsql:關聯查詢union(并集)、except(差集)、intersect(交集)_pgsql except-CSDN博客

微信小程序中使用ECharts 并且動態設置數據

項目下載地址 GitHub 地址 https://github.com/ecomfe/echarts-for-weixin 將當前文件夾里的內容拷貝到項目中 目錄&#xff1a; json: {"usingComponents": {"ec-canvas": "../components/ec-canvas/ec-canvas"} }wxml&#xff1a; <ec…

RV1126 人臉識別門禁系統解決方案

1. 方案簡介 本方案為類人臉門禁機的產品級解決方案,已為用戶構建一個帶調度框架的UI應用工程;準備好我司的easyeai-api鏈接調用;準備好UI的開發環境。具備低模塊耦合度的特點。其目的在于方便用戶快速拓展自定義的業務功能模塊,以及快速更換UI皮膚。 2. 快速上手 2.1 開…

深度學習ResNet模型提取影響特征

大家好&#xff0c;我是帶我去滑雪&#xff01; 影像組學作為近年來醫學影像分析領域的重要研究方向&#xff0c;致力于通過從醫學圖像中高通量提取大量定量特征&#xff0c;以輔助疾病診斷、分型、預后評估及治療反應預測。這些影像特征涵蓋了形狀、紋理、灰度統計及波形變換等…

DeepSeek 接入 Word 完整教程

一、前期準備 1.1 注冊并獲取 API 密鑰 訪問 DeepSeek 平臺&#xff1a; 打開瀏覽器&#xff0c;訪問 DeepSeek 官方網站&#xff08;或您使用的相應平臺&#xff09;。注冊并登錄您的賬戶。 創建 API 密鑰&#xff1a; 在用戶控制面板中&#xff0c;找到“API Keys”或“API…

驅動開發硬核特訓 · Day 7:深入掌握 Linux 驅動資源管理機制(Resource Management)

&#x1f50d; B站相應的視屏教程&#xff1a; &#x1f4cc; 內核&#xff1a;博文視頻 - 總線驅動模型實戰全解析 —— 以 PCA9450 PMIC 為例 敬請關注&#xff0c;記得標為原始粉絲。 &#x1f6a9; 在 Linux 驅動開發中&#xff0c;資源管理機制決定了驅動的穩定性與可靠性…

什么是TensorFlow?

TensorFlow 是由 Google Brain 團隊開發的開源機器學習框架&#xff0c;被廣泛應用于深度學習和人工智能領域。它的基本概念包括&#xff1a; 1. 張量&#xff08;Tensor&#xff09;&#xff1a;在 TensorFlow 中&#xff0c;數據以張量的形式進行處理。張量是多維數組的泛化…

【ChCore Lab 01】Bomb Lab 拆炸彈實驗(ARM匯編逆向工程)

文章目錄 1. 前言2. 實驗代碼版本問題3. 關于使用問題4. 宏觀分析5. read_line 函數介紹6. phase_0 函數6.1. read_int 函數6.2. 回到 phase_0 函數繼續分析6.3. 驗證結果 7. phase_1 函數7.2. 驗證結果 8. phase_2 函數8.1. read_8_numbers 函數8.2. 回到 phase_2 函數繼續分析…

《Vue Router實戰教程》20.路由懶加載

歡迎觀看《Vue Router 實戰&#xff08;第4版&#xff09;》視頻課程 路由懶加載 當打包構建應用時&#xff0c;JavaScript 包會變得非常大&#xff0c;影響頁面加載。如果我們能把不同路由對應的組件分割成不同的代碼塊&#xff0c;然后當路由被訪問的時候才加載對應組件&am…