mirror of
https://github.com/esphome/esphome.git
synced 2025-03-14 06:38:17 +00:00
Add basic support for new esp-idf 5.x.x i2s driver.
This commit is contained in:
parent
02bf33c548
commit
be0f033541
@ -8,7 +8,15 @@ from esphome.components.esp32.const import (
|
||||
VARIANT_ESP32S3,
|
||||
)
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_BITS_PER_SAMPLE, CONF_CHANNEL, CONF_ID, CONF_SAMPLE_RATE
|
||||
from esphome.const import (
|
||||
CONF_BITS_PER_SAMPLE,
|
||||
CONF_CHANNEL,
|
||||
CONF_ID,
|
||||
CONF_SAMPLE_RATE,
|
||||
KEY_CORE,
|
||||
KEY_FRAMEWORK_VERSION,
|
||||
)
|
||||
from esphome.core import CORE
|
||||
from esphome.cpp_generator import MockObjClass
|
||||
import esphome.final_validate as fv
|
||||
|
||||
@ -35,6 +43,9 @@ CONF_MONO = "mono"
|
||||
CONF_LEFT = "left"
|
||||
CONF_RIGHT = "right"
|
||||
CONF_STEREO = "stereo"
|
||||
CONF_BOTH = "both"
|
||||
|
||||
CONF_USE_LEGACY = "use_legacy"
|
||||
|
||||
i2s_audio_ns = cg.esphome_ns.namespace("i2s_audio")
|
||||
I2SAudioComponent = i2s_audio_ns.class_("I2SAudioComponent", cg.Component)
|
||||
@ -50,6 +61,12 @@ I2S_MODE_OPTIONS = {
|
||||
CONF_SECONDARY: i2s_mode_t.I2S_MODE_SLAVE, # NOLINT
|
||||
}
|
||||
|
||||
i2s_role_t = cg.global_ns.enum("i2s_role_t")
|
||||
I2S_ROLE_OPTIONS = {
|
||||
CONF_PRIMARY: i2s_role_t.I2S_ROLE_MASTER, # NOLINT
|
||||
CONF_SECONDARY: i2s_role_t.I2S_ROLE_SLAVE, # NOLINT
|
||||
}
|
||||
|
||||
# https://github.com/espressif/esp-idf/blob/master/components/soc/{variant}/include/soc/soc_caps.h
|
||||
I2S_PORTS = {
|
||||
VARIANT_ESP32: 2,
|
||||
@ -60,10 +77,23 @@ I2S_PORTS = {
|
||||
|
||||
i2s_channel_fmt_t = cg.global_ns.enum("i2s_channel_fmt_t")
|
||||
I2S_CHANNELS = {
|
||||
CONF_MONO: i2s_channel_fmt_t.I2S_CHANNEL_FMT_ALL_LEFT,
|
||||
CONF_LEFT: i2s_channel_fmt_t.I2S_CHANNEL_FMT_ONLY_LEFT,
|
||||
CONF_RIGHT: i2s_channel_fmt_t.I2S_CHANNEL_FMT_ONLY_RIGHT,
|
||||
CONF_STEREO: i2s_channel_fmt_t.I2S_CHANNEL_FMT_RIGHT_LEFT,
|
||||
CONF_MONO: i2s_channel_fmt_t.I2S_CHANNEL_FMT_ALL_LEFT, # left data to both channels
|
||||
CONF_LEFT: i2s_channel_fmt_t.I2S_CHANNEL_FMT_ONLY_LEFT, # mono data
|
||||
CONF_RIGHT: i2s_channel_fmt_t.I2S_CHANNEL_FMT_ONLY_RIGHT, # mono data
|
||||
CONF_STEREO: i2s_channel_fmt_t.I2S_CHANNEL_FMT_RIGHT_LEFT, # stereo data to both channels
|
||||
}
|
||||
|
||||
i2s_slot_mode_t = cg.global_ns.enum("i2s_slot_mode_t")
|
||||
I2S_SLOT_MODE = {
|
||||
CONF_MONO: i2s_slot_mode_t.I2S_SLOT_MODE_MONO,
|
||||
CONF_STEREO: i2s_slot_mode_t.I2S_SLOT_MODE_STEREO,
|
||||
}
|
||||
|
||||
i2s_std_slot_mask_t = cg.global_ns.enum("i2s_std_slot_mask_t")
|
||||
I2S_STD_SLOT_MASK = {
|
||||
CONF_LEFT: i2s_std_slot_mask_t.I2S_STD_SLOT_LEFT,
|
||||
CONF_RIGHT: i2s_std_slot_mask_t.I2S_STD_SLOT_RIGHT,
|
||||
CONF_BOTH: i2s_std_slot_mask_t.I2S_STD_SLOT_BOTH,
|
||||
}
|
||||
|
||||
i2s_bits_per_sample_t = cg.global_ns.enum("i2s_bits_per_sample_t")
|
||||
@ -74,6 +104,14 @@ I2S_BITS_PER_SAMPLE = {
|
||||
32: i2s_bits_per_sample_t.I2S_BITS_PER_SAMPLE_32BIT,
|
||||
}
|
||||
|
||||
i2s_data_bit_width_t = cg.global_ns.enum("i2s_data_bit_width_t")
|
||||
I2S_DATA_BIT_WIDTH = {
|
||||
8: i2s_data_bit_width_t.I2S_DATA_BIT_WIDTH_8BIT,
|
||||
16: i2s_data_bit_width_t.I2S_DATA_BIT_WIDTH_16BIT,
|
||||
24: i2s_data_bit_width_t.I2S_DATA_BIT_WIDTH_24BIT,
|
||||
32: i2s_data_bit_width_t.I2S_DATA_BIT_WIDTH_32BIT,
|
||||
}
|
||||
|
||||
i2s_bits_per_chan_t = cg.global_ns.enum("i2s_bits_per_chan_t")
|
||||
I2S_BITS_PER_CHANNEL = {
|
||||
"default": i2s_bits_per_chan_t.I2S_BITS_PER_CHAN_DEFAULT,
|
||||
@ -83,8 +121,19 @@ I2S_BITS_PER_CHANNEL = {
|
||||
32: i2s_bits_per_chan_t.I2S_BITS_PER_CHAN_32BIT,
|
||||
}
|
||||
|
||||
i2s_slot_bit_width_t = cg.global_ns.enum("i2s_slot_bit_width_t")
|
||||
I2S_SLOT_BIT_WIDTH = {
|
||||
"default": i2s_slot_bit_width_t.I2S_SLOT_BIT_WIDTH_AUTO,
|
||||
8: i2s_slot_bit_width_t.I2S_SLOT_BIT_WIDTH_8BIT,
|
||||
16: i2s_slot_bit_width_t.I2S_SLOT_BIT_WIDTH_16BIT,
|
||||
24: i2s_slot_bit_width_t.I2S_SLOT_BIT_WIDTH_24BIT,
|
||||
32: i2s_slot_bit_width_t.I2S_SLOT_BIT_WIDTH_32BIT,
|
||||
}
|
||||
|
||||
_validate_bits = cv.float_with_unit("bits", "bit")
|
||||
|
||||
_use_legacy_driver = None
|
||||
|
||||
|
||||
def i2s_audio_component_schema(
|
||||
class_: MockObjClass,
|
||||
@ -97,20 +146,22 @@ def i2s_audio_component_schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(class_),
|
||||
cv.GenerateID(CONF_I2S_AUDIO_ID): cv.use_id(I2SAudioComponent),
|
||||
cv.Optional(CONF_CHANNEL, default=default_channel): cv.enum(I2S_CHANNELS),
|
||||
cv.Optional(CONF_CHANNEL, default=default_channel): cv.one_of(
|
||||
*I2S_CHANNELS
|
||||
),
|
||||
cv.Optional(CONF_SAMPLE_RATE, default=default_sample_rate): cv.int_range(
|
||||
min=1
|
||||
),
|
||||
cv.Optional(CONF_BITS_PER_SAMPLE, default=default_bits_per_sample): cv.All(
|
||||
_validate_bits, cv.enum(I2S_BITS_PER_SAMPLE)
|
||||
_validate_bits, cv.one_of(*I2S_BITS_PER_SAMPLE)
|
||||
),
|
||||
cv.Optional(CONF_I2S_MODE, default=CONF_PRIMARY): cv.enum(
|
||||
I2S_MODE_OPTIONS, lower=True
|
||||
cv.Optional(CONF_I2S_MODE, default=CONF_PRIMARY): cv.one_of(
|
||||
*I2S_MODE_OPTIONS, lower=True
|
||||
),
|
||||
cv.Optional(CONF_USE_APLL, default=False): cv.boolean,
|
||||
cv.Optional(CONF_BITS_PER_CHANNEL, default="default"): cv.All(
|
||||
cv.Any(cv.float_with_unit("bits", "bit"), "default"),
|
||||
cv.enum(I2S_BITS_PER_CHANNEL),
|
||||
cv.one_of(*I2S_BITS_PER_CHANNEL),
|
||||
),
|
||||
}
|
||||
)
|
||||
@ -118,22 +169,64 @@ def i2s_audio_component_schema(
|
||||
|
||||
async def register_i2s_audio_component(var, config):
|
||||
await cg.register_parented(var, config[CONF_I2S_AUDIO_ID])
|
||||
|
||||
cg.add(var.set_i2s_mode(config[CONF_I2S_MODE]))
|
||||
cg.add(var.set_channel(config[CONF_CHANNEL]))
|
||||
if use_legacy():
|
||||
print("Use legacy")
|
||||
cg.add(var.set_i2s_mode(I2S_MODE_OPTIONS[config[CONF_I2S_MODE]]))
|
||||
cg.add(var.set_channel(I2S_CHANNELS[config[CONF_CHANNEL]]))
|
||||
cg.add(
|
||||
var.set_bits_per_sample(I2S_BITS_PER_SAMPLE[config[CONF_BITS_PER_SAMPLE]])
|
||||
)
|
||||
cg.add(
|
||||
var.set_bits_per_channel(
|
||||
I2S_BITS_PER_CHANNEL[config[CONF_BITS_PER_CHANNEL]]
|
||||
)
|
||||
)
|
||||
cg.add(var.set_use_apll(config[CONF_USE_APLL]))
|
||||
else:
|
||||
cg.add(var.set_i2s_role(I2S_ROLE_OPTIONS[config[CONF_I2S_MODE]]))
|
||||
slot_mode = config[CONF_CHANNEL]
|
||||
if slot_mode != CONF_STEREO:
|
||||
slot_mode = CONF_MONO
|
||||
slot_mask = config[CONF_CHANNEL]
|
||||
if slot_mask not in [CONF_LEFT, CONF_RIGHT]:
|
||||
slot_mask = CONF_BOTH
|
||||
cg.add(var.set_slot_mode(I2S_SLOT_MODE[slot_mode]))
|
||||
cg.add(var.set_std_slot_mask(I2S_STD_SLOT_MASK[slot_mask]))
|
||||
cg.add(var.set_data_bit_width(I2S_DATA_BIT_WIDTH[config[CONF_BITS_PER_SAMPLE]]))
|
||||
cg.add(
|
||||
var.set_slot_bit_width(I2S_SLOT_BIT_WIDTH[config[CONF_BITS_PER_CHANNEL]])
|
||||
)
|
||||
# cg.add(var.set_use_apll(config[CONF_USE_APLL]))
|
||||
cg.add_define("USE_I2S_APLL")
|
||||
cg.add(var.set_sample_rate(config[CONF_SAMPLE_RATE]))
|
||||
cg.add(var.set_bits_per_sample(config[CONF_BITS_PER_SAMPLE]))
|
||||
cg.add(var.set_bits_per_channel(config[CONF_BITS_PER_CHANNEL]))
|
||||
cg.add(var.set_use_apll(config[CONF_USE_APLL]))
|
||||
|
||||
|
||||
CONFIG_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(I2SAudioComponent),
|
||||
cv.Required(CONF_I2S_LRCLK_PIN): pins.internal_gpio_output_pin_number,
|
||||
cv.Optional(CONF_I2S_BCLK_PIN): pins.internal_gpio_output_pin_number,
|
||||
cv.Optional(CONF_I2S_MCLK_PIN): pins.internal_gpio_output_pin_number,
|
||||
}
|
||||
def validate_use_legacy(value):
|
||||
global _use_legacy_driver # noqa: PLW0603
|
||||
if CONF_USE_LEGACY in value:
|
||||
if (_use_legacy_driver is not None) and (
|
||||
_use_legacy_driver != value[CONF_USE_LEGACY]
|
||||
):
|
||||
raise cv.Invalid(
|
||||
f"All i2s_audio components must set {CONF_USE_LEGACY} to the same value."
|
||||
)
|
||||
if (not value[CONF_USE_LEGACY]) and (CORE.using_arduino):
|
||||
raise cv.Invalid("Arduino supports only the legacy i2s driver.")
|
||||
_use_legacy_driver = value[CONF_USE_LEGACY]
|
||||
return value
|
||||
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(I2SAudioComponent),
|
||||
cv.Required(CONF_I2S_LRCLK_PIN): pins.internal_gpio_output_pin_number,
|
||||
cv.Optional(CONF_I2S_BCLK_PIN): pins.internal_gpio_output_pin_number,
|
||||
cv.Optional(CONF_I2S_MCLK_PIN): pins.internal_gpio_output_pin_number,
|
||||
cv.Optional(CONF_USE_LEGACY): cv.boolean,
|
||||
},
|
||||
),
|
||||
validate_use_legacy,
|
||||
)
|
||||
|
||||
|
||||
@ -148,12 +241,22 @@ def _final_validate(_):
|
||||
)
|
||||
|
||||
|
||||
def use_legacy():
|
||||
framework_version = CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION]
|
||||
if CORE.using_esp_idf and framework_version >= cv.Version(5, 0, 0):
|
||||
if not _use_legacy_driver:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
FINAL_VALIDATE_SCHEMA = _final_validate
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
if use_legacy():
|
||||
cg.add_define("USE_I2S_LEGACY")
|
||||
|
||||
cg.add(var.set_lrclk_pin(config[CONF_I2S_LRCLK_PIN]))
|
||||
if CONF_I2S_BCLK_PIN in config:
|
||||
|
@ -2,9 +2,14 @@
|
||||
|
||||
#ifdef USE_ESP32
|
||||
|
||||
#include <driver/i2s.h>
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#ifdef USE_I2S_LEGACY
|
||||
#include <driver/i2s.h>
|
||||
#else
|
||||
#include <driver/i2s_std.h>
|
||||
#endif
|
||||
|
||||
namespace esphome {
|
||||
namespace i2s_audio {
|
||||
@ -13,19 +18,35 @@ class I2SAudioComponent;
|
||||
|
||||
class I2SAudioBase : public Parented<I2SAudioComponent> {
|
||||
public:
|
||||
#ifdef USE_I2S_LEGACY
|
||||
void set_i2s_mode(i2s_mode_t mode) { this->i2s_mode_ = mode; }
|
||||
void set_channel(i2s_channel_fmt_t channel) { this->channel_ = channel; }
|
||||
void set_sample_rate(uint32_t sample_rate) { this->sample_rate_ = sample_rate; }
|
||||
void set_bits_per_sample(i2s_bits_per_sample_t bits_per_sample) { this->bits_per_sample_ = bits_per_sample; }
|
||||
void set_bits_per_channel(i2s_bits_per_chan_t bits_per_channel) { this->bits_per_channel_ = bits_per_channel; }
|
||||
#else
|
||||
void set_i2s_role(i2s_role_t role) { this->i2s_role_ = role; }
|
||||
void set_slot_mode(i2s_slot_mode_t slot_mode) { this->slot_mode_ = slot_mode; }
|
||||
void set_std_slot_mask(i2s_std_slot_mask_t std_slot_mask) { this->std_slot_mask_ = std_slot_mask; }
|
||||
void set_data_bit_width(i2s_data_bit_width_t data_bit_width) { this->data_bit_width_ = data_bit_width; }
|
||||
void set_slot_bit_width(i2s_slot_bit_width_t slot_bit_width) { this->slot_bit_width_ = slot_bit_width; }
|
||||
#endif
|
||||
void set_sample_rate(uint32_t sample_rate) { this->sample_rate_ = sample_rate; }
|
||||
void set_use_apll(uint32_t use_apll) { this->use_apll_ = use_apll; }
|
||||
|
||||
protected:
|
||||
#ifdef USE_I2S_LEGACY
|
||||
i2s_mode_t i2s_mode_{};
|
||||
i2s_channel_fmt_t channel_;
|
||||
uint32_t sample_rate_;
|
||||
i2s_bits_per_sample_t bits_per_sample_;
|
||||
i2s_bits_per_chan_t bits_per_channel_;
|
||||
#else
|
||||
i2s_role_t i2s_role_{};
|
||||
i2s_slot_mode_t slot_mode_;
|
||||
i2s_std_slot_mask_t std_slot_mask_;
|
||||
i2s_data_bit_width_t data_bit_width_;
|
||||
i2s_slot_bit_width_t slot_bit_width_;
|
||||
#endif
|
||||
uint32_t sample_rate_;
|
||||
bool use_apll_;
|
||||
};
|
||||
|
||||
@ -37,6 +58,7 @@ class I2SAudioComponent : public Component {
|
||||
public:
|
||||
void setup() override;
|
||||
|
||||
#ifdef USE_I2S_LEGACY
|
||||
i2s_pin_config_t get_pin_config() const {
|
||||
return {
|
||||
.mck_io_num = this->mclk_pin_,
|
||||
@ -46,6 +68,20 @@ class I2SAudioComponent : public Component {
|
||||
.data_in_num = I2S_PIN_NO_CHANGE,
|
||||
};
|
||||
}
|
||||
#else
|
||||
i2s_std_gpio_config_t get_pin_config() const {
|
||||
return {.mclk = (gpio_num_t) this->mclk_pin_,
|
||||
.bclk = (gpio_num_t) this->bclk_pin_,
|
||||
.ws = (gpio_num_t) this->lrclk_pin_,
|
||||
.dout = I2S_GPIO_UNUSED, // add local ports
|
||||
.din = I2S_GPIO_UNUSED,
|
||||
.invert_flags = {
|
||||
.mclk_inv = false,
|
||||
.bclk_inv = false,
|
||||
.ws_inv = false,
|
||||
}};
|
||||
}
|
||||
#endif
|
||||
|
||||
void set_mclk_pin(int pin) { this->mclk_pin_ = pin; }
|
||||
void set_bclk_pin(int pin) { this->bclk_pin_ = pin; }
|
||||
@ -62,9 +98,13 @@ class I2SAudioComponent : public Component {
|
||||
|
||||
I2SAudioIn *audio_in_{nullptr};
|
||||
I2SAudioOut *audio_out_{nullptr};
|
||||
|
||||
#ifdef USE_I2S_LEGACY
|
||||
int mclk_pin_{I2S_PIN_NO_CHANGE};
|
||||
int bclk_pin_{I2S_PIN_NO_CHANGE};
|
||||
#else
|
||||
int mclk_pin_{I2S_GPIO_UNUSED};
|
||||
int bclk_pin_{I2S_GPIO_UNUSED};
|
||||
#endif
|
||||
int lrclk_pin_;
|
||||
i2s_port_t port_{};
|
||||
};
|
||||
|
@ -1,21 +1,20 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import media_player, esp32
|
||||
import esphome.config_validation as cv
|
||||
|
||||
from esphome import pins
|
||||
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import esp32, media_player
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_ID, CONF_MODE
|
||||
|
||||
from .. import (
|
||||
i2s_audio_ns,
|
||||
I2SAudioComponent,
|
||||
I2SAudioOut,
|
||||
CONF_I2S_AUDIO_ID,
|
||||
CONF_I2S_DOUT_PIN,
|
||||
CONF_LEFT,
|
||||
CONF_RIGHT,
|
||||
CONF_MONO,
|
||||
CONF_RIGHT,
|
||||
CONF_STEREO,
|
||||
I2SAudioComponent,
|
||||
I2SAudioOut,
|
||||
i2s_audio_ns,
|
||||
use_legacy,
|
||||
)
|
||||
|
||||
CODEOWNERS = ["@jesserockz"]
|
||||
@ -89,6 +88,14 @@ CONFIG_SCHEMA = cv.All(
|
||||
)
|
||||
|
||||
|
||||
def _final_validate(_):
|
||||
if not use_legacy():
|
||||
raise cv.Invalid("I2S media player is only compatible with legacy i2s driver.")
|
||||
|
||||
|
||||
FINAL_VALIDATE_SCHEMA = _final_validate
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
|
@ -12,6 +12,7 @@ from .. import (
|
||||
i2s_audio_component_schema,
|
||||
i2s_audio_ns,
|
||||
register_i2s_audio_component,
|
||||
use_legacy,
|
||||
)
|
||||
|
||||
CODEOWNERS = ["@jesserockz"]
|
||||
@ -74,6 +75,17 @@ CONFIG_SCHEMA = cv.All(
|
||||
)
|
||||
|
||||
|
||||
def _final_validate(config):
|
||||
if not use_legacy():
|
||||
if config[CONF_ADC_TYPE] == "internal":
|
||||
raise cv.Invalid("Internal DAC is only compatible with legacy i2s driver.")
|
||||
if config.get(CONF_PDM):
|
||||
raise cv.Invalid("PDM mode is only compatible with legacy i2s driver.")
|
||||
|
||||
|
||||
FINAL_VALIDATE_SCHEMA = _final_validate
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
|
@ -2,7 +2,11 @@
|
||||
|
||||
#ifdef USE_ESP32
|
||||
|
||||
#ifdef USE_I2S_LEGACY
|
||||
#include <driver/i2s.h>
|
||||
#else
|
||||
#include <driver/i2s_std.h>
|
||||
#endif
|
||||
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/log.h"
|
||||
@ -16,6 +20,7 @@ static const char *const TAG = "i2s_audio.microphone";
|
||||
|
||||
void I2SAudioMicrophone::setup() {
|
||||
ESP_LOGCONFIG(TAG, "Setting up I2S Audio Microphone...");
|
||||
#ifdef USE_I2S_LEGACY
|
||||
#if SOC_I2S_SUPPORTS_ADC
|
||||
if (this->adc_) {
|
||||
if (this->parent_->get_port() != I2S_NUM_0) {
|
||||
@ -24,6 +29,7 @@ void I2SAudioMicrophone::setup() {
|
||||
return;
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
#endif
|
||||
{
|
||||
if (this->pdm_) {
|
||||
@ -47,6 +53,9 @@ void I2SAudioMicrophone::start_() {
|
||||
if (!this->parent_->try_lock()) {
|
||||
return; // Waiting for another i2s to return lock
|
||||
}
|
||||
esp_err_t err;
|
||||
|
||||
#ifdef USE_I2S_LEGACY
|
||||
i2s_driver_config_t config = {
|
||||
.mode = (i2s_mode_t) (this->i2s_mode_ | I2S_MODE_RX),
|
||||
.sample_rate = this->sample_rate_,
|
||||
@ -63,8 +72,6 @@ void I2SAudioMicrophone::start_() {
|
||||
.bits_per_chan = this->bits_per_channel_,
|
||||
};
|
||||
|
||||
esp_err_t err;
|
||||
|
||||
#if SOC_I2S_SUPPORTS_ADC
|
||||
if (this->adc_) {
|
||||
config.mode = (i2s_mode_t) (config.mode | I2S_MODE_ADC_BUILT_IN);
|
||||
@ -111,6 +118,64 @@ void I2SAudioMicrophone::start_() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
#else
|
||||
i2s_chan_config_t chan_cfg = {
|
||||
.id = this->parent_->get_port(),
|
||||
.role = this->i2s_role_,
|
||||
.dma_desc_num = 4,
|
||||
.dma_frame_num = 256,
|
||||
.auto_clear = false,
|
||||
};
|
||||
/* Allocate a new RX channel and get the handle of this channel */
|
||||
err = i2s_new_channel(&chan_cfg, NULL, &this->rx_handle);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGW(TAG, "Error creating new I2S channel: %s", esp_err_to_name(err));
|
||||
this->status_set_error();
|
||||
return;
|
||||
}
|
||||
|
||||
/* Setting the configurations, the slot configuration and clock configuration can be generated by the macros
|
||||
* These two helper macros are defined in 'i2s_std.h' which can only be used in STD mode.
|
||||
* They can help to specify the slot and clock configurations for initialization or updating */
|
||||
#ifdef USE_I2S_APLL
|
||||
i2s_clock_src_t clk_src = I2S_CLK_SRC_APLL;
|
||||
#else
|
||||
i2s_clock_src_t clk_src = I2S_CLK_SRC_DEFAULT;
|
||||
#endif
|
||||
i2s_std_clk_config_t clk_cfg = {
|
||||
.sample_rate_hz = this->sample_rate_,
|
||||
.clk_src = clk_src,
|
||||
.mclk_multiple = I2S_MCLK_MULTIPLE_256,
|
||||
};
|
||||
i2s_std_slot_config_t std_slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(this->data_bit_width_, this->slot_mode_);
|
||||
std_slot_cfg.slot_bit_width = this->slot_bit_width_;
|
||||
std_slot_cfg.slot_mask = this->std_slot_mask_;
|
||||
|
||||
i2s_std_gpio_config_t pin_config = this->parent_->get_pin_config();
|
||||
pin_config.din = this->din_pin_;
|
||||
|
||||
i2s_std_config_t std_cfg = {
|
||||
.clk_cfg = clk_cfg,
|
||||
.slot_cfg = std_slot_cfg,
|
||||
.gpio_cfg = pin_config,
|
||||
};
|
||||
/* Initialize the channel */
|
||||
err = i2s_channel_init_std_mode(this->rx_handle, &std_cfg);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGW(TAG, "Error initializing I2S channel: %s", esp_err_to_name(err));
|
||||
this->status_set_error();
|
||||
return;
|
||||
}
|
||||
|
||||
/* Before reading data, start the RX channel first */
|
||||
i2s_channel_enable(this->rx_handle);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGW(TAG, "Error enabling I2S Microphone: %s", esp_err_to_name(err));
|
||||
this->status_set_error();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
this->state_ = microphone::STATE_RUNNING;
|
||||
this->high_freq_.start();
|
||||
this->status_clear_error();
|
||||
@ -128,6 +193,7 @@ void I2SAudioMicrophone::stop() {
|
||||
|
||||
void I2SAudioMicrophone::stop_() {
|
||||
esp_err_t err;
|
||||
#ifdef USE_I2S_LEGACY
|
||||
#if SOC_I2S_SUPPORTS_ADC
|
||||
if (this->adc_) {
|
||||
err = i2s_adc_disable(this->parent_->get_port());
|
||||
@ -150,6 +216,22 @@ void I2SAudioMicrophone::stop_() {
|
||||
this->status_set_error();
|
||||
return;
|
||||
}
|
||||
#else
|
||||
/* Have to stop the channel before deleting it */
|
||||
err = i2s_channel_disable(this->rx_handle);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGW(TAG, "Error stopping I2S microphone: %s", esp_err_to_name(err));
|
||||
this->status_set_error();
|
||||
return;
|
||||
}
|
||||
/* If the handle is not needed any more, delete it to release the channel resources */
|
||||
err = i2s_del_channel(this->rx_handle);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGW(TAG, "Error deleting I2S channel: %s", esp_err_to_name(err));
|
||||
this->status_set_error();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
this->parent_->unlock();
|
||||
this->state_ = microphone::STATE_STOPPED;
|
||||
this->high_freq_.stop();
|
||||
@ -158,7 +240,11 @@ void I2SAudioMicrophone::stop_() {
|
||||
|
||||
size_t I2SAudioMicrophone::read(int16_t *buf, size_t len) {
|
||||
size_t bytes_read = 0;
|
||||
#ifdef USE_I2S_LEGACY
|
||||
esp_err_t err = i2s_read(this->parent_->get_port(), buf, len, &bytes_read, (100 / portTICK_PERIOD_MS));
|
||||
#else
|
||||
esp_err_t err = i2s_channel_read(this->rx_handle, buf, len, &bytes_read, (100 / portTICK_PERIOD_MS));
|
||||
#endif
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGW(TAG, "Error reading from I2S microphone: %s", esp_err_to_name(err));
|
||||
this->status_set_warning();
|
||||
@ -171,6 +257,7 @@ size_t I2SAudioMicrophone::read(int16_t *buf, size_t len) {
|
||||
this->status_clear_warning();
|
||||
// ESP-IDF I2S implementation right-extends 8-bit data to 16 bits,
|
||||
// and 24-bit data to 32 bits.
|
||||
#ifdef USE_I2S_LEGACY
|
||||
switch (this->bits_per_sample_) {
|
||||
case I2S_BITS_PER_SAMPLE_8BIT:
|
||||
case I2S_BITS_PER_SAMPLE_16BIT:
|
||||
@ -188,6 +275,25 @@ size_t I2SAudioMicrophone::read(int16_t *buf, size_t len) {
|
||||
ESP_LOGE(TAG, "Unsupported bits per sample: %d", this->bits_per_sample_);
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
switch (this->data_bit_width_) {
|
||||
case I2S_DATA_BIT_WIDTH_8BIT:
|
||||
case I2S_DATA_BIT_WIDTH_16BIT:
|
||||
return bytes_read;
|
||||
case I2S_DATA_BIT_WIDTH_24BIT:
|
||||
case I2S_DATA_BIT_WIDTH_32BIT: {
|
||||
size_t samples_read = bytes_read / sizeof(int32_t);
|
||||
for (size_t i = 0; i < samples_read; i++) {
|
||||
int32_t temp = reinterpret_cast<int32_t *>(buf)[i] >> 14;
|
||||
buf[i] = clamp<int16_t>(temp, INT16_MIN, INT16_MAX);
|
||||
}
|
||||
return samples_read * sizeof(int16_t);
|
||||
}
|
||||
default:
|
||||
ESP_LOGE(TAG, "Unsupported data bit width: %d", this->data_bit_width_);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void I2SAudioMicrophone::read_() {
|
||||
|
@ -17,17 +17,23 @@ class I2SAudioMicrophone : public I2SAudioIn, public microphone::Microphone, pub
|
||||
void stop() override;
|
||||
|
||||
void loop() override;
|
||||
|
||||
#ifdef USE_I2S_LEGACY
|
||||
void set_din_pin(int8_t pin) { this->din_pin_ = pin; }
|
||||
#else
|
||||
void set_din_pin(int8_t pin) { this->din_pin_ = (gpio_num_t) pin; }
|
||||
#endif
|
||||
|
||||
void set_pdm(bool pdm) { this->pdm_ = pdm; }
|
||||
|
||||
size_t read(int16_t *buf, size_t len) override;
|
||||
|
||||
#ifdef USE_I2S_LEGACY
|
||||
#if SOC_I2S_SUPPORTS_ADC
|
||||
void set_adc_channel(adc1_channel_t channel) {
|
||||
this->adc_channel_ = channel;
|
||||
this->adc_ = true;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
protected:
|
||||
@ -35,10 +41,15 @@ class I2SAudioMicrophone : public I2SAudioIn, public microphone::Microphone, pub
|
||||
void stop_();
|
||||
void read_();
|
||||
|
||||
#ifdef USE_I2S_LEGACY
|
||||
int8_t din_pin_{I2S_PIN_NO_CHANGE};
|
||||
#if SOC_I2S_SUPPORTS_ADC
|
||||
adc1_channel_t adc_channel_{ADC1_CHANNEL_MAX};
|
||||
bool adc_{false};
|
||||
#endif
|
||||
#else
|
||||
gpio_num_t din_pin_{I2S_GPIO_UNUSED};
|
||||
i2s_chan_handle_t rx_handle;
|
||||
#endif
|
||||
bool pdm_{false};
|
||||
|
||||
|
@ -26,6 +26,7 @@ from .. import (
|
||||
i2s_audio_component_schema,
|
||||
i2s_audio_ns,
|
||||
register_i2s_audio_component,
|
||||
use_legacy,
|
||||
)
|
||||
|
||||
AUTO_LOAD = ["audio"]
|
||||
@ -157,6 +158,14 @@ CONFIG_SCHEMA = cv.All(
|
||||
)
|
||||
|
||||
|
||||
def _final_validate(_):
|
||||
if not use_legacy():
|
||||
raise cv.Invalid("I2S speaker is only compatible with legacy i2s driver.")
|
||||
|
||||
|
||||
FINAL_VALIDATE_SCHEMA = _final_validate
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
|
Loading…
x
Reference in New Issue
Block a user