-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathWM8741.cpp
401 lines (338 loc) · 11.7 KB
/
WM8741.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
////////////////////////////////////////////////////////
//
// WM8741 control through I2C
// Version 1.0 Aug 2012
// Version 1.0.1 March 2015
//
// Functions
// - Setting mono / stereo
// - Volume management
// - Mute management
// - filter management
// - registers setting / reading
//
// Uses Wire library from Arduino
//
// As default, the volume is set to default during chips setup but volume is muted. It's the role of the main sketch to unmute at the right time
////////////////////////////////////////////////////////
#include <Wire.h>
#include "Arduino.h"
/*
I2C addresses and registers
*/
const unsigned char wm8741_i2c_one = 0x1A; //wm8741 #1 I2C address - the one used in stereo mode, right in dual mono mode
const unsigned char wm8741_i2c_two = 0x1B; //wm8741 #2 I2C address - not used in stereo mode, left in dual mono mode
// see WM8741 datasheet to know how to set addresses
/*
Register addresses - Warning, this is 7bits addresses without R/W bit. Check what is exactly transmited by the Wire.h lib.
*/
const unsigned char wm8741_reg0_addr = 0x00;
const unsigned char wm8741_reg1_addr = 0x01;
const unsigned char wm8741_reg2_addr = 0x02;
const unsigned char wm8741_reg3_addr = 0x03;
const unsigned char wm8741_reg4_addr = 0x04;
const unsigned char wm8741_reg5_addr = 0x05;
const unsigned char wm8741_reg6_addr = 0x06;
const unsigned char wm8741_reg7_addr = 0x07;
const unsigned char wm8741_reg8_addr = 0x08;
const unsigned char wm8741_reg9_addr = 0x09;
const unsigned char wm8741_reg32_addr = 0x20;
/*
Init values for WM8741
*/
const unsigned char wm8741_reg0_default = 0x1F; //volume attenuation LSB left - 127.5db
const unsigned char wm8741_reg1_default = 0x1F; //volume attenuation MSB left - 127.5db
const unsigned char wm8741_reg2_default = 0x1F; //volume attenuation LSB Right - 127.5db
const unsigned char wm8741_reg3_default = 0x3F; //volume attenuation MSB Right - 127.5db + write instruction reg0-3
const unsigned char wm8741_reg4_default = 0x19; //volume control : ramp-up, Anti clipping off, Attuenation Right = right, Soft Mute enable, Infinte zero detect enabled, zero flag left AND right
const unsigned char wm8741_reg5_default = 0x0A; //Format control : 24bits, I2S, Normal polarity, BCLK normal polarity, Output phase normal, Power normal mode
const unsigned char wm8741_reg6_default = 0x00; //Filter control : default chip conf
const unsigned char wm8741_reg7_default = 0x40; //Mode control 1 : PCM, Sampling auto detect, OSR High, Mode 8x off
const unsigned char wm8741_reg8_st_default = 0x00; //Mode control 2 : Dither off, Stereo, Daisy chain audio off, daisy chain control off, dsd gain low
const unsigned char wm8741_reg8_ml_default = 0x04; //Mode control 2 : Dither off, Dual mono left, Daisy chain audio off, daisy chain control off, dsd gain low
const unsigned char wm8741_reg8_mr_default = 0x0C; //Mode control 2 : Dither off, Dual mono right, Daisy chain audio off, daisy chain control off, dsd gain low
const unsigned char wm8741_reg9_default = 0x00; //Do a soft reset
const unsigned char wm8741_reg32_default = 0x00; //Additionnal control - default chip conf
/*
Autres constantes
*/
const unsigned char wm8741_VolDef = 0x190; //-50 dB default volume
const unsigned char wm8741_VolMin = 0x3FF; // -127dB
const unsigned char wm8741_VolMax = 0x00; // -0dB - zero attenuation
/*
Variables
*/
bool isMono = false;
bool muted = false;
byte reg4value = wm8741_reg4_default;
int unsigned CurrentVolume = 0;
int unsigned MaxVolume = 0;
int unsigned MinVolume = 0;
int unsigned FilterActive = 1; //value of the reg6 set ahead
////////////////////////////////////////////////////////
//Constructor of WM8741 class
////////////////////////////////////////////////////////
WM8741::WM8741(bool monomode, char unsigned VolMax = 0x400, char unsigned VolMin = 0x400 , char unsigned VolCurr = 0x400)
{
delay(2000); //wait for WM8741 to fully start up
Wire.begin(); //Init I2C bus
//Input varaiables
if (VolMax == 0x400) {
MaxVolume = wm8741_VolMax;
}
else {
MaxVolume = VolMax;
}
if (VolMin == 0x400) {
MinVolume = wm8741_VolMin;
}
else {
MinVolume = VolMin;
}
if (VolCurr == 0x400) {
CurrentVolume = wm8741_VolDef;
}
else {
CurrentVolume = VolCurr;
}
//set the devices
if (monomode) {
SetupMonoMode();
}
else {
SetupStereoMode();
}
//Set variables
CheckMuteStatus(); //read value from device reg4
//Set default volume
SetVolume(CurrentVolume, false);
}
///////////////////////////////////////////////
// Set two DAC chip to differential mono mode
///////////////////////////////////////////////
void WM8741::SetupMonoMode(void) {
isMono = true;
softReset();
//Chip One - i2C address one
setReg (wm8741_i2c_one, wm8741_reg0_addr, wm8741_reg0_default);
setReg (wm8741_i2c_one, wm8741_reg1_addr, wm8741_reg1_default);
setReg (wm8741_i2c_one, wm8741_reg2_addr, wm8741_reg2_default);
setReg (wm8741_i2c_one, wm8741_reg3_addr, wm8741_reg3_default);
setReg (wm8741_i2c_one, wm8741_reg4_addr, wm8741_reg4_default);
setReg (wm8741_i2c_one, wm8741_reg5_addr, wm8741_reg5_default);
setReg (wm8741_i2c_one, wm8741_reg6_addr, wm8741_reg6_default);
setReg (wm8741_i2c_one, wm8741_reg7_addr, wm8741_reg7_default);
setReg (wm8741_i2c_one, wm8741_reg8_addr, wm8741_reg8_mr_default);
//Chip two - i2C address two
setReg (wm8741_i2c_two, wm8741_reg0_addr, wm8741_reg0_default);
setReg (wm8741_i2c_two, wm8741_reg1_addr, wm8741_reg1_default);
setReg (wm8741_i2c_two, wm8741_reg2_addr, wm8741_reg2_default);
setReg (wm8741_i2c_two, wm8741_reg3_addr, wm8741_reg3_default);
setReg (wm8741_i2c_two, wm8741_reg4_addr, wm8741_reg4_default);
setReg (wm8741_i2c_two, wm8741_reg5_addr, wm8741_reg5_default);
setReg (wm8741_i2c_two, wm8741_reg6_addr, wm8741_reg6_default);
setReg (wm8741_i2c_two, wm8741_reg7_addr, wm8741_reg7_default);
setReg (wm8741_i2c_two, wm8741_reg8_addr, wm8741_reg8_ml_default);
}
///////////////////////////////////////////////
// Set one DAC chip to Stereo mode
///////////////////////////////////////////////
void WM8741::SetupStereoMode(void) {
isMono = false;
softReset();
//Chip One - i2C address one
setReg (wm8741_i2c_one, wm8741_reg0_addr, wm8741_reg0_default);
setReg (wm8741_i2c_one, wm8741_reg1_addr, wm8741_reg1_default);
setReg (wm8741_i2c_one, wm8741_reg2_addr, wm8741_reg2_default);
setReg (wm8741_i2c_one, wm8741_reg3_addr, wm8741_reg3_default);
setReg (wm8741_i2c_one, wm8741_reg4_addr, wm8741_reg4_default);
setReg (wm8741_i2c_one, wm8741_reg5_addr, wm8741_reg5_default);
setReg (wm8741_i2c_one, wm8741_reg6_addr, wm8741_reg6_default);
setReg (wm8741_i2c_one, wm8741_reg7_addr, wm8741_reg7_default);
setReg (wm8741_i2c_one, wm8741_reg8_addr, wm8741_reg8_st_default);
}
///////////////////////////////////////////////
// Set register through I2C bus
///////////////////////////////////////////////
void WM8741::setReg (uint8_t ChipAddress, uint8_t RegAddress, uint8_t RegValue) {
Wire.beginTransmission(ChipAddress);
Wire.write(RegAddress);
Wire.write(RegValue);
Wire.endTransmission();
}
//////////////////////////////////
// Set volume to indicated level
//////////////////////////////////
void WM8741::SetVolume(int unsigned volume, bool changeMute = true) {
//check flags / revert mute
if (muted && changeMute) Mute();
//split the 10 bits into 2x 5 bits = 2 regs
reg0 = (((byte)volume) & 0x1F);
reg1 = ((byte)(volume>>5));
reg1action = (reg1 | 0x20); //Set bit6 to 1 for volume setting start on chip - writes reg 0-3
if (isMono) {
setReg (wm8741_i2c_one, wm8741_reg0_addr, reg0);
setReg (wm8741_i2c_two, wm8741_reg0_addr, reg0);
setReg (wm8741_i2c_one, wm8741_reg1_addr, reg1);
setReg (wm8741_i2c_two, wm8741_reg1_addr, reg1);
setReg (wm8741_i2c_one, wm8741_reg2_addr, reg0);
setReg (wm8741_i2c_two, wm8741_reg2_addr, reg0);
setReg (wm8741_i2c_one, wm8741_reg3_addr, reg1action);
setReg (wm8741_i2c_two, wm8741_reg3_addr, reg1action);
}
else {
setReg (wm8741_i2c_one, wm8741_reg0_addr, reg0);
setReg (wm8741_i2c_one, wm8741_reg1_addr, reg1);
setReg (wm8741_i2c_one, wm8741_reg2_addr, reg0);
setReg (wm8741_i2c_one, wm8741_reg3_addr, reg1action);
}
}
///////////////////////////////////////////////
// Set FIR filters (1 out of 5)
///////////////////////////////////////////////
void WM8741::SetFilter(int filter) {
//Effect of filter change according to sample rate
//switch / case
//bit oprtaion
//setreg
byte filtre1 = 0x00;
byte filtre2 = 0x01;
byte filtre3 = 0x02;
byte filtre4 = 0x03;
byte filtre5 = 0x04;
byte reg6 = ((byte) getReg(wm8741_i2c_one, wm8741_reg6_addr));
switch (filter) {
case 1:
reg6 = (reg6 | filter1); //default value
FilterActive = 1;
break;
case 2:
reg6 = (reg6 | filter2);
FilterActive = 2;
break;
case 3:
reg6 = (reg6 | filter3);
FilterActive = 3;
break;
case 4:
reg6 = (reg6 | filter4);
FilterActive = 4;
break;
case 5:
reg6 = (reg6 | filter5);
FilterActive = 5;
break;
default:
reg6 = (reg6 | filter4);
FilterActive = 5;
}
if (isMono) {
setReg (wm8741_i2c_one, wm8741_reg6_addr, reg6);
setReg (wm8741_i2c_two, wm8741_reg6_addr, reg6);
}
else {
setReg (wm8741_i2c_one, wm8741_reg6_addr, reg6);
}
}
/////////////////////////////////
// Set next filter
/////////////////////////////////
void WM8741::NextFilter(void) {
if (FilterActive == 6) FilterActive = 0;
SetFilter(++FilterActive);
}
/////////////////////////////////
// Set previous filter
/////////////////////////////////
void WM8741::PrevFilter(void) {
if (FilterActive == 0) FilterActive = 6;
SetFilter(--FilterActive);
}
/////////////////////////////////
// Increase volume by 1 dB
/////////////////////////////////
void WM8741::VolumeUp(void) {
CurrentVolume -= 8; //decrease attenuation by 1dB (8x0.125dB)
SetVolume(CurrentVolume, true);
}
/////////////////////////////////
// Decrease volume by 1 dB
/////////////////////////////////
void WM8741::VolumeDown(void) {
CurrentVolume += 8; //increase attenuation by 1dB (8x0.125dB)
SetVolume(CurrentVolume, true);
}
///////////////////////////
// Mute / Unmute function
// flip flop mode
///////////////////////////
void WM8741::Mute(void) {
if (muted) {
reg4 = wm8741_reg4_default & 0x17; //set bit4 to 0
if (isMono) {
setReg (wm8741_i2c_one, wm8741_reg4_addr, reg4);
setReg (wm8741_i2c_two, wm8741_reg4_addr, reg4);
}
else {
setReg (wm8741_i2c_one, wm8741_reg4_addr, reg4);
}
muted = false;
}
else {
reg4 = wm8741_reg4_default | 0x08; //set bit4 to 1
if (isMono) {
setReg (wm8741_i2c_one, wm8741_reg4_addr, reg4);
setReg (wm8741_i2c_two, wm8741_reg4_addr, reg4);
}
else {
setReg (wm8741_i2c_one, wm8741_reg4_addr, reg4);
}
muted = true;
}
}
///////////////////////////
// Set volume to -127.5 dB
///////////////////////////
void WM8741::ZeroSound(void) {
CurrentVolume = wm8741_VolMin;
SetVolume(CurrentVolume, false);
}
///////////////////////////
// Soft reset of the Dac chip
///////////////////////////
void WM8741::SoftReset(void){
if (isMono) {
setReg (wm8741_i2c_one, wm8741_reg9_addr, 0x00);
setReg (wm8741_i2c_two, wm8741_reg9_addr, 0x00);
}
else {
setReg (wm8741_i2c_one, wm8741_reg9_addr, 0x00);
}
}
///////////////////////////
// Read one register
///////////////////////////
uint8_t WM8741::getReg(uint8_t ChipAddress, uint8_t RegAddress){
byte reg = 0x00;
Wire.beginTransmission(ChipAddress);
Wire.send(RegAddress);
Wire.endTransmission();
Wire.requestFrom(ChipAddress,1);
if (Wire.available()) reg = Wire.receive();
return reg;
}
/////////////////////////////////
// retrieve the Mute flag in Reg4
/////////////////////////////////
void WM8741::CheckMuteStatus (void) {
byte status = (((byte) getReg(wm8741_i2c_one, wm8741_reg9_addr)) & 0x08); //assume volume control reg4 is equivalent in mono or stereo mode
if (status == 0x08) {
muted = true;
}
else {
muted = false;
}
}
bool WM8741::isMuted (void) {
return muted;
}