您现在的位置是:首页 >其他 >ESP32开发路程I2S篇——音频播放,自定义分区网站首页其他
ESP32开发路程I2S篇——音频播放,自定义分区
简介esp32 i2s
自定义分区
官方给出的分区表介绍: esp-idf 编程指南
相关操作:
- 在 VSCODE 的 ESP-IDF 环境中点击底层菜单栏的 menuconfig 图标
- 修改 Serial flasher config 的 Flash size 大小(我的为 4MB)
- 将 Partition Table 选择为 Custom partition table,其余默认,save保存
- 查看->命令面板(Ctrl + Shift + P) 搜索 partition table,选择 Open Partition Table Editor UI
- 通过 add new row 添加分区,并参考下图编辑分区表,编写后 save 保存
音频文件写入自定义分区
音频文件编译为二进制文件
将 wav 形式的音频文件放进 audio 文件夹中
在 spiffsgen.py 所在的文件夹下打开命令行,输入以下命令(示例):python spiffsgen.py 0x200000 audio audio.bin
即可生成 audio.bin
- python spiffsgen.py A B C
- A:系统的空间大小,使用十六进制(预设的 audio 分区为 2M,所以为 0x200000,注意不是 audio 分区的偏移地址)
- B:文件夹名称
- C:生成的bin文件名称
二进制文件烧录到自定义分区
使用 ESP32 FLASH DOWNLOAD TOOL 对 audio.bin 文件进行烧录
按照上面步骤对 audio 分区的设定,将 audio.bin 烧录在 @ 0x110000 ,选择相应 COM 口,点击 START 烧录
ps:如果报错 Permission Denied ,将 ESP32 FLASH DOWNLOAD TOOL 文件夹里的 dl-temp 删掉,重新烧录
音频播放代码
几个需要根据自身情况修改的地方:
- BCK、WS、DATA 引脚根据 ESP32 情况去修改
- esp_vfs_spiffs_conf_t 结构体里的 max_files 的值根据音频文件数量去修改
- fopen(“/audio/what_edit.wav”, “rb”) 根据要播放的文件名称去修改
完整代码如下:
#include <stdio.h>
#include <string.h>
#include "driver/i2s.h"
#include "esp_system.h"
#include "esp_log.h"
#include "esp_spiffs.h"
typedef struct
{
// RIFF Section
char RIFFSectionID[4]; // Letters "RIFF"
uint32_t Size; // Size of entire file less 8
char RiffFormat[4]; // Letters "WAVE"
// Format Section
char FormatSectionID[4]; // letters "fmt"
uint32_t FormatSize; // Size of format section less 8
uint16_t FormatID; // 1=uncompressed PCM
uint16_t NumChannels; // 1=mono,2=stereo
uint32_t SampleRate; // 44100, 16000, 8000 etc.
uint32_t ByteRate; // =SampleRate * Channels * (BitsPerSample/8)
uint16_t BlockAlign; // =Channels * (BitsPerSample/8)
uint16_t BitsPerSample; // 8,16,24 or 32
// Data Section
char DataSectionID[4]; // The letters "data"
uint32_t DataSize; // Size of the data that follows
}WavHeader_Struct;
WavHeader_Struct WavHeader;
bool ValidWavData(WavHeader_Struct* Wav);
void sound_terminate(void);
void app_main()
{
// 初始化I2S引脚
i2s_config_t i2s_config = {
.mode = I2S_MODE_MASTER | I2S_MODE_TX, // 使用主模式并设置为发送数据
.sample_rate = 44100, // 设置采样率为44100Hz
.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, // 设置每个采样点的位数为16位
.channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT, // 只使用右声道
.communication_format = I2S_COMM_FORMAT_STAND_I2S, // I2S通信格式
.dma_buf_count = 8, // 设置DMA缓冲区数量为8
.dma_buf_len = 1024, // 每个DMA缓冲区的长度为1024字节
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, // 分配中断标志
};
i2s_pin_config_t pin_config = {
.bck_io_num = 26, // BCLK引脚号
.ws_io_num = 27, // LRCK引脚号
.data_out_num = GPIO_NUM_25, // DATA引脚号
.data_in_num = -1, // DATA_IN引脚号
};
i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL);
i2s_set_pin(I2S_NUM_0, &pin_config);
// 使用 SPIFFS 进行文件系统的初始化
esp_vfs_spiffs_conf_t conf = {
.base_path = "/audio",
.partition_label = "audio",
.max_files = 5, // 包含的最大文件数
.format_if_mount_failed = true
};
esp_err_t ret = esp_vfs_spiffs_register(&conf);
// 打开文件进行读取
FILE* f = fopen("/audio/what_edit.wav", "rb");
if (f == NULL)
{
ESP_LOGI("AUDIO", "Failed to open file for reading");
}
else
{
char wavBuffer[1024]; // 接收缓冲区
// 读取wav文件信息
fseek(f, 0, SEEK_SET); // 重新将指针指向文件首部
fread(wavBuffer, sizeof(char), 44, f);
memcpy(&WavHeader, wavBuffer, 44);
if(ValidWavData(&WavHeader))
{
i2s_set_sample_rates(I2S_NUM_0, WavHeader.SampleRate);
}
else
{
ESP_LOGI("AUDIO", "Failed to read wav file");
}
uint32_t wavData_size = WavHeader.DataSize; // 保存文件字符数
uint16_t readTimes = 0; // 需要读的次数
size_t BytesWritten;
for (readTimes = 0; readTimes < (wavData_size / 1024); readTimes++)
{
fread(wavBuffer, sizeof(char), 1024, f); // 读文件
i2s_write(I2S_NUM_0, wavBuffer, 1024, &BytesWritten, portMAX_DELAY);
}
memset(wavBuffer, 0, 1024); // 清空,准备读少于1024的字节
fread(wavBuffer, sizeof(char), (wavData_size % 1024), f); // 读文件
i2s_write(I2S_NUM_0, wavBuffer, (wavData_size % 1024) + 20, &BytesWritten, portMAX_DELAY);
printf("readtimes=%d,last read len = %d
", readTimes, (wavData_size % 1024));
printf("sound play over
");
sound_terminate(); //清空缓存
}
// 关闭文件
fclose(f);
// 卸载 SPIFFS 文件系统
esp_vfs_spiffs_unregister(NULL);
// 卸载 I2S 驱动
i2s_driver_uninstall(I2S_NUM_0);
}
bool ValidWavData(WavHeader_Struct* Wav)
{
if(memcmp(Wav->RIFFSectionID,"RIFF",4)!=0)
{
printf("Invlaid data - Not RIFF format");
return false;
}
if(memcmp(Wav->RiffFormat,"WAVE",4)!=0)
{
printf("Invlaid data - Not Wave file");
return false;
}
if(memcmp(Wav->FormatSectionID,"fmt",3)!=0)
{
printf("Invlaid data - No format section found");
return false;
}
if(memcmp(Wav->DataSectionID,"data",4)!=0)
{
printf("Invlaid data - data section not found");
return false;
}
if(Wav->FormatID!=1)
{
printf("Invlaid data - format Id must be 1");
return false;
}
if(Wav->FormatSize!=16)
{
printf("Invlaid data - format section size must be 16.");
return false;
}
if((Wav->NumChannels!=1)&(Wav->NumChannels!=2))
{
printf("Invlaid data - only mono or stereo permitted.");
return false;
}
if(Wav->SampleRate>48000)
{
printf("Invlaid data - Sample rate cannot be greater than 48000");
return false;
}
if((Wav->BitsPerSample!=8)& (Wav->BitsPerSample!=16))
{
printf("Invlaid data - Only 8 or 16 bits per sample permitted.");
return false;
}
return true;
}
void sound_terminate(void)
{
vTaskDelay(pdMS_TO_TICKS(200)); //延时200ms
i2s_zero_dma_buffer(I2S_NUM_0); // Clean the DMA buffer
i2s_stop(I2S_NUM_0);
i2s_start(I2S_NUM_0);//增加测试
}
风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。