ESPHome  2024.11.0
es8311.cpp
Go to the documentation of this file.
1 #include "es8311.h"
2 #include "es8311_const.h"
3 #include "esphome/core/hal.h"
4 #include "esphome/core/log.h"
5 #include <cinttypes>
6 
7 namespace esphome {
8 namespace es8311 {
9 
10 static const char *const TAG = "es8311";
11 
12 // Mark the component as failed; use only in setup
13 #define ES8311_ERROR_FAILED(func) \
14  if (!(func)) { \
15  this->mark_failed(); \
16  return; \
17  }
18 // Return false; use outside of setup
19 #define ES8311_ERROR_CHECK(func) \
20  if (!(func)) { \
21  return false; \
22  }
23 
24 void ES8311::setup() {
25  ESP_LOGCONFIG(TAG, "Setting up ES8311...");
26 
27  // Reset
28  ES8311_ERROR_FAILED(this->write_byte(ES8311_REG00_RESET, 0x1F));
29  ES8311_ERROR_FAILED(this->write_byte(ES8311_REG00_RESET, 0x00));
30 
31  ES8311_ERROR_FAILED(this->configure_clock_());
32  ES8311_ERROR_FAILED(this->configure_format_());
33  ES8311_ERROR_FAILED(this->configure_mic_());
34 
35  // Set initial volume
36  this->set_volume(0.75); // 0.75 = 0xBF = 0dB
37 
38  // Power up analog circuitry
39  ES8311_ERROR_FAILED(this->write_byte(ES8311_REG0D_SYSTEM, 0x01));
40  // Enable analog PGA, enable ADC modulator
41  ES8311_ERROR_FAILED(this->write_byte(ES8311_REG0E_SYSTEM, 0x02));
42  // Power up DAC
43  ES8311_ERROR_FAILED(this->write_byte(ES8311_REG12_SYSTEM, 0x00));
44  // Enable output to HP drive
45  ES8311_ERROR_FAILED(this->write_byte(ES8311_REG13_SYSTEM, 0x10));
46  // ADC Equalizer bypass, cancel DC offset in digital domain
47  ES8311_ERROR_FAILED(this->write_byte(ES8311_REG1C_ADC, 0x6A));
48  // Bypass DAC equalizer
49  ES8311_ERROR_FAILED(this->write_byte(ES8311_REG37_DAC, 0x08));
50  // Power On
51  ES8311_ERROR_FAILED(this->write_byte(ES8311_REG00_RESET, 0x80));
52 }
53 
55  ESP_LOGCONFIG(TAG, "ES8311 Audio Codec:");
56  ESP_LOGCONFIG(TAG, " Use MCLK: %s", YESNO(this->use_mclk_));
57  ESP_LOGCONFIG(TAG, " Use Microphone: %s", YESNO(this->use_mic_));
58  ESP_LOGCONFIG(TAG, " DAC Bits per Sample: %" PRIu8, this->resolution_out_);
59  ESP_LOGCONFIG(TAG, " Sample Rate: %" PRIu32, this->sample_frequency_);
60 
61  if (this->is_failed()) {
62  ESP_LOGCONFIG(TAG, " Failed to initialize!");
63  return;
64  }
65 }
66 
68  volume = clamp(volume, 0.0f, 1.0f);
69  uint8_t reg32 = remap<uint8_t, float>(volume, 0.0f, 1.0f, 0, 255);
70  return this->write_byte(ES8311_REG32_DAC, reg32);
71 }
72 
73 float ES8311::volume() {
74  uint8_t reg32;
75  this->read_byte(ES8311_REG32_DAC, &reg32);
76  return remap<float, uint8_t>(reg32, 0, 255, 0.0f, 1.0f);
77 }
78 
80  switch (resolution) {
82  return (3 << 2);
84  return (2 << 2);
86  return (1 << 2);
88  return (0 << 2);
90  return (4 << 2);
91  default:
92  return 0;
93  }
94 }
95 
96 const ES8311Coefficient *ES8311::get_coefficient(uint32_t mclk, uint32_t rate) {
97  for (const auto &coefficient : ES8311_COEFFICIENTS) {
98  if (coefficient.mclk == mclk && coefficient.rate == rate)
99  return &coefficient;
100  }
101  return nullptr;
102 }
103 
105  // Register 0x01: select clock source for internal MCLK and determine its frequency
106  uint8_t reg01 = 0x3F; // Enable all clocks
107 
108  uint32_t mclk_frequency = this->sample_frequency_ * this->mclk_multiple_;
109  if (!this->use_mclk_) {
110  reg01 |= BIT(7); // Use SCLK
111  mclk_frequency = this->sample_frequency_ * (int) this->resolution_out_ * 2;
112  }
113  if (this->mclk_inverted_) {
114  reg01 |= BIT(6); // Invert MCLK pin
115  }
116  ES8311_ERROR_CHECK(this->write_byte(ES8311_REG01_CLK_MANAGER, reg01));
117 
118  // Get clock coefficients from coefficient table
119  auto *coefficient = get_coefficient(mclk_frequency, this->sample_frequency_);
120  if (coefficient == nullptr) {
121  ESP_LOGE(TAG, "Unable to configure sample rate %" PRIu32 "Hz with %" PRIu32 "Hz MCLK", this->sample_frequency_,
122  mclk_frequency);
123  return false;
124  }
125 
126  // Register 0x02
127  uint8_t reg02;
128  ES8311_ERROR_CHECK(this->read_byte(ES8311_REG02_CLK_MANAGER, &reg02));
129  reg02 &= 0x07;
130  reg02 |= (coefficient->pre_div - 1) << 5;
131  reg02 |= coefficient->pre_mult << 3;
132  ES8311_ERROR_CHECK(this->write_byte(ES8311_REG02_CLK_MANAGER, reg02));
133 
134  // Register 0x03
135  const uint8_t reg03 = (coefficient->fs_mode << 6) | coefficient->adc_osr;
136  ES8311_ERROR_CHECK(this->write_byte(ES8311_REG03_CLK_MANAGER, reg03));
137 
138  // Register 0x04
139  ES8311_ERROR_CHECK(this->write_byte(ES8311_REG04_CLK_MANAGER, coefficient->dac_osr));
140 
141  // Register 0x05
142  const uint8_t reg05 = ((coefficient->adc_div - 1) << 4) | (coefficient->dac_div - 1);
143  ES8311_ERROR_CHECK(this->write_byte(ES8311_REG05_CLK_MANAGER, reg05));
144 
145  // Register 0x06
146  uint8_t reg06;
147  ES8311_ERROR_CHECK(this->read_byte(ES8311_REG06_CLK_MANAGER, &reg06));
148  if (this->sclk_inverted_) {
149  reg06 |= BIT(5);
150  } else {
151  reg06 &= ~BIT(5);
152  }
153  reg06 &= 0xE0;
154  if (coefficient->bclk_div < 19) {
155  reg06 |= (coefficient->bclk_div - 1) << 0;
156  } else {
157  reg06 |= (coefficient->bclk_div) << 0;
158  }
159  ES8311_ERROR_CHECK(this->write_byte(ES8311_REG06_CLK_MANAGER, reg06));
160 
161  // Register 0x07
162  uint8_t reg07;
163  ES8311_ERROR_CHECK(this->read_byte(ES8311_REG07_CLK_MANAGER, &reg07));
164  reg07 &= 0xC0;
165  reg07 |= coefficient->lrck_h << 0;
166  ES8311_ERROR_CHECK(this->write_byte(ES8311_REG07_CLK_MANAGER, reg07));
167 
168  // Register 0x08
169  ES8311_ERROR_CHECK(this->write_byte(ES8311_REG08_CLK_MANAGER, coefficient->lrck_l));
170 
171  // Successfully configured the clock
172  return true;
173 }
174 
176  // Configure I2S mode and format
177  uint8_t reg00;
178  ES8311_ERROR_CHECK(this->read_byte(ES8311_REG00_RESET, &reg00));
179  reg00 &= 0xBF;
180  ES8311_ERROR_CHECK(this->write_byte(ES8311_REG00_RESET, reg00));
181 
182  // Configure SDP in resolution
183  uint8_t reg09 = calculate_resolution_value(this->resolution_in_);
184  ES8311_ERROR_CHECK(this->write_byte(ES8311_REG09_SDPIN, reg09));
185 
186  // Configure SDP out resolution
187  uint8_t reg0a = calculate_resolution_value(this->resolution_out_);
188  ES8311_ERROR_CHECK(this->write_byte(ES8311_REG0A_SDPOUT, reg0a));
189 
190  // Successfully configured the format
191  return true;
192 }
193 
195  uint8_t reg14 = 0x1A; // Enable analog MIC and max PGA gain
196  if (this->use_mic_) {
197  reg14 |= BIT(6); // Enable PDM digital microphone
198  }
199  ES8311_ERROR_CHECK(this->write_byte(ES8311_REG14_SYSTEM, reg14));
200 
201  ES8311_ERROR_CHECK(this->write_byte(ES8311_REG16_ADC, this->mic_gain_)); // ADC gain scale up
202  ES8311_ERROR_CHECK(this->write_byte(ES8311_REG17_ADC, 0xC8)); // Set ADC gain
203 
204  // Successfully configured the microphones
205  return true;
206 }
207 
208 bool ES8311::set_mute_state_(bool mute_state) {
209  uint8_t reg31;
210 
211  this->is_muted_ = mute_state;
212 
213  if (!this->read_byte(ES8311_REG31_DAC, &reg31)) {
214  return false;
215  }
216 
217  if (mute_state) {
218  reg31 |= BIT(6) | BIT(5);
219  } else {
220  reg31 &= ~(BIT(6) | BIT(5));
221  }
222 
223  return this->write_byte(ES8311_REG31_DAC, reg31);
224 }
225 
226 } // namespace es8311
227 } // namespace esphome
bool read_byte(uint8_t a_register, uint8_t *data, bool stop=true)
Definition: i2c.h:235
bool set_volume(float volume) override
Writes the volume out to the DAC.
Definition: es8311.cpp:67
bool configure_format_()
Configures the ES8311 registers for the chosen bits per sample.
Definition: es8311.cpp:175
float volume() override
Gets the current volume out from the DAC.
Definition: es8311.cpp:73
bool is_failed() const
Definition: component.cpp:143
ES8311Resolution resolution_in_
Definition: es8311.h:130
static uint8_t calculate_resolution_value(ES8311Resolution resolution)
Computes the register value for the configured resolution (bits per sample)
Definition: es8311.cpp:79
bool set_mute_state_(bool mute_state)
Mutes or unmute the DAC audio out.
Definition: es8311.cpp:208
uint32_t sample_frequency_
Definition: es8311.h:129
ES8311MicGain mic_gain_
Definition: es8311.h:122
constexpr const T & clamp(const T &v, const T &lo, const T &hi, Compare comp)
Definition: helpers.h:93
bool configure_clock_()
Configures the ES8311 registers for the chosen sample rate.
Definition: es8311.cpp:104
uint32_t mclk_multiple_
Definition: es8311.h:127
ES8311Resolution resolution_out_
Definition: es8311.h:131
static const ES8311Coefficient * get_coefficient(uint32_t mclk, uint32_t rate)
Retrieves the appropriate registers values for the configured mclk and rate.
Definition: es8311.cpp:96
bool write_byte(uint8_t a_register, uint8_t data, bool stop=true)
Definition: i2c.h:262
Implementation of SPI Controller mode.
Definition: a01nyub.cpp:7
bool configure_mic_()
Configures the ES8311 microphone registers.
Definition: es8311.cpp:194
void dump_config() override
Definition: es8311.cpp:54
void setup() override
Definition: es8311.cpp:24