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