mirror of
https://github.com/esphome/esphome.git
synced 2026-10-07 11:26:39 +00:00
[tas58xx] Add support for tas5825 dac (#19971)
This commit is contained in:
@@ -1 +1,10 @@
|
||||
from esphome.config_helpers import filter_source_files_from_defines
|
||||
|
||||
CODEOWNERS = ["@mrtoy-me", "@remcom"]
|
||||
|
||||
FILTER_SOURCE_FILES = filter_source_files_from_defines(
|
||||
{
|
||||
"model_tas5805m.cpp": "USE_TAS58XX_TAS5805M",
|
||||
"model_tas5825m.cpp": "USE_TAS58XX_TAS5825M",
|
||||
}
|
||||
)
|
||||
|
||||
@@ -11,6 +11,8 @@ from esphome.const import CONF_ENABLE_PIN, CONF_ID, CONF_MODEL
|
||||
from esphome.cpp_generator import MockObj
|
||||
from esphome.types import ConfigType
|
||||
|
||||
from . import FILTER_SOURCE_FILES # noqa: F401 pylint: disable=unused-import
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
DEPENDENCIES = ["i2c"]
|
||||
@@ -23,6 +25,9 @@ CONF_VOLUME_MIN_DB = "volume_min_db"
|
||||
CONF_VOLUME_MAX_DB = "volume_max_db"
|
||||
CONF_TAS58XX_ID = "tas58xx_id"
|
||||
|
||||
DAC_TAS5805M = "tas5805m"
|
||||
DAC_TAS5825M = "tas5825m"
|
||||
|
||||
tas58xx_ns = cg.esphome_ns.namespace("tas58xx")
|
||||
TAS58xx = tas58xx_ns.class_("TAS58xx", AudioDac, cg.PollingComponent, i2c.I2CDevice)
|
||||
|
||||
@@ -54,13 +59,20 @@ class Model:
|
||||
|
||||
|
||||
MODELS: dict[str, Model] = {
|
||||
"tas5805m": Model(
|
||||
DAC_TAS5805M: Model(
|
||||
model_info=tas58xx_ns.TAS5805M_MODEL,
|
||||
analog_gain_min_db=-15.5,
|
||||
volume_min_db=-103.0,
|
||||
volume_max_db=24.0,
|
||||
default_address=0x2D,
|
||||
),
|
||||
DAC_TAS5825M: Model(
|
||||
model_info=tas58xx_ns.TAS5825M_MODEL,
|
||||
analog_gain_min_db=-15.5,
|
||||
volume_min_db=-103.0,
|
||||
volume_max_db=24.0,
|
||||
default_address=0x4C,
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
@@ -162,6 +174,7 @@ for _name, _call in (
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
model = MODELS[config[CONF_MODEL]]
|
||||
cg.add_define(f"USE_TAS58XX_{config[CONF_MODEL].upper()}")
|
||||
var = cg.new_Pvariable(config[CONF_ID], cg.RawExpression(f"&{model.model_info}"))
|
||||
await cg.register_component(var, config)
|
||||
await i2c.register_i2c_device(var, config)
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import binary_sensor
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import DEVICE_CLASS_PROBLEM, ENTITY_CATEGORY_DIAGNOSTIC
|
||||
from esphome.const import CONF_MODEL, DEVICE_CLASS_PROBLEM, ENTITY_CATEGORY_DIAGNOSTIC
|
||||
import esphome.final_validate as fv
|
||||
from esphome.types import ConfigType
|
||||
|
||||
from .audio_dac import CONF_TAS58XX_ID, TAS58xx, tas58xx_ns
|
||||
from . import FILTER_SOURCE_FILES # noqa: F401 pylint: disable=unused-import
|
||||
from .audio_dac import CONF_TAS58XX_ID, DAC_TAS5825M, TAS58xx, tas58xx_ns
|
||||
|
||||
CONF_HAVE_FAULT = "have_fault"
|
||||
|
||||
# Each name matches a FaultSensor value in tas58xx.h
|
||||
FAULT_SENSORS = (
|
||||
# Faults common to TAS5805M and TAS5825M
|
||||
COMMON_FAULT_SENSORS = (
|
||||
"left_channel_dc_fault",
|
||||
"right_channel_dc_fault",
|
||||
"left_channel_over_current",
|
||||
@@ -20,8 +22,22 @@ FAULT_SENSORS = (
|
||||
"pvdd_over_voltage",
|
||||
"pvdd_under_voltage",
|
||||
"over_temp_shutdown",
|
||||
"over_temp_warning",
|
||||
"over_temp_warning", # tas582x OTW Level 3 - use tas5805 naming
|
||||
)
|
||||
# Additional faults available on TAS5825M
|
||||
TAS5825M_ONLY_FAULT_SENSORS = (
|
||||
"load_eeprom_error",
|
||||
"right_channel_cbc_over_current",
|
||||
"left_channel_cbc_over_current",
|
||||
"left_channel_cbc_over_current_warning",
|
||||
"right_channel_cbc_over_current_warning",
|
||||
"over_temp_146c_warning", # tas582x OTW Level 4
|
||||
# "over_temp_122c_warning", # tas582x OTW Level 2 - not currently included
|
||||
# "over_temp_112c_warning", # tas582x OTW Level 1 - not currently included
|
||||
)
|
||||
|
||||
# Each name matches a FaultSensor value in tas58xx.h
|
||||
FAULT_SENSORS = COMMON_FAULT_SENSORS + TAS5825M_ONLY_FAULT_SENSORS
|
||||
|
||||
FaultSensor = tas58xx_ns.enum("FaultSensor")
|
||||
|
||||
@@ -39,6 +55,26 @@ CONFIG_SCHEMA = cv.Schema(
|
||||
)
|
||||
|
||||
|
||||
def _final_validate(config: ConfigType) -> ConfigType:
|
||||
fconf = fv.full_config.get()
|
||||
hub_path = fconf.get_path_for_id(config[CONF_TAS58XX_ID])
|
||||
hub_conf = fconf.get_config_for_path(hub_path[:-1])
|
||||
|
||||
if hub_conf[CONF_MODEL] == DAC_TAS5825M:
|
||||
return config
|
||||
|
||||
unsupported = sorted(frozenset(TAS5825M_ONLY_FAULT_SENSORS).intersection(config))
|
||||
if unsupported:
|
||||
raise cv.Invalid(
|
||||
f"binary_sensor platform tas58xx: '{', '.join(unsupported)}' is only available for 'model: {DAC_TAS5825M}' "
|
||||
f"- Remove from YAML for 'model: {hub_conf[CONF_MODEL]}'"
|
||||
)
|
||||
return config
|
||||
|
||||
|
||||
FINAL_VALIDATE_SCHEMA = _final_validate
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
hub = await cg.get_variable(config[CONF_TAS58XX_ID])
|
||||
binary_sensors = binary_sensor.sub_binary_sensors(config)
|
||||
|
||||
@@ -67,17 +67,23 @@ const ModelInfo TAS5805M_MODEL = {
|
||||
.fault_name = fault_name,
|
||||
.fault_sensor_bits =
|
||||
{
|
||||
3, // FAULT_SENSOR_LEFT_CHANNEL_DC_FAULT
|
||||
2, // FAULT_SENSOR_RIGHT_CHANNEL_DC_FAULT
|
||||
1, // FAULT_SENSOR_LEFT_CHANNEL_OVER_CURRENT
|
||||
0, // FAULT_SENSOR_RIGHT_CHANNEL_OVER_CURRENT
|
||||
15, // FAULT_SENSOR_OTP_CRC_CHECK
|
||||
14, // FAULT_SENSOR_BQ_WRITE_FAILED
|
||||
10, // FAULT_SENSOR_CLOCK_FAULT
|
||||
9, // FAULT_SENSOR_PVDD_OVER_VOLTAGE
|
||||
8, // FAULT_SENSOR_PVDD_UNDER_VOLTAGE
|
||||
16, // FAULT_SENSOR_OVER_TEMP_SHUTDOWN
|
||||
26, // FAULT_SENSOR_OVER_TEMP_WARNING
|
||||
3, // FAULT_SENSOR_LEFT_CHANNEL_DC_FAULT
|
||||
2, // FAULT_SENSOR_RIGHT_CHANNEL_DC_FAULT
|
||||
1, // FAULT_SENSOR_LEFT_CHANNEL_OVER_CURRENT
|
||||
0, // FAULT_SENSOR_RIGHT_CHANNEL_OVER_CURRENT
|
||||
15, // FAULT_SENSOR_OTP_CRC_CHECK
|
||||
14, // FAULT_SENSOR_BQ_WRITE_FAILED
|
||||
10, // FAULT_SENSOR_CLOCK_FAULT
|
||||
9, // FAULT_SENSOR_PVDD_OVER_VOLTAGE
|
||||
8, // FAULT_SENSOR_PVDD_UNDER_VOLTAGE
|
||||
16, // FAULT_SENSOR_OVER_TEMP_SHUTDOWN
|
||||
26, // FAULT_SENSOR_OVER_TEMP_WARNING
|
||||
NO_BIT, // FAULT_SENSOR_LOAD_EEPROM_ERROR
|
||||
NO_BIT, // FAULT_SENSOR_RIGHT_CHANNEL_CBC_OVER_CURRENT
|
||||
NO_BIT, // FAULT_SENSOR_LEFT_CHANNEL_CBC_OVER_CURRENT
|
||||
NO_BIT, // FAULT_SENSOR_LEFT_CHANNEL_CBC_OVER_CURRENT_WARNING
|
||||
NO_BIT, // FAULT_SENSOR_RIGHT_CHANNEL_CBC_OVER_CURRENT_WARNING
|
||||
NO_BIT, // FAULT_SENSOR_OVER_TEMP_146C_WARNING
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
#include "tas58xx.h"
|
||||
|
||||
#include "esphome/core/hal.h"
|
||||
|
||||
namespace esphome::tas58xx {
|
||||
|
||||
// Remainder of the startup sequence, run after the reset. Register 0x00 selects the page.
|
||||
// All writes except starting and last three register writes are exactly the startup sequence from TI PurePath Console.
|
||||
// Registers 0x46, 0x7D, 0x7E, page 1 register 0x51 and Page 2 registers 0x1D, 0x19 are not documented in the datasheet.
|
||||
|
||||
// clang-format off
|
||||
static const uint8_t STARTUP_SEQUENCE[][2] PROGMEM = {
|
||||
{0x03, 0x00}, // Deep Sleep
|
||||
{0x7D, 0x11},
|
||||
{0x7E, 0xFF},
|
||||
{0x00, 0x01}, // Page 1
|
||||
{0x51, 0x05},
|
||||
{0x00, 0x02}, // Page 2
|
||||
{0x1D, 0x00},
|
||||
{0x19, 0x80},
|
||||
{0x00, 0x00}, // Page 0
|
||||
{0x46, 0x11},
|
||||
{0x02, 0x00}, // DEVICE_CTRL_1 - BD MODE, Damp to PBL MODE
|
||||
{0x53, 0x01}, // ANA_CTRL PWM Phase Control - in phase
|
||||
{0x54, 0x00}, // AGAIN 0dB
|
||||
{0x03, 0x02}, // Hi-Z
|
||||
{0x61, 0x0B}, // GPIO0_SEL - GPIO0 as FAULTZ output
|
||||
{0x60, 0x01}, // GPIO0_OE - Enable GPIO0 as output
|
||||
{0x77, 0x07}, // CBC_CONTROL enabling CBC function for warnings and faults
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
static const LogString *model_name() { return LOG_STR("TAS5825M"); }
|
||||
|
||||
// An if chain rather than a switch: a switch table would land in rodata, which is RAM on ESP8266.
|
||||
static const LogString *fault_name(uint8_t index) {
|
||||
if (index == 0)
|
||||
return LOG_STR("Right channel over current");
|
||||
if (index == 1)
|
||||
return LOG_STR("Left channel over current");
|
||||
if (index == 2)
|
||||
return LOG_STR("Right channel DC fault");
|
||||
if (index == 3)
|
||||
return LOG_STR("Left channel DC fault");
|
||||
if (index == 8)
|
||||
return LOG_STR("PVDD under voltage");
|
||||
if (index == 9)
|
||||
return LOG_STR("PVDD over voltage");
|
||||
if (index == 10)
|
||||
return LOG_STR("Clock fault");
|
||||
if (index == 13)
|
||||
return LOG_STR("Load EEPROM error");
|
||||
if (index == 14)
|
||||
return LOG_STR("BQ write failed");
|
||||
if (index == 15)
|
||||
return LOG_STR("OTP CRC check error");
|
||||
if (index == 16)
|
||||
return LOG_STR("Over temperature shutdown");
|
||||
if (index == 17)
|
||||
return LOG_STR("Left channel CBC over current");
|
||||
if (index == 18)
|
||||
return LOG_STR("Right channel CBC over current");
|
||||
// if (index == 24)
|
||||
// return LOG_STR("Over temperature warning 112C"); // not currently included
|
||||
// if (index == 25)
|
||||
// return LOG_STR("Over temperature warning 122C"); // not currently included
|
||||
if (index == 26)
|
||||
return LOG_STR("Over temperature warning");
|
||||
if (index == 27)
|
||||
return LOG_STR("Over temperature warning 146C");
|
||||
if (index == 28)
|
||||
return LOG_STR("Right channel CBC over current warning");
|
||||
if (index == 29)
|
||||
return LOG_STR("Left channel CBC over current warning");
|
||||
return LOG_STR("Unknown fault");
|
||||
}
|
||||
|
||||
const ModelInfo TAS5825M_MODEL = {
|
||||
.name = model_name,
|
||||
.startup_sequence = STARTUP_SEQUENCE,
|
||||
.startup_sequence_length = sizeof(STARTUP_SEQUENCE) / sizeof(STARTUP_SEQUENCE[0]),
|
||||
.mixer_book = 0x8C,
|
||||
.mixer_page = 0x0B,
|
||||
.mixer_register = 0x14,
|
||||
// The clock fault is left out of the log and have_fault: it is set whenever the I2S clock stops, which is normal
|
||||
.fault_error_mask = 0x0007E30F,
|
||||
.fault_warning_mask =
|
||||
0x3C000000, // 0x3F000000 if OVER_TEMP_122C_WARNING and OVER_TEMP_112C_WARNING included in future
|
||||
// DC and over current faults keep the output off until cleared (datasheet 7.5.3.3.1, 7.5.3.3.2). They are not
|
||||
// cleared automatically: a DC fault re-trips only after 570 ms, so a clear on every poll would pass DC to the
|
||||
// speaker.
|
||||
.fault_output_off_mask = 0x0000000F,
|
||||
.fault_latched_mask = 0x3F07E70F,
|
||||
.fault_name = fault_name,
|
||||
.fault_sensor_bits =
|
||||
{
|
||||
3, // FAULT_SENSOR_LEFT_CHANNEL_DC_FAULT
|
||||
2, // FAULT_SENSOR_RIGHT_CHANNEL_DC_FAULT
|
||||
1, // FAULT_SENSOR_LEFT_CHANNEL_OVER_CURRENT
|
||||
0, // FAULT_SENSOR_RIGHT_CHANNEL_OVER_CURRENT
|
||||
15, // FAULT_SENSOR_OTP_CRC_CHECK
|
||||
14, // FAULT_SENSOR_BQ_WRITE_FAILED
|
||||
10, // FAULT_SENSOR_CLOCK_FAULT
|
||||
9, // FAULT_SENSOR_PVDD_OVER_VOLTAGE
|
||||
8, // FAULT_SENSOR_PVDD_UNDER_VOLTAGE
|
||||
16, // FAULT_SENSOR_OVER_TEMP_SHUTDOWN
|
||||
26, // FAULT_SENSOR_OVER_TEMP_WARNING
|
||||
13, // FAULT_SENSOR_LOAD_EEPROM_ERROR
|
||||
18, // FAULT_SENSOR_RIGHT_CHANNEL_CBC_OVER_CURRENT
|
||||
17, // FAULT_SENSOR_LEFT_CHANNEL_CBC_OVER_CURRENT
|
||||
29, // FAULT_SENSOR_LEFT_CHANNEL_CBC_OVER_CURRENT_WARNING
|
||||
28, // FAULT_SENSOR_RIGHT_CHANNEL_CBC_OVER_CURRENT_WARNING
|
||||
27, // FAULT_SENSOR_OVER_TEMP_146C_WARNING
|
||||
// 25, // FAULT_SENSOR_OVER_TEMP_122C_WARNING // not currently included
|
||||
// 24, // FAULT_SENSOR_OVER_TEMP_112C_WARNING // not currently included
|
||||
},
|
||||
};
|
||||
|
||||
} // namespace esphome::tas58xx
|
||||
@@ -196,8 +196,9 @@ bool TAS58xx::read_faults_() {
|
||||
if (this->have_fault_binary_sensor_ != nullptr)
|
||||
this->have_fault_binary_sensor_->publish_state((active & model.fault_error_mask) != 0);
|
||||
for (uint8_t fault = 0; fault < FAULT_SENSOR_COUNT; fault++) {
|
||||
if (this->fault_binary_sensors_[fault] != nullptr)
|
||||
this->fault_binary_sensors_[fault]->publish_state(faults & (uint32_t{1} << model.fault_sensor_bits[fault]));
|
||||
const uint8_t bit = model.fault_sensor_bits[fault];
|
||||
if (this->fault_binary_sensors_[fault] != nullptr && bit != NO_BIT)
|
||||
this->fault_binary_sensors_[fault]->publish_state(faults & (uint32_t{1} << bit));
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -264,7 +265,7 @@ void TAS58xx::dump_config() {
|
||||
" Analog Gain: %.1f dB\n"
|
||||
" DAC Mode: %s\n"
|
||||
" Mixer Mode: %s\n"
|
||||
" Volume Range: %.1f dB - %.1f dB",
|
||||
" Volume Range: %.1f dB to %.1f dB",
|
||||
this->analog_gain_db_,
|
||||
this->dac_mode_ == DAC_MODE_PBTL ? LOG_STR_LITERAL("PBTL") : LOG_STR_LITERAL("BTL"),
|
||||
LOG_STR_ARG(mixer_mode), this->volume_min_db_, this->volume_max_db_);
|
||||
|
||||
@@ -27,9 +27,11 @@ enum MixerMode : uint8_t {
|
||||
MIXER_MODE_RIGHT,
|
||||
};
|
||||
|
||||
/// Fault binary sensors that map to a single fault bit. The Python FAULT_SENSORS list uses the same names.
|
||||
/// Fault binary sensors that map to a single fault bit. Note that ordering is important.
|
||||
/// The Python FAULT_SENSORS list in binary_sensor.py uses the same names and ordering.
|
||||
enum FaultSensor : uint8_t {
|
||||
FAULT_SENSOR_LEFT_CHANNEL_DC_FAULT = 0,
|
||||
// Faults common to TAS5805M and TAS5825M
|
||||
FAULT_SENSOR_LEFT_CHANNEL_DC_FAULT,
|
||||
FAULT_SENSOR_RIGHT_CHANNEL_DC_FAULT,
|
||||
FAULT_SENSOR_LEFT_CHANNEL_OVER_CURRENT,
|
||||
FAULT_SENSOR_RIGHT_CHANNEL_OVER_CURRENT,
|
||||
@@ -40,9 +42,26 @@ enum FaultSensor : uint8_t {
|
||||
FAULT_SENSOR_PVDD_UNDER_VOLTAGE,
|
||||
FAULT_SENSOR_OVER_TEMP_SHUTDOWN,
|
||||
FAULT_SENSOR_OVER_TEMP_WARNING,
|
||||
FAULT_SENSOR_COUNT,
|
||||
// Faults available for TAS5825M Only
|
||||
FAULT_SENSOR_LOAD_EEPROM_ERROR,
|
||||
FAULT_SENSOR_RIGHT_CHANNEL_CBC_OVER_CURRENT,
|
||||
FAULT_SENSOR_LEFT_CHANNEL_CBC_OVER_CURRENT,
|
||||
FAULT_SENSOR_LEFT_CHANNEL_CBC_OVER_CURRENT_WARNING,
|
||||
FAULT_SENSOR_RIGHT_CHANNEL_CBC_OVER_CURRENT_WARNING,
|
||||
FAULT_SENSOR_OVER_TEMP_146C_WARNING,
|
||||
// FAULT_SENSOR_OVER_TEMP_122C_WARNING,
|
||||
// FAULT_SENSOR_OVER_TEMP_112C_WARNING,
|
||||
FAULT_SENSOR_COUNT, // keep last
|
||||
};
|
||||
|
||||
// compile time check
|
||||
static_assert(
|
||||
FAULT_SENSOR_COUNT == 17,
|
||||
"enum FaultSensor altered: Update FAULT_SENSORS in binary_sensor.py and fault_sensor_bits in each model's cpp");
|
||||
|
||||
/// ModelInfo::fault_sensor_bits value for a FaultSensor that the model does not have.
|
||||
static constexpr uint8_t NO_BIT = 0xFF;
|
||||
|
||||
/// Everything that differs between models of the family. One constant instance exists per model, see
|
||||
/// model_*.cpp, and each TAS58xx instance points to the one for its model.
|
||||
///
|
||||
@@ -67,11 +86,16 @@ struct ModelInfo {
|
||||
/// Faults that stay set after the condition is gone and are cleared after each read
|
||||
uint32_t fault_latched_mask;
|
||||
const LogString *(*fault_name)(uint8_t index);
|
||||
/// Bit index for each FaultSensor
|
||||
/// Bit index for each FaultSensor, NO_BIT used where the model does not have that fault bit
|
||||
uint8_t fault_sensor_bits[FAULT_SENSOR_COUNT];
|
||||
};
|
||||
|
||||
#ifdef USE_TAS58XX_TAS5805M
|
||||
extern const ModelInfo TAS5805M_MODEL;
|
||||
#endif
|
||||
#ifdef USE_TAS58XX_TAS5825M
|
||||
extern const ModelInfo TAS5825M_MODEL;
|
||||
#endif
|
||||
|
||||
class TAS58xx : public audio_dac::AudioDac, public PollingComponent, public i2c::I2CDevice {
|
||||
public:
|
||||
|
||||
@@ -12,10 +12,9 @@ audio_dac:
|
||||
volume_max_db: 0dB
|
||||
update_interval: 1s
|
||||
- platform: tas58xx
|
||||
model: TAS5805M
|
||||
model: TAS5825M
|
||||
id: tas58xx_amp_2
|
||||
i2c_id: i2c_bus
|
||||
address: 0x2C
|
||||
ignore_enable_pin_warning: true
|
||||
|
||||
esphome:
|
||||
@@ -59,3 +58,17 @@ binary_sensor:
|
||||
name: Second Amp Any Fault
|
||||
left_channel_dc_fault:
|
||||
name: Second Amp Left Channel DC Fault
|
||||
left_channel_cbc_over_current:
|
||||
name: Left Channel CBC Current Fault
|
||||
right_channel_cbc_over_current:
|
||||
name: Right Channel CBC Current Fault
|
||||
over_temp_shutdown:
|
||||
name: Over Temperature Shutdown Fault
|
||||
left_channel_cbc_over_current_warning:
|
||||
name: Left Channel CBC Current Warning
|
||||
right_channel_cbc_over_current_warning:
|
||||
name: Right Channel CBC Current Warning
|
||||
over_temp_146c_warning:
|
||||
name: Over Temperature 146C Warning
|
||||
over_temp_warning:
|
||||
name: Over Temperature 134C Warning
|
||||
|
||||
Reference in New Issue
Block a user