Files
esp32s3_player/components/BSP/MYI2S/myi2s.c
T

222 lines
8.0 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
****************************************************************************************************
* @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;
}