本章介绍下使用mic录制音频存放到tf卡中。
使用的是官方示例:https://gitee.com/EspressifSystems/esp-idf/tree/master/examples/peripherals/i2s/i2s_audio_recorder_sdcard
主要两块,tf卡的spi接线,mic的i2s接线
SPI_MOSI_GPIO 35
SPI_MISO_GPIO 37
SPI_SCLK_GPIO 36
SPI_CS_GPIO 34
I2S_BCK_GPIO 41
I2S_WS_GPIO 40
I2S_DATA_GPIO 42
/* I2S Digital Microphone Recording Example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include
#include
#include
#include
#include
#include "esp_log.h"
#include "esp_err.h"
#include "esp_system.h"
#include "esp_vfs_fat.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/i2s.h"
#include "driver/gpio.h"
#include "driver/spi_common.h"
#include "sdmmc_cmd.h"
#include "sdkconfig.h"
static const char* TAG = "pdm_rec_example";
#define SPI_DMA_CHAN SPI_DMA_CH_AUTO
#define SD_MOUNT_POINT "/sdcard"
#define NUM_CHANNELS (1) // For mono recording only!
#define CONFIG_EXAMPLE_BIT_SAMPLE (16)
#define CONFIG_EXAMPLE_SAMPLE_RATE (36000)
#define SAMPLE_SIZE (CONFIG_EXAMPLE_BIT_SAMPLE * 1024)
#define BYTE_RATE (CONFIG_EXAMPLE_SAMPLE_RATE * (CONFIG_EXAMPLE_BIT_SAMPLE / 8)) * NUM_CHANNELS
#define CONFIG_EXAMPLE_I2S_CH 0
#define CONFIG_EXAMPLE_REC_TIME 2
#define CONFIG_EXAMPLE_I2S_BCK_GPIO 41
#define CONFIG_EXAMPLE_I2S_WS_GPIO 40
#define CONFIG_EXAMPLE_I2S_DATA_GPIO 42
// When testing SD and SPI modes, keep in mind that once the card has been
// initialized in SPI mode, it can not be reinitialized in SD mode without
// toggling power to the card.
sdmmc_host_t host = SDSPI_HOST_DEFAULT();
sdmmc_card_t* card;
#define CONFIG_EXAMPLE_SPI_MOSI_GPIO 35
#define CONFIG_EXAMPLE_SPI_MISO_GPIO 37
#define CONFIG_EXAMPLE_SPI_SCLK_GPIO 36
#define CONFIG_EXAMPLE_SPI_CS_GPIO 34
static int16_t i2s_readraw_buff[SAMPLE_SIZE];
size_t bytes_read;
const int WAVE_HEADER_SIZE = 44;
void mount_sdcard(void)
{
esp_err_t ret;
// Options for mounting the filesystem.
// If format_if_mount_failed is set to true, SD card will be partitioned and
// formatted in case when mounting fails.
esp_vfs_fat_sdmmc_mount_config_t mount_config = {
.format_if_mount_failed = true,
.max_files = 5,
.allocation_unit_size = 8 * 1024
};
ESP_LOGI(TAG, "Initializing SD card");
spi_bus_config_t bus_cfg = {
.mosi_io_num = CONFIG_EXAMPLE_SPI_MOSI_GPIO,
.miso_io_num = CONFIG_EXAMPLE_SPI_MISO_GPIO,
.sclk_io_num = CONFIG_EXAMPLE_SPI_SCLK_GPIO,
.quadwp_io_num = -1,
.quadhd_io_num = -1,
.max_transfer_sz = 4000,
};
ret = spi_bus_initialize(host.slot, &bus_cfg, SPI_DMA_CHAN);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to initialize bus.");
return;
}
// This initializes the slot without card detect (CD) and write protect (WP) signals.
// Modify slot_config.gpio_cd and slot_config.gpio_wp if your board has these signals.
sdspi_device_config_t slot_config = SDSPI_DEVICE_CONFIG_DEFAULT();
slot_config.gpio_cs = CONFIG_EXAMPLE_SPI_CS_GPIO;
slot_config.host_id = host.slot;
ret = esp_vfs_fat_sdspi_mount(SD_MOUNT_POINT, &host, &slot_config, &mount_config, &card);
if (ret != ESP_OK) {
if (ret == ESP_FAIL) {
ESP_LOGE(TAG, "Failed to mount filesystem.");
} else {
ESP_LOGE(TAG, "Failed to initialize the card (%s). "
"Make sure SD card lines have pull-up resistors in place.", esp_err_to_name(ret));
}
return;
}
// Card has been initialized, print its properties
sdmmc_card_print_info(stdout, card);
}
void generate_wav_header(char* wav_header, uint32_t wav_size, uint32_t sample_rate){
// See this for reference: http://soundfile.sapp.org/doc/WaveFormat/
uint32_t file_size = wav_size + WAVE_HEADER_SIZE - 8;
uint32_t byte_rate = BYTE_RATE;
const char set_wav_header[] = {
'R','I','F','F', // ChunkID
file_size, file_size >> 8, file_size >> 16, file_size >> 24, // ChunkSize
'W','A','V','E', // Format
'f','m','t',' ', // Subchunk1ID
0x10, 0x00, 0x00, 0x00, // Subchunk1Size (16 for PCM)
0x01, 0x00, // AudioFormat (1 for PCM)
0x01, 0x00, // NumChannels (1 channel)
sample_rate, sample_rate >> 8, sample_rate >> 16, sample_rate >> 24, // SampleRate
byte_rate, byte_rate >> 8, byte_rate >> 16, byte_rate >> 24, // ByteRate
0x02, 0x00, // BlockAlign
0x10, 0x00, // BitsPerSample (16 bits)
'd','a','t','a', // Subchunk2ID
wav_size, wav_size >> 8, wav_size >> 16, wav_size >> 24, // Subchunk2Size
};
memcpy(wav_header, set_wav_header, sizeof(set_wav_header));
}
void record_wav(uint32_t rec_time)
{
// Use POSIX and C standard library functions to work with files.
int flash_wr_size = 0;
ESP_LOGI(TAG, "Opening file");
char wav_header_fmt[WAVE_HEADER_SIZE];
uint32_t flash_rec_time = BYTE_RATE * rec_time;
generate_wav_header(wav_header_fmt, flash_rec_time, CONFIG_EXAMPLE_SAMPLE_RATE);
// First check if file exists before creating a new file.
struct stat st;
if (stat(SD_MOUNT_POINT"/record.wav", &st) == 0) {
// Delete it if it exists
unlink(SD_MOUNT_POINT"/record.wav");
}
// Create new WAV file
FILE* f = fopen(SD_MOUNT_POINT"/record.wav", "a");
if (f == NULL) {
ESP_LOGE(TAG, "Failed to open file for writing");
return;
}
// Write the header to the WAV file
fwrite(wav_header_fmt, 1, WAVE_HEADER_SIZE, f);
// Start recording
while (flash_wr_size < flash_rec_time) {
// Read the RAW samples from the microphone (char *)
i2s_read(CONFIG_EXAMPLE_I2S_CH, i2s_readraw_buff, SAMPLE_SIZE, &bytes_read, 100);
// Write the samples to the WAV file
// printf("%ls\n",i2s_readraw_buff);
fwrite(i2s_readraw_buff, 1, bytes_read, f);
flash_wr_size += bytes_read;
printf("flash_wr_size = %d\n",flash_wr_size);
}
ESP_LOGI(TAG, "Recording done!");
fclose(f);
ESP_LOGI(TAG, "File written on SDCard");
// All done, unmount partition and disable SPI peripheral
esp_vfs_fat_sdcard_unmount(SD_MOUNT_POINT, card);
ESP_LOGI(TAG, "Card unmounted");
// Deinitialize the bus after all devices are removed
spi_bus_free(host.slot);
}
void init_microphone(void)
{
// Set the I2S configuration as PDM and 16bits per sample
i2s_config_t i2s_config = {
.mode = I2S_MODE_MASTER | I2S_MODE_RX , // | I2S_MODE_PDM,
.sample_rate = CONFIG_EXAMPLE_SAMPLE_RATE,
.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT,
.communication_format = I2S_COMM_FORMAT_STAND_I2S,
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL2,
.dma_buf_count = 8,
.dma_buf_len = 1024,
.use_apll = 0,
};
// Set the pinout configuration (set using menuconfig)
i2s_pin_config_t pin_config = {
.mck_io_num = I2S_PIN_NO_CHANGE,
.bck_io_num = CONFIG_EXAMPLE_I2S_BCK_GPIO,
.ws_io_num = CONFIG_EXAMPLE_I2S_WS_GPIO,
.data_out_num = I2S_PIN_NO_CHANGE,
.data_in_num = CONFIG_EXAMPLE_I2S_DATA_GPIO,
};
// Call driver installation function before any I2S R/W operation.
ESP_ERROR_CHECK( i2s_driver_install(CONFIG_EXAMPLE_I2S_CH, &i2s_config, 0, NULL) );
ESP_ERROR_CHECK( i2s_set_pin(CONFIG_EXAMPLE_I2S_CH, &pin_config) );
ESP_ERROR_CHECK( i2s_set_clk(CONFIG_EXAMPLE_I2S_CH, CONFIG_EXAMPLE_SAMPLE_RATE, I2S_BITS_PER_SAMPLE_16BIT, I2S_CHANNEL_MONO) );
}
void app_main(void)
{
ESP_LOGI(TAG, "PDM microphone recording Example start");
// Mount the SDCard for recording the audio file
mount_sdcard();
// Init the PDM digital microphone
init_microphone();
ESP_LOGI(TAG, "Starting recording for %d seconds!", CONFIG_EXAMPLE_REC_TIME);
// Start Recording
record_wav(CONFIG_EXAMPLE_REC_TIME);
// Stop I2S driver and destroy
ESP_ERROR_CHECK( i2s_driver_uninstall(CONFIG_EXAMPLE_I2S_CH) );
}
代码有微调,需要注意。
我没有将i2s_readraw_buff强制转换为char。
采样率等可以自行设置,不过样本的位深是16,对应的是数据的uint_16,如果想要更换位深,注意类型更改,例如32,uint_32。
其中前半秒应该是mic初始化导致的,这种很常见,部分手机采用前部分静音避免该问题。
所以录制注意时长超过半秒再听效果。