arduino-audio-driver
Loading...
Searching...
No Matches
WM8978.h
Go to the documentation of this file.
1#pragma once
2
3#include <stdio.h>
4#include "DriverCommon.h"
5#include "Platforms/API_I2C.h"
6
7
8namespace audio_driver {
9
10// WM8978 register value buffer zone (total 58 registers 0 to 57), occupies 116
11// bytes of memory. Because the IIC WM8978 operation does not support read
12// operations, all register values are saved locally. Write WM8978 register,
13// synchronized to the local register values; register read returns locally
14// stored value. Note: WM8978 register value is 9 bits, so use uint16_t.
15static uint16_t REGVAL_TBL[58] = {
16 0X0000, 0X0000, 0X0000, 0X0000, 0X0050, 0X0000, 0X0140, 0X0000, 0X0000,
17 0X0000, 0X0000, 0X00FF, 0X00FF, 0X0000, 0X0100, 0X00FF, 0X00FF, 0X0000,
18 0X012C, 0X002C, 0X002C, 0X002C, 0X002C, 0X0000, 0X0032, 0X0000, 0X0000,
19 0X0000, 0X0000, 0X0000, 0X0000, 0X0000, 0X0038, 0X000B, 0X0032, 0X0000,
20 0X0008, 0X000C, 0X0093, 0X00E9, 0X0000, 0X0000, 0X0000, 0X0000, 0X0003,
21 0X0010, 0X0010, 0X0100, 0X0100, 0X0002, 0X0001, 0X0001, 0X0039, 0X0039,
22 0X0039, 0X0039, 0X0001, 0X0001};
23
24class WM8978 {
25 public:
26 static constexpr int WM8978_ADDR = 0x1A;
27 static constexpr uint8_t EQ1_80Hz = 0x00;
28 static constexpr uint8_t EQ1_105Hz = 0x01;
29 static constexpr uint8_t EQ1_135Hz = 0x02;
30 static constexpr uint8_t EQ1_175Hz = 0x03;
31 static constexpr uint8_t EQ2_230Hz = 0x00;
32 static constexpr uint8_t EQ2_300Hz = 0x01;
33 static constexpr uint8_t EQ2_385Hz = 0x02;
34 static constexpr uint8_t EQ2_500Hz = 0x03;
35 static constexpr uint8_t EQ3_650Hz = 0x00;
36 static constexpr uint8_t EQ3_850Hz = 0x01;
37 static constexpr uint8_t EQ3_1100Hz = 0x02;
38 static constexpr uint8_t EQ3_14000Hz = 0x03;
39 static constexpr uint8_t EQ4_1800Hz = 0x00;
40 static constexpr uint8_t EQ4_2400Hz = 0x01;
41 static constexpr uint8_t EQ4_3200Hz = 0x02;
42 static constexpr uint8_t EQ4_4100Hz = 0x03;
43 static constexpr uint8_t EQ5_5300Hz = 0x00;
44 static constexpr uint8_t EQ5_6900Hz = 0x01;
45 static constexpr uint8_t EQ5_9000Hz = 0x02;
46 static constexpr uint8_t EQ5_11700Hz = 0x03;
47 WM8978() {}
49 bool begin(i2c_bus_handle_t p_wire, int address);
50 void cfgADDA(uint8_t dacen, uint8_t adcen);
51 void cfgInput(uint8_t micen, uint8_t lineinen, uint8_t auxen);
52 void cfgOutput(uint8_t dacen, uint8_t bpsen);
53 void cfgI2S(uint8_t fmt, uint8_t len);
54 void setMICgain(uint8_t gain);
55 void setLINEINgain(uint8_t gain);
56 void setAUXgain(uint8_t gain);
57 void setHPvol(uint8_t voll, uint8_t volr);
58 void setSPKvol(uint8_t volx);
59 void set3D(uint8_t depth);
60 void set3Ddir(uint8_t dir);
61 void setEQ1(uint8_t cfreq, uint8_t gain);
62 void setEQ2(uint8_t cfreq, uint8_t gain);
63 void setEQ3(uint8_t cfreq, uint8_t gain);
64 void setEQ4(uint8_t cfreq, uint8_t gain);
65 void setEQ5(uint8_t cfreq, uint8_t gain);
66 void setNoise(uint8_t enable, uint8_t gain);
67 void setALC(uint8_t enable, uint8_t maxgain, uint8_t mingain);
68 void setHPF(uint8_t enable);
70 p_wire = wire;
71 }
72
73 private:
74 i2c_bus_handle_t p_wire = nullptr;
75 int address = WM8978_ADDR;
76 bool begin(); /* use this function if you want to setup i2c before */
77 uint8_t Init(void);
78 uint8_t Write_Reg(uint8_t reg, uint16_t val);
79 uint16_t Read_Reg(uint8_t reg);
80};
81
82// WM8978 write register
83// reg: register address
84// val: the value to be written to the register
85// Return value: 0, success; Other, error code
86inline uint8_t WM8978::Write_Reg(uint8_t reg, uint16_t val) {
87 uint8_t byte_val = val;
88 i2c_bus_write_bytes(p_wire, address, &reg, 1, &byte_val, 1);
89 REGVAL_TBL[reg] = val; // Save register value to local
90 return 0;
91}
92
93// WM8978 init
94// Return value: 0, initialization is normal; Other, error code
95inline uint8_t WM8978::Init(void) {
96 uint8_t res;
97 res = Write_Reg(0, 0); // Soft reset WM8978
98 if (res) return 1; // Failed to send command, WM8978 exception
99 // The following are common settings
100 Write_Reg(1, 0X1B); // R1, MICEN is set to 1 (MIC enabled), BIASEN is set to
101 // 1 (simulator works), VMIDSEL[1:0] is set to: 11 (5K)
102 Write_Reg(2, 0X1B0); // R2, ROUT1, LOUT1 output enable (headphones can work),
103 // BOOSTENR, BOOSTENL enable
104 Write_Reg(3, 0X6C); // R3, LOUT2, ROUT2 output enable (speaker operation),
105 // RMIX, LMIX enable
106 Write_Reg(6, 0); // R6, MCLK is provided externally
107 Write_Reg(43, 1 << 4); // R43, INVROUT2 reverse, drives the speaker
108 Write_Reg(
109 47,
110 1 << 8); // R47 setting, PGABOOSTL, left channel MIC gets 20 times gain
111 Write_Reg(
112 48,
113 1 << 8); // R48 setting, PGABOOSTR, right channel MIC gets 20 times gain
114 Write_Reg(49, 1 << 1); // R49, TSDEN, turn on overheating protection
115 Write_Reg(10, 1 << 3); // R10, SOFTMUTE off, 128x sampling, best SNR
116 Write_Reg(14, 1 << 3 | 1 << 8); // R14,ADC 128x sampling rate and enable high
117 // pass filter (3.7Hz cut-off)
118 return 0;
119}
120
121// WM8978 read register
122// Reads the value of the local register buffer zone
123// reg: Register Address
124// Return Value: Register value
125inline uint16_t WM8978::Read_Reg(uint8_t reg) { return REGVAL_TBL[reg]; }
126
127// WM8978 DAC/ADC configuration
128// adcen: adc enable(1)/disable(0)
129// dacen: dac enable(1)/disable(0)
130inline void WM8978::cfgADDA(uint8_t dacen, uint8_t adcen) {
131 uint16_t regval;
132 regval = WM8978::Read_Reg(3); // Read R3
133 if (dacen)
134 regval |= 3 << 0; // Set the lowest 2 bits of R3 to 1, turn on DACR&DACL
135 else
136 regval &= ~(
137 3 << 0); // Clear the lowest 2 bits of R3 to 0 and turn off DACR&DACL.
138 Write_Reg(3, regval); // Set R3
139 regval = WM8978::Read_Reg(2); // Read R2
140 if (adcen)
141 regval |= 3 << 0; // Set the lowest 2 bits of R2 to 1, turn on ADCR&ADCL
142 else
143 regval &=
144 ~(3 << 0); // Clear the lowest 2 bits of R2 and turn off ADCR&ADCL.
145 Write_Reg(2, regval); // Set R2
146}
147
148// WM8978 input channel configuration
149// micen: MIC on (1)/off (0)
150// lineinen: Line In on (1)/off (0)
151// auxen: aux on (1)/off (0)
152inline void WM8978::cfgInput(uint8_t micen, uint8_t lineinen, uint8_t auxen) {
153 uint16_t regval;
154 regval = WM8978::Read_Reg(2); // Read R2
155 if (micen)
156 regval |= 3 << 2; // Turn on INPPGAENR, INPPGAENL (MIC's PGA amplification)
157 else
158 regval &= ~(3 << 2); // Close INPPGAENR,INPPGAENL.
159 Write_Reg(2, regval); // Set R2
160
161 regval = WM8978::Read_Reg(44); // Read R44
162 if (micen)
163 regval |= 3 << 4 |
164 3 << 0; // Enable LIN2INPPGA, LIP2INPGA, RIN2INPPGA, RIP2INPGA.
165 else
166 regval &= ~(3 << 4 |
167 3 << 0); // Close LIN2INPPGA, LIP2INPGA, RIN2INPPGA, RIP2INPGA.
168 Write_Reg(44, regval); // Set R44
169
170 if (lineinen)
171 WM8978::setLINEINgain(5); // LINE IN 0dB gain
172 else
173 WM8978::setLINEINgain(0); // Close LINE IN
174 if (auxen)
175 WM8978::setAUXgain(7); // AUX 6dB gain
176 else
177 WM8978::setAUXgain(0); // Close AUX input
178}
179
180// WM8978 output configuration
181// dacen: DAC output (playback) on (1)/off (0)
182// bpsen: Bypass output (recording, including MIC, LINE IN, AUX, etc.) on
183// (1)/off (0)
184inline void WM8978::cfgOutput(uint8_t dacen, uint8_t bpsen) {
185 uint16_t regval = 0;
186 if (dacen) regval |= 1 << 0; // DAC output enable
187 if (bpsen) {
188 regval |= 1 << 1; // BYPASS enabled
189 regval |= 5 << 2; // 0dB gain
190 }
191 Write_Reg(50, regval); // R50 setting
192 Write_Reg(51, regval); // R51 setting
193}
194
195// WM8978 MIC gain setting (excluding BOOST's 20dB, MIC-->gain of the ADC input
196// part) gain: 0~63, corresponding to -12dB~35.25dB, 0.75dB/Step
197inline void WM8978::setMICgain(uint8_t gain) {
198 gain &= 0X3F;
199 Write_Reg(45, gain); // R45, left channel PGA setting
200 Write_Reg(46, gain | 1 << 8); // R46, right channel PGA setting
201}
202
203// WM8978 L2/R2 (that is, Line In) gain setting (L2/R2-->gain of the ADC input
204// part) gain: 0~7, 0 means channel is disabled, 1~7, corresponds to -12dB~6dB,
205// 3dB/Step
206inline void WM8978::setLINEINgain(uint8_t gain) {
207 uint16_t regval;
208 gain &= 0X07;
209 regval = WM8978::Read_Reg(47); // Read R47
210 regval &= ~(7 << 4); // Clear the original settings
211 Write_Reg(47, regval | gain << 4); // Set R47
212 regval = WM8978::Read_Reg(48); // Read R48
213 regval &= ~(7 << 4); // Clear the original settings
214 Write_Reg(48, regval | gain << 4); // Set R48
215}
216
217// WM8978 AUXR, AUXL (PWM audio part) gain setting (AUXR/L-->gain of ADC input
218// part) gain: 0~7, 0 means channel is disabled, 1~7, corresponds to -12dB~6dB,
219// 3dB/Step
220inline void WM8978::setAUXgain(uint8_t gain) {
221 uint16_t regval;
222 gain &= 0X07;
223 regval = WM8978::Read_Reg(47); // Read R47
224 regval &= ~(7 << 0); // Clear the original settings
225 Write_Reg(47, regval | gain << 0); // Set R47
226 regval = WM8978::Read_Reg(48); // Read R48
227 regval &= ~(7 << 0); // Clear the original settings
228 Write_Reg(48, regval | gain << 0); // Set R48
229}
230
231// Set I2S working mode
232// fmt: 0,LSB(right-aligned); 1,MSB(left-aligned); 2,Philips standard I2S;
233// 3,PCM/DSP
234// len: 0, 16 digits; 1, 20 digits; 2, 24 digits; 3, 32 digits
235inline void WM8978::cfgI2S(uint8_t fmt, uint8_t len) {
236 fmt &= 0X03;
237 len &= 0X03; // Limited range
238 Write_Reg(4, (fmt << 3) | (len << 5)); // R4, WM8978 working mode setting
239}
240
241// Set the left and right channel volume of the headset
242// voll: left channel volume (0~63)
243// volr: right channel volume (0~63)
244inline void WM8978::setHPvol(uint8_t voll, uint8_t volr) {
245 voll &= 0X3F;
246 volr &= 0X3F; // Limited range
247 if (voll == 0) voll |= 1 << 6; // When the volume is 0, mute directly
248 if (volr == 0) volr |= 1 << 6; // When the volume is 0, mute directly
249 Write_Reg(52, voll); // R52, headphone left channel volume setting
250 Write_Reg(53, volr | (1 << 8)); // R53, headphone right channel volume
251 // setting, updated synchronously (HPVU=1)
252}
253
254// Set speaker volume
255// volx: left channel volume (0~63)
256inline void WM8978::setSPKvol(uint8_t volx) {
257 volx &= 0X3F; // Limited range
258 if (volx == 0) volx |= 1 << 6; // When the volume is 0, mute directly
259 Write_Reg(54, volx); // R54, speaker left channel volume setting
260 Write_Reg(55, volx | (1 << 8)); // R55, speaker right channel volume setting,
261 // updated synchronously (SPKVU=1)
262}
263
264// Set 3D surround sound
265// depth: 0~15 (3D intensity, 0 is the weakest, 15 is the strongest)
266inline void WM8978::set3D(uint8_t depth) {
267 depth &= 0XF; // Limited range
268 Write_Reg(41, depth); // R41, 3D surround setting
269}
270
271// Set EQ/3D action direction
272// dir: 0, works in ADC
273// 1, works in DAC (default)
274inline void WM8978::set3Ddir(uint8_t dir) {
275 uint16_t regval;
276 regval = WM8978::Read_Reg(0X12);
277 if (dir)
278 regval |= 1 << 8;
279 else
280 regval &= ~(1 << 8);
281 Write_Reg(18, regval); // R18, the 9th bit of EQ1 controls the EQ/3D direction
282}
283
284// Set EQ1
285// cfreq: cutoff frequency, 0~3, corresponding to: 80/105/135/175Hz
286// gain: Gain, 0~24, corresponding to -12~+12dB
287inline void WM8978::setEQ1(uint8_t cfreq, uint8_t gain) {
288 uint16_t regval;
289 cfreq &= 0X3; // Limited range
290 if (gain > 24) gain = 24;
291 gain = 24 - gain;
292 regval = WM8978::Read_Reg(18);
293 regval &= 0X100;
294 regval |= cfreq << 5; // Set cutoff frequency
295 regval |= gain; // Set gain
296 Write_Reg(18, regval); // R18, EQ1 setting
297}
298
299// Set EQ2
300// cfreq: center frequency, 0~3, corresponding to: 230/300/385/500Hz
301// gain: Gain, 0~24, corresponding to -12~+12dB
302inline void WM8978::setEQ2(uint8_t cfreq, uint8_t gain) {
303 uint16_t regval = 0;
304 cfreq &= 0X3; // Limited range
305 if (gain > 24) gain = 24;
306 gain = 24 - gain;
307 regval |= cfreq << 5; // Set cutoff frequency
308 regval |= gain; // Set gain
309 Write_Reg(19, regval); // R19, EQ2 setting
310}
311
312// Set EQ3
313// cfreq: center frequency, 0~3, corresponding to: 650/850/1100/1400Hz
314// gain: Gain, 0~24, corresponding to -12~+12dB
315inline void WM8978::setEQ3(uint8_t cfreq, uint8_t gain) {
316 uint16_t regval = 0;
317 cfreq &= 0X3; // Limited range
318 if (gain > 24) gain = 24;
319 gain = 24 - gain;
320 regval |= cfreq << 5; // Set cutoff frequency
321 regval |= gain; // Set gain
322 Write_Reg(20, regval); // R20, EQ3 setting
323}
324
325// Set EQ4
326// cfreq: center frequency, 0~3, corresponding to: 1800/2400/3200/4100Hz
327// gain: Gain, 0~24, corresponding to -12~+12dB
328inline void WM8978::setEQ4(uint8_t cfreq, uint8_t gain) {
329 uint16_t regval = 0;
330 cfreq &= 0X3; // Limited range
331 if (gain > 24) gain = 24;
332 gain = 24 - gain;
333 regval |= cfreq << 5; // Set cutoff frequency
334 regval |= gain; // Set gain
335 Write_Reg(21, regval); // R21, EQ4 settings
336}
337
338// Set EQ5
339// cfreq: center frequency, 0~3, corresponding to: 5300/6900/9000/11700Hz
340// gain: Gain, 0~24, corresponding to -12~+12dB
341inline void WM8978::setEQ5(uint8_t cfreq, uint8_t gain) {
342 uint16_t regval = 0;
343 cfreq &= 0X3; // Limited range
344 if (gain > 24) gain = 24;
345 gain = 24 - gain;
346 regval |= cfreq << 5; // Set cutoff frequency
347 regval |= gain; // Set gain
348 Write_Reg(22, regval); // R22, EQ5 setting
349}
350
351inline void WM8978::setALC(uint8_t enable, uint8_t maxgain, uint8_t mingain) {
352 uint16_t regval;
353
354 if (maxgain > 7) maxgain = 7;
355 if (mingain > 7) mingain = 7;
356
357 regval = WM8978::Read_Reg(32);
358 if (enable) {
359 regval |= (3 << 7);
360 } else {
361 regval &= ~(3 << 7);
362 }
363 regval &= ~((7 << 3) | (7 << 0));
364 regval |= (maxgain << 3) | (mingain << 0);
365 Write_Reg(32, regval);
366}
367
368inline void WM8978::setNoise(uint8_t enable, uint8_t gain) {
369 uint16_t regval;
370
371 if (gain > 7) gain = 7;
372
373 regval = WM8978::Read_Reg(35);
374 regval = (enable << 3);
375 regval |= gain; // Set gain
376 Write_Reg(35, regval); // R35, noise gate setting
377}
378
379inline void WM8978::setHPF(uint8_t enable) {
380 uint16_t regval;
381
382 regval = WM8978::Read_Reg(14);
383 regval &= ~(1 << 8);
384 regval |= (enable << 8);
385 Write_Reg(14, regval); // R14, high pass filter
386}
387
388inline bool WM8978::begin() {
389 const int err = Init();
390 if (err) {
391 AD_LOGE("WM8978 init err: 0x%X", err);
392 return false;
393 }
394 cfgI2S(2, 0); // Philips 16bit
395 cfgADDA(1, 1); // Enable ADC DAC
396 cfgInput(0, 0, 0); // mic, linein, aux - Note: M5Stack node has only internal
397 // microphones connected
398 setMICgain(0);
399 setAUXgain(0);
400 setLINEINgain(0);
401 setSPKvol(0); // 0-63
402 setHPvol(0, 0); // 0-63
403 set3Ddir(0);
404 setEQ1(0, 24);
405 setEQ2(0, 24);
406 setEQ3(0, 24);
407 setEQ4(0, 24);
408 setEQ5(0, 24);
409 cfgOutput(1, 0); // Output enabled, bypass disabled
410 return true;
411}
412
413inline bool WM8978::begin(i2c_bus_handle_t p_wire, int address) {
414 this->address = address;
415 this->p_wire = p_wire;
416 return begin();
417}
418
419} // namespace audio_driver
#define AD_LOGE(...)
Definition Logger.h:15
Definition WM8978.h:24
static constexpr uint8_t EQ1_135Hz
Definition WM8978.h:29
void setEQ4(uint8_t cfreq, uint8_t gain)
Definition WM8978.h:328
WM8978()
Definition WM8978.h:47
static constexpr uint8_t EQ5_11700Hz
Definition WM8978.h:46
void setAUXgain(uint8_t gain)
Definition WM8978.h:220
void cfgADDA(uint8_t dacen, uint8_t adcen)
Definition WM8978.h:130
static constexpr uint8_t EQ1_175Hz
Definition WM8978.h:30
static constexpr uint8_t EQ3_1100Hz
Definition WM8978.h:37
static constexpr uint8_t EQ4_3200Hz
Definition WM8978.h:41
static constexpr uint8_t EQ1_80Hz
Definition WM8978.h:27
void cfgOutput(uint8_t dacen, uint8_t bpsen)
Definition WM8978.h:184
void set3D(uint8_t depth)
Definition WM8978.h:266
void cfgI2S(uint8_t fmt, uint8_t len)
Definition WM8978.h:235
static constexpr uint8_t EQ2_300Hz
Definition WM8978.h:32
static constexpr uint8_t EQ5_6900Hz
Definition WM8978.h:44
void setNoise(uint8_t enable, uint8_t gain)
Definition WM8978.h:368
static constexpr uint8_t EQ3_650Hz
Definition WM8978.h:35
void setEQ1(uint8_t cfreq, uint8_t gain)
Definition WM8978.h:287
static constexpr uint8_t EQ2_385Hz
Definition WM8978.h:33
static constexpr uint8_t EQ2_500Hz
Definition WM8978.h:34
void setEQ3(uint8_t cfreq, uint8_t gain)
Definition WM8978.h:315
static constexpr uint8_t EQ2_230Hz
Definition WM8978.h:31
~WM8978()
Definition WM8978.h:48
void setMICgain(uint8_t gain)
Definition WM8978.h:197
void setSPKvol(uint8_t volx)
Definition WM8978.h:256
static constexpr uint8_t EQ5_9000Hz
Definition WM8978.h:45
void setHPF(uint8_t enable)
Definition WM8978.h:379
static constexpr uint8_t EQ4_2400Hz
Definition WM8978.h:40
static constexpr uint8_t EQ4_1800Hz
Definition WM8978.h:39
static constexpr uint8_t EQ5_5300Hz
Definition WM8978.h:43
void setEQ2(uint8_t cfreq, uint8_t gain)
Definition WM8978.h:302
static constexpr int WM8978_ADDR
Definition WM8978.h:26
static constexpr uint8_t EQ1_105Hz
Definition WM8978.h:28
bool begin(i2c_bus_handle_t p_wire, int address)
Definition WM8978.h:413
void setLINEINgain(uint8_t gain)
Definition WM8978.h:206
static constexpr uint8_t EQ4_4100Hz
Definition WM8978.h:42
static constexpr uint8_t EQ3_850Hz
Definition WM8978.h:36
void cfgInput(uint8_t micen, uint8_t lineinen, uint8_t auxen)
Definition WM8978.h:152
void setHPvol(uint8_t voll, uint8_t volr)
Definition WM8978.h:244
static constexpr uint8_t EQ3_14000Hz
Definition WM8978.h:38
void setALC(uint8_t enable, uint8_t maxgain, uint8_t mingain)
Definition WM8978.h:351
void setEQ5(uint8_t cfreq, uint8_t gain)
Definition WM8978.h:341
void setWire(i2c_bus_handle_t wire)
Definition WM8978.h:69
void set3Ddir(uint8_t dir)
Definition WM8978.h:274
Namespace for audio driver components.
Definition AudioBoard.h:5
static uint16_t REGVAL_TBL[58]
Definition WM8978.h:15
void * i2c_bus_handle_t
Definition DriverCommon.h:155