HiFi player project, first commit.

This commit is contained in:
francis.wang committed 2026-10-26 20:07:12 +08:00
commit 4126ea13a1
49 files changed
+6333

No files matched your search

+221
View File
@@ -0,0 +1,221 @@
/**
****************************************************************************************************
* @file i2s.c
* @author 正点原子团队(ALIENTEK)
* @version V1.0
* @date 2025-01-01
* @brief I2S驱动代码
* @license Copyright (c) 2020-2032, 广州市星翼电子科技有限公司
****************************************************************************************************
* @attention
*
* 实验平台:正点原子 ESP32-S3 开发板
* 在线视频:www.yuanzige.com
* 技术论坛:www.openedv.com
* 公司网址:www.alientek.com
* 购买地址:openedv.taobao.com
*
****************************************************************************************************
*/
#include "myi2s.h"
#include <stdbool.h>
i2s_chan_handle_t tx_handle = NULL; /* I2S发送通道句柄 */
i2s_chan_handle_t rx_handle = NULL; /* I2S接收通道句柄 */
i2s_std_config_t my_std_cfg; /* 标准模式配置结构体 */
static bool s_trx_enabled = false; /* 通道是否已经使能(避免重复 enable/disable) */
/*
* @brief 初始化I2S
* @param 无
* @retval ESP_OK:初始化成功;其他:失败
*/
esp_err_t myi2s_init(void)
{
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM, I2S_ROLE_MASTER); /* 默认的通道配置(I2S0,主机) */
chan_cfg.auto_clear = true; /* 自动清除DMA缓冲区遗留的数据 */
chan_cfg.dma_desc_num = 8; /* DMA 描述符数量 */
chan_cfg.dma_frame_num = 480; /* 每个描述符的帧数(约 87ms 缓冲 @44.1kHz) */
ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, &tx_handle, &rx_handle)); /* 分配新的I2S通道 */
i2s_std_config_t std_cfg = { /* 标准通信模式配置 */
.clk_cfg = { /* 时钟配置 可用I2S_STD_CLK_DEFAULT_CONFIG(I2S_SAMPLE_RATE)宏函数辅助配置 */
.sample_rate_hz = I2S_SAMPLE_RATE, /* I2S采样率 */
.clk_src = I2S_CLK_SRC_DEFAULT, /* I2S时钟源 */
.mclk_multiple = I2S_MCLK_MULTIPLE, /* I2S主时钟MCLK相对于采样率的倍数(默认256) */
},
.slot_cfg = { /* 声道配置,可用I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO)宏函数辅助配置(支持16位宽采样数据) */
.data_bit_width = I2S_DATA_BIT_WIDTH_16BIT, /* 声道支持16位宽的采样数据 */
.slot_bit_width = I2S_SLOT_BIT_WIDTH_AUTO, /* 通道位宽 */
.slot_mode = I2S_SLOT_MODE_STEREO, /* 立体声 */
.slot_mask = I2S_STD_SLOT_BOTH, /* 启用通道 */
.ws_width = I2S_DATA_BIT_WIDTH_16BIT, /* WS信号位宽 */
.ws_pol = false, /* WS信号极性 */
.bit_shift = true, /* 位移位(Philips模式下配置) */
.left_align = true, /* 左对齐 */
.big_endian = false, /* 小端模式 */
.bit_order_lsb = false /* MSB */
},
.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,
},
},
};
my_std_cfg = std_cfg;
ESP_ERROR_CHECK(i2s_channel_init_std_mode(tx_handle, &std_cfg)); /* 初始化TX通道 */
ESP_ERROR_CHECK(i2s_channel_init_std_mode(rx_handle, &std_cfg)); /* 初始化RX通道 */
ESP_ERROR_CHECK(i2s_channel_enable(tx_handle)); /* 启用TX通道 */
ESP_ERROR_CHECK(i2s_channel_enable(rx_handle)); /* 启用RX通道 */
s_trx_enabled = true;
return ESP_OK;
}
/**
* @brief I2S TRX启动
* @param 无
* @retval 无
*/
void i2s_trx_start(void)
{
if (s_trx_enabled)
{
return;
}
ESP_ERROR_CHECK(i2s_channel_enable(tx_handle));
ESP_ERROR_CHECK(i2s_channel_enable(rx_handle));
s_trx_enabled = true;
}
/**
* @brief I2S TRX停止
* @param 无
* @retval 无
*/
void i2s_trx_stop(void)
{
if (!s_trx_enabled)
{
return;
}
ESP_ERROR_CHECK(i2s_channel_disable(tx_handle));
ESP_ERROR_CHECK(i2s_channel_disable(rx_handle));
s_trx_enabled = false;
}
/**
* @brief I2S卸载
* @param 无
* @retval 无
*/
void i2s_deinit(void)
{
if (tx_handle != NULL)
{
i2s_channel_disable(tx_handle);
i2s_del_channel(tx_handle);
tx_handle = NULL;
}
if (rx_handle != NULL)
{
i2s_channel_disable(rx_handle);
i2s_del_channel(rx_handle);
rx_handle = NULL;
}
s_trx_enabled = false;
}
/**
* @brief 设置采样率和位宽
* @param sampleRate :采样率
* @param bits_sample :位宽(16 / 24 / 32)
* @retval 无
* @note 调用前会先关闭通道,调用后需要再调用 i2s_trx_start() 才能出声
* @note ESP-IDF 限制:slot 位宽为 24bit 时 mclk_multiple 必须是 3 的倍数,
* 因此 24bit 用 384 倍(MCLK=384fs,BCLK=48fs,分频比 8 整数),
* 16/32bit 保持 256 倍(分频比分别为 8 / 4)。
*/
void i2s_set_samplerate_bits_sample(int samplerate, int bits_sample)
{
i2s_data_bit_width_t data_width;
i2s_mclk_multiple_t mclk_multiple;
switch (bits_sample)
{
case 24:
data_width = I2S_DATA_BIT_WIDTH_24BIT;
mclk_multiple = I2S_MCLK_MULTIPLE_384; /* 必须是 3 的倍数 */
break;
case 32:
data_width = I2S_DATA_BIT_WIDTH_32BIT;
mclk_multiple = I2S_MCLK_MULTIPLE_256;
break;
case 16:
default:
data_width = I2S_DATA_BIT_WIDTH_16BIT;
mclk_multiple = I2S_MCLK_MULTIPLE_256;
break;
}
i2s_trx_stop();
/*
* 重配 slot 时驱动内部会用当前 mclk_multiple 重算时钟,
* 因此 MCLK 倍数必须在改 slot 之前就设好。
*/
my_std_cfg.clk_cfg.mclk_multiple = mclk_multiple;
my_std_cfg.slot_cfg.data_bit_width = data_width; /* 数据位宽 */
my_std_cfg.slot_cfg.slot_bit_width = I2S_SLOT_BIT_WIDTH_AUTO; /* 跟随数据位宽 */
my_std_cfg.slot_cfg.ws_width = data_width; /* WS 位宽,与数据位宽一致(50% 占空比) */
/* 如果需要更新声道或时钟配置,需要在更新前先禁用通道 */
ESP_ERROR_CHECK(i2s_channel_reconfig_std_slot(tx_handle, &my_std_cfg.slot_cfg));
my_std_cfg.clk_cfg.sample_rate_hz = samplerate; /* 设置采样率 */
ESP_ERROR_CHECK(i2s_channel_reconfig_std_clock(tx_handle, &my_std_cfg.clk_cfg));
}
/**
* @brief I2S传输数据
* @param buffer: 数据存储区的首地址
* @param frame_size: 数据大小
* @retval 发送的数据长度
*/
size_t i2s_tx_write(uint8_t *buffer, uint32_t frame_size)
{
size_t bytes_written;
ESP_ERROR_CHECK(i2s_channel_write(tx_handle, buffer, frame_size, &bytes_written, 1000));
return bytes_written;
}
/**
* @brief I2S读取数据
* @param buffer: 读取数据存储区的首地址
* @param frame_size: 读取数据大小
* @retval 接收的数据长度
*/
size_t i2s_rx_read(uint8_t *buffer, uint32_t frame_size)
{
size_t bytes_written;
ESP_ERROR_CHECK(i2s_channel_read(rx_handle, buffer, frame_size, &bytes_written, 1000));
return bytes_written;
}
+52
View File
@@ -0,0 +1,52 @@
/**
****************************************************************************************************
* @file i2s.h
* @author 正点原子团队(ALIENTEK)
* @version V1.0
* @date 2025-01-01
* @brief I2S驱动代码
* @license Copyright (c) 2020-2032, 广州市星翼电子科技有限公司
****************************************************************************************************
* @attention
*
* 实验平台:正点原子 ESP32-S3 开发板
* 在线视频:www.yuanzige.com
* 技术论坛:www.openedv.com
* 公司网址:www.alientek.com
* 购买地址:openedv.taobao.com
*
****************************************************************************************************
*/
#ifndef _MYI2S_H_
#define _MYI2S_H_
#include <stdio.h>
#include <string.h>
#include "driver/gpio.h"
#include "driver/i2s_std.h"
#define I2S_NUM (I2S_NUM_0) /* I2S port */
#define I2S_BCK_IO (GPIO_NUM_46) /* ES8388_SCLK */
#define I2S_WS_IO (GPIO_NUM_9) /* ES8388_LRCK */
#define I2S_DO_IO (GPIO_NUM_10) /* ES8388_SDIN */
#define I2S_DI_IO (GPIO_NUM_14) /* ES8388_SDOUT */
#define I2S_MCK_IO (GPIO_NUM_3) /* ES8388_MCLK */
#define I2S_RECV_BUF_SIZE (2400) /* 接收大小 */
#define I2S_SAMPLE_RATE (44100) /* 采样率 */
#define I2S_MCLK_MULTIPLE (256) /* 如果不使用24位数据宽度,256应该足够了 */
extern i2s_chan_handle_t tx_handle;
extern i2s_chan_handle_t rx_handle;
/* 函数声明 */
esp_err_t myi2s_init(void); /* I2S初始化 */
void i2s_trx_start(void); /* 启动I2S */
void i2s_trx_stop(void); /* 停止I2S */
void i2s_deinit(void); /* 卸载I2S */
size_t i2s_tx_write(uint8_t *buffer, uint32_t frame_size); /* I2S传输数据 */
size_t i2s_rx_read(uint8_t *buffer, uint32_t frame_size); /* I2S接收数据 */
void i2s_set_samplerate_bits_sample(int samplerate,int bits_sample);/* 设置采样率和位宽 */
#endif