详细介绍:spiffs分区文件系统在esp idf的创建
我用的是vscdoe,为了测试一个ns4168的音频播放,我使用了spiffs分区来存储wav文件。
创建分区开始。
首先要求在配置文件中打开spiffs分区功能。

这个要拉倒最下面才看到
然后就是有时候如果配置里看不到,
REQUIRES driver spiffs # 关键:添加 spiffs 依赖
这个是加载main目录里的cmake配置文件

如果配置文件没有选项可以通过CMakeLists设置
再下来就是把分区表设定为自定义的

分区表大家都懂,在根目录下面

内容我给个例子
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x6000,
phy_init, data, phy, 0xf000, 0x1000,
factory, app, factory, 0x10000, 1M,
spiffs, data, spiffs, , 3M ,
也可以指定分区地址哈
# ESP-IDF Partition Table
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x4000,
otadata, data, ota, 0xd000, 0x2000,
phy_init, data, phy, 0xf000, 0x1000,
model, data, spiffs, 0x10000, 0xF0000,
ota_0, app, ota_0, 0x100000, 6M,
ota_1, app, ota_1, 0x700000, 6M,
spiffs, data, spiffs, 0xD00000, 1M
类似于这种。都行。
然后最关键的一步,要在根目录下面的CMakeLists.txt添加自动烧录的配置。放在project项目后面。
# 配置 SPIFFS 自动烧录(关键代码)
spiffs_create_partition_image(spiffs data FLASH_IN_PROJECT)

这个时候在根目录下创建data文件夹,把你要的内容放进去就行了。

