diff --git a/config/common/developer.yaml b/config/common/developer.yaml index 42b4104e..32055fd5 100644 --- a/config/common/developer.yaml +++ b/config/common/developer.yaml @@ -68,4 +68,70 @@ switch: id(udp_streamer).request_stop(); // Stop the stream when Comm mic is turned off } - delay: 500ms - - micro_wake_word.start: \ No newline at end of file + - micro_wake_word.start: + +select: + - platform: template + name: TAS2780 AMP LEVEL (dbV) + id: tas_amp_level_select + options: + - "11" + - "11.5" + - "12" + - "12.5" + - "13" + - "13.5" + - "14" + - "14.5" + - "15" + - "15.5" + - "16" + - "16.5" + - "17" + - "17.5" + - "18" + - "18.5" + - "19" + - "19.5" + - "20" + - "20.5" + - "21" + initial_option: "15" + optimistic: true + on_value: + - tas2780.update_config: + amp_level_idx: !lambda |- + auto index = id(tas_amp_level_select).active_index(); + if (index.has_value()){ + return index.value(); + } + ESP_LOGI("main", "No amp level selected"); + +number: + - platform: template + name: Volume Range Min + id: vol_rng_min + mode: "slider" + optimistic: true + min_value: 0 + max_value: 1. + step: .05 + initial_value: .3 + on_value: + then: + - tas2780.update_config: + vol_range_min: !lambda "return x;" + + - platform: template + name: Volume Range Max + id: vol_rng_max + mode: "slider" + optimistic: true + min_value: 0 + max_value: 1. + initial_value: 1. + step: .05 + on_value: + then: + - tas2780.update_config: + vol_range_max: !lambda "return x;" \ No newline at end of file diff --git a/config/common/media_player.yaml b/config/common/media_player.yaml index 170d7837..6d92e422 100644 --- a/config/common/media_player.yaml +++ b/config/common/media_player.yaml @@ -96,6 +96,10 @@ fusb302b: return id(pd_fusb302b).contract_voltage >= 9; then: - tas2780.activate: + mode: 2 + else: + - tas2780.activate: + mode: 0 speaker: - platform: i2s_audio id: i2s_audio_speaker diff --git a/config/satellite1.base.yaml b/config/satellite1.base.yaml index 2a03dfa7..9b6986a2 100644 --- a/config/satellite1.base.yaml +++ b/config/satellite1.base.yaml @@ -164,6 +164,10 @@ memory_flasher: return id(pd_fusb302b).contract_voltage >= 9; then: - tas2780.activate: + mode: 2 + else: + - tas2780.activate: + mode: 0 on_flashing_failed: then: diff --git a/esphome/components/tas2780/audio_dac.py b/esphome/components/tas2780/audio_dac.py index acc4a581..f1e53607 100644 --- a/esphome/components/tas2780/audio_dac.py +++ b/esphome/components/tas2780/audio_dac.py @@ -17,14 +17,20 @@ ) ActivateAction = tas2780_ns.class_( - "ActivateAction", automation.Action, cg.Parented.template(tas2780) + "ActivateAction", automation.Action +) + +UpdateConfigAction = tas2780_ns.class_( + "UpdateConfigAction", automation.Action ) DeactivateAction = tas2780_ns.class_( "DeactivateAction", automation.Action, cg.Parented.template(tas2780) ) - +CONF_VOL_RNG_MIN = "vol_range_min" +CONF_VOL_RNG_MAX = "vol_range_max" +CONF_AMP_LEVEL_IDX = "amp_level_idx" CONFIG_SCHEMA = ( cv.Schema( @@ -37,16 +43,59 @@ ) -TAS2780_ACTION_SCHEMA = automation.maybe_simple_id({cv.GenerateID(): cv.use_id(tas2780)}) +TAS2780_ACTION_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.use_id(tas2780), + cv.Optional(CONF_MODE, default=2) : cv.int_range(0,3) + } +) @automation.register_action("tas2780.deactivate", DeactivateAction, TAS2780_ACTION_SCHEMA) -@automation.register_action("tas2780.activate", ActivateAction, TAS2780_ACTION_SCHEMA) @automation.register_action("tas2780.reset", RestAction, TAS2780_ACTION_SCHEMA) async def tas2780_action(config, action_id, template_arg, args): var = cg.new_Pvariable(action_id, template_arg) await cg.register_parented(var, config[CONF_ID]) return var +@automation.register_action("tas2780.activate", ActivateAction, TAS2780_ACTION_SCHEMA) +async def tas2780_action(config, action_id, template_arg, args): + tas2780 = await cg.get_variable(config[CONF_ID]) + var = cg.new_Pvariable(action_id, template_arg, tas2780) + mode_ = config.get(CONF_MODE) + template_ = await cg.templatable(mode_, args, cg.uint8) + cg.add(var.set_mode(template_)) + return var + + + +TAS2780_UPDATE_CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.use_id(tas2780), + cv.Optional(CONF_VOL_RNG_MIN, default=.3) : cv.templatable(cv.float_range(0.,1.)), + cv.Optional(CONF_VOL_RNG_MAX, default=1.) : cv.templatable(cv.float_range(0.,1.)), + cv.Optional(CONF_AMP_LEVEL_IDX) : cv.templatable(cv.int_range(0, 20)) + } +) + + +@automation.register_action("tas2780.update_config", UpdateConfigAction, TAS2780_UPDATE_CONFIG_SCHEMA) +async def tas2780_action(config, action_id, template_arg, args): + tas2780 = await cg.get_variable(config[CONF_ID]) + var = cg.new_Pvariable(action_id, template_arg, tas2780) + vol_min_ = config.get(CONF_VOL_RNG_MIN) + template_ = await cg.templatable(vol_min_, args, float) + cg.add(var.set_vol_range_min(template_)) + vol_max_ = config.get(CONF_VOL_RNG_MAX) + template_ = await cg.templatable(vol_max_, args, float) + cg.add(var.set_vol_range_max(template_)) + if CONF_AMP_LEVEL_IDX in config: + amp_level_idx = config.get(CONF_AMP_LEVEL_IDX) + template_ = await cg.templatable(amp_level_idx, args, cg.uint8) + cg.add(var.set_amp_level(template_)) + + return var + + async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) diff --git a/esphome/components/tas2780/automation.h b/esphome/components/tas2780/automation.h index cc46996e..dd6b3a8b 100644 --- a/esphome/components/tas2780/automation.h +++ b/esphome/components/tas2780/automation.h @@ -14,11 +14,53 @@ class ResetAction : public Action, public Parented { }; template< typename... Ts> -class ActivateAction : public Action, public Parented { +class ActivateAction : public Action { public: - void play(Ts... x) override { this->parent_->activate(); } + ActivateAction(TAS2780 *parent) : parent_(parent) {} + TEMPLATABLE_VALUE(uint8_t, mode) + + void play(Ts... x) override { + if( this->mode_.has_value() ){ + this->parent_->activate(this->mode_.value(x...)); + } + else{ + this->parent_->activate(); + } + } + +protected: + TAS2780 *parent_; +}; + +template< typename... Ts> +class UpdateConfigAction : public Action { + public: + UpdateConfigAction(TAS2780 *parent) : parent_(parent) {} + TEMPLATABLE_VALUE(uint8_t, amp_level) + TEMPLATABLE_VALUE(float, vol_range_min) + TEMPLATABLE_VALUE(float, vol_range_max) + + void play(Ts... x) override { + if( this->amp_level_.has_value() ){ + this->parent_->set_amp_level(this->amp_level_.value(x...)); + this->parent_->update_register(); + } + if( this->vol_range_min_.has_value() ){ + this->parent_->set_vol_range_min(this->vol_range_min_.value(x...)); + } + if( this->vol_range_max_.has_value() ){ + this->parent_->set_vol_range_max(this->vol_range_max_.value(x...)); + } + } + +protected: + TAS2780 *parent_; }; + + + + template< typename... Ts> class DeactivateAction : public Action, public Parented { public: diff --git a/esphome/components/tas2780/tas2780.cpp b/esphome/components/tas2780/tas2780.cpp index 35fb04c6..afe1022c 100644 --- a/esphome/components/tas2780/tas2780.cpp +++ b/esphome/components/tas2780/tas2780.cpp @@ -12,23 +12,29 @@ static const char *const TAG = "tas2780"; static const uint8_t TAS2780_PAGE_SELECT = 0x00; // Page Select /* PAGE 0*/ -static const uint8_t TAS2780_MODE_CTRL = 0x02; +static const uint8_t TAS2780_SW_RESET = 0x01; //Software Reset +static const uint8_t TAS2780_MODE_CTRL = 0x02; //Device operational mode static const uint8_t TAS2780_MODE_CTRL_BOP_SRC__PVDD_UVLO = 0x80; static const uint8_t TAS2780_MODE_CTRL_MODE_MASK = 0x07; static const uint8_t TAS2780_MODE_CTRL_MODE__ACTIVE = 0x00; static const uint8_t TAS2780_MODE_CTRL_MODE__ACTIVE_MUTED = 0x01; static const uint8_t TAS2780_MODE_CTRL_MODE__SFTW_SHTDWN = 0x02; -static const uint8_t TAS2780_CHNL_0 = 0x03; +static const uint8_t TAS2780_CHNL_0 = 0x03; //Y Bridge and Channel settings static const uint8_t TAS2780_CHNL_0_CDS_MODE_SHIFT = 6; static const uint8_t TAS2780_CHNL_0_CDS_MODE_MASK = (0x03 << TAS2780_CHNL_0_CDS_MODE_SHIFT); static const uint8_t TAS2780_CHNL_0_AMP_LEVEL_SHIFT = 1; static const uint8_t TAS2780_CHNL_0_AMP_LEVEL_MASK = (0x1F) << TAS2780_CHNL_0_AMP_LEVEL_SHIFT; -static const uint8_t TAS2780_DC_BLK0 = 0x04; +static const uint8_t TAS2780_DC_BLK0 = 0x04; //SAR Filter and DC Path Blocker static const uint8_t TAS2780_DC_BLK0_VBAT1S_MODE_SHIFT = 7; +static const uint8_t TAS2780_DC_BLK1 = 0x05; //Record DC Blocker +static const uint8_t TAS2780_MISC_CFG1 = 0x06; //Misc Configuration 1 +static const uint8_t TAS2780_MISC_CFG2 = 0x07; //Misc Configuration 2 +static const uint8_t TAS2780_TDM_CFG0 = 0x08; //TDM Configuration 0 +static const uint8_t TAS2780_TDM_CFG1 = 0x09; //TDM Configuration 1 -static const uint8_t TAS2780_TDM_CFG2 = 0x0a; +static const uint8_t TAS2780_TDM_CFG2 = 0x0A; //TDM Configuration 2 static const uint8_t TAS2780_TDM_CFG2_RX_SCFG_SHIFT = 4; static const uint8_t TAS2780_TDM_CFG2_RX_SCFG_MASK = (3 << TAS2780_TDM_CFG2_RX_SCFG_SHIFT); static const uint8_t TAS2780_TDM_CFG2_RX_SCFG__STEREO_DWN_MIX = (3 << TAS2780_TDM_CFG2_RX_SCFG_SHIFT); @@ -40,18 +46,201 @@ static const uint8_t TAS2780_TDM_CFG2_RX_WLEN__32BIT = (3 << TAS2780_TDM_CFG2_RX static const uint8_t TAS2780_TDM_CFG2_RX_SLEN_MASK = (3 << 0); static const uint8_t TAS2780_TDM_CFG2_RX_SLEN__32BIT = 2; -static const uint8_t TAS2780_DVC = 0x1a; +static const uint8_t TAS2780_LIM_MAX_ATTN = 0x0B; //Limiter +static const uint8_t TAS2780_TDM_CFG3 = 0x0C; //TDM Configuration 3 +static const uint8_t TAS2780_TDM_CFG4 = 0x0D; //TDM Configuration 4 +static const uint8_t TAS2780_TDM_CFG5 = 0x0E; //TDM Configuration 5 +static const uint8_t TAS2780_TDM_CFG6 = 0x0F; //TDM Configuration 6 +static const uint8_t TAS2780_TDM_CFG7 = 0x10; //TDM Configuration 7 +static const uint8_t TAS2780_TDM_CFG8 = 0x11; //TDM Configuration 8 +static const uint8_t TAS2780_TDM_CFG9 = 0x12; //TDM Configuration 9 +static const uint8_t TAS2780_TDM_CFG10 = 0x13; //TDM Configuration 10 +static const uint8_t TAS2780_TDM_CFG11 = 0x14; //TDM Configuration 11 +static const uint8_t TAS2780_ICC_CNFG2 = 0x15; //ICC Mode +static const uint8_t TAS2780_TDM_CFG12 = 0x16; //TDM Configuration 12 +static const uint8_t TAS2780_ICLA_CFG0 = 0x17; //Inter Chip Limiter Alignment 0 +static const uint8_t TAS2780_ICLA_CFG1 = 0x18; //Inter Chip Gain Alignment 1 +static const uint8_t TAS2780_DG_0 = 0x19; //Diagnostic Signal + +static const uint8_t TAS2780_DVC = 0x1A; //Digital Volume Control +static const uint8_t TAS2780_LIM_CFG0 = 0x1B; //Limiter Configuration 0 +static const uint8_t TAS2780_LIM_CFG1 = 0x1C; //Limiter Configuration 1 +static const uint8_t TAS2780_BOP_CFG0 = 0x1D; //Brown Out Prevention 0 +static const uint8_t TAS2780_BOP_CFG1 = 0x1E; //Brown Out Prevention 1 +static const uint8_t TAS2780_BOP_CFG2 = 0x1F; //Brown Out Prevention 2 +static const uint8_t TAS2780_BOP_CFG3 = 0x20; //Brown Out Prevention 3 +static const uint8_t TAS2780_BOP_CFG4 = 0x21; //Brown Out Prevention 4 +static const uint8_t TAS2780_BOP_CFG5 = 0x22; //BOP Configuration 5 +static const uint8_t TAS2780_BOP_CFG6 = 0x23; //Brown Out Prevention 6 +static const uint8_t TAS2780_BOP_CFG7 = 0x24; //Brown Out Prevention 7 +static const uint8_t TAS2780_BOP_CFG8 = 0x25; //Brown Out Prevention 8 +static const uint8_t TAS2780_BOP_CFG9 = 0x26; //Brown Out Prevention 9 +static const uint8_t TAS2780_BOP_CFG10 = 0x27; //BOP Configuration 10 +static const uint8_t TAS2780_BOP_CFG11 = 0x28; //Brown Out Prevention 11 +static const uint8_t TAS2780_BOP_CFG12 = 0x29; //Brown Out Prevention 12 +static const uint8_t TAS2780_BOP_CFG13 = 0x2A; //Brown Out Prevention 13 +static const uint8_t TAS2780_BOP_CFG14 = 0x2B; //Brown Out Prevention 14 +static const uint8_t TAS2780_BOP_CFG15 = 0x2C; //BOP Configuration 15 +static const uint8_t TAS2780_BOP_CFG17 = 0x2D; //Brown Out Prevention 17 +static const uint8_t TAS2780_BOP_CFG18 = 0x2E; //Brown Out Prevention 18 +static const uint8_t TAS2780_BOP_CFG19 = 0x2F; //Brown Out Prevention 19 +static const uint8_t TAS2780_BOP_CFG20 = 0x30; //Brown Out Prevention 20 +static const uint8_t TAS2780_BOP_CFG21 = 0x31; //BOP Configuration 21 +static const uint8_t TAS2780_BOP_CFG22 = 0x32; //Brown Out Prevention 22 +static const uint8_t TAS2780_BOP_CFG23 = 0x33; //Lowest PVDD Measured +static const uint8_t TAS2780_BOP_CFG24 = 0x34; //Lowest BOP Attack Rate +static const uint8_t TAS2780_NG_CFG0 = 0x35; //Noise Gate 0 +static const uint8_t TAS2780_NG_CFG1 = 0x36; //Noise Gate 1 +static const uint8_t TAS2780_LVS_CFG0 = 0x37; //Low Voltage Signaling +static const uint8_t TAS2780_DIN_PD = 0x38; //Digital Input Pin Pull Down + +/* Interrupts */ +static const uint8_t TAS2780_INT_MASK0 = 0x3B; //Interrupt Mask 0 +static const uint8_t TAS2780_INT_MASK1 = 0x3C; //Interrupt Mask 1 +static const uint8_t TAS2780_INT_MASK4 = 0x3D; //Interrupt Mask 4 +static const uint8_t TAS2780_INT_MASK2 = 0x40; //Interrupt Mask 2 +static const uint8_t TAS2780_INT_MASK3 = 0x41; //Interrupt Mask 3 +static const uint8_t TAS2780_INT_LIVE0 = 0x42; //Live Interrupt Read-back 0 +static const uint8_t TAS2780_INT_LIVE1 = 0x43; //Live Interrupt Read-back 1 +static const uint8_t TAS2780_INT_LIVE1_0 = 0x44; //Live Interrupt Read-back 1_0 +static const uint8_t TAS2780_INT_LIVE2 = 0x47; //Live Interrupt Read-back 2 +static const uint8_t TAS2780_INT_LIVE3 = 0x48; //Live Interrupt Read-back 3 +static const uint8_t TAS2780_INT_LTCH0 = 0x49; //Latched Interrupt Read-back 0 +static const uint8_t TAS2780_INT_LTCH1 = 0x4A; //Latched Interrupt Read-back 1 +static const uint8_t TAS2780_INT_LTCH1_0 = 0x4B; //Latched Interrupt Read-back 1_0 +static const uint8_t TAS2780_INT_LTCH2 = 0x4F; //Latched Interrupt Read-back 2 +static const uint8_t TAS2780_INT_LTCH3 = 0x50; //Latched Interrupt Read-back 3 +static const uint8_t TAS2780_INT_LTCH4 = 0x51; //Latched Interrupt Read-back 4 + +static const uint8_t TAS2780_VBAT_MSB = 0x52; //SAR VBAT1S 0 +static const uint8_t TAS2780_VBAT_LSB = 0x53; //SAR VBAT1S 1 +static const uint8_t TAS2780_PVDD_MSB = 0x54; //SAR PVDD 0 +static const uint8_t TAS2780_PVDD_LSB = 0x55; //SAR PVDD 1 +static const uint8_t TAS2780_TEMP = 0x56; //SAR ADC Conversion 2 +static const uint8_t TAS2780_INT_CLK_CFG = 0x5C; //Clock Setting and IRQZ +static const uint8_t TAS2780_MISC_CFG3 = 0x5D; //Misc Configuration 3 +static const uint8_t TAS2780_CLOCK_CFG = 0x60; //Clock Configuration +static const uint8_t TAS2780_IDLE_IND = 0x63; //Idle channel current optimization +static const uint8_t TAS2780_SAR_SAMP = 0x64; //SAR Sampling Time +static const uint8_t TAS2780_MISC_CFG4 = 0x65; //Misc Configuration 4 +static const uint8_t TAS2780_TG_CFG0 = 0x67; //Tone Generator +static const uint8_t TAS2780_CLK_CFG = 0x68; //Detect Clock Ration and Sample Rate +static const uint8_t TAS2780_LV_EN_CFG = 0x6A; //Class-D and LVS Delays +static const uint8_t TAS2780_NG_CFG2 = 0x6B; //Noise Gate 2 +static const uint8_t TAS2780_NG_CFG3 = 0x6C; //Noise Gate 3 +static const uint8_t TAS2780_NG_CFG4 = 0x6D; //Noise Gate 4 +static const uint8_t TAS2780_NG_CFG5 = 0x6E; //Noise Gate 5 +static const uint8_t TAS2780_NG_CFG6 = 0x6F; //Noise Gate 6 +static const uint8_t TAS2780_NG_CFG7 = 0x70; //Noise Gate 7 +static const uint8_t TAS2780_PVDD_UVLO = 0x71; //UVLO Threshold +static const uint8_t TAS2780_DMD = 0x73; //DAC Modulator Dither +static const uint8_t TAS2780_I2C_CKSUM = 0x7E; //I2C Checksum +static const uint8_t TAS2780_BOOK = 0x7F; //Device Book + +/* PAGE 0x01*/ +static const uint8_t TAS2780_INIT_0 = 0x17; //Initialization +static const uint8_t TAS2780_LSR = 0x19; //Modulation +static const uint8_t TAS2780_INIT_1 = 0x21; //Initialization +static const uint8_t TAS2780_INIT_2 = 0x35; //Initialization +static const uint8_t TAS2780_INT_LDO = 0x36; //Internal LDO Setting +static const uint8_t TAS2780_SDOUT_HIZ_1 = 0x3D; //Slots Control +static const uint8_t TAS2780_SDOUT_HIZ_2 = 0x3E; //Slots Control +static const uint8_t TAS2780_SDOUT_HIZ_3 = 0x3F; //Slots Control +static const uint8_t TAS2780_SDOUT_HIZ_4 = 0x40; //Slots Control +static const uint8_t TAS2780_SDOUT_HIZ_5 = 0x41; //Slots Control +static const uint8_t TAS2780_SDOUT_HIZ_6 = 0x42; //Slots Control +static const uint8_t TAS2780_SDOUT_HIZ_7 = 0x43; //Slots Control +static const uint8_t TAS2780_SDOUT_HIZ_8 = 0x44; //Slots Control +static const uint8_t TAS2780_SDOUT_HIZ_9 = 0x45; //Slots Control +static const uint8_t TAS2780_TG_EN = 0x47; //Thermal Detection Enable +static const uint8_t TAS2780_EDGE_CTRL = 0x4C; //Slew rate control + +/* PAGE 0x04*/ +static const uint8_t TAS2780_DG_DC_VAL1 = 0x08; //Diagnostic DC Level +static const uint8_t TAS2780_DG_DC_VAL2 = 0x09; //Diagnostic DC Level +static const uint8_t TAS2780_DG_DC_VAL3 = 0x0A; //Diagnostic DC Level +static const uint8_t TAS2780_DG_DC_VAL4 = 0x0B; //Diagnostic DC Level +static const uint8_t TAS2780_LIM_TH_MAX1 = 0x0C; //Limiter Maximum Threshold +static const uint8_t TAS2780_LIM_TH_MAX2 = 0x0D; //Limiter Maximum Threshold +static const uint8_t TAS2780_LIM_TH_MAX3 = 0x0E; //Limiter Maximum Threshold +static const uint8_t TAS2780_LIM_TH_MAX4 = 0x0F; //Limiter Maximum Threshold +static const uint8_t TAS2780_LIM_TH_MIN1 = 0x10; //Limiter Minimum Threshold +static const uint8_t TAS2780_LIM_TH_MIN2 = 0x11; //Limiter Minimum Threshold +static const uint8_t TAS2780_LIM_TH_MIN3 = 0x12; //Limiter Minimum Threshold +static const uint8_t TAS2780_LIM_TH_MIN4 = 0x13; //Limiter Minimum Threshold +static const uint8_t TAS2780_LIM_INF_PT1 = 0x14; //Limiter Inflection Point +static const uint8_t TAS2780_LIM_INF_PT2 = 0x15; //Limiter Inflection Point +static const uint8_t TAS2780_LIM_INF_PT3 = 0x16; //Limiter Inflection Point +static const uint8_t TAS2780_LIM_INF_PT4 = 0x17; //Limiter Inflection Point +static const uint8_t TAS2780_LIM_SLOPE1 = 0x18; //Limiter Slope +static const uint8_t TAS2780_LIM_SLOPE2 = 0x19; //Limiter Slope +static const uint8_t TAS2780_LIM_SLOPE3 = 0x1A; //Limiter Slope +static const uint8_t TAS2780_LIM_SLOPE4 = 0x1B; //Limiter Slope +static const uint8_t TAS2780_TF_HLD1 = 0x1C; //TFB Maximum Hold +static const uint8_t TAS2780_TF_HLD2 = 0x1D; //TFB Maximum Hold +static const uint8_t TAS2780_TF_HLD3 = 0x1E; //TFB Maximum Hold +static const uint8_t TAS2780_TF_HLD4 = 0x1F; //TFB Maximum Hold +static const uint8_t TAS2780_TF_RLS1 = 0x20; //TFB Release Rate +static const uint8_t TAS2780_TF_RLS2 = 0x21; //TFB Release Rate +static const uint8_t TAS2780_TF_RLS3 = 0x22; //TFB Release Rate +static const uint8_t TAS2780_TF_RLS4 = 0x23; //TFB Release Rate +static const uint8_t TAS2780_TF_SLOPE1 = 0x24; //TFB Limiter Slope +static const uint8_t TAS2780_TF_SLOPE2 = 0x25; //TFB Limiter Slope +static const uint8_t TAS2780_TF_SLOPE3 = 0x26; //TFB Limiter Slope +static const uint8_t TAS2780_TF_SLOPE4 = 0x27; //TFB Limiter Slope +static const uint8_t TAS2780_TF_TEMP_TH1 = 0x28; //TFB Threshold +static const uint8_t TAS2780_TF_TEMP_TH2 = 0x29; //TFB Threshold +static const uint8_t TAS2780_TF_TEMP_TH3 = 0x2A; //TFB Threshold +static const uint8_t TAS2780_TF_TEMP_TH4 = 0x2B; //TFB Threshold +static const uint8_t TAS2780_TF_MAX_ATTN1 = 0x2C; //TFB Gain Reduction +static const uint8_t TAS2780_TF_MAX_ATTN2 = 0x2D; //TFB Gain Reduction +static const uint8_t TAS2780_TF_MAX_ATTN3 = 0x2E; //TFB Gain Reduction +static const uint8_t TAS2780_TF_MAX_ATTN4 = 0x2F; //TFB Gain Reduction +static const uint8_t TAS2780_LD_CFG0 = 0x40; //Load Diagnostics Resistance Upper Threshold +static const uint8_t TAS2780_LD_CFG1 = 0x41; //Load Diagnostics Resistance Upper Threshold +static const uint8_t TAS2780_LD_CFG2 = 0x42; //Load Diagnostics Resistance Upper Threshold +static const uint8_t TAS2780_LD_CFG3 = 0x43; //Load Diagnostics Resistance Upper Threshold +static const uint8_t TAS2780_LD_CFG4 = 0x44; //Load Diagnostics Resistance Lower Threshold +static const uint8_t TAS2780_LD_CFG5 = 0x45; //Load Diagnostics Resistance Lower Threshold +static const uint8_t TAS2780_LD_CFG6 = 0x46; //Load Diagnostics Resistance Lower Threshold +static const uint8_t TAS2780_LD_CFG7 = 0x47; //Load Diagnostics Resistance Lower Threshold +static const uint8_t TAS2780_CLD_EFF_1 = 0x48; //Class D Efficiency +static const uint8_t TAS2780_CLD_EFF_2 = 0x49; //Class D Efficiency +static const uint8_t TAS2780_CLD_EFF_3 = 0x4A; //Class D Efficiency +static const uint8_t TAS2780_CLD_EFF_4 = 0x4B; //Class D Efficiency +static const uint8_t TAS2780_LDG_RES1 = 0x4C; //Load Diagnostics Resistance Value +static const uint8_t TAS2780_LDG_RES2 = 0x4D; //Load Diagnostics Resistance Value +static const uint8_t TAS2780_LDG_RES3 = 0x4E; //Load Diagnostics Resistance Value +static const uint8_t TAS2780_LDG_RES4 = 0x4F; //Load Diagnostics Resistance Value + +/* PAGE 0x0FD*/ +static const uint8_t TAS2780_INIT_3 = 0x3E; //Initialization + +static const uint8_t TAS2780_INT_LTCH0_IR_OT = (1 << 0); // over temp error +static const uint8_t TAS2780_INT_LTCH0_IR_OC = (1 << 1); // over current error +static const uint8_t TAS2780_INT_LTCH0_IR_TDMCE = (1 << 2); // TDM_CLOCK_ERROR +static const uint8_t TAS2780_INT_LTCH0_IR_LIMA = (1 << 3); // limiter active +static const uint8_t TAS2780_INT_LTCH0_IR_PBIP = (1 << 4); // PVDD below limiter inflection point +static const uint8_t TAS2780_INT_LTCH0_IR_LIMMA = (1 << 5); // limiter max attenuation +static const uint8_t TAS2780_INT_LTCH0_IR_BOPIH = (1 << 6); // BOP infinite hold +static const uint8_t TAS2780_INT_LTCH0_IR_BOPM = (1 << 7); // due to bop mute + +static const uint8_t TAS2780_INT_LTCH1_IR_VBATLIM = (1 << 0); // Gain Limiter interrupt +static const uint8_t TAS2780_INT_LTCH1_IR_LDMODE = (3 << 3); // Load Diagnostic mode fault status +static const uint8_t TAS2780_INT_LTCH1_IR_LDC = (1 << 5); // Load diagnostic completion +static const uint8_t TAS2780_INT_LTCH1_IR_OTPCRC = (1 << 6); // OTP CRC error flag + +static const uint8_t TAS2780_INT_LTCH1_0_IR_VBAT1S_UVLO = (1 << 5); // VBAT1S Under Voltage +static const uint8_t TAS2780_INT_LTCH1_0_IR_PLL_CLK = (1 << 7); // Internal PLL Clock Error + +static const uint8_t TAS2780_INT_LTCH2_IR_PUVLO = (1 << 0); // PVDD UVLO +static const uint8_t TAS2780_INT_LTCH2_IR_LDO_OL = (1 << 1); // Internal VBAT1S LDO Over Load +static const uint8_t TAS2780_INT_LTCH2_IR_LDO_OV = (1 << 2); // Internal VBAT1S LDO Over Voltage +static const uint8_t TAS2780_INT_LTCH2_IR_LDO_UV = (1 << 3); // Internal VBAT1S LDO Under Voltage -static const uint8_t TAS2780_INT_MASK0 = 0x3b; -static const uint8_t TAS2780_INT_MASK1 = 0x3c; -static const uint8_t TAS2780_INT_MASK4 = 0x3d; -static const uint8_t TAS2780_INT_MASK2 = 0x40; -static const uint8_t TAS2780_INT_MASK3 = 0x41; -/* PAGE 1*/ -static const uint8_t TAS2780_INT_LDO = 0x36; static const uint8_t POWER_MODES[4][2] = { {2, 0}, // PWR_MODE0: CDS_MODE=10, VBAT1S_MODE=0 @@ -60,14 +249,20 @@ static const uint8_t POWER_MODES[4][2] = { {1, 0}, // PWR_MODE3: CDS_MODE=01, VBAT1S_MODE=0 }; - void TAS2780::setup(){ + this->init(); + //set to software shutdown + this->reg(TAS2780_MODE_CTRL) = (TAS2780_MODE_CTRL_BOP_SRC__PVDD_UVLO & ~TAS2780_MODE_CTRL_MODE_MASK) | TAS2780_MODE_CTRL_MODE__SFTW_SHTDWN; + } + + +void TAS2780::init(){ // select page 0 this->reg(TAS2780_PAGE_SELECT) = 0x00; - + // software reset this->reg(0x01) = 0x01; - + uint8_t chd1 = this->reg(0x05).get(); uint8_t chd2 = this->reg(0x68).get(); uint8_t chd3 = this->reg(0x02).get(); @@ -83,10 +278,10 @@ void TAS2780::setup(){ this->mark_failed(); return; } - + this->reg(TAS2780_PAGE_SELECT) = 0x00; - this->reg(0x0e) = 0x44; - this->reg(0x0f) = 0x40; + this->reg(TAS2780_TDM_CFG5) = 0x44; //TDM tx vsns transmit enable with slot 4 + this->reg(TAS2780_TDM_CFG6) = 0x40; //TDM tx isns transmit enable with slot 0 this->reg(TAS2780_TDM_CFG2) = ( TAS2780_TDM_CFG2_RX_SCFG__STEREO_DWN_MIX | @@ -94,26 +289,29 @@ void TAS2780::setup(){ TAS2780_TDM_CFG2_RX_SLEN__32BIT ); - this->reg(TAS2780_PAGE_SELECT) = 0x01; - this->reg(0x17) = 0xc0; - this->reg(0x19) = 0x00; - this->reg(0x21) = 0x00; - this->reg(0x35) = 0x74; + this->reg(TAS2780_LSR) = 0x00; //LSR Mode + this->reg(TAS2780_INIT_0) = 0xC8; //SARBurstMask=0, CMP_HYST_LP=1 + this->reg(TAS2780_INIT_1) = 0x00; //Disable Comparator Hysterisis + this->reg(TAS2780_INIT_2) = 0x74; //Noise minimized this->reg(TAS2780_PAGE_SELECT) = 0xFD; - this->reg(0x0d) = 0x0d; - this->reg(0x3e) = 0x4a; - this->reg(0x0d) = 0x00; + this->reg(0x0D) = 0x0D; //Access Page 0xFD + this->reg(TAS2780_INIT_3) = 0x4a; //Optimal Dmin + this->reg(0x0D) = 0x00; //Remove access Page 0xFD this->reg(TAS2780_PAGE_SELECT) = 0x00; //Power Mode 2 (no external VBAT) - //this->reg(0x03) = 0xe8; - this->reg(0x03) = 0xa8; - this->reg(0x04) = 0xa1; - this->reg(0x71) = 0x12; - + //this->reg(TAS2780_CHNL_0) = 0xA8; + //this->reg(TAS2780_CHNL_0) = 0xA1; + this->set_power_mode_(this->power_mode_); + + //When Y bridge is used (eg. PWR_MODE1) PVDD UVLO threshold needs to be set 2.5 V above VBAT1S level. + // UVLO = 1.753V + val * 0.332V + //this->reg(TAS2780_PVDD_UVLO) = 0x12; //PVDD UVLO set to 7.73V + this->reg(TAS2780_PVDD_UVLO) = 0x03; //PVDD UVLO set to 2.76V + //Set interrupt masks this->reg(TAS2780_PAGE_SELECT) = 0x00; @@ -123,23 +321,26 @@ void TAS2780::setup(){ this->reg(TAS2780_INT_MASK2) = 0xFF; this->reg(TAS2780_INT_MASK3) = 0xFF; this->reg(TAS2780_INT_MASK1) = 0xFF; - - + + // set interrupt to trigger For // 0h : On any unmasked live interrupts // 3h : 2 - 4 ms every 4 ms on any unmasked latched - uint8_t reg_0x5c = this->reg(0x5c).get(); - this->reg(0x5c) = (reg_0x5c & ~0x03) | 0x00; - - //set to software shutdown - this->reg(TAS2780_MODE_CTRL) = (TAS2780_MODE_CTRL_BOP_SRC__PVDD_UVLO & ~TAS2780_MODE_CTRL_MODE_MASK) | TAS2780_MODE_CTRL_MODE__SFTW_SHTDWN; - } + uint8_t reg_0x5c = this->reg(TAS2780_INT_CLK_CFG).get(); + this->reg(TAS2780_INT_CLK_CFG) = (reg_0x5c & ~0x03) | 0x00; + + this->update_register(); +} -void TAS2780::activate(){ - ESP_LOGD(TAG, "Activating TAS2780"); +void TAS2780::activate(uint8_t power_mode){ + ESP_LOGD(TAG, "Activating TAS2780 (PWR_MODE:%d)", power_mode); // clear interrupt latches - this->reg(0x5c) = 0x19 | (1 << 2); + this->reg(TAS2780_INT_CLK_CFG) = 0x19 | (1 << 2); + if (power_mode != this->power_mode_){ + this->power_mode_ = power_mode; + this->init(); + } // activate this->reg(TAS2780_MODE_CTRL) = (TAS2780_MODE_CTRL_BOP_SRC__PVDD_UVLO & ~TAS2780_MODE_CTRL_MODE_MASK) | TAS2780_MODE_CTRL_MODE__ACTIVE; } @@ -152,77 +353,127 @@ void TAS2780::deactivate(){ void TAS2780::reset(){ - // select page 0 - this->reg(TAS2780_PAGE_SELECT) = 0x00; - - // software reset - this->reg(0x01) = 0x01; - - uint8_t chd1 = this->reg(0x05).get(); - uint8_t chd2 = this->reg(0x68).get(); - uint8_t chd3 = this->reg(0x02).get(); + this->init(); + this->activate(this->power_mode_); +} - if( chd1 == 0x41 ){ - ESP_LOGD(TAG, "TAS2780 chip found."); - ESP_LOGD(TAG, "Reg 0x68: %d.", chd2 ); - ESP_LOGD(TAG, "Reg 0x02: %d.", chd3 ); +void TAS2780::set_power_mode_(const uint8_t power_mode){ + // PWR_MODE0: PVDD is the only supply used to deliver output power. VBAT external + // PWR_MODE1: VBAT1S is used to deliver output power based on level and headroom configured. + // When audio signal crosses a programmed threshold Class-D output is switched over PVDD + // PWR_MODE2: PVDD is the only supply. VBAT1S is delivered by an internal LDO and used to supply at + // signals close to idle channel levels. When audio signal levels crosses -100dBFS (default), + // Class-D output switches to PVDD. + // PWR_MODE3: The device can be forced to work out of a low power rail mode of operation. + + assert( power_mode < 4); + uint8_t chnl_0 = this->reg(TAS2780_CHNL_0).get(); + this->reg(TAS2780_CHNL_0) = (chnl_0 & ~TAS2780_CHNL_0_CDS_MODE_MASK) | (POWER_MODES[power_mode][0] << TAS2780_CHNL_0_CDS_MODE_SHIFT); + uint8_t dc_blk0 = this->reg(TAS2780_DC_BLK0).get(); + this->reg(TAS2780_DC_BLK0) = (dc_blk0 & ~(1 << TAS2780_DC_BLK0_VBAT1S_MODE_SHIFT)) | (POWER_MODES[power_mode][1] << TAS2780_DC_BLK0_VBAT1S_MODE_SHIFT); +} + + +void TAS2780::log_error_states(){ + const uint8_t latched_its = this->reg(TAS2780_INT_LTCH0).get(); + //Temperature + if ( latched_its & TAS2780_INT_LTCH0_IR_OT ){ + ESP_LOGE(TAG, "Over temperature error!"); } - else - { - ESP_LOGD(TAG, "TAS2780 chip not found."); - this->mark_failed(); - return; + //Over Current + if ( latched_its & TAS2780_INT_LTCH0_IR_OC ){ + ESP_LOGE(TAG, "Over current error!"); } - this->reg(TAS2780_PAGE_SELECT) = 0x00; - this->reg(0x0e) = 0x44; - this->reg(0x0f) = 0x40; + //TDM CLOCK + if ( latched_its & TAS2780_INT_LTCH0_IR_TDMCE ){ + ESP_LOGE(TAG, "TDM Clock Error!"); + } - this->reg(TAS2780_TDM_CFG2) = ( - TAS2780_TDM_CFG2_RX_SCFG__STEREO_DWN_MIX | - TAS2780_TDM_CFG2_RX_WLEN__32BIT | - TAS2780_TDM_CFG2_RX_SLEN__32BIT - ); + if ( latched_its & TAS2780_INT_LTCH0_IR_LIMA ){ + ESP_LOGE(TAG, " limiter active error!"); + } - this->reg(TAS2780_PAGE_SELECT) = 0x01; - this->reg(0x17) = 0xc0; - this->reg(0x19) = 0x00; - this->reg(0x21) = 0x00; - this->reg(0x35) = 0x74; + if ( latched_its & TAS2780_INT_LTCH0_IR_PBIP ){ + ESP_LOGE(TAG, "PVDD below limiter inflection point!"); + } + + if ( latched_its & TAS2780_INT_LTCH0_IR_LIMMA ){ + ESP_LOGE(TAG, "Limiter max attenuation!"); + } - this->reg(TAS2780_PAGE_SELECT) = 0xFD; - this->reg(0x0d) = 0x0d; - this->reg(0x3e) = 0x4a; - this->reg(0x0d) = 0x00; + if ( latched_its & TAS2780_INT_LTCH0_IR_BOPIH ){ + ESP_LOGE(TAG, "BOP infinite hold!"); + } + if ( latched_its & TAS2780_INT_LTCH0_IR_BOPM ){ + ESP_LOGE(TAG, "BOP Mute!"); + } - this->reg(TAS2780_PAGE_SELECT) = 0x00; - //Power Mode 2 (no external VBAT) - this->reg(0x03) = 0xa8; - this->reg(0x04) = 0xa1; - this->reg(0x71) = 0x12; + const uint8_t latched1_its = this->reg(TAS2780_INT_LTCH1).get(); - - //Set interrupt masks - this->reg(TAS2780_PAGE_SELECT) = 0x00; - //mask VBAT1S Under Voltage - this->reg(0x3d) = 0xFF; - //mask all PVDD and VBAT1S interrupts - this->reg(0x40) = 0xFF; - this->reg(0x41) = 0xFF; + if( latched1_its & TAS2780_INT_LTCH1_IR_VBATLIM) + { + ESP_LOGE(TAG, "Gain Limiter interrupt!"); + } + if( latched1_its & TAS2780_INT_LTCH1_IR_LDMODE) + { + ESP_LOGE(TAG, "Load Diagnostic mode fault status!"); + } + + if( latched1_its & TAS2780_INT_LTCH1_IR_LDC) + { + ESP_LOGE(TAG, "Load diagnostic completion!"); + } + + if( latched1_its & TAS2780_INT_LTCH1_IR_OTPCRC) + { + ESP_LOGE(TAG, "OTP CRC error flag!"); + } + + const uint8_t latched1_0_its = this->reg(TAS2780_INT_LTCH1_0).get(); + if( latched1_0_its & TAS2780_INT_LTCH1_0_IR_VBAT1S_UVLO) + { + ESP_LOGE(TAG, "VBAT1S Under Voltage!"); + } + + if( latched1_0_its & TAS2780_INT_LTCH1_0_IR_PLL_CLK) + { + ESP_LOGE(TAG, "Internal PLL Clock Error!"); + } + + const uint8_t latched2_its = this->reg(TAS2780_INT_LTCH2).get(); + if( latched2_its & TAS2780_INT_LTCH2_IR_PUVLO) + { + ESP_LOGE(TAG, "PVDD UVLO!"); + } + if( latched2_its & TAS2780_INT_LTCH2_IR_LDO_OL) + { + ESP_LOGE(TAG, "Internal VBAT1S LDO Over Load!"); + } + if( latched2_its & TAS2780_INT_LTCH2_IR_LDO_OV) + { + ESP_LOGE(TAG, "Internal VBAT1S LDO Over Voltage!"); + } + if( latched2_its & TAS2780_INT_LTCH2_IR_LDO_UV) + { + ESP_LOGE(TAG, "Internal VBAT1S LDO Under Voltage!"); + } - // set interrupt to trigger For - // 0h : On any unmasked live interrupts - // 3h : 2 - 4 ms every 4 ms on any unmasked latched - uint8_t reg_0x5c = this->reg(0x5c).get(); - this->reg(0x5c) = (reg_0x5c & ~0x03) | 0x00; - this->activate(); } - void TAS2780::loop() { + static uint32_t last_call = millis(); + if( millis() - last_call > 1000 ){ + last_call = millis(); + uint8_t curr_mode = this->reg(TAS2780_MODE_CTRL).get() & 7; + if( curr_mode == 2 ){ + ESP_LOGD(TAG, "Current Mode: SOFTWARE_SHUTDOWN (PWR_MODE: %d)", this->power_mode_); + this->log_error_states(); + } + } } @@ -256,7 +507,7 @@ float TAS2780::volume() { bool TAS2780::write_mute_() { if( this->is_muted_ ){ - this->reg(TAS2780_DVC) = 0xc9; + this->reg(TAS2780_DVC) = 0xC9; } else { this->write_volume_(); } @@ -272,22 +523,27 @@ bool TAS2780::write_volume_() { A_{DVC}: is the digital volume control setting as a number of dB (default 0 dB) A_{AMP}: the amplifier output level setting as a number of dBV - DVC_LVL[7:0] : 0dB to -100dB [0x00, 0xc8] c8 = 200 + DVC_LVL[7:0] : 0dB to -100dB [0x00, 0xC8] c8 = 200 AMP_LEVEL[4:0] : @48ksps 11dBV - 21dBV [0x00, 0x14] */ - float attenuation = (1. - this->volume_) * 90.f; - uint8_t dvc = clamp(attenuation, 0, 0xc8); + float range_len = this->vol_range_max_ - this->vol_range_min_; + float volume = this->volume_ * range_len + this->vol_range_min_; + float attenuation = (1. - volume) * 100.f; + ESP_LOGD(TAG, "Setting attenuation to: %4.2f", attenuation); + uint8_t dvc = clamp(attenuation, 0, 0xC8); this->reg(TAS2780_DVC) = dvc; - uint8_t amp_level = 8; // 7: 15dBV - uint8_t reg_val = this->reg(TAS2780_CHNL_0).get(); - reg_val &= ~TAS2780_CHNL_0_AMP_LEVEL_MASK; - reg_val |= amp_level << TAS2780_CHNL_0_AMP_LEVEL_SHIFT; - this->reg(TAS2780_CHNL_0) = reg_val; return true; } +void TAS2780::update_register(){ + uint8_t reg_val = this->reg(TAS2780_CHNL_0).get(); + reg_val &= ~TAS2780_CHNL_0_AMP_LEVEL_MASK; + reg_val |= this->amp_level_ << TAS2780_CHNL_0_AMP_LEVEL_SHIFT; + this->reg(TAS2780_CHNL_0) = reg_val; + ESP_LOGD(TAG, "Update amp to level idx: %d", this->amp_level_); +} } diff --git a/esphome/components/tas2780/tas2780.h b/esphome/components/tas2780/tas2780.h index 8f301b73..918a0e58 100644 --- a/esphome/components/tas2780/tas2780.h +++ b/esphome/components/tas2780/tas2780.h @@ -16,22 +16,35 @@ class TAS2780 : public audio_dac::AudioDac, public Component, public i2c::I2CDev float get_setup_priority() const override { return setup_priority::DATA; } void loop() override; + void init(); void reset(); - void activate(); + void activate(uint8_t power_mode=2); void deactivate(); + void update_register(); + void log_error_states(); bool set_mute_off() override; bool set_mute_on() override; bool set_volume(float volume) override; bool is_muted() override; - float volume() override; + float volume() override; + + void set_amp_level(uint8_t amp_level){this->amp_level_ = amp_level;} + void set_vol_range_min(float min_val){this->vol_range_min_ = min_val;} + void set_vol_range_max(float max_val){this->vol_range_max_ = max_val;} protected: + void set_power_mode_(const uint8_t power_mode); bool write_mute_(); bool write_volume_(); float volume_{0}; + uint8_t power_mode_{2}; + uint8_t amp_level_{8}; + float vol_range_min_{.3}; + float vol_range_max_{1.}; + }; }