Files

233 lines
8.3 KiB
C
Raw Permalink 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 = { /* 声道配置,保持 Philips I2S 标准相位,避免采样位移造成噪声 */
.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 = false, /* 保持 Philips I2S 标准对齐,避免噪声 */
.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 无
* @note 可重复调用(通道已关闭时直接返回),保证后续 reconfig 一定处于 READY 状态
*/
void i2s_trx_stop(void)
{
if (tx_handle != NULL)
{
esp_err_t err = i2s_channel_disable(tx_handle);
if ((err != ESP_OK) && (err != ESP_ERR_INVALID_STATE))
{
ESP_ERROR_CHECK(err);
}
}
if (rx_handle != NULL)
{
esp_err_t err = i2s_channel_disable(rx_handle);
if ((err != ESP_OK) && (err != ESP_ERR_INVALID_STATE))
{
ESP_ERROR_CHECK(err);
}
}
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; /* 24bit slot 必须是 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(); /* 必须在 READY 状态才能重配 */
/* 先更新我们自己的配置副本 */
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% 占空比) */
my_std_cfg.slot_cfg.left_align = false; /* Philips I2S 标准相位,左对齐会产生噪声 */
my_std_cfg.clk_cfg.sample_rate_hz = (uint32_t)samplerate;
my_std_cfg.clk_cfg.mclk_multiple = mclk_multiple;
ESP_ERROR_CHECK(i2s_channel_reconfig_std_clock(tx_handle, &my_std_cfg.clk_cfg));
ESP_ERROR_CHECK(i2s_channel_reconfig_std_slot(tx_handle, &my_std_cfg.slot_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;
}