add some code
This commit is contained in:
@@ -0,0 +1,223 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include "audio_codec_ctrl_if.h"
|
||||
#include "esp_codec_dev_defaults.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_idf_version.h"
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
#define TICK_PER_MS portTICK_PERIOD_MS
|
||||
#else
|
||||
#define TICK_PER_MS portTICK_RATE_MS
|
||||
#endif
|
||||
#define DEFAULT_I2C_CLOCK (100000)
|
||||
#define DEFAULT_I2C_TRANS_TIMEOUT (100)
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0) && !CONFIG_CODEC_I2C_BACKWARD_COMPATIBLE
|
||||
#include "driver/i2c_master.h"
|
||||
#define USE_IDF_I2C_MASTER
|
||||
#else
|
||||
#include "driver/i2c.h"
|
||||
#endif
|
||||
|
||||
#define TAG "I2C_If"
|
||||
typedef struct {
|
||||
audio_codec_ctrl_if_t base;
|
||||
bool is_open;
|
||||
uint8_t port;
|
||||
uint8_t addr;
|
||||
#ifdef USE_IDF_I2C_MASTER
|
||||
i2c_master_dev_handle_t dev_handle;
|
||||
#endif
|
||||
} i2c_ctrl_t;
|
||||
|
||||
static int _i2c_ctrl_open(const audio_codec_ctrl_if_t *ctrl, void *cfg, int cfg_size)
|
||||
{
|
||||
if (ctrl == NULL || cfg == NULL || cfg_size != sizeof(audio_codec_i2c_cfg_t)) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
i2c_ctrl_t *i2c_ctrl = (i2c_ctrl_t *) ctrl;
|
||||
audio_codec_i2c_cfg_t *i2c_cfg = (audio_codec_i2c_cfg_t *) cfg;
|
||||
i2c_ctrl->port = i2c_cfg->port;
|
||||
i2c_ctrl->addr = i2c_cfg->addr;
|
||||
#ifdef USE_IDF_I2C_MASTER
|
||||
if (i2c_cfg->bus_handle == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
i2c_device_config_t dev_cfg = {
|
||||
.dev_addr_length = I2C_ADDR_BIT_LEN_7,
|
||||
.device_address = (i2c_cfg->addr >> 1),
|
||||
.scl_speed_hz = DEFAULT_I2C_CLOCK,
|
||||
};
|
||||
int ret = i2c_master_bus_add_device(i2c_cfg->bus_handle, &dev_cfg, &i2c_ctrl->dev_handle);
|
||||
return (ret == ESP_OK) ? 0 : ESP_CODEC_DEV_DRV_ERR;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool _i2c_ctrl_is_open(const audio_codec_ctrl_if_t *ctrl)
|
||||
{
|
||||
if (ctrl) {
|
||||
i2c_ctrl_t *i2c_ctrl = (i2c_ctrl_t *) ctrl;
|
||||
return i2c_ctrl->is_open;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef USE_IDF_I2C_MASTER
|
||||
static int _i2c_master_read_reg(i2c_ctrl_t *i2c_ctrl, int addr, int addr_len, void *data, int data_len)
|
||||
{
|
||||
uint8_t addr_data[2] = {0};
|
||||
if (addr_len > 1) {
|
||||
addr_data[0] = addr >> 8;
|
||||
addr_data[1] = addr & 0xff;
|
||||
} else {
|
||||
addr_data[0] = addr & 0xff;
|
||||
}
|
||||
int ret = i2c_master_transmit_receive(i2c_ctrl->dev_handle, addr_data, addr_len, data, data_len, DEFAULT_I2C_TRANS_TIMEOUT);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Fail to read from dev %x", i2c_ctrl->addr);
|
||||
}
|
||||
return ret ? ESP_CODEC_DEV_READ_FAIL : ESP_CODEC_DEV_OK;
|
||||
}
|
||||
|
||||
static int _i2c_master_write_reg(i2c_ctrl_t *i2c_ctrl, int addr, int addr_len, void *data, int data_len)
|
||||
{
|
||||
esp_err_t ret = ESP_CODEC_DEV_NOT_SUPPORT;
|
||||
int len = addr_len + data_len;
|
||||
if (len <= 4) {
|
||||
// Not support write huge data
|
||||
uint8_t write_data[4] = {0};
|
||||
int i = 0;
|
||||
if (addr_len > 1) {
|
||||
write_data[i++] = addr >> 8;
|
||||
write_data[i++] = addr & 0xff;
|
||||
} else {
|
||||
write_data[i++] = addr & 0xff;
|
||||
}
|
||||
uint8_t *w = (uint8_t*)data;
|
||||
while (i < len) {
|
||||
write_data[i++] = *(w++);
|
||||
}
|
||||
ret = i2c_master_transmit(i2c_ctrl->dev_handle, write_data, len, DEFAULT_I2C_TRANS_TIMEOUT);
|
||||
}
|
||||
if (ret != 0) {
|
||||
ESP_LOGE(TAG, "Fail to write to dev %x", i2c_ctrl->addr);
|
||||
}
|
||||
return ret ? ESP_CODEC_DEV_WRITE_FAIL : ESP_CODEC_DEV_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int _i2c_ctrl_read_reg(const audio_codec_ctrl_if_t *ctrl, int addr, int addr_len, void *data, int data_len)
|
||||
{
|
||||
if (ctrl == NULL || data == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
i2c_ctrl_t *i2c_ctrl = (i2c_ctrl_t *) ctrl;
|
||||
if (i2c_ctrl->is_open == false) {
|
||||
return ESP_CODEC_DEV_WRONG_STATE;
|
||||
}
|
||||
#ifdef USE_IDF_I2C_MASTER
|
||||
return _i2c_master_read_reg(i2c_ctrl, addr, addr_len, data, data_len);
|
||||
#else
|
||||
esp_err_t ret = ESP_OK;
|
||||
i2c_cmd_handle_t cmd;
|
||||
cmd = i2c_cmd_link_create();
|
||||
ret |= i2c_master_start(cmd);
|
||||
ret |= i2c_master_write_byte(cmd, i2c_ctrl->addr, 1);
|
||||
ret |= i2c_master_write(cmd, (uint8_t *) &addr, addr_len, 1);
|
||||
ret |= i2c_master_stop(cmd);
|
||||
ret |= i2c_master_cmd_begin(i2c_ctrl->port, cmd, DEFAULT_I2C_TRANS_TIMEOUT / TICK_PER_MS);
|
||||
i2c_cmd_link_delete(cmd);
|
||||
|
||||
cmd = i2c_cmd_link_create();
|
||||
ret |= i2c_master_start(cmd);
|
||||
ret |= i2c_master_write_byte(cmd, i2c_ctrl->addr | 0x01, 1);
|
||||
|
||||
for (int i = 0; i < data_len - 1; i++) {
|
||||
ret |= i2c_master_read_byte(cmd, (uint8_t *) data + i, 0);
|
||||
}
|
||||
ret |= i2c_master_read_byte(cmd, (uint8_t *) data + (data_len - 1), 1);
|
||||
|
||||
ret |= i2c_master_stop(cmd);
|
||||
ret |= i2c_master_cmd_begin(i2c_ctrl->port, cmd, DEFAULT_I2C_TRANS_TIMEOUT / TICK_PER_MS);
|
||||
i2c_cmd_link_delete(cmd);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Fail to read from dev %x", i2c_ctrl->addr);
|
||||
}
|
||||
return ret ? ESP_CODEC_DEV_READ_FAIL : ESP_CODEC_DEV_OK;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int _i2c_ctrl_write_reg(const audio_codec_ctrl_if_t *ctrl, int addr, int addr_len, void *data, int data_len)
|
||||
{
|
||||
i2c_ctrl_t *i2c_ctrl = (i2c_ctrl_t *) ctrl;
|
||||
if (ctrl == NULL || data == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
if (i2c_ctrl->is_open == false) {
|
||||
return ESP_CODEC_DEV_WRONG_STATE;
|
||||
}
|
||||
#ifdef USE_IDF_I2C_MASTER
|
||||
return _i2c_master_write_reg(i2c_ctrl, addr, addr_len, data, data_len);
|
||||
#else
|
||||
esp_err_t ret = ESP_OK;
|
||||
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
||||
ret |= i2c_master_start(cmd);
|
||||
ret |= i2c_master_write_byte(cmd, i2c_ctrl->addr, 1);
|
||||
ret |= i2c_master_write(cmd, (uint8_t *) &addr, addr_len, 1);
|
||||
if (data_len) {
|
||||
ret |= i2c_master_write(cmd, data, data_len, 1);
|
||||
}
|
||||
ret |= i2c_master_stop(cmd);
|
||||
ret |= i2c_master_cmd_begin(i2c_ctrl->port, cmd, DEFAULT_I2C_TRANS_TIMEOUT / TICK_PER_MS);
|
||||
if (ret != 0) {
|
||||
ESP_LOGE(TAG, "Fail to write to dev %x", i2c_ctrl->addr);
|
||||
}
|
||||
i2c_cmd_link_delete(cmd);
|
||||
return ret ? ESP_CODEC_DEV_WRITE_FAIL : ESP_CODEC_DEV_OK;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int _i2c_ctrl_close(const audio_codec_ctrl_if_t *ctrl)
|
||||
{
|
||||
if (ctrl == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
i2c_ctrl_t *i2c_ctrl = (i2c_ctrl_t *) ctrl;
|
||||
#ifdef USE_IDF_I2C_MASTER
|
||||
if (i2c_ctrl->dev_handle) {
|
||||
i2c_master_bus_rm_device(i2c_ctrl->dev_handle);
|
||||
}
|
||||
#endif
|
||||
i2c_ctrl->is_open = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
const audio_codec_ctrl_if_t *audio_codec_new_i2c_ctrl(audio_codec_i2c_cfg_t *i2c_cfg)
|
||||
{
|
||||
if (i2c_cfg == NULL) {
|
||||
ESP_LOGE(TAG, "Bad configuration");
|
||||
return NULL;
|
||||
}
|
||||
i2c_ctrl_t *ctrl = calloc(1, sizeof(i2c_ctrl_t));
|
||||
if (ctrl == NULL) {
|
||||
ESP_LOGE(TAG, "No memory for instance");
|
||||
return NULL;
|
||||
}
|
||||
ctrl->base.open = _i2c_ctrl_open;
|
||||
ctrl->base.is_open = _i2c_ctrl_is_open;
|
||||
ctrl->base.read_reg = _i2c_ctrl_read_reg;
|
||||
ctrl->base.write_reg = _i2c_ctrl_write_reg;
|
||||
ctrl->base.close = _i2c_ctrl_close;
|
||||
int ret = _i2c_ctrl_open(&ctrl->base, i2c_cfg, sizeof(audio_codec_i2c_cfg_t));
|
||||
if (ret != 0) {
|
||||
free(ctrl);
|
||||
return NULL;
|
||||
}
|
||||
ctrl->is_open = true;
|
||||
return &ctrl->base;
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include "audio_codec_ctrl_if.h"
|
||||
#include "esp_codec_dev_defaults.h"
|
||||
#include "driver/spi_common.h"
|
||||
#include "driver/spi_master.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
#define TAG "SPI_If"
|
||||
|
||||
typedef struct {
|
||||
audio_codec_ctrl_if_t base;
|
||||
bool is_open;
|
||||
uint8_t port;
|
||||
spi_device_handle_t spi_handle;
|
||||
} spi_ctrl_t;
|
||||
|
||||
int _spi_ctrl_open(const audio_codec_ctrl_if_t *ctrl, void *cfg, int cfg_size)
|
||||
{
|
||||
if (ctrl == NULL || cfg == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
if (cfg_size != sizeof(audio_codec_spi_cfg_t)) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
spi_ctrl_t *spi_ctrl = (spi_ctrl_t *) ctrl;
|
||||
audio_codec_spi_cfg_t *spi_cfg = (audio_codec_spi_cfg_t *) cfg;
|
||||
int speed = spi_cfg->clock_speed ? spi_cfg->clock_speed : 1000000;
|
||||
spi_device_interface_config_t dev_cfg = {
|
||||
.clock_speed_hz = speed, // Clock out at 10 MHz
|
||||
.mode = 0, // SPI mode 0
|
||||
.queue_size = 6, // queue 7 transactions at a time
|
||||
};
|
||||
dev_cfg.spics_io_num = spi_cfg->cs_pin;
|
||||
#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 4))
|
||||
spi_host_device_t host_id = SPI2_HOST;
|
||||
#elif (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 0, 0))
|
||||
spi_host_device_t host_id = SPI3_HOST;
|
||||
#else
|
||||
spi_host_device_t host_id = HSPI_HOST;
|
||||
#endif
|
||||
int ret = spi_bus_add_device(host_id, &dev_cfg, &spi_ctrl->spi_handle);
|
||||
if (ret == 0) {
|
||||
gpio_set_pull_mode(spi_cfg->cs_pin, GPIO_FLOATING);
|
||||
spi_ctrl->is_open = true;
|
||||
}
|
||||
return ret == 0 ? ESP_CODEC_DEV_OK : ESP_CODEC_DEV_DRV_ERR;
|
||||
}
|
||||
|
||||
bool _spi_ctrl_is_open(const audio_codec_ctrl_if_t *ctrl)
|
||||
{
|
||||
if (ctrl) {
|
||||
spi_ctrl_t *spi_ctrl = (spi_ctrl_t *) ctrl;
|
||||
return spi_ctrl->is_open;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static int _spi_ctrl_read_reg(const audio_codec_ctrl_if_t *ctrl, int addr, int addr_len, void *data, int data_len)
|
||||
{
|
||||
if (ctrl == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
spi_ctrl_t *spi_ctrl = (spi_ctrl_t *) ctrl;
|
||||
if (spi_ctrl->is_open == false) {
|
||||
return ESP_CODEC_DEV_WRONG_STATE;
|
||||
}
|
||||
esp_err_t ret = ESP_OK;
|
||||
if (addr_len) {
|
||||
uint16_t *v = (uint16_t *) &addr;
|
||||
while (addr_len >= 2) {
|
||||
spi_transaction_t t = {0};
|
||||
t.length = 2 * 8;
|
||||
t.tx_buffer = v;
|
||||
ret = spi_device_transmit(spi_ctrl->spi_handle, &t);
|
||||
v++;
|
||||
addr_len -= 2;
|
||||
}
|
||||
}
|
||||
if (data_len) {
|
||||
spi_transaction_t t = {0};
|
||||
t.length = data_len * 8;
|
||||
t.rxlength = data_len * 8;
|
||||
t.rx_buffer = data;
|
||||
ret = spi_device_transmit(spi_ctrl->spi_handle, &t);
|
||||
}
|
||||
return ret == ESP_OK ? ESP_CODEC_DEV_OK : ESP_CODEC_DEV_DRV_ERR;
|
||||
}
|
||||
|
||||
static int _spi_ctrl_write_reg(const audio_codec_ctrl_if_t *ctrl, int addr, int addr_len, void *data, int data_len)
|
||||
{
|
||||
spi_ctrl_t *spi_ctrl = (spi_ctrl_t *) ctrl;
|
||||
if (ctrl == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
if (spi_ctrl->is_open == false) {
|
||||
return ESP_CODEC_DEV_WRONG_STATE;
|
||||
}
|
||||
esp_err_t ret = ESP_OK;
|
||||
if (addr_len) {
|
||||
uint16_t *v = (uint16_t *) &addr;
|
||||
while (addr_len >= 2) {
|
||||
spi_transaction_t t = {0};
|
||||
t.length = 2 * 8;
|
||||
t.tx_buffer = v;
|
||||
ret = spi_device_transmit(spi_ctrl->spi_handle, &t);
|
||||
v++;
|
||||
addr_len -= 2;
|
||||
}
|
||||
}
|
||||
if (data_len) {
|
||||
spi_transaction_t t = {0};
|
||||
t.length = data_len * 8;
|
||||
t.tx_buffer = data;
|
||||
ret = spi_device_transmit(spi_ctrl->spi_handle, &t);
|
||||
}
|
||||
return ret == ESP_OK ? ESP_CODEC_DEV_OK : ESP_CODEC_DEV_DRV_ERR;
|
||||
}
|
||||
|
||||
int _spi_ctrl_close(const audio_codec_ctrl_if_t *ctrl)
|
||||
{
|
||||
spi_ctrl_t *spi_ctrl = (spi_ctrl_t *) ctrl;
|
||||
if (ctrl == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
int ret = 0;
|
||||
if (spi_ctrl->spi_handle) {
|
||||
ret = spi_bus_remove_device(spi_ctrl->spi_handle);
|
||||
}
|
||||
spi_ctrl->is_open = false;
|
||||
return ret == ESP_OK ? ESP_CODEC_DEV_OK : ESP_CODEC_DEV_DRV_ERR;
|
||||
}
|
||||
|
||||
const audio_codec_ctrl_if_t *audio_codec_new_spi_ctrl(audio_codec_spi_cfg_t *spi_cfg)
|
||||
{
|
||||
spi_ctrl_t *ctrl = calloc(1, sizeof(spi_ctrl_t));
|
||||
if (ctrl == NULL) {
|
||||
ESP_LOGE(TAG, "No memory for instance");
|
||||
return NULL;
|
||||
}
|
||||
ctrl->base.open = _spi_ctrl_open;
|
||||
ctrl->base.is_open = _spi_ctrl_is_open;
|
||||
ctrl->base.read_reg = _spi_ctrl_read_reg;
|
||||
ctrl->base.write_reg = _spi_ctrl_write_reg;
|
||||
ctrl->base.close = _spi_ctrl_close;
|
||||
int ret = _spi_ctrl_open(&ctrl->base, spi_cfg, sizeof(audio_codec_spi_cfg_t));
|
||||
if (ret != 0) {
|
||||
ESP_LOGE(TAG, "Fail to open SPI driver");
|
||||
free(ctrl);
|
||||
return NULL;
|
||||
}
|
||||
return &ctrl->base;
|
||||
}
|
||||
@@ -0,0 +1,565 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "audio_codec_data_if.h"
|
||||
#include "esp_codec_dev_defaults.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "esp_idf_version.h"
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
#include "driver/i2s_std.h"
|
||||
#include "driver/i2s_tdm.h"
|
||||
#include "driver/i2s_pdm.h"
|
||||
#else
|
||||
#include "driver/i2s.h"
|
||||
#endif
|
||||
#include "esp_codec_dev_os.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
#define TAG "I2S_IF"
|
||||
|
||||
typedef struct {
|
||||
audio_codec_data_if_t base;
|
||||
bool is_open;
|
||||
uint8_t port;
|
||||
void *out_handle;
|
||||
void *in_handle;
|
||||
bool out_enable;
|
||||
bool in_enable;
|
||||
bool in_disable_pending;
|
||||
bool out_disable_pending;
|
||||
bool in_reconfig;
|
||||
bool out_reconfig;
|
||||
esp_codec_dev_sample_info_t in_fs;
|
||||
esp_codec_dev_sample_info_t out_fs;
|
||||
esp_codec_dev_sample_info_t fs;
|
||||
} i2s_data_t;
|
||||
|
||||
static bool _i2s_valid_fmt(esp_codec_dev_sample_info_t *fs)
|
||||
{
|
||||
if (fs->sample_rate == 0 ||
|
||||
fs->sample_rate >= 192000) {
|
||||
ESP_LOGE(TAG, "Bad sample_rate %d", (int) fs->sample_rate);
|
||||
}
|
||||
if (fs->channel == 0 ||
|
||||
(fs->channel >> 1 << 1) != fs->channel) {
|
||||
ESP_LOGE(TAG, "Not support channel %d", fs->channel);
|
||||
return false;
|
||||
}
|
||||
if (fs->bits_per_sample < 8 || fs->bits_per_sample > 32 ||
|
||||
(fs->bits_per_sample >> 3 << 3) != fs->bits_per_sample) {
|
||||
ESP_LOGE(TAG, "Not support bits_per_sample %d", fs->bits_per_sample);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static int _i2s_drv_enable(i2s_data_t *i2s_data, bool playback, bool enable)
|
||||
{
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
i2s_chan_handle_t channel = (i2s_chan_handle_t) (
|
||||
playback ? i2s_data->out_handle : i2s_data->in_handle);
|
||||
if (channel == NULL) {
|
||||
return ESP_CODEC_DEV_NOT_FOUND;
|
||||
}
|
||||
int ret;
|
||||
if (enable) {
|
||||
ret = i2s_channel_enable(channel);
|
||||
} else {
|
||||
ret = i2s_channel_disable(channel);
|
||||
}
|
||||
return ret == ESP_OK ? ESP_CODEC_DEV_OK : ESP_CODEC_DEV_DRV_ERR;
|
||||
#endif
|
||||
return ESP_CODEC_DEV_OK;
|
||||
}
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
static uint8_t get_active_channel(esp_codec_dev_sample_info_t *fs)
|
||||
{
|
||||
if (fs->channel_mask == 0) {
|
||||
return fs->channel;
|
||||
}
|
||||
int channel = 0;
|
||||
uint16_t mask = fs->channel_mask;
|
||||
while (mask > 0) {
|
||||
if (mask & 1) {
|
||||
channel++;
|
||||
}
|
||||
mask >>= 1;
|
||||
}
|
||||
return channel;
|
||||
}
|
||||
|
||||
static uint8_t get_bits(i2s_data_t *i2s_data, bool playback)
|
||||
{
|
||||
uint8_t total_bits = i2s_data->fs.bits_per_sample * i2s_data->fs.channel;
|
||||
if (playback) {
|
||||
return total_bits / i2s_data->out_fs.channel;
|
||||
}
|
||||
return total_bits / i2s_data->in_fs.channel;
|
||||
}
|
||||
|
||||
static int set_drv_fs(i2s_chan_handle_t channel, bool playback, uint8_t slot_bits, esp_codec_dev_sample_info_t *fs)
|
||||
{
|
||||
i2s_chan_info_t channel_info = {0};
|
||||
int ret = ESP_CODEC_DEV_OK;
|
||||
i2s_channel_get_info(channel, &channel_info);
|
||||
ESP_LOGI(TAG, "channel mode %d bits:%d/%d channel:%d mask:%x",
|
||||
channel_info.mode, fs->bits_per_sample, slot_bits, (int)fs->channel, (int)fs->channel_mask);
|
||||
switch (channel_info.mode) {
|
||||
case I2S_COMM_MODE_STD: {
|
||||
uint8_t bits = fs->bits_per_sample;
|
||||
uint8_t active_channel = get_active_channel(fs);
|
||||
uint16_t channel_mask = fs->channel_mask;
|
||||
if (fs->channel > 2) {
|
||||
slot_bits = slot_bits * fs->channel / 2;
|
||||
active_channel = 2;
|
||||
bits = slot_bits;
|
||||
channel_mask = 0;
|
||||
}
|
||||
i2s_std_slot_mask_t slot_mask = fs->channel_mask ?
|
||||
(i2s_std_slot_mask_t) fs->channel_mask : I2S_STD_SLOT_BOTH;
|
||||
i2s_std_slot_config_t slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(slot_bits, active_channel);
|
||||
slot_cfg.slot_mask = slot_mask;
|
||||
if (slot_bits > bits) {
|
||||
slot_cfg.data_bit_width = bits;
|
||||
slot_cfg.slot_bit_width = slot_bits;
|
||||
}
|
||||
i2s_std_clk_config_t clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(fs->sample_rate);
|
||||
if (fs->mclk_multiple) {
|
||||
clk_cfg.mclk_multiple = fs->mclk_multiple;
|
||||
}
|
||||
if (slot_bits == 24 && (clk_cfg.mclk_multiple % 3) != 0) {
|
||||
clk_cfg.mclk_multiple = I2S_MCLK_MULTIPLE_384;
|
||||
}
|
||||
ret = i2s_channel_reconfig_std_slot(channel, &slot_cfg);
|
||||
if (ret != ESP_OK) {
|
||||
*(int *) 0 = 0;
|
||||
return ESP_CODEC_DEV_DRV_ERR;
|
||||
}
|
||||
ret = i2s_channel_reconfig_std_clock(channel, &clk_cfg);
|
||||
ESP_LOGI(TAG, "STD Mode %d bits:%d/%d channel:%d sample_rate:%d mask:%x",
|
||||
playback, bits, slot_bits, fs->channel,
|
||||
(int)fs->sample_rate, channel_mask);
|
||||
}
|
||||
break;
|
||||
#if SOC_I2S_SUPPORTS_PDM
|
||||
case I2S_COMM_MODE_PDM: {
|
||||
if (playback == false) {
|
||||
#if SOC_I2S_SUPPORTS_PDM_RX
|
||||
i2s_pdm_rx_clk_config_t clk_cfg = I2S_PDM_RX_CLK_DEFAULT_CONFIG(fs->sample_rate);
|
||||
i2s_pdm_rx_slot_config_t slot_cfg = I2S_PDM_RX_SLOT_DEFAULT_CONFIG(slot_bits, I2S_SLOT_MODE_STEREO);
|
||||
i2s_pdm_slot_mask_t slot_mask = fs->channel_mask ?
|
||||
(i2s_pdm_slot_mask_t) fs->channel_mask : I2S_PDM_SLOT_BOTH;
|
||||
// Stereo channel mask is ignored in driver, need use mono instead
|
||||
if (fs->channel_mask && fs->channel_mask < 3) {
|
||||
slot_cfg.slot_mode = I2S_SLOT_MODE_MONO;
|
||||
}
|
||||
slot_cfg.slot_mask = slot_mask;
|
||||
if (slot_bits > fs->bits_per_sample) {
|
||||
slot_cfg.data_bit_width = fs->bits_per_sample;
|
||||
slot_cfg.slot_bit_width = slot_bits;
|
||||
}
|
||||
ret = i2s_channel_reconfig_pdm_rx_clock(channel, &clk_cfg);
|
||||
if (ret != ESP_OK) {
|
||||
return ESP_CODEC_DEV_DRV_ERR;
|
||||
}
|
||||
ret = i2s_channel_reconfig_pdm_rx_slot(channel, &slot_cfg);
|
||||
if (ret != ESP_OK) {
|
||||
return ESP_CODEC_DEV_DRV_ERR;
|
||||
}
|
||||
#else
|
||||
ESP_LOGE(TAG, "PDM RX not supported");
|
||||
return ESP_CODEC_DEV_NOT_SUPPORT;
|
||||
#endif
|
||||
} else {
|
||||
#if SOC_I2S_SUPPORTS_PDM_TX
|
||||
i2s_pdm_tx_clk_config_t clk_cfg = I2S_PDM_TX_CLK_DEFAULT_CONFIG(fs->sample_rate);
|
||||
clk_cfg.up_sample_fs = fs->sample_rate / 100;
|
||||
i2s_pdm_tx_slot_config_t slot_cfg = I2S_PDM_TX_SLOT_DEFAULT_CONFIG(slot_bits, I2S_SLOT_MODE_STEREO);
|
||||
// Stereo channel mask is ignored, need use mono instead
|
||||
if (fs->channel_mask && fs->channel_mask < 3) {
|
||||
slot_cfg.slot_mode = I2S_SLOT_MODE_MONO;
|
||||
}
|
||||
#if SOC_I2S_HW_VERSION_1
|
||||
i2s_pdm_slot_mask_t slot_mask = fs->channel_mask ?
|
||||
(i2s_pdm_slot_mask_t) fs->channel_mask : I2S_PDM_SLOT_BOTH;
|
||||
slot_cfg.slot_mask = slot_mask;
|
||||
#endif
|
||||
if (slot_bits > fs->bits_per_sample) {
|
||||
slot_cfg.data_bit_width = fs->bits_per_sample;
|
||||
slot_cfg.slot_bit_width = slot_bits;
|
||||
}
|
||||
ret = i2s_channel_reconfig_pdm_tx_clock(channel, &clk_cfg);
|
||||
if (ret != ESP_OK) {
|
||||
return ESP_CODEC_DEV_DRV_ERR;
|
||||
}
|
||||
ret = i2s_channel_reconfig_pdm_tx_slot(channel, &slot_cfg);
|
||||
if (ret != ESP_OK) {
|
||||
return ESP_CODEC_DEV_DRV_ERR;
|
||||
}
|
||||
#else
|
||||
ESP_LOGE(TAG, "PDM TX not supported");
|
||||
return ESP_CODEC_DEV_NOT_SUPPORT;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
#if SOC_I2S_SUPPORTS_TDM
|
||||
case I2S_COMM_MODE_TDM: {
|
||||
i2s_tdm_clk_config_t clk_cfg = I2S_TDM_CLK_DEFAULT_CONFIG(fs->sample_rate);
|
||||
if (slot_bits == 24) {
|
||||
clk_cfg.mclk_multiple = I2S_MCLK_MULTIPLE_384;
|
||||
}
|
||||
i2s_tdm_slot_config_t slot_cfg = I2S_TDM_PHILIPS_SLOT_DEFAULT_CONFIG(
|
||||
slot_bits,
|
||||
I2S_SLOT_MODE_STEREO,
|
||||
(i2s_tdm_slot_mask_t)fs->channel_mask);
|
||||
slot_cfg.total_slot = fs->channel;
|
||||
if (slot_bits > fs->bits_per_sample) {
|
||||
slot_cfg.data_bit_width = fs->bits_per_sample;
|
||||
slot_cfg.slot_bit_width = slot_bits;
|
||||
}
|
||||
ret = i2s_channel_reconfig_tdm_slot(channel, &slot_cfg);
|
||||
if (ret != ESP_OK) {
|
||||
return ESP_CODEC_DEV_DRV_ERR;
|
||||
}
|
||||
ret = i2s_channel_reconfig_tdm_clock(channel, &clk_cfg);
|
||||
if (ret != ESP_OK) {
|
||||
return ESP_CODEC_DEV_DRV_ERR;
|
||||
}
|
||||
ESP_LOGI(TAG, "TDM Mode %d bits:%d/%d channel:%d sample_rate:%d mask:%x",
|
||||
playback, fs->bits_per_sample, slot_bits, fs->channel,
|
||||
(int)fs->sample_rate, fs->channel_mask);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
return ESP_CODEC_DEV_NOT_SUPPORT;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_fs(i2s_data_t *i2s_data, bool playback, bool skip)
|
||||
{
|
||||
i2s_chan_handle_t channel = (i2s_chan_handle_t) playback ? i2s_data->out_handle : i2s_data->in_handle;
|
||||
i2s_chan_info_t channel_info = {0};
|
||||
esp_codec_dev_sample_info_t *fs = playback ? &i2s_data->out_fs : &i2s_data->in_fs;
|
||||
uint8_t bits_per_sample = get_bits(i2s_data, playback);
|
||||
i2s_channel_get_info(channel, &channel_info);
|
||||
int ret = set_drv_fs(channel, playback, bits_per_sample, fs);
|
||||
if (ret != ESP_CODEC_DEV_OK) {
|
||||
return ret;
|
||||
}
|
||||
// Set RX clock will not take effect if in full duplex mode, need update TX clock also
|
||||
if (skip == false && playback == false && i2s_data->out_handle != NULL && i2s_data->out_enable == false) {
|
||||
// TX is master, set to RX not take effect need reconfig TX also
|
||||
channel = (i2s_chan_handle_t) i2s_data->out_handle;
|
||||
_i2s_drv_enable(i2s_data, true, false);
|
||||
ret = set_drv_fs(channel, true, bits_per_sample, fs);
|
||||
_i2s_drv_enable(i2s_data, true, true);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int check_fs_compatible(i2s_data_t *i2s_data, bool playback, esp_codec_dev_sample_info_t *fs)
|
||||
{
|
||||
// Set fs directly when only enable one channel
|
||||
esp_codec_dev_sample_info_t *channel_fs = playback ? &i2s_data->out_fs : &i2s_data->in_fs;
|
||||
if ((playback && i2s_data->in_enable == false) ||
|
||||
(!playback && i2s_data->out_enable == false)) {
|
||||
memcpy(&i2s_data->fs, fs, sizeof(esp_codec_dev_sample_info_t));
|
||||
memcpy(channel_fs, fs, sizeof(esp_codec_dev_sample_info_t));
|
||||
return set_fs(i2s_data, playback, false);
|
||||
}
|
||||
if (fs->sample_rate != i2s_data->fs.sample_rate) {
|
||||
ESP_LOGE(TAG, "Mode %d conflict sample_rate %d with %d",
|
||||
playback, (int)fs->sample_rate, (int)i2s_data->fs.sample_rate);
|
||||
return ESP_CODEC_DEV_NOT_SUPPORT;
|
||||
}
|
||||
// Channel and bits same, set directly
|
||||
if (fs->channel == i2s_data->fs.channel &&
|
||||
fs->bits_per_sample == i2s_data->fs.bits_per_sample) {
|
||||
memcpy(channel_fs, fs, sizeof(esp_codec_dev_sample_info_t));
|
||||
return set_fs(i2s_data, playback, false);
|
||||
}
|
||||
memcpy(channel_fs, fs, sizeof(esp_codec_dev_sample_info_t));
|
||||
uint16_t want_bits = fs->channel * fs->bits_per_sample;
|
||||
uint16_t run_bits = i2s_data->fs.channel * i2s_data->fs.bits_per_sample;
|
||||
int ret;
|
||||
// Need expand peer channel bits
|
||||
ESP_LOGI(TAG, "Mode %d need extend bits %d to %d", !playback, run_bits, want_bits);
|
||||
do {
|
||||
if (want_bits > run_bits) {
|
||||
if (playback == false) {
|
||||
i2s_data->out_reconfig = true;
|
||||
} else {
|
||||
i2s_data->in_reconfig = true;
|
||||
}
|
||||
ret = _i2s_drv_enable(i2s_data, !playback, false);
|
||||
if (ret != ESP_CODEC_DEV_OK) {
|
||||
break;
|
||||
}
|
||||
memcpy(&i2s_data->fs, fs, sizeof(esp_codec_dev_sample_info_t));
|
||||
}
|
||||
// Need set fs before enable
|
||||
ret = set_fs(i2s_data, playback, false);
|
||||
if (ret != ESP_CODEC_DEV_OK) {
|
||||
break;
|
||||
}
|
||||
if (want_bits > run_bits) {
|
||||
ret = set_fs(i2s_data, !playback, true);
|
||||
if (ret != ESP_CODEC_DEV_OK) {
|
||||
break;
|
||||
}
|
||||
ret = _i2s_drv_enable(i2s_data, !playback, true);
|
||||
if (ret != ESP_CODEC_DEV_OK) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (0);
|
||||
i2s_data->out_reconfig = i2s_data->in_reconfig = false;
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int _i2s_data_open(const audio_codec_data_if_t *h, void *data_cfg, int cfg_size)
|
||||
{
|
||||
i2s_data_t *i2s_data = (i2s_data_t *) h;
|
||||
if (h == NULL || data_cfg == NULL || cfg_size != sizeof(audio_codec_i2s_cfg_t)) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
audio_codec_i2s_cfg_t *i2s_cfg = (audio_codec_i2s_cfg_t *) data_cfg;
|
||||
i2s_data->is_open = true;
|
||||
i2s_data->port = i2s_cfg->port;
|
||||
i2s_data->out_handle = i2s_cfg->tx_handle;
|
||||
i2s_data->in_handle = i2s_cfg->rx_handle;
|
||||
return ESP_CODEC_DEV_OK;
|
||||
}
|
||||
|
||||
static bool _i2s_data_is_open(const audio_codec_data_if_t *h)
|
||||
{
|
||||
i2s_data_t *i2s_data = (i2s_data_t *) h;
|
||||
if (i2s_data) {
|
||||
return i2s_data->is_open;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static int _i2s_data_enable(const audio_codec_data_if_t *h, esp_codec_dev_type_t dev_type, bool enable)
|
||||
{
|
||||
i2s_data_t *i2s_data = (i2s_data_t *) h;
|
||||
if (i2s_data == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
if (i2s_data->is_open == false) {
|
||||
return ESP_CODEC_DEV_WRONG_STATE;
|
||||
}
|
||||
int ret = ESP_CODEC_DEV_OK;
|
||||
if (dev_type == ESP_CODEC_DEV_TYPE_IN_OUT) {
|
||||
ret = _i2s_drv_enable(i2s_data, true, enable);
|
||||
ret = _i2s_drv_enable(i2s_data, false, enable);
|
||||
} else {
|
||||
bool playback = dev_type & ESP_CODEC_DEV_TYPE_OUT ? true : false;
|
||||
// When RX is working TX disable should be blocked
|
||||
if (enable == false && i2s_data->in_enable && playback && i2s_data->out_handle) {
|
||||
ESP_LOGI(TAG, "Pending out channel for in channel running");
|
||||
i2s_data->out_disable_pending = true;
|
||||
}
|
||||
#if SOC_I2S_HW_VERSION_1
|
||||
// For ESP32 and ESP32S3 if disable RX, TX also not work need pending until TX not used
|
||||
else if (enable == false && i2s_data->out_enable && playback == false && i2s_data->in_handle) {
|
||||
ESP_LOGI(TAG, "Pending in channel for out channel running");
|
||||
i2s_data->in_disable_pending = true;
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
ret = _i2s_drv_enable(i2s_data, playback, enable);
|
||||
// Disable TX when RX disable if TX disable is pending
|
||||
if (enable == false) {
|
||||
if (playback == false && i2s_data->out_disable_pending) {
|
||||
ret = _i2s_drv_enable(i2s_data, true, enable);
|
||||
i2s_data->out_disable_pending = false;
|
||||
}
|
||||
if (playback == true && i2s_data->in_disable_pending) {
|
||||
ret = _i2s_drv_enable(i2s_data, false, enable);
|
||||
i2s_data->in_disable_pending = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (dev_type & ESP_CODEC_DEV_TYPE_IN) {
|
||||
i2s_data->in_enable = enable;
|
||||
}
|
||||
if (dev_type & ESP_CODEC_DEV_TYPE_OUT) {
|
||||
i2s_data->out_enable = enable;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int _i2s_data_set_fmt(const audio_codec_data_if_t *h, esp_codec_dev_type_t dev_type, esp_codec_dev_sample_info_t *fs)
|
||||
{
|
||||
i2s_data_t *i2s_data = (i2s_data_t *) h;
|
||||
if (i2s_data == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
if (i2s_data->is_open == false) {
|
||||
return ESP_CODEC_DEV_WRONG_STATE;
|
||||
}
|
||||
esp_codec_dev_sample_info_t eq_fs;
|
||||
if (fs->channel == 1) {
|
||||
// When using one channel replace to select channel 0 in default
|
||||
memcpy(&eq_fs, fs, sizeof(esp_codec_dev_sample_info_t));
|
||||
fs = &eq_fs;
|
||||
fs->channel = 2;
|
||||
fs->channel_mask = ESP_CODEC_DEV_MAKE_CHANNEL_MASK(0);
|
||||
}
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
if (fs->channel_mask == 0) {
|
||||
// Add channel mask automatically when not set
|
||||
memcpy(&eq_fs, fs, sizeof(esp_codec_dev_sample_info_t));
|
||||
fs = &eq_fs;
|
||||
for (int i = 0; i < fs->channel; i++) {
|
||||
fs->channel_mask |= ESP_CODEC_DEV_MAKE_CHANNEL_MASK(i);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (_i2s_valid_fmt(fs) == false) {
|
||||
return ESP_CODEC_DEV_NOT_SUPPORT;
|
||||
}
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
// disable internally
|
||||
if (dev_type & ESP_CODEC_DEV_TYPE_OUT) {
|
||||
_i2s_drv_enable(i2s_data, true, false);
|
||||
}
|
||||
if (dev_type & ESP_CODEC_DEV_TYPE_IN) {
|
||||
_i2s_drv_enable(i2s_data, false, false);
|
||||
}
|
||||
int ret;
|
||||
if ((dev_type & ESP_CODEC_DEV_TYPE_IN) != 0 &&
|
||||
(dev_type & ESP_CODEC_DEV_TYPE_OUT) != 0) {
|
||||
// Device support playback and record at same time
|
||||
memcpy(&i2s_data->fs, fs, sizeof(esp_codec_dev_sample_info_t));
|
||||
memcpy(&i2s_data->in_fs, fs, sizeof(esp_codec_dev_sample_info_t));
|
||||
memcpy(&i2s_data->out_fs, fs, sizeof(esp_codec_dev_sample_info_t));
|
||||
ret = set_fs(i2s_data, true, true);
|
||||
ret = set_fs(i2s_data, false, true);
|
||||
} else {
|
||||
ret = check_fs_compatible(i2s_data, dev_type & ESP_CODEC_DEV_TYPE_OUT ? true : false, fs);
|
||||
}
|
||||
return ret;
|
||||
#else
|
||||
// When use multichannel data
|
||||
if (fs->channel_mask) {
|
||||
i2s_channel_t sel_channel = 0;
|
||||
#if SOC_I2S_SUPPORTS_TDM
|
||||
sel_channel = (i2s_channel_t)(fs->channel_mask << 16);
|
||||
#else
|
||||
if (fs->channel_mask == ESP_CODEC_DEV_MAKE_CHANNEL_MASK(0)) {
|
||||
sel_channel = 1;
|
||||
} else {
|
||||
ESP_LOGE(TAG, "IC not support TDM");
|
||||
return ESP_CODEC_DEV_NOT_FOUND;
|
||||
}
|
||||
#endif
|
||||
i2s_set_clk(i2s_data->port, fs->sample_rate, fs->bits_per_sample, sel_channel);
|
||||
} else {
|
||||
i2s_set_clk(i2s_data->port, fs->sample_rate, fs->bits_per_sample, fs->channel);
|
||||
}
|
||||
i2s_zero_dma_buffer(i2s_data->port);
|
||||
memcpy(&i2s_data->fs, fs, sizeof(esp_codec_dev_sample_info_t));
|
||||
#endif
|
||||
return ESP_CODEC_DEV_OK;
|
||||
}
|
||||
|
||||
static int _i2s_data_read(const audio_codec_data_if_t *h, uint8_t *data, int size)
|
||||
{
|
||||
i2s_data_t *i2s_data = (i2s_data_t *) h;
|
||||
if (i2s_data == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
if (i2s_data->is_open == false) {
|
||||
return ESP_CODEC_DEV_WRONG_STATE;
|
||||
}
|
||||
size_t bytes_read = 0;
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
i2s_chan_handle_t rx_chan = (i2s_chan_handle_t) i2s_data->in_handle;
|
||||
if (rx_chan == NULL) {
|
||||
return ESP_CODEC_DEV_DRV_ERR;
|
||||
}
|
||||
if (i2s_data->in_reconfig) {
|
||||
memset(data, 0, size);
|
||||
esp_codec_dev_sleep(10);
|
||||
return ESP_CODEC_DEV_OK;
|
||||
}
|
||||
int ret = i2s_channel_read(rx_chan, data, size, &bytes_read, 1000);
|
||||
#else
|
||||
int ret = i2s_read(i2s_data->port, data, size, &bytes_read, portMAX_DELAY);
|
||||
#endif
|
||||
return ret == 0 ? ESP_CODEC_DEV_OK : ESP_CODEC_DEV_DRV_ERR;
|
||||
}
|
||||
|
||||
static int _i2s_data_write(const audio_codec_data_if_t *h, uint8_t *data, int size)
|
||||
{
|
||||
i2s_data_t *i2s_data = (i2s_data_t *) h;
|
||||
if (i2s_data == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
if (i2s_data->is_open == false) {
|
||||
return ESP_CODEC_DEV_WRONG_STATE;
|
||||
}
|
||||
size_t bytes_written = 0;
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
i2s_chan_handle_t tx_chan = (i2s_chan_handle_t) i2s_data->out_handle;
|
||||
if (tx_chan == NULL) {
|
||||
return ESP_CODEC_DEV_DRV_ERR;
|
||||
}
|
||||
if (i2s_data->out_reconfig) {
|
||||
esp_codec_dev_sleep(10);
|
||||
return ESP_CODEC_DEV_OK;
|
||||
}
|
||||
int ret = i2s_channel_write(tx_chan, data, size, &bytes_written, 1000);
|
||||
#else
|
||||
int ret = i2s_write(i2s_data->port, data, size, &bytes_written, portMAX_DELAY);
|
||||
#endif
|
||||
return ret == 0 ? ESP_CODEC_DEV_OK : ESP_CODEC_DEV_DRV_ERR;
|
||||
}
|
||||
|
||||
static int _i2s_data_close(const audio_codec_data_if_t *h)
|
||||
{
|
||||
i2s_data_t *i2s_data = (i2s_data_t *) h;
|
||||
if (i2s_data == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
memset(&i2s_data->fs, 0, sizeof(esp_codec_dev_sample_info_t));
|
||||
memset(&i2s_data->in_fs, 0, sizeof(esp_codec_dev_sample_info_t));
|
||||
memset(&i2s_data->out_fs, 0, sizeof(esp_codec_dev_sample_info_t));
|
||||
i2s_data->is_open = false;
|
||||
return ESP_CODEC_DEV_OK;
|
||||
}
|
||||
|
||||
const audio_codec_data_if_t *audio_codec_new_i2s_data(audio_codec_i2s_cfg_t *i2s_cfg)
|
||||
{
|
||||
i2s_data_t *i2s_data = calloc(1, sizeof(i2s_data_t));
|
||||
if (i2s_data == NULL) {
|
||||
ESP_LOGE(TAG, "No memory for instance");
|
||||
return NULL;
|
||||
}
|
||||
i2s_data->base.open = _i2s_data_open;
|
||||
i2s_data->base.is_open = _i2s_data_is_open;
|
||||
i2s_data->base.enable = _i2s_data_enable;
|
||||
i2s_data->base.read = _i2s_data_read;
|
||||
i2s_data->base.write = _i2s_data_write;
|
||||
i2s_data->base.set_fmt = _i2s_data_set_fmt;
|
||||
i2s_data->base.close = _i2s_data_close;
|
||||
int ret = _i2s_data_open(&i2s_data->base, i2s_cfg, sizeof(audio_codec_i2s_cfg_t));
|
||||
if (ret != 0) {
|
||||
free(i2s_data);
|
||||
return NULL;
|
||||
}
|
||||
return &i2s_data->base;
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include "audio_codec_gpio_if.h"
|
||||
#include "esp_codec_dev_types.h"
|
||||
#include "esp_err.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "esp_log.h"
|
||||
#include <string.h>
|
||||
|
||||
#define TAG "GPIO_If"
|
||||
|
||||
static int _gpio_cfg(int16_t gpio, audio_gpio_dir_t dir, audio_gpio_mode_t mode)
|
||||
{
|
||||
if (gpio == -1) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
gpio_config_t io_conf;
|
||||
memset(&io_conf, 0, sizeof(io_conf));
|
||||
io_conf.mode = (dir == AUDIO_GPIO_DIR_OUT ? GPIO_MODE_OUTPUT : GPIO_MODE_INPUT);
|
||||
io_conf.pin_bit_mask = BIT64(gpio);
|
||||
io_conf.pull_down_en = ((mode & AUDIO_GPIO_MODE_PULL_DOWN) != 0);
|
||||
io_conf.pull_up_en = ((mode & AUDIO_GPIO_MODE_PULL_UP) != 0);
|
||||
esp_err_t ret = 0;
|
||||
ret |= gpio_config(&io_conf);
|
||||
return ret == 0 ? ESP_CODEC_DEV_OK : ESP_CODEC_DEV_DRV_ERR;
|
||||
}
|
||||
static int _gpio_set(int16_t gpio, bool high)
|
||||
{
|
||||
if (gpio == -1) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
int ret = gpio_set_level((gpio_num_t) gpio, high ? 1 : 0);
|
||||
return ret == 0 ? ESP_CODEC_DEV_OK : ESP_CODEC_DEV_DRV_ERR;
|
||||
}
|
||||
|
||||
static bool _gpio_get(int16_t gpio)
|
||||
{
|
||||
if (gpio == -1) {
|
||||
return false;
|
||||
}
|
||||
return (bool) gpio_get_level((gpio_num_t) gpio);
|
||||
}
|
||||
|
||||
const audio_codec_gpio_if_t *audio_codec_new_gpio(void)
|
||||
{
|
||||
audio_codec_gpio_if_t *gpio_if = (audio_codec_gpio_if_t *) calloc(1, sizeof(audio_codec_gpio_if_t));
|
||||
if (gpio_if == NULL) {
|
||||
ESP_LOGE(TAG, "No memory for instance");
|
||||
return NULL;
|
||||
}
|
||||
gpio_if->setup = _gpio_cfg;
|
||||
gpio_if->set = _gpio_set;
|
||||
gpio_if->get = _gpio_get;
|
||||
return gpio_if;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_idf_version.h"
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
#define TICK_PER_MS portTICK_PERIOD_MS
|
||||
#else
|
||||
#define TICK_PER_MS portTICK_RATE_MS
|
||||
#endif
|
||||
|
||||
void esp_codec_dev_sleep(int ms)
|
||||
{
|
||||
vTaskDelay(ms / TICK_PER_MS);
|
||||
}
|
||||
Reference in New Issue
Block a user