-
Notifications
You must be signed in to change notification settings - Fork 1
/
wm8960-AudioDac.ino
242 lines (200 loc) · 9.31 KB
/
wm8960-AudioDac.ino
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
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// GPL3 license Tobias van Dyk Aug 2020
// Based on Wolfson WM8960 STM32 demo code from Waveshare
// Demo code BSD license from STMicroelectronics - folder STM32-Open103Z
// Also based on WM8960 ALSA SoC Audio driver from Wolfson Microelectronics
// 2007 GPL2 license Liam Girdwood - folder WM8960-ALSA-driver
// Teensy libraries property of PJRC.com
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// WM8960 i2c ADDRESS = 0x1a
//
// WM8960-DAC Teensy 4.0 and 4.1 Teensy 3.6 WM8960 RPi GPIO Hat Bus
// +5v +5v 2,4 +5v
// 1,2 VCC +3v3 +3v3
// 3,4 GND GND GND (not AGnd) 6 GND
// 5,6 SDA 18 SDA 18 SDA 3 SDA i2c
// 7,8 SCL 19 SCL 19 SCL 5 SCL i2c
// 9,10 SCLK 21 BCLK 9 BCLK 12 PCMCLK BCLK
// 11,12 WS 20 LRCLK 23 LRCLK 35 PCM_FS LRCLK i2s Frame clock input
// 13 RXSDA 7 OUT1A 22 40 PCM_OUT i2s Data output
// 14 TXSDA 8 IN1 13 RX 38 PCM_IN i2s Data input
// 15 RX-MCLK MCLK N/A i2s Master Clock (Send)
// 16 TX-MCLK MCLK N/A i2s Master Clock (Receive)
// MCLK available but not connected MCLK not available at GPIO connector
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#include <Wire.h>
int led = LED_BUILTIN;
#define WM8960_ADDRESS 0x1A
//register values
static uint16_t WM8960_REG_VAL[56] =
{
0x0097, 0x0097, 0x0000, 0x0000, 0x0000, 0x0008, 0x0000, 0x000A,
0x01C0, 0x0000, 0x00FF, 0x00FF, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x007B, 0x0100, 0x0032, 0x0000, 0x00C3, 0x00C3, 0x01C0,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0100, 0x0100, 0x0050, 0x0050, 0x0050, 0x0050, 0x0000, 0x0000,
0x0000, 0x0000, 0x0040, 0x0000, 0x0000, 0x0050, 0x0050, 0x0000,
0x0000, 0x0037, 0x004D, 0x0080, 0x0008, 0x0031, 0x0026, 0x00ED
};
#include <Audio.h>
#include <SPI.h>
// GUItool: begin automatically generated code
AudioInputUSB usb1; //xy=121,162
AudioOutputI2S i2s1; //xy=559,166
AudioConnection patchCord1(usb1, 0, i2s1, 0);
AudioConnection patchCord2(usb1, 1, i2s1, 1);
// GUItool: end automatically generated code
int delay1 = 10;
int delay2 = 500;
//////////////////////////////////////////////////////////////////////////////////////////////////
// Registers of WM8960 are 9-bit. Thus, when we send data to it,
// should split data to two bytes and add the ID of register to recognized before
// transmitting. The ID of register flagged by 7-bit.
//
// Byte0 = bit7 to bit1 = device address, bit0 = read/write
// Byte1 = bit7 to bit1 = register number 0-57, bit0 = MSB bit8 of register value
// Byte2 = bit7-bit0 register value
/////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////
// WM89060 Write RegisterNumber = Value
/////////////////////////////////////////
uint8_t WM8960_Write_Reg(uint8_t reg, uint16_t dat)
{
uint8_t res,I2C_Data[2];
I2C_Data[0] = (reg<<1)|((uint8_t)((dat>>8)&0x0001)); //RegAddr
I2C_Data[1] = (uint8_t)(dat&0x00FF); //RegValue
//digitalWrite(led, HIGH); // briefly flash the LED
Wire.beginTransmission(WM8960_ADDRESS); // transmit to device lsb=0 => write
//Wire.write(I2C_Data[0]); // buffer 1 byte reg1 in b7-b1
//Wire.write(I2C_Data[1]); // buffer 1 byte lsb of val1
Wire.write(I2C_Data, 2); // buffer 1 byte lsb of val1
res = Wire.endTransmission(); // transmit buffer and then stop
if(res == 0) { WM8960_REG_VAL[reg] = dat;
//digitalWrite(led, LOW);
}
return res;
}
/////////////////////////////////////////
// WM89060 Inititialise
/////////////////////////////////////////
uint8_t WM89060_Init(void) {
uint8_t res;
//////////////////////////////////////////////////////////
// Reset Device 0x0f, 0x0000
// #define WM8960_RESET 0xf
// reg<<1 register now in bit7-bit1
// msb=b8 (ninth bit) of val now in bit0
// uint8_t reg1 = ((reg << 1) |(uint8_t)((val >> 8) & 1))
// val = lsb b7-b0 val and with val msb b15-b18 all = 0
////////////////////////////////////////////////////////
res = WM8960_Write_Reg(0x0f, 0x0000);
//if (res == 0) Serial.println("WM8960 reset completed"); else return res;
delay(delay1);
// Set Power Source
// #define WM8960_POWER1 0x19
// #define WM8960_POWER2 0x1a
// #define WM8960_ADDCTL3 0x1b **************
// #define WM8960_POWER3 0x2f
res = WM8960_Write_Reg(0x19, 1<<8 | 1<<7 | 1<<6);
delayMicroseconds(delay2);
res += WM8960_Write_Reg(0x1A, 1<<8 | 1<<7 | 1<<6 | 1<<5 | 1<<4 | 1<<3 | 1);
delayMicroseconds(delay2);
res += WM8960_Write_Reg(0x2F, 1<<3 | 1<<2);
//if (res == 0) Serial.println("WM8960 power 1,2,3 completed"); else return res;
delay(delay1);
// Configure clock
// MCLK->PLL->SYSCLK->DAC/ADC sample Freq = 24MHz/544.217 = 44.100kHz
// See calculation pages 60 and table 44 in manual
// f2 = 4 x 2 x 11.2896Hz = 90.3168MHz
// R = 90.316 / 12 = 7.5264
// PLLN = int R = 7
// k = int ( 2^24 x (7.5264 – 7)) = 8831526
// N = 7
// Fractional 24bit value = 86C226h
res = WM8960_Write_Reg(0x04, 0x0001); // Select PLL
//if (res == 0) Serial.println("WM8960 Configure clock"); else return res;
delay(delay1);
// Configure PLL 1 0011 0111 = 37h
// 00010 0111 27h
res = WM8960_Write_Reg(0x34, 0x0027); // Select PLL 1
delay(delay1);
// Configure PLL 2 bit 8 reserved 7-0 data
res = WM8960_Write_Reg(0x35, 0x0086); // Select PLL 2
delay(delay1);
// Configure PLL 3
res = WM8960_Write_Reg(0x36, 0x00C2); // Select PLL 3
delay(delay1);
// Configure PLL 4
res = WM8960_Write_Reg(0x37, 0x0026); // Select PLL 4
delay(delay1);
// Configure ADC/DAC
// bit0 = 1 ADC High Pass Filter Disable
// bit1,2 De-emphasis 00 = No de-emphasis
res = WM8960_Write_Reg(0x05, 0x0000);
//if (res == 0) Serial.println("WM8960 Configure ADC/DAC"); else return res;
delay(delay1);
// Configure audio interface
// I2S format 16 bits word length
res = WM8960_Write_Reg(0x07, 0x0002);
//if (res == 0) Serial.println("WM8960 Configure audio interface"); else return res;
delay(delay1);
// Configure HP_L and HP_R OUTPUTS
res = WM8960_Write_Reg(0x02, 0x006F | 0x0100); //LOUT1 Volume Set
delayMicroseconds(delay2);
res += WM8960_Write_Reg(0x03, 0x006F | 0x0100); //ROUT1 Volume Set
//if (res == 0) Serial.println("WM8960 Configure HP_L and HP_R OUTPUTS"); else return res;
delay(delay1);
// Configure SPK_RP and SPK_RN
res = WM8960_Write_Reg(0x28, 0x007F | 0x0100); //Left Speaker Volume
delayMicroseconds(delay2);
res += WM8960_Write_Reg(0x29, 0x007F | 0x0100); //Right Speaker Volume
//if (res == 0) Serial.println("WM8960 Configure SPK_RP and SPK_RN"); else return res;
delay(delay1);
// Enable the OUTPUTS
res = WM8960_Write_Reg(0x31, 0x00F7); //Enable Class D Speaker Outputs
//if (res == 0) Serial.println("WM8960 Enable Class D Speaker Outputs"); else return res;
delay(delay1);
// Configure DAC volume
res = WM8960_Write_Reg(0x0a, 0x00FF | 0x0100);
delayMicroseconds(delay2);
res += WM8960_Write_Reg(0x0b, 0x00FF | 0x0100);
//if (res == 0) Serial.println("WM8960 Configure DAC volume"); else return res;
delay(delay1);
// 3D
// WM8960_Write_Reg(0x10, 0x001F);
// Configure MIXER
res = WM8960_Write_Reg(0x22, 1<<8 | 1<<7);
delayMicroseconds(delay2);
res += WM8960_Write_Reg(0x25, 1<<8 | 1<<7);
//if (res == 0) Serial.println("WM8960 Configure MIXER"); else return res;
delay(delay1);
// Jack Detect
res = WM8960_Write_Reg(0x18, 1<<6 | 0<<5);
delayMicroseconds(delay2);
res += WM8960_Write_Reg(0x17, 0x01C3);
delayMicroseconds(delay2);
res += WM8960_Write_Reg(0x30, 0x0009); //0x000D,0x0005
//if (res == 0) Serial.println("WM8960 Jack Detect"); else return res;
delay(delay1);
return 0;
}
/////////////////////////////////////////
// Setup
/////////////////////////////////////////
void setup()
{
uint8_t res;
pinMode(led, OUTPUT);
AudioMemory(12);
Wire.begin(); // join i2c bus (address optional for master)
//Serial.begin(9600);
res = WM89060_Init();
//if (res != 0) Serial.println(res);
}
/////////////////////////////////////////
// Main
/////////////////////////////////////////
void loop()
{
delay(100); // wait 5 seconds for next scan
}