【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】
在esp32开发的时候,其实rom区域的大小是一定的。也就是说,code+data生成的bin文件大小是一定的。但是整个esp32的flash还是挺大的,所以如果要存储文件的话,最好是单独创建一个分区,然后操作这个分区即可。

1、调整flash大小
idf上面,默认flash的大小都是2m,所以需要在menuconfig下面,把flash大小调整为4m。在Serial Flash Config里面调整下即可。
2、创建partitions.csv文件
之前是没有这个文件的,现在需要单独创建这个文件。
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x6000,
phy_init, data, phy, 0xf000, 0x1000,
factory, app, factory, 0x10000, 0x100000,
storage, data, spiffs, , 0x100000,
3、csv文件的存放位置
csv文件放到prj根目录下即可,不需要放到main的子目录下。
4、用ai创建代码
为了验证分区是否ok,可以创建一个demo,比如录制一个10s的wav文件,放到这个分区,确认是否ok。如果整个流程没有问题的话,那么就说明分区是可以的。
#include <stdio.h>
#include <string.h>
#include <math.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "esp_spiffs.h"
#include "driver/i2s_pdm.h"
static const char *TAG = "PDM_MIC";
// ------- Configuration -------
#define PDM_CLK_GPIO GPIO_NUM_4
#define PDM_DATA_GPIO GPIO_NUM_5
#define SAMPLE_RATE 16000
#define BITS_PER_SAMPLE 16
#define NUM_CHANNELS 1
#define RECORD_SECONDS 10
#define READ_CHUNK_SAMPLES 1024 // samples per read
#define WAV_FILE_PATH "/spiffs/record.wav"
static i2s_chan_handle_t rx_chan = NULL;
// ---------- Standard 44-byte WAV header ----------
typedef struct __attribute__((packed)) {
char riff_id[4]; // "RIFF"
uint32_t riff_size; // file size - 8
char wave_id[4]; // "WAVE"
char fmt_id[4]; // "fmt "
uint32_t fmt_size; // 16 for PCM
uint16_t audio_format; // 1 = PCM
uint16_t num_channels;
uint32_t sample_rate;
uint32_t byte_rate; // sample_rate * num_channels * bits_per_sample/8
uint16_t block_align; // num_channels * bits_per_sample/8
uint16_t bits_per_sample;
char data_id[4]; // "data"
uint32_t data_size; // number of bytes of audio data
} wav_header_t;
static void wav_header_init(wav_header_t *h, uint32_t data_size)
{
memcpy(h->riff_id, "RIFF", 4);
h->riff_size = data_size + sizeof(wav_header_t) - 8;
memcpy(h->wave_id, "WAVE", 4);
memcpy(h->fmt_id, "fmt ", 4);
h->fmt_size = 16;
h->audio_format = 1; // PCM
h->num_channels = NUM_CHANNELS;
h->sample_rate = SAMPLE_RATE;
h->bits_per_sample = BITS_PER_SAMPLE;
h->block_align = h->num_channels * h->bits_per_sample / 8;
h->byte_rate = h->sample_rate * h->block_align;
memcpy(h->data_id, "data", 4);
h->data_size = data_size;
}
// ---------- SPIFFS init ----------
static void spiffs_init(void)
{
esp_vfs_spiffs_conf_t conf = {
.base_path = "/spiffs",
.partition_label = NULL,
.max_files = 5,
.format_if_mount_failed = true,
};
ESP_ERROR_CHECK(esp_vfs_spiffs_register(&conf));
size_t total = 0, used = 0;
esp_spiffs_info(NULL, &total, &used);
ESP_LOGI(TAG, "SPIFFS mounted, total=%d used=%d", (int)total, (int)used);
}
// ---------- PDM mic init ----------
static void pdm_mic_init(void)
{
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER);
ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, NULL, &rx_chan));
i2s_pdm_rx_config_t pdm_rx_cfg = {
.clk_cfg = I2S_PDM_RX_CLK_DEFAULT_CONFIG(SAMPLE_RATE),
.slot_cfg = I2S_PDM_RX_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT,
I2S_SLOT_MODE_MONO),
.gpio_cfg = {
.clk = PDM_CLK_GPIO,
.din = PDM_DATA_GPIO,
.invert_flags = { .clk_inv = false },
},
};
pdm_rx_cfg.slot_cfg.slot_mask = I2S_PDM_SLOT_LEFT;
ESP_ERROR_CHECK(i2s_channel_init_pdm_rx_mode(rx_chan, &pdm_rx_cfg));
ESP_ERROR_CHECK(i2s_channel_enable(rx_chan));
ESP_LOGI(TAG, "PDM mic init done, CLK=%d, DATA=%d, %dHz",
PDM_CLK_GPIO, PDM_DATA_GPIO, SAMPLE_RATE);
}
// ---------- Record N seconds to a WAV file ----------
static void record_wav_task(void *arg)
{
const uint32_t total_samples = SAMPLE_RATE * RECORD_SECONDS * NUM_CHANNELS;
const uint32_t total_bytes = total_samples * (BITS_PER_SAMPLE / 8);
FILE *f = fopen(WAV_FILE_PATH, "wb");
if (!f) {
ESP_LOGE(TAG, "Failed to open %s for writing", WAV_FILE_PATH);
vTaskDelete(NULL);
return;
}
// Write a placeholder header first, fix up data_size after recording
wav_header_t header;
wav_header_init(&header, total_bytes);
fwrite(&header, sizeof(header), 1, f);
int16_t *buf = malloc(READ_CHUNK_SAMPLES * sizeof(int16_t));
if (!buf) {
ESP_LOGE(TAG, "malloc failed");
fclose(f);
vTaskDelete(NULL);
return;
}
uint32_t bytes_written = 0;
ESP_LOGI(TAG, "Recording %d seconds...", RECORD_SECONDS);
while (bytes_written < total_bytes) {
size_t bytes_read = 0;
uint32_t bytes_remaining = total_bytes - bytes_written;
size_t want = READ_CHUNK_SAMPLES * sizeof(int16_t);
if (want > bytes_remaining) {
want = bytes_remaining; // last chunk may be smaller
}
esp_err_t ret = i2s_channel_read(rx_chan, buf, want, &bytes_read, portMAX_DELAY);
if (ret != ESP_OK) {
ESP_LOGW(TAG, "i2s read err: %d", ret);
continue;
}
fwrite(buf, 1, bytes_read, f);
bytes_written += bytes_read;
}
fclose(f);
free(buf);
ESP_LOGI(TAG, "Recording done: %s (%d bytes audio data)",
WAV_FILE_PATH, (int)bytes_written);
vTaskDelete(NULL);
}
void app_main(void)
{
spiffs_init();
pdm_mic_init();
xTaskCreate(record_wav_task, "record_wav_task", 4096, NULL, 5, NULL);
}
5、编译测试
编译的话,就按照一般的idf编译方法去处理就好了。调试的话,主要看打印。可以看到,第一次spiffs mount不成功,还会格式化一下。后续再调试的话,就直接加载了。