231 lines
8.3 KiB
C
Executable File
231 lines
8.3 KiB
C
Executable File
/* SD card and FAT filesystem example.
|
|
This example uses SPI peripheral to communicate with SD card.
|
|
|
|
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 <string.h>
|
|
#include <sys/unistd.h>
|
|
#include <sys/stat.h>
|
|
#include "esp_vfs_fat.h"
|
|
#include "sdmmc_cmd.h"
|
|
#include "esp_io_expander_tca95xx_16bit.h"
|
|
#include "driver/i2c.h"
|
|
#include "driver/i2s_pdm.h"
|
|
#include "driver/gpio.h"
|
|
#include "esp_log.h"
|
|
|
|
|
|
|
|
#define EXAMPLE_PDM_TX_CLK_IO GPIO_NUM_13 // I2S PDM TX clock io number
|
|
#define EXAMPLE_PDM_TX_DOUT_IO GPIO_NUM_12 // I2S PDM TX data out io number
|
|
|
|
#define EXAMPLE_PDM_TX_FREQ_HZ 44100 // I2S PDM TX frequency
|
|
#define EXAMPLE_WAVE_AMPLITUDE (20000.0) // 1~32767
|
|
#define CONST_PI (3.1416f)
|
|
#define EXAMPLE_SINE_WAVE_LEN(tone) (uint32_t)((EXAMPLE_PDM_TX_FREQ_HZ / (float)tone) + 0.5) // The sample point number per sine wave to generate the tone
|
|
#define EXAMPLE_TONE_LAST_TIME_MS 500
|
|
#define EXAMPLE_BYTE_NUM_EVERY_TONE (EXAMPLE_TONE_LAST_TIME_MS * EXAMPLE_PDM_TX_FREQ_HZ / 1000)
|
|
|
|
|
|
#define EXAMPLE_MAX_CHAR_SIZE 64
|
|
|
|
static const char *TAG = "example";
|
|
|
|
#define MOUNT_POINT "/sdcard"
|
|
|
|
// Pin assignments can be set in menuconfig, see "SD SPI Example Configuration" menu.
|
|
// You can also change the pin assignments here by changing the following 4 lines.
|
|
#define PIN_NUM_MISO CONFIG_EXAMPLE_PIN_MISO
|
|
#define PIN_NUM_MOSI CONFIG_EXAMPLE_PIN_MOSI
|
|
#define PIN_NUM_CLK CONFIG_EXAMPLE_PIN_CLK
|
|
#define PIN_NUM_CS CONFIG_EXAMPLE_PIN_CS
|
|
|
|
#define AUDIO_BUFFER 2048
|
|
|
|
|
|
|
|
i2s_chan_handle_t tx_handle = NULL;
|
|
|
|
esp_io_expander_handle_t io_expander = NULL;
|
|
|
|
void init_io_expander() {
|
|
i2c_port_t i2c_master_port = I2C_NUM_0;
|
|
i2c_config_t conf = {
|
|
.mode = I2C_MODE_MASTER,
|
|
.sda_io_num = 4,
|
|
.scl_io_num = 5,
|
|
.sda_pullup_en = GPIO_PULLUP_ENABLE,
|
|
.scl_pullup_en = GPIO_PULLUP_ENABLE
|
|
};
|
|
conf.master.clk_speed = 100000;
|
|
|
|
i2c_param_config(i2c_master_port, &conf);
|
|
i2c_driver_install(i2c_master_port, conf.mode, 0, 0, 0);
|
|
|
|
esp_io_expander_new_i2c_tca95xx_16bit(I2C_NUM_0, ESP_IO_EXPANDER_I2C_TCA9555_ADDRESS_000, &io_expander);
|
|
esp_io_expander_set_dir(io_expander, IO_EXPANDER_PIN_NUM_11 | IO_EXPANDER_PIN_NUM_12 | IO_EXPANDER_PIN_NUM_13, IO_EXPANDER_OUTPUT);
|
|
esp_io_expander_set_dir(io_expander, IO_EXPANDER_PIN_NUM_0 | IO_EXPANDER_PIN_NUM_1 | IO_EXPANDER_PIN_NUM_2 | IO_EXPANDER_PIN_NUM_3, IO_EXPANDER_OUTPUT);
|
|
esp_io_expander_set_level(io_expander, IO_EXPANDER_PIN_NUM_11 | IO_EXPANDER_PIN_NUM_12 | IO_EXPANDER_PIN_NUM_13, 1);
|
|
esp_io_expander_set_level(io_expander, IO_EXPANDER_PIN_NUM_0 | IO_EXPANDER_PIN_NUM_1 | IO_EXPANDER_PIN_NUM_2 | IO_EXPANDER_PIN_NUM_3, 1);
|
|
esp_io_expander_set_level(io_expander, IO_EXPANDER_PIN_NUM_1, 0);
|
|
}
|
|
|
|
|
|
esp_err_t play_wav(char *fp)
|
|
{
|
|
FILE *fh = fopen(fp, "rb");
|
|
if (fh == NULL)
|
|
{
|
|
ESP_LOGE(TAG, "Failed to open file");
|
|
return ESP_ERR_INVALID_ARG;
|
|
}
|
|
|
|
// skip the header...
|
|
fseek(fh, 44, SEEK_SET);
|
|
|
|
// create a writer buffer
|
|
int16_t *buf = calloc(AUDIO_BUFFER, sizeof(int16_t));
|
|
size_t bytes_read = 0;
|
|
size_t bytes_written = 0;
|
|
|
|
bytes_read = fread(buf, sizeof(int16_t), AUDIO_BUFFER, fh);
|
|
|
|
// i2s_channel_enable(tx_handle);
|
|
|
|
while (bytes_read > 0)
|
|
{
|
|
// write the buffer to the i2s
|
|
i2s_channel_write(tx_handle, buf, bytes_read * sizeof(int16_t), &bytes_written, portMAX_DELAY);
|
|
bytes_read = fread(buf, sizeof(int16_t), AUDIO_BUFFER, fh);
|
|
ESP_LOGV(TAG, "Bytes read: %d", bytes_read);
|
|
}
|
|
|
|
i2s_channel_disable(tx_handle);
|
|
free(buf);
|
|
|
|
return ESP_OK;
|
|
}
|
|
|
|
|
|
static i2s_chan_handle_t i2s_example_init_pdm_tx(void)
|
|
{
|
|
i2s_chan_handle_t tx_chan; // I2S tx channel handler
|
|
/* Setp 1: Determine the I2S channel configuration and allocate TX channel only
|
|
* The default configuration can be generated by the helper macro,
|
|
* it only requires the I2S controller id and I2S role,
|
|
* but note that PDM channel can only be registered on I2S_NUM_0 */
|
|
i2s_chan_config_t tx_chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER);
|
|
tx_chan_cfg.auto_clear = true;
|
|
ESP_ERROR_CHECK(i2s_new_channel(&tx_chan_cfg, &tx_chan, NULL));
|
|
|
|
/* Step 2: Setting the configurations of PDM TX mode and initialize the TX channel
|
|
* The slot configuration and clock configuration can be generated by the macros
|
|
* These two helper macros is defined in 'i2s_pdm.h' which can only be used in PDM TX mode.
|
|
* They can help to specify the slot and clock configurations for initialization or re-configuring */
|
|
i2s_pdm_tx_config_t pdm_tx_cfg = {
|
|
.clk_cfg = I2S_PDM_TX_CLK_DEFAULT_CONFIG(EXAMPLE_PDM_TX_FREQ_HZ),
|
|
/* The data bit-width of PDM mode is fixed to 16 */
|
|
.slot_cfg = I2S_PDM_TX_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_MONO),
|
|
.gpio_cfg = {
|
|
.clk = EXAMPLE_PDM_TX_CLK_IO,
|
|
.dout = EXAMPLE_PDM_TX_DOUT_IO,
|
|
.invert_flags = {
|
|
.clk_inv = false,
|
|
},
|
|
},
|
|
};
|
|
ESP_ERROR_CHECK(i2s_channel_init_pdm_tx_mode(tx_chan, &pdm_tx_cfg));
|
|
|
|
/* Step 3: Enable the tx channel before writing data */
|
|
ESP_ERROR_CHECK(i2s_channel_enable(tx_chan));
|
|
|
|
return tx_chan;
|
|
}
|
|
|
|
|
|
|
|
void app_main(void)
|
|
{
|
|
init_io_expander();
|
|
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 = 16 * 1024
|
|
};
|
|
sdmmc_card_t *card;
|
|
const char mount_point[] = MOUNT_POINT;
|
|
ESP_LOGI(TAG, "Initializing SD card");
|
|
|
|
// Use settings defined above to initialize SD card and mount FAT filesystem.
|
|
// Note: esp_vfs_fat_sdmmc/sdspi_mount is all-in-one convenience functions.
|
|
// Please check its source code and implement error recovery when developing
|
|
// production applications.
|
|
ESP_LOGI(TAG, "Using SPI peripheral");
|
|
|
|
// By default, SD card frequency is initialized to SDMMC_FREQ_DEFAULT (20MHz)/
|
|
// For setting a specific frequency, use host.max_freq_khz (range 400kHz - 20MHz for SDSPI)
|
|
// Example: for fixed frequency of 10MHz, use host.max_freq_khz = 10000;
|
|
sdmmc_host_t host = SDSPI_HOST_DEFAULT();
|
|
|
|
spi_bus_config_t bus_cfg = {
|
|
.mosi_io_num = PIN_NUM_MOSI,
|
|
.miso_io_num = PIN_NUM_MISO,
|
|
.sclk_io_num = PIN_NUM_CLK,
|
|
.quadwp_io_num = -1,
|
|
.quadhd_io_num = -1,
|
|
.max_transfer_sz = 4000,
|
|
};
|
|
ret = spi_bus_initialize(host.slot, &bus_cfg, SDSPI_DEFAULT_DMA);
|
|
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 = PIN_NUM_CS;
|
|
slot_config.host_id = host.slot;
|
|
|
|
ESP_LOGI(TAG, "Mounting filesystem");
|
|
ret = esp_vfs_fat_sdspi_mount(mount_point, &host, &slot_config, &mount_config, &card);
|
|
|
|
if (ret != ESP_OK) {
|
|
if (ret == ESP_FAIL) {
|
|
ESP_LOGE(TAG, "Failed to mount filesystem. "
|
|
"If you want the card to be formatted, set the CONFIG_EXAMPLE_FORMAT_IF_MOUNT_FAILED menuconfig option.");
|
|
} 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;
|
|
}
|
|
ESP_LOGI(TAG, "Filesystem mounted");
|
|
|
|
// Card has been initialized, print its properties
|
|
sdmmc_card_print_info(stdout, card);
|
|
|
|
tx_handle = i2s_example_init_pdm_tx();
|
|
|
|
ESP_ERROR_CHECK_WITHOUT_ABORT(play_wav("/sdcard/audio.wav"));
|
|
|
|
|
|
// All done, unmount partition and disable SPI peripheral
|
|
esp_vfs_fat_sdcard_unmount(mount_point, card);
|
|
ESP_LOGI(TAG, "Card unmounted");
|
|
|
|
//deinitialize the bus after all devices are removed
|
|
spi_bus_free(host.slot);
|
|
}
|
|
|