/** **************************************************************************************************** * @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 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; }