//代码:循环播放4首内置的wav音乐,I2S连接d类功放用NS4168芯片
//文件取样格式:Wave PCM 签字的 16bit, 采样频率:16KHz ,比特率705kbps
//demo工程打包下载:https://download.csdn.net/download/wabil/89515015
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/i2s_std.h"
#include "esp_system.h"
#include "esp_check.h"
#include "esp_log.h"
/* Example configurations */
#define EXAMPLE_SAMPLE_RATE (16000) // 16KHZ
#define EXAMPLE_MCLK_MULTIPLE (384) // If not using 24-bit data width, 256 should be enough
#define EXAMPLE_MCLK_FREQ_HZ (EXAMPLE_SAMPLE_RATE * EXAMPLE_MCLK_MULTIPLE)
#define EXAMPLE_VOICE_VOLUME CONFIG_EXAMPLE_VOICE_VOLUME
/* I2S port and GPIOs */
#define I2S_NUM (0)
#define I2S_MCK_IO (GPIO_NUM_1) //[No used]
#define I2S_BCK_IO (GPIO_NUM_36) // SerialCK
#define I2S_WS_IO (GPIO_NUM_35) // LRCK
#define I2S_DO_IO (GPIO_NUM_37) // SDATA out
#define I2S_DI_IO (GPIO_NUM_19) // SDATA in for micphone [No used]
static const char *TAG = "i2s_ns4168";
static const char err_reason[][30] = {"input param is invalid", "operation timeout"};
static i2s_chan_handle_t tx_handle = NULL;
static i2s_chan_handle_t rx_handle = NULL;
/* Import music file as buffer */
#define DECLARE_WAV_START(SND) extern const uint8_t SND##_wav_start[] asm("_binary_" #SND "_wav_start")
#define DECALARE_WAV_END(SND) extern const uint8_t SND##_wav_end[] asm("_binary_" #SND "_wav_end")
DECLARE_WAV_START(Snd01);
DECALARE_WAV_END(Snd01);
DECLARE_WAV_START(Snd02);
DECALARE_WAV_END(Snd02);
DECLARE_WAV_START(Snd03);
DECALARE_WAV_END(Snd03);
DECLARE_WAV_START(Snd_Hotel); // california_hotel
DECALARE_WAV_END(Snd_Hotel);
const uint8_t *music_group[][2] = {
{&Snd01_wav_start, &Snd01_wav_end},
{&Snd02_wav_start, &Snd02_wav_end},
{&Snd03_wav_start, &Snd03_wav_end},
{&Snd_Hotel_wav_start, &Snd_Hotel_wav_end},
};
esp_err_t play_snd_id(uint8_t snd_id, uint32_t timeout_ms)
{
uint8_t *data_ptr_start = (uint8_t *)music_group[snd_id][0];
uint8_t *data_ptr_end = (uint8_t *)music_group[snd_id][1];
size_t data_len = data_ptr_end - data_ptr_start;
uint32_t bytes_write = 0;
// ESP_ERROR_CHECK(i2s_channel_enable(tx_handle));
vTaskDelay(150 / portTICK_PERIOD_MS);
uint32_t tick1 = esp_log_timestamp();
printf("[%ld] read to play:%d ,length:%d bytes.\n", tick1, snd_id, data_len);
esp_err_t ret = i2s_channel_write(tx_handle, data_ptr_start, data_len, &bytes_write, timeout_ms);
uint32_t tick2 = esp_log_timestamp();
printf("[%ld] end play,written:%ld,take:%ld ms\n", tick2,bytes_write, tick2 - tick1);
vTaskDelay(150 / portTICK_PERIOD_MS);
// ESP_ERROR_CHECK(i2s_channel_disable(tx_handle));
if (ret != ESP_OK)
{
ESP_LOGE(TAG, "[music] i2s write failed, %s", err_reason[ret == ESP_ERR_TIMEOUT]);
abort();
}
if (bytes_write <= 0)
{
ESP_LOGE(TAG, "[music] i2s music play failed.");
abort();
}
return ret;
}
static esp_err_t i2s_driver_init(void)
{
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM, I2S_ROLE_MASTER);
chan_cfg.auto_clear = true; // Auto clear the legacy data in the DMA buffer
ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, &tx_handle, &rx_handle));
i2s_std_config_t std_cfg = {
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(EXAMPLE_SAMPLE_RATE),
.slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO),
.gpio_cfg = {
.mclk = I2S_MCK_IO,
.bclk = I2S_BCK_IO,
.ws = I2S_WS_IO,
.dout = I2S_DO_IO,
.din = I2S_DI_IO,
.invert_flags = {
.mclk_inv = false,
.bclk_inv = false,
.ws_inv = false,
},
},
};
std_cfg.clk_cfg.mclk_multiple = EXAMPLE_MCLK_MULTIPLE;
ESP_ERROR_CHECK(i2s_channel_init_std_mode(tx_handle, &std_cfg));
ESP_ERROR_CHECK(i2s_channel_init_std_mode(rx_handle, &std_cfg));
ESP_ERROR_CHECK(i2s_channel_enable(tx_handle));
ESP_ERROR_CHECK(i2s_channel_enable(rx_handle));
return ESP_OK;
}
static void i2s_music_task(void *args)
{
esp_err_t ret = ESP_OK;
size_t bytes_write = 0;
uint8_t *data_ptr = (uint8_t *)Snd01_wav_start;
bytes_write = Snd01_wav_end - Snd01_wav_start;
printf("the music bin data length:%d bytes.start_addr:%p\n", bytes_write, data_ptr);
/* (Optional) Disable TX channel and preload the data before enabling the TX channel,
* so that the valid data can be transmitted immediately */
ESP_ERROR_CHECK(i2s_channel_disable(tx_handle));
ESP_ERROR_CHECK(i2s_channel_preload_data(tx_handle, data_ptr, Snd01_wav_end - data_ptr, &bytes_write));
// data_ptr += bytes_write; // Move forward the data pointer
printf("preload data length:%d.EXAMPLE_SAMPLE_RATE=%d\n", bytes_write, EXAMPLE_SAMPLE_RATE);
/* Enable the TX channel */
ESP_ERROR_CHECK(i2s_channel_enable(tx_handle));
vTaskDelay(5000 / portTICK_PERIOD_MS);
uint8_t snd_id = 0;
while (1)
{
play_snd_id(snd_id, portMAX_DELAY);
snd_id = (snd_id + 1) % 4; // 共4个wav音效
vTaskDelay(3000 / portTICK_PERIOD_MS);
}
vTaskDelete(NULL);
}
void app_main(void)
{
// 634240 bytes are written
printf("I2S_NS4168: BCK=%d,WS=%d,DAT_IO=%d.\n", I2S_BCK_IO, I2S_WS_IO, I2S_DO_IO);
/* Initialize i2s peripheral */
if (i2s_driver_init() != ESP_OK)
{
ESP_LOGE(TAG, "i2s driver init failed");
abort();
}
else
{
ESP_LOGI(TAG, "i2s driver init success");
}
xTaskCreate(i2s_music_task, "i2s_music_task", 4096, NULL, 5, NULL);
}