ESPHome 2026.8.0-dev
Loading...
Searching...
No Matches
pcm5122.cpp
Go to the documentation of this file.
1#include "pcm5122.h"
2
4#include "esphome/core/log.h"
5
6#include <cmath>
7
8namespace esphome::pcm5122 {
9
10static const char *const TAG = "pcm5122";
11
13 // Hold XSMT low (soft mute asserted) until init completes
14 if (this->enable_pin_ != nullptr) {
15 this->enable_pin_->setup();
16 this->enable_pin_->digital_write(false);
17 }
18
19 // Select page 0 and verify chip presence via I2C ACK
20 if (!this->select_page_(0)) {
21 ESP_LOGE(TAG, "Write failed");
22 this->status_set_error(LOG_STR("Write failed"));
23 this->mark_failed();
24 return;
25 }
26
27 // Reset audio modules
28 this->reg(PCM5122_REG_RESET) = PCM5122_RESET_MODULES;
29 delay(20);
30 this->reg(PCM5122_REG_RESET) = 0x00;
31
32 // Ignore clock halt detection; enable clock divider autoset
33 optional<uint8_t> err_detect = this->read_byte(PCM5122_REG_ERROR_DETECT);
34 if (!err_detect.has_value()) {
35 ESP_LOGE(TAG, "Failed to read ERROR_DETECT");
36 this->mark_failed();
37 return;
38 }
39 uint8_t err_detect_val = err_detect.value();
40 err_detect_val |= PCM5122_ERROR_DETECT_IGNORE_CLKHALT;
41 err_detect_val &= ~PCM5122_ERROR_DETECT_DISABLE_DIV_AUTOSET;
42 this->reg(PCM5122_REG_ERROR_DETECT) = err_detect_val;
43
44 // I2S format with the configured word length
45 uint8_t alen;
46 switch (this->bits_per_sample_) {
48 alen = PCM5122_AUDIO_FORMAT_ALEN_16BIT;
49 break;
51 alen = PCM5122_AUDIO_FORMAT_ALEN_24BIT;
52 break;
54 default:
55 alen = PCM5122_AUDIO_FORMAT_ALEN_32BIT;
56 break;
57 }
58 this->reg(PCM5122_REG_AUDIO_FORMAT) = PCM5122_AUDIO_FORMAT_I2S | alen;
59
60 if (!this->write_channel_mix_()) {
61 this->mark_failed();
62 return;
63 }
64
65 if (!this->write_analog_gain_()) {
66 this->mark_failed();
67 return;
68 }
69
70 // PLL reference clock: BCK
71 if (!this->select_page_(0)) {
72 ESP_LOGE(TAG, "Write failed");
73 this->mark_failed();
74 return;
75 }
76 optional<uint8_t> pll_ref = this->read_byte(PCM5122_REG_PLL_REF);
77 if (!pll_ref.has_value()) {
78 ESP_LOGE(TAG, "Failed to read PLL_REF");
79 this->mark_failed();
80 return;
81 }
82 uint8_t pll_ref_val = pll_ref.value();
83 pll_ref_val &= ~PCM5122_PLL_REF_MASK;
84 pll_ref_val |= PCM5122_PLL_REF_SOURCE_BCK;
85 this->reg(PCM5122_REG_PLL_REF) = pll_ref_val;
86
87 if (!this->set_mute_on() || !this->set_volume(this->volume_)) {
88 this->mark_failed();
89 return;
90 }
91
92 // Release XSMT (soft un-mute) now that init has completed
93 if (this->enable_pin_ != nullptr) {
94 this->enable_pin_->digital_write(true);
95 }
96}
97
99 const char *channel_mix_str;
100 switch (this->channel_mix_) {
102 channel_mix_str = "left only";
103 break;
105 channel_mix_str = "right only";
106 break;
108 channel_mix_str = "swapped";
109 break;
110 default:
111 channel_mix_str = "stereo";
112 break;
113 }
114 ESP_LOGCONFIG(TAG, "Audio DAC:");
115 LOG_I2C_DEVICE(this);
116 ESP_LOGCONFIG(TAG,
117 " Bits per sample: %u\n"
118 " Analog gain: %s\n"
119 " Channel mix: %s\n"
120 " Volume range: %.1f dB to %.1f dB\n"
121 " Muted: %s",
122 this->bits_per_sample_, this->analog_gain_ == PCM5122_ANALOG_GAIN_0DB ? "0 dB" : "-6 dB",
123 channel_mix_str, this->volume_min_db_, this->volume_max_db_, YESNO(this->is_muted_));
124 LOG_PIN(" Enable Pin: ", this->enable_pin_);
125}
126
128 this->is_muted_ = false;
129 return this->write_mute_();
130}
131
133 this->is_muted_ = true;
134 return this->write_mute_();
135}
136
137bool PCM5122::set_volume(float volume) {
138 this->volume_ = clamp<float>(volume, 0.0f, 1.0f);
139 return this->write_volume_();
140}
141
142bool PCM5122::is_muted() { return this->is_muted_; }
143
144float PCM5122::volume() { return this->volume_; }
145
146bool PCM5122::select_page_(uint8_t page) {
147 if (this->current_page_ == page)
148 return true;
149 if (!this->write_byte(PCM5122_REG_PAGE_SELECT, page)) {
150 this->current_page_ = -1;
151 return false;
152 }
153 this->current_page_ = page;
154 return true;
155}
156
158 uint8_t mute_byte = this->is_muted() ? 0x11 : 0x00;
159 if (!this->select_page_(0) || !this->write_byte(PCM5122_REG_MUTE, mute_byte)) {
160 ESP_LOGE(TAG, "Writing mute failed");
161 return false;
162 }
163 return true;
164}
165
167 // DVOL register: 0x00 = +24 dB, 0x30 = 0 dB, 0xFE = -103 dB, 0xFF = mute (-0.5 dB/step).
168 // Note: volume=0.0 maps to volume_min_db_, which is not true silence unless set to -103 dB.
169 // Use set_mute_on() for silence.
170 const uint8_t dvol_max_volume = static_cast<uint8_t>(lroundf(0x30 - this->volume_max_db_ * 2.0f));
171 const uint8_t dvol_min_volume = static_cast<uint8_t>(lroundf(0x30 - this->volume_min_db_ * 2.0f));
172
173 const uint8_t volume_byte =
174 dvol_max_volume + static_cast<uint8_t>(lroundf((1.0f - this->volume_) * (dvol_min_volume - dvol_max_volume)));
175
176 ESP_LOGV(TAG, "Setting volume to 0x%.2x", volume_byte);
177
178 if (!this->select_page_(0) || !this->write_byte(PCM5122_REG_DVOL_LEFT, volume_byte) ||
179 !this->write_byte(PCM5122_REG_DVOL_RIGHT, volume_byte)) {
180 ESP_LOGE(TAG, "Writing volume failed");
181 return false;
182 }
183 return true;
184}
185
187 uint8_t gain_byte = this->analog_gain_;
188 if (!this->select_page_(1) || !this->write_byte(PCM5122_REG_ANALOG_GAIN, gain_byte)) {
189 ESP_LOGE(TAG, "Writing analog gain failed");
190 return false;
191 }
192 return true;
193}
194
196 uint8_t channel_mix_byte = this->channel_mix_;
197 if (!this->select_page_(0) || !this->write_byte(PCM5122_REG_DAC_DATA_PATH, channel_mix_byte)) {
198 ESP_LOGE(TAG, "Writing channel mix failed");
199 return false;
200 }
201 return true;
202}
203
204bool PCM5122::set_standby(bool enable) {
205 bool prev_standby = this->standby_;
206 this->standby_ = enable;
207 if (!this->write_power_control_()) {
208 this->standby_ = prev_standby;
209 return false;
210 }
211 return true;
212}
213
214bool PCM5122::set_powerdown(bool enable) {
215 bool prev_powerdown = this->powerdown_;
216 this->powerdown_ = enable;
217 if (!this->write_power_control_()) {
218 this->powerdown_ = prev_powerdown;
219 return false;
220 }
221 return true;
222}
223
225 uint8_t power_byte =
226 (this->standby_ ? PCM5122_POWER_CONTROL_RQST : 0) | (this->powerdown_ ? PCM5122_POWER_CONTROL_RQPD : 0);
227 if (!this->select_page_(0) || !this->write_byte(PCM5122_REG_POWER_CONTROL, power_byte)) {
228 ESP_LOGE(TAG, "Writing power control failed");
229 return false;
230 }
231 return true;
232}
233
234} // namespace esphome::pcm5122
void mark_failed()
Mark this component as failed.
virtual void setup()=0
virtual void digital_write(bool value)=0
bool write_byte(uint8_t a_register, uint8_t data) const
Definition i2c.h:265
bool read_byte(uint8_t a_register, uint8_t *data)
Definition i2c.h:240
I2CRegister reg(uint8_t a_register)
calls the I2CRegister constructor
Definition i2c.h:152
bool set_standby(bool enable)
Definition pcm5122.cpp:204
void setup() override
Definition pcm5122.cpp:12
bool set_volume(float volume) override
Definition pcm5122.cpp:137
bool is_muted() override
Definition pcm5122.cpp:142
bool set_mute_on() override
Definition pcm5122.cpp:132
float volume() override
Definition pcm5122.cpp:144
bool set_mute_off() override
Definition pcm5122.cpp:127
void dump_config() override
Definition pcm5122.cpp:98
bool select_page_(uint8_t page)
Definition pcm5122.cpp:146
bool set_powerdown(bool enable)
Definition pcm5122.cpp:214
PCM5122AnalogGain analog_gain_
Definition pcm5122.h:113
PCM5122BitsPerSample bits_per_sample_
Definition pcm5122.h:112
PCM5122ChannelMix channel_mix_
Definition pcm5122.h:114
@ PCM5122_ANALOG_GAIN_0DB
Definition pcm5122.h:59
@ PCM5122_CHANNEL_MIX_SWAPPED
Definition pcm5122.h:68
@ PCM5122_CHANNEL_MIX_LEFT_ONLY
Definition pcm5122.h:66
@ PCM5122_CHANNEL_MIX_RIGHT_ONLY
Definition pcm5122.h:67
@ PCM5122_BITS_PER_SAMPLE_16
Definition pcm5122.h:53
@ PCM5122_BITS_PER_SAMPLE_32
Definition pcm5122.h:55
@ PCM5122_BITS_PER_SAMPLE_24
Definition pcm5122.h:54
void HOT delay(uint32_t ms)
Definition hal.cpp:85