然后,清空项目,烧录,写个测试代码看看
// -------------------------- SPIFFS初始化 --------------------------
esp_err_t spiffs_init(void) {
ESP_LOGI(TAG, "Initializing SPIFFS");
esp_vfs_spiffs_conf_t conf = {
.base_path = "/spiffs",
.partition_label = NULL,
.max_files = 5,
.format_if_mount_failed = true
};
// 使用所有默认参数挂载SPIFFS
esp_err_t ret = esp_vfs_spiffs_register(&conf);
if (ret != ESP_OK) {
if (ret == ESP_FAIL) {
ESP_LOGE(TAG, "Failed to mount or format filesystem");
} else if (ret == ESP_ERR_NOT_FOUND) {
ESP_LOGE(TAG, "Failed to find SPIFFS partition");
} else {
ESP_LOGE(TAG, "Failed to initialize SPIFFS (%s)", esp_err_to_name(ret));
}
return ret;
}
size_t total = 0, used = 0;
ret = esp_spiffs_info(NULL, &total, &used);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to get SPIFFS partition info (%s)", esp_err_to_name(ret));
esp_vfs_spiffs_unregister(NULL);
return ret;
}
ESP_LOGI(TAG, "SPIFFS initialized, total: %d, used: %d", total, used);
return ESP_OK;
}
// 在代码中添加列出文件的测试函数
void AudioCodec::list_spiffs_files() {
DIR *dir = opendir("/spiffs");
if (dir == NULL) {
ESP_LOGE(TAG, "Failed to open directory");
return;
}
struct dirent *ent;
while ((ent = readdir(dir)) != NULL) {
ESP_LOGI(TAG, "File: %s", ent->d_name);
}
closedir(dir);
}
如果有内容,说明ok了。
调用就直接调用
// 1. 打开WAV文件
FILE *wav_fp = fopen(wav_path, "rb");
if (!wav_fp) {
ESP_LOGE(TAG, "无法打开文件: %s", wav_path);
return ESP_FAIL;
}
// 2. 获取文件总大小
fseek(wav_fp, 0, SEEK_END);
size_t total_size = ftell(wav_fp);
rewind(wav_fp);
------------------------------------------------------------
我贴出来整个音频测试项目文件。
#include
#include
#include
#include
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/i2s_std.h"
#include "driver/gpio.h"
#include "esp_err.h"
#include "esp_log.h"
#include "sdkconfig.h"
#include "esp_vfs.h"
#include "esp_spiffs.h" // 添加SPIFFS支持
// -------------------------- 配置参数(根据硬件修改) --------------------------
#define TAG "WAV_PLAYER"
#define I2S_BCLK GPIO_NUM_15 // I2S时钟线引脚
#define I2S_WS GPIO_NUM_16 // I2S声道选择线(LRCK)引脚
#define I2S_DOUT GPIO_NUM_7 // I2S数据输出引脚
#define PLAY_BUFFER_SIZE 2048 // 播放缓冲区大小(2KB,可按需调整)
#define WAV_SAMPLE_RATE 16000 // 目标WAV采样率(16kHz,需与播放文件匹配)
#define WAV_BIT_DEPTH 16 // 目标WAV位深(16bit,需与播放文件匹配)
// 全局I2S播放通道句柄(供初始化和播放任务共用)
static i2s_chan_handle_t tx_chan = NULL;
// -------------------------- I2S初始化(仅播放配置) --------------------------
/**
* @brief 初始化I2S硬件(适配16kHz/16bit WAV播放)
* @return ESP_OK: 成功;其他: 失败
*/
esp_err_t ns4168_init(void) {
// 1. 配置I2S播放通道(主模式)
i2s_chan_config_t tx_chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER);
esp_err_t err = i2s_new_channel(&tx_chan_cfg, &tx_chan, NULL);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to create I2S TX channel: %s", esp_err_to_name(err));
return err;
}
// 2. 配置I2S标准模式(适配16kHz/16bit/立体声,若WAV是单声道需修改SLOT_MODE)
i2s_std_config_t std_cfg = {
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(WAV_SAMPLE_RATE), // 采样率匹配WAV文件
.slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(
I2S_DATA_BIT_WIDTH_16BIT, // 位深匹配16bit
I2S_SLOT_MODE_MONO // 若播放单声道WAV,改为I2S_SLOT_MODE_MONO
),
.gpio_cfg = {
.mclk = I2S_GPIO_UNUSED, // 多数音频放大器无需MCLK,按需启用
.bclk = I2S_BCLK,
.ws = I2S_WS,
.dout = I2S_DOUT,
.din = I2S_GPIO_UNUSED, // 播放无需输入,禁用DIN引脚
.invert_flags = {
.mclk_inv = false,
.bclk_inv = false,
.ws_inv = false,
},
},
};
// 3. 初始化I2S模式并启用通道
err = i2s_channel_init_std_mode(tx_chan, &std_cfg);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to init I2S std mode: %s", esp_err_to_name(err));
return err;
}
ESP_LOGI(TAG, "I2S init success (16kHz/16bit)");
return ESP_OK;
}
// -------------------------- WAV播放任务 --------------------------
/**
* @brief 读取WAV文件并通过I2S播放
* @param args: WAV文件路径(SPIFFS中的路径)
*/
static void i2s_play_task(void *args) {
const char *wav_path = (const char *)args;
if (wav_path == NULL || tx_chan == NULL) {
ESP_LOGE(TAG, "Invalid play params (path=%p, tx_chan=%p)", wav_path, tx_chan);
vTaskDelete(NULL);
return;
}
// 1. 分配播放缓冲区(栈外分配,避免栈溢出)
uint8_t *play_buf = (uint8_t *)malloc(PLAY_BUFFER_SIZE);
if (play_buf == NULL) {
ESP_LOGE(TAG, "Failed to malloc play buffer (%d bytes)", PLAY_BUFFER_SIZE);
vTaskDelete(NULL);
return;
}
memset(play_buf, 0, PLAY_BUFFER_SIZE);
// 2. 打开WAV文件(从SPIFFS读取)
FILE *wav_fp = fopen(wav_path, "rb");
if (wav_fp == NULL) {
ESP_LOGE(TAG, "Failed to open WAV file: %s", wav_path);
free(play_buf);
vTaskDelete(NULL);
return;
}
ESP_LOGI(TAG, "Open WAV file success: %s", wav_path);
// 3. 跳过WAV文件头(标准WAV头为44字节,若文件有扩展头需调整)
fseek(wav_fp, 44, SEEK_SET);
// 4. 启用I2S播放通道并开始播放
esp_err_t err = i2s_channel_enable(tx_chan);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to enable I2S channel: %s", esp_err_to_name(err));
fclose(wav_fp);
free(play_buf);
vTaskDelete(NULL);
return;
}
size_t read_bytes = 0; // 从文件读取的字节数
size_t written_bytes = 0;// I2S实际写入的字节数
ESP_LOGI(TAG, "Start playing WAV...");
// 循环读取WAV数据并播放
while (1) {
// 从文件读取数据到缓冲区
read_bytes = fread(play_buf, 1, PLAY_BUFFER_SIZE, wav_fp);
if (read_bytes == 0) {
ESP_LOGI(TAG, "End of WAV file (read all data)");
break;
}
// 将缓冲区数据通过I2S发送(阻塞等待发送完成)
err = i2s_channel_write(tx_chan, play_buf, read_bytes, &written_bytes, portMAX_DELAY);
if (err != ESP_OK) {
ESP_LOGE(TAG, "I2S write failed: %s", esp_err_to_name(err));
break;
}
ESP_LOGD(TAG, "Play progress: read=%d bytes, written=%d bytes", (int)read_bytes, (int)written_bytes);
// 若读取的字节数小于缓冲区大小,说明文件已读完
if (read_bytes < PLAY_BUFFER_SIZE) {
ESP_LOGI(TAG, "Partial buffer read, play complete");
break;
}
}
// 5. 播放完成,清理资源
i2s_channel_disable(tx_chan); // 禁用I2S通道
fclose(wav_fp); // 关闭WAV文件
free(play_buf); // 释放缓冲区
ESP_LOGI(TAG, "WAV play task finished");
vTaskDelete(NULL);
}
// -------------------------- SPIFFS初始化 --------------------------
esp_err_t spiffs_init(void) {
ESP_LOGI(TAG, "Initializing SPIFFS");
esp_vfs_spiffs_conf_t conf = {
.base_path = "/spiffs",
.partition_label = NULL,
.max_files = 5,
.format_if_mount_failed = true
};
// 使用所有默认参数挂载SPIFFS
esp_err_t ret = esp_vfs_spiffs_register(&conf);
if (ret != ESP_OK) {
if (ret == ESP_FAIL) {
ESP_LOGE(TAG, "Failed to mount or format filesystem");
} else if (ret == ESP_ERR_NOT_FOUND) {
ESP_LOGE(TAG, "Failed to find SPIFFS partition");
} else {
ESP_LOGE(TAG, "Failed to initialize SPIFFS (%s)", esp_err_to_name(ret));
}
return ret;
}
size_t total = 0, used = 0;
ret = esp_spiffs_info(NULL, &total, &used);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to get SPIFFS partition info (%s)", esp_err_to_name(ret));
esp_vfs_spiffs_unregister(NULL);
return ret;
}
ESP_LOGI(TAG, "SPIFFS initialized, total: %d, used: %d", total, used);
return ESP_OK;
}
// 在代码中添加列出文件的测试函数
void list_spiffs_files() {
DIR *dir = opendir("/spiffs");
if (dir == NULL) {
ESP_LOGE(TAG, "Failed to open directory");
return;
}
struct dirent *ent;
while ((ent = readdir(dir)) != NULL) {
ESP_LOGI(TAG, "File: %s", ent->d_name);
}
closedir(dir);
}
// -------------------------- 主函数 --------------------------
void app_main(void) {
// 1. 初始化SPIFFS文件系统
esp_err_t spiffs_err = spiffs_init();
if (spiffs_err != ESP_OK) {
ESP_LOGE(TAG, "SPIFFS init failed, exit app");
return;
}
// 2. 初始化I2S播放硬件
esp_err_t init_err = ns4168_init();
if (init_err != ESP_OK) {
ESP_LOGE(TAG, "I2S init failed, exit app");
return;
}
list_spiffs_files(); // 列出 SPIFFS 中的文件
// 3. 启动播放任务(使用SPIFFS中的文件路径)
const char *target_wav = "/spiffs/hi_idf_audio.wav"; // SPIFFS中的WAV文件路径
xTaskCreate(i2s_play_task, "i2s_play_task", 4096, (void *)target_wav, 5, NULL);
}
浙公网安备 33010602011771号