南沙网站建设价格,传媒公司名称,平面设计公司理念,fuzzz的wordpress模板本课程我们讲解Micropython for ESP32 的i2s及其应用#xff0c;比如INMP441音频录制、MAX98357A音频播放等#xff0c;还有SD卡的读写。
一、硬件准备
1、支持micropython的ESP32S3开发板 2、INMP441数字全向麦克风模块 3、MAX98357A音频播放模块 4、SD卡模块 5、面包板及…本课程我们讲解Micropython for ESP32 的i2s及其应用比如INMP441音频录制、MAX98357A音频播放等还有SD卡的读写。
一、硬件准备
1、支持micropython的ESP32S3开发板 2、INMP441数字全向麦克风模块 3、MAX98357A音频播放模块 4、SD卡模块 5、面包板及连接线若干
连接方式
inmp441MAX98357AESP32S3SDIO13WSIO12SCKIO11L/R接地SD接VCCGAIN接地DINIO37BCLKIO38LRCIO39
SD卡模块ESP32S3SCKIO4MOSIIO5MISOIO16CSIO17 二、i2s介绍 一、I2S协议基础
I2SInter-IC Sound是一种同步串行通信协议专为数字音频设备设计支持单向/双向音频数据传输。其物理层包含三条信号线
SCK串行时钟同步数据传输速率WS字选择区分左右声道或定义采样率SD串行数据传输实际音频数据流
二、MicroPython I2S类特性 A. 仅支持主设备操作模式可控制SCK和WS信号的生成适用于连接麦克风、 DAC等从设备 B. 支持ESP32、STM32、RP2等主流微控制器平台通过统一接口简化跨硬件开发
三、核心功能实现 音频输入/输出 录音从麦克风模块获取PCM音频数据播放向DAC或音频解码器发送音频流27。 参数灵活配置 初始化时可设置关键参数 i2s I2S(id, # 硬件实例编号如I2S.NUM0sckPin(11), wsPin(12), sdPin(13), # 引脚映射modeI2S.RX, # 模式RX/TXbits16, # 采样位深formatI2S.MONO, # 声道格式 MONO为单声道STEREO为立体声rate16000, # 采样率ibuf8092) # 输入缓冲区大小:ml-citation{ref4,7 datacitationList}中断与DMA支持 支持异步数据读写通过DMA减少CPU占用率提升实时性 四、典型应用场景 音频播放器 播放WAV/MP3文件需解码库支持。 语音采集系统 连接INMP441等数字麦克风实现环境音录制。 实时语音处理 结合神经网络进行关键词识别或声纹分析
三、MicroPython SD卡介绍
一、SD卡初始化与挂载
硬件接口配置 使用SPI模式连接SD卡需4线CLK/MOSI/MISO/CS典型ESP32配置示例
from sdcard import SDCard
import os, time, gcspi SPI(2,baudrate80000000,polarity0,phase0,sckPin(4),mosiPin(5),misoPin(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(filenameaudiofilename, duration5, sample_rate16000):
# # 硬件诊断print(初始化I2S...)try:i2s I2S(0,sckPin(11), wsPin(12), sdPin(13),modeI2S.RX,bits16,formatI2S.MONO,ratesample_rate,ibuf4096)except Exception as e:print(I2S初始化失败:, e)return# 计算数据量bytes_per_second sample_rate * 2 # 16bit2字节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.pcmaudio_out I2S(1, sckPin(38), wsPin(39), sdPin(37), modeI2S.TX, bits16, formatI2S.MONO, rate16000, ibuf20000)
def play_audio(filename/sd/rec.wav, duration5, sample_rate16000): # 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,baudrate20000000,polarity0,phase0,sckPin(4),mosiPin(5),misoPin(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(filenameaudiofilename, duration5, sample_rate16000):
# # 硬件诊断print(初始化I2S...)try:i2s I2S(0,sckPin(11), wsPin(12), sdPin(13),modeI2S.RX,bits16,formatI2S.MONO,ratesample_rate,ibuf4096)except Exception as e:print(I2S初始化失败:, e)return# 计算数据量bytes_per_second sample_rate * 2 # 16bit2字节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, sckPin(38), wsPin(39), sdPin(37), modeI2S.TX, bits16, formatI2S.MONO, rate16000, ibuf20000)
def play_audio(filename/sd/rec.wav, duration5, sample_rate16000): # 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(duration5)play_audio()except Exception as e:print(异常,e)
# 测试import time
from machine import I2S, Pin
import math# I2S配置
i2s I2S(0,sckPin(22), wsPin(23), sdPin(21),modeI2S.RX,bits16,rate16000,channel_formatI2S.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{ref6 datacitationList}# 计算当前块RMS值sum_sq 0for i in range(0, len(buffer), 2):sample int.from_bytes(buffer[i:i2], little, True)sum_sq (sample / 32768) ** 2 # 16位有符号转浮点:ml-citation{ref6 datacitationList}rms math.sqrt(sum_sq / 512)# 更新最后有声时间戳if rms SILENCE_THRESHOLD:last_sound_time time.time()# 判断静默持续时间if (time.time() - last_sound_time) SILENCE_DURATION:print(检测到持续静默)# 触发后续处理