Skip to content

Commit 47a29a7

Browse files
committed
First attempt at getting audio out. Both speaker and headphone produce sound but the gain seems off.
1 parent 7c07c36 commit 47a29a7

File tree

5 files changed

+508
-0
lines changed

5 files changed

+508
-0
lines changed

src/AudioBoard.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,8 @@ static AudioBoard NoBoard{NoDriver, NoPins};
131131
static AudioBoard GenericWM8960{AudioDriverWM8960, NoPins};
132132
/// @ingroup audio_driver
133133
static AudioBoard GenericCS43l22{AudioDriverCS43l22, NoPins};
134+
/// @ingroup audio_driver
135+
static AudioBoard GenericTLV320DAC3100{AudioDriverTLV320DAC3100, NoPins};
134136
#if defined(ARDUINO_GENERIC_F411VETX)
135137
/// @ingroup audio_driver
136138
static AudioBoard STM32F411Disco{AudioDriverCS43l22, PinsSTM32F411Disco};

src/Driver.h

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "Driver/es8388/es8388.h"
1414
#include "Driver/pcm3168/pcm3168.h"
1515
#include "Driver/tas5805m/tas5805m.h"
16+
#include "Driver/tlv320dac3100/tlv320dac3100.h"
1617
#include "Driver/wm8960/mtb_wm8960.h"
1718
#include "Driver/wm8978/WM8978.h"
1819
#include "Driver/wm8994/wm8994.h"
@@ -1530,6 +1531,36 @@ class AudioDriverAD1938Class : public AudioDriver {
15301531
int volumes[8] = {100};
15311532
};
15321533

1534+
1535+
/**
1536+
* @brief Driver API for TLV320DAC3100 codec chip
1537+
* @author Phil Schatzmann
1538+
* @copyright GPLv3
1539+
*/
1540+
class AudioDriverTLV320DAC3100Class : public AudioDriver {
1541+
public:
1542+
AudioDriverTLV320DAC3100Class() { i2c_default_address = 0x18; }
1543+
bool setMute(bool mute) { return tlv320dac3100_set_mute(mute); }
1544+
bool setVolume(int volume) {
1545+
return tlv320dac3100_set_volume(limitValue(volume, 0, 100));
1546+
}
1547+
int getVolume() {
1548+
int vol = 0;
1549+
tlv320dac3100_get_volume(&vol);
1550+
return vol;
1551+
}
1552+
1553+
protected:
1554+
bool init(codec_config_t codec_cfg) {
1555+
return tlv320dac3100_init(&codec_cfg, getI2C(), getI2CAddress()) == RESULT_OK;
1556+
}
1557+
bool deinit() { return tlv320dac3100_deinit() == RESULT_OK; }
1558+
bool controlState(codec_mode_t mode) { return true; }
1559+
bool configInterface(codec_mode_t mode, I2SDefinition iface) {
1560+
return tlv320dac3100_config_i2s(mode, &iface) == RESULT_OK;
1561+
}
1562+
};
1563+
15331564
#endif
15341565

15351566
// -- Drivers
@@ -1573,6 +1604,8 @@ static NoDriverClass NoDriver;
15731604
static AudioDriverCS42448Class AudioDriverCS42448;
15741605
/// @ingroup audio_driver
15751606
static AudioDriverPCM3168Class AudioDriverPCM3168;
1607+
/// @ingroup audio_driver
1608+
static AudioDriverTLV320DAC3100Class AudioDriverTLV320DAC3100;
15761609

15771610
#ifdef ARDUINO
15781611
/// @ingroup audio_driver

src/Driver/tlv320dac3100/README.md

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# TLV320DAC3100 driver
2+
3+
Below is the example sketch I'm using with the Adafruit breakout and a Wemos Lolin S3 Pro. It plays a WAV from the SD card.
4+
5+
```
6+
#include "AudioTools.h"
7+
#include "AudioTools/Disk/AudioSourceSD.h"
8+
#include "AudioTools/AudioCodecs/CodecWAV.h"
9+
#include "AudioTools/AudioLibs/I2SCodecStream.h"
10+
11+
// Pins
12+
#define I2C_SDA_PIN 9
13+
#define I2C_SCL_PIN 10
14+
#define I2S_BCLK_PIN 42
15+
#define I2S_WS_PIN 41
16+
#define I2S_DI_PIN 40
17+
#define I2S_RST_PIN 39
18+
#define SD_SCK_PIN 12
19+
#define SD_MISO_PIN 13
20+
#define SD_MOSI_PIN 11
21+
#define SD_CS_PIN 46
22+
23+
I2SCodecStream i2s(GenericTLV320DAC3100);
24+
EncodedAudioStream decoder(&i2s, new WAVDecoder());
25+
StreamCopy copier;
26+
File file;
27+
28+
void setup() {
29+
Serial.begin(115200);
30+
delay(2000);
31+
AudioLogger::instance().begin(Serial, AudioLogger::Warning); // Enable warning logging
32+
AudioDriverLogger.begin(Serial, AudioDriverLogLevel::Info); // Enable info logging
33+
Serial.println("Setup starting...");
34+
35+
// Set SD pins first
36+
Serial.println("Initializing SD card...");
37+
SPI.begin(SD_SCK_PIN, SD_MISO_PIN, SD_MOSI_PIN, SD_CS_PIN);
38+
if (!SD.begin(SD_CS_PIN)) {
39+
Serial.println("Card Mount Failed");
40+
return;
41+
}
42+
43+
Serial.println("I2C begin...");
44+
Wire.begin(I2C_SDA_PIN, I2C_SCL_PIN);
45+
46+
Serial.println("I2S begin...");
47+
I2SCodecConfig config;
48+
config.sample_rate = 48000; // 48kHz to match DAC configuration
49+
config.channels = 2;
50+
config.bits_per_sample = 16;
51+
config.pin_bck = I2S_BCLK_PIN; // define your i2s pins
52+
config.pin_ws = I2S_WS_PIN;
53+
config.pin_data = I2S_DI_PIN;
54+
config.buffer_size = 1024;
55+
56+
i2s.begin(config);
57+
decoder.begin();
58+
copier.begin(decoder, file);
59+
60+
file = SD.open("/test.wav");
61+
}
62+
63+
void loop() {
64+
copier.copy();
65+
}
66+
```
Lines changed: 258 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,258 @@
1+
#include "tlv320dac3100.h"
2+
3+
static i2c_bus_handle_t i2c_handle = NULL;
4+
static int address = TLV320DAC3100_ADDR;
5+
6+
#define TLV_ASSERT(a, format, b, ...) \
7+
if ((a) != 0) { \
8+
AD_LOGE(format, ##__VA_ARGS__); \
9+
return b; \
10+
}
11+
12+
static error_t tlv_write_reg(uint8_t slave_addr, uint8_t reg_add, uint8_t data) {
13+
return i2c_bus_write_bytes(i2c_handle, slave_addr, &reg_add, sizeof(reg_add),
14+
&data, sizeof(data));
15+
}
16+
17+
static error_t tlv_read_reg(uint8_t reg_add, uint8_t *p_data) {
18+
return i2c_bus_read_bytes(i2c_handle, address, &reg_add, sizeof(reg_add),
19+
p_data, 1);
20+
}
21+
22+
static error_t tlv_set_page(uint8_t page) {
23+
return tlv_write_reg(address, TLV320DAC3100_REG_PAGE_SELECT, page);
24+
}
25+
26+
error_t tlv320dac3100_init(codec_config_t *cfg, i2c_bus_handle_t handle, int addr) {
27+
AD_LOGI("TLV320DAC3100 start init");
28+
29+
AD_TRACED();
30+
i2c_handle = handle;
31+
if (addr > 0) address = addr;
32+
33+
// 0. Reset the codec
34+
AD_LOGI("Resetting codec");
35+
TLV_ASSERT(tlv_write_reg(address, TLV320DAC3100_REG_RESET, 0x01), "Failed to reset codec", RESULT_FAIL);
36+
delay(100); // Increased delay to match working sketch
37+
TLV_ASSERT(tlv_write_reg(address, TLV320DAC3100_REG_RESET, 0x00), "Failed to reset codec", RESULT_FAIL);
38+
delay(10); // Additional delay after reset
39+
40+
// 1. Interface Control - Set codec interface (I2S, 16-bit)
41+
AD_LOGI("Configuring codec interface");
42+
TLV_ASSERT(tlv_set_page(0), "Failed to set page 0", RESULT_FAIL);
43+
uint8_t if_ctrl = (TLV320DAC3100_FORMAT_I2S << 6) | (TLV320DAC3100_DATA_LEN_16 << 4);
44+
TLV_ASSERT(tlv_write_reg(address, TLV320DAC3100_REG_CODEC_IF_CTRL1, if_ctrl), "Failed to configure I2S interface", RESULT_FAIL);
45+
46+
// 2. Clock MUX and PLL settings
47+
AD_LOGI("Configuring clocks and PLL");
48+
// 2.1 Set codec clock input to PLL and PLL clock input to BCLK
49+
uint8_t clock_mux = (TLV320DAC3100_PLL_CLKIN_BCLK << 2) | TLV320DAC3100_CODEC_CLKIN_PLL;
50+
TLV_ASSERT(tlv_write_reg(address, TLV320DAC3100_REG_CLOCK_MUX1, clock_mux), "Failed to configure clock sources", RESULT_FAIL);
51+
52+
// 2.2 Set PLL values: P=1, R=2, J=32, D=0 (matching working sketch)
53+
uint8_t pll_pr = (1 << 4) | 2; // P=1, R=2
54+
TLV_ASSERT(tlv_write_reg(address, TLV320DAC3100_REG_PLL_PROG_PR, pll_pr), "Failed to set PLL P&R", RESULT_FAIL);
55+
TLV_ASSERT(tlv_write_reg(address, TLV320DAC3100_REG_PLL_PROG_J, 32), "Failed to set PLL J", RESULT_FAIL);
56+
TLV_ASSERT(tlv_write_reg(address, TLV320DAC3100_REG_PLL_PROG_D_MSB, 0x00), "Failed to set PLL D MSB", RESULT_FAIL);
57+
TLV_ASSERT(tlv_write_reg(address, TLV320DAC3100_REG_PLL_PROG_D_LSB, 0x00), "Failed to set PLL D LSB", RESULT_FAIL);
58+
59+
// 3. DAC/ADC config
60+
AD_LOGI("Configuring DAC dividers");
61+
// 3.1 Set NDAC = 8 (bit 7 = enable, bits 6:0 = value)
62+
TLV_ASSERT(tlv_write_reg(address, TLV320DAC3100_REG_NDAC, 0x80 | 8), "Failed to set NDAC", RESULT_FAIL);
63+
64+
// 3.2 Set MDAC = 2 (bit 7 = enable, bits 6:0 = value)
65+
TLV_ASSERT(tlv_write_reg(address, TLV320DAC3100_REG_MDAC, 0x80 | 2), "Failed to set MDAC", RESULT_FAIL);
66+
67+
// 4. Power up the PLL (bit 7 = 1 to power up)
68+
AD_LOGI("Powering up PLL");
69+
uint8_t pll_power = 0x80 | pll_pr; // Set power bit (7) and keep P&R values
70+
TLV_ASSERT(tlv_write_reg(address, TLV320DAC3100_REG_PLL_PROG_PR, pll_power), "Failed to power up PLL", RESULT_FAIL);
71+
72+
// 5. DAC Setup
73+
AD_LOGI("Configuring DAC data path");
74+
// 5.1 Set DAC data path (power up both DACs, normal paths, 1 sample volume step)
75+
uint8_t dac_path = (1 << 7) | // Left DAC power up
76+
(1 << 6) | // Right DAC power up
77+
(TLV320DAC3100_DAC_PATH_NORMAL << 4) | // Left path
78+
(TLV320DAC3100_DAC_PATH_NORMAL << 2) | // Right path
79+
TLV320DAC3100_VOLUME_STEP_1SAMPLE; // Volume step
80+
TLV_ASSERT(tlv_write_reg(address, TLV320DAC3100_REG_DAC_DATAPATH, dac_path), "Failed to configure DAC data path", RESULT_FAIL);
81+
82+
// 5.2 Configure analog inputs - switch to page 1
83+
TLV_ASSERT(tlv_set_page(1), "Failed to set page 1", RESULT_FAIL);
84+
uint8_t routing = (TLV320DAC3100_DAC_ROUTE_MIXER << 6) | // Left DAC to mixer
85+
(TLV320DAC3100_DAC_ROUTE_MIXER << 2); // Right DAC to mixer
86+
TLV_ASSERT(tlv_write_reg(address, TLV320DAC3100_REG_OUT_ROUTING, routing), "Failed to configure analog routing", RESULT_FAIL);
87+
88+
// 6. DAC volume control - switch back to page 0
89+
AD_LOGI("Configuring DAC volume");
90+
TLV_ASSERT(tlv_set_page(0), "Failed to set page 0", RESULT_FAIL);
91+
// Unmute both channels, independent volume control
92+
TLV_ASSERT(tlv_write_reg(address, TLV320DAC3100_REG_DAC_VOL_CTRL, TLV320DAC3100_VOL_INDEPENDENT), "Failed to configure volume control", RESULT_FAIL);
93+
94+
// Set channel volumes to +0dB (register value 0x01) - matching working sketch
95+
TLV_ASSERT(tlv_write_reg(address, TLV320DAC3100_REG_DAC_LVOL, 0x01), "Failed to set left volume", RESULT_FAIL);
96+
TLV_ASSERT(tlv_write_reg(address, TLV320DAC3100_REG_DAC_RVOL, 0x01), "Failed to set right volume", RESULT_FAIL);
97+
98+
// 7. Headphone and speaker setup - switch to page 1
99+
AD_LOGI("Configuring headphone and speaker outputs");
100+
TLV_ASSERT(tlv_set_page(1), "Failed to set page 1", RESULT_FAIL);
101+
102+
// 7.1 Configure headphone driver (power up both, 1.35V common mode, no power down on SCD)
103+
uint8_t hp_ctrl = (1 << 7) | // Left HP power up
104+
(1 << 6) | // Right HP power up
105+
(1 << 2) | // Required bit 2 = 1
106+
(TLV320DAC3100_HP_COMMON_1_35V << 3); // Common mode voltage
107+
TLV_ASSERT(tlv_write_reg(address, TLV320DAC3100_REG_HP_DRIVERS, hp_ctrl), "Failed to configure headphone drivers", RESULT_FAIL);
108+
109+
// 7.2 Configure HPL PGA (0dB gain, unmute)
110+
TLV_ASSERT(tlv_write_reg(address, TLV320DAC3100_REG_HPL_DRIVER, (0 << 3) | (1 << 2)), "Failed to configure HPL PGA", RESULT_FAIL);
111+
112+
// 7.3 Configure HPR PGA (0dB gain, unmute)
113+
TLV_ASSERT(tlv_write_reg(address, TLV320DAC3100_REG_HPR_DRIVER, (0 << 3) | (1 << 2)), "Failed to configure HPR PGA", RESULT_FAIL);
114+
115+
// 7.4 Set HPL volume (enable routing, volume=1)
116+
TLV_ASSERT(tlv_write_reg(address, TLV320DAC3100_REG_HPL_VOL, 0x80 | 1), "Failed to set HPL volume", RESULT_FAIL);
117+
118+
// 7.5 Set HPR volume (enable routing, volume=1)
119+
TLV_ASSERT(tlv_write_reg(address, TLV320DAC3100_REG_HPR_VOL, 0x80 | 1), "Failed to set HPR volume", RESULT_FAIL);
120+
121+
// 7.6 Enable speaker amplifier
122+
TLV_ASSERT(tlv_write_reg(address, TLV320DAC3100_REG_SPK_AMP, 0x80), "Failed to enable speaker", RESULT_FAIL);
123+
124+
// 7.7 Configure speaker PGA (6dB gain, unmute)
125+
TLV_ASSERT(tlv_write_reg(address, TLV320DAC3100_REG_SPK_DRIVER, (TLV320DAC3100_SPK_GAIN_6DB << 3) | (1 << 2)), "Failed to configure speaker PGA", RESULT_FAIL);
126+
127+
// 7.8 Set speaker volume (enable routing, volume=1)
128+
TLV_ASSERT(tlv_write_reg(address, TLV320DAC3100_REG_SPK_VOL, 0x80 | 1), "Failed to set speaker volume", RESULT_FAIL);
129+
130+
// 8. MICBIAS and headset detection
131+
AD_LOGI("Configuring MICBIAS and headset detection");
132+
// 8.1 Configure MICBIAS (not powered down, always on, AVDD voltage)
133+
TLV_ASSERT(tlv_write_reg(address, TLV320DAC3100_REG_MICBIAS, (1 << 3) | TLV320DAC3100_MICBIAS_AVDD), "Failed to configure MICBIAS", RESULT_FAIL);
134+
135+
// 8.2 Set headset detect - switch to page 0
136+
TLV_ASSERT(tlv_set_page(0), "Failed to set page 0", RESULT_FAIL);
137+
TLV_ASSERT(tlv_write_reg(address, TLV320DAC3100_REG_HEADSET_DETECT, 0x80), "Failed to enable headset detection", RESULT_FAIL);
138+
139+
// 8.3 Set INT1 source (headset detect + button press)
140+
TLV_ASSERT(tlv_write_reg(address, TLV320DAC3100_REG_INT1_CTRL, (1 << 7) | (1 << 6)), "Failed to configure INT1", RESULT_FAIL);
141+
142+
// 8.4 Set GPIO1 mode to INT1
143+
TLV_ASSERT(tlv_write_reg(address, TLV320DAC3100_REG_GPIO1_CTRL, TLV320DAC3100_GPIO1_INT1 << 2), "Failed to configure GPIO1", RESULT_FAIL);
144+
145+
AD_LOGI("TLV320DAC3100 initialized successfully");
146+
return RESULT_OK;
147+
}
148+
149+
error_t tlv320dac3100_deinit(void) {
150+
AD_TRACED();
151+
TLV_ASSERT(tlv_set_page(0), "Failed to set page 0", RESULT_FAIL);
152+
TLV_ASSERT(tlv_write_reg(address, TLV320DAC3100_REG_DAC_DATAPATH, 0x00), "Failed to power down DACs", RESULT_FAIL);
153+
TLV_ASSERT(tlv_set_page(1), "Failed to set page 1", RESULT_FAIL);
154+
TLV_ASSERT(tlv_write_reg(address, TLV320DAC3100_REG_HP_DRIVERS, 0x00), "Failed to power down HP", RESULT_FAIL);
155+
TLV_ASSERT(tlv_write_reg(address, TLV320DAC3100_REG_SPK_AMP, 0x00), "Failed to power down speaker", RESULT_FAIL);
156+
return RESULT_OK;
157+
}
158+
159+
error_t tlv320dac3100_config_i2s(codec_mode_t mode, I2SDefinition *iface) {
160+
AD_TRACED();
161+
TLV_ASSERT(tlv_set_page(0), "Failed to set page 0", RESULT_FAIL);
162+
163+
uint8_t format_bits = TLV320DAC3100_FORMAT_I2S;
164+
switch (iface->fmt) {
165+
case I2S_NORMAL: format_bits = TLV320DAC3100_FORMAT_I2S; break;
166+
case I2S_LEFT: format_bits = TLV320DAC3100_FORMAT_LEFT_JUSTIFIED; break;
167+
case I2S_RIGHT: format_bits = TLV320DAC3100_FORMAT_RIGHT_JUSTIFIED; break;
168+
case I2S_DSP: format_bits = TLV320DAC3100_FORMAT_DSP; break;
169+
default: AD_LOGE("Unsupported I2S format: %d", iface->fmt); return RESULT_FAIL;
170+
}
171+
172+
uint8_t len_bits = TLV320DAC3100_DATA_LEN_16;
173+
switch (iface->bits) {
174+
case BIT_LENGTH_16BITS: len_bits = TLV320DAC3100_DATA_LEN_16; break;
175+
case BIT_LENGTH_20BITS: len_bits = TLV320DAC3100_DATA_LEN_20; break;
176+
case BIT_LENGTH_24BITS: len_bits = TLV320DAC3100_DATA_LEN_24; break;
177+
case BIT_LENGTH_32BITS: len_bits = TLV320DAC3100_DATA_LEN_32; break;
178+
default: AD_LOGE("Unsupported bit length: %d", iface->bits); return RESULT_FAIL;
179+
}
180+
181+
uint8_t if_ctrl = (format_bits << 6) | (len_bits << 4);
182+
TLV_ASSERT(tlv_write_reg(address, TLV320DAC3100_REG_CODEC_IF_CTRL1, if_ctrl), "Failed to configure I2S", RESULT_FAIL);
183+
184+
return RESULT_OK;
185+
}
186+
187+
error_t tlv320dac3100_set_volume(int volume) {
188+
AD_LOGD("tlv320dac3100_set_volume: %d", volume);
189+
TLV_ASSERT(tlv_set_page(0), "Failed to set page 0", RESULT_FAIL);
190+
191+
if (volume < 0) volume = 0;
192+
if (volume > 100) volume = 100;
193+
194+
// Convert 0-100 to dB range (-63.5 to +24 dB)
195+
float dB;
196+
if (volume == 0) {
197+
dB = -63.5f; // Mute
198+
} else if (volume == 100) {
199+
dB = 24.0f; // Max volume
200+
} else {
201+
// Map 0-100 to -63.5 to +24 dB with better linearity
202+
dB = -63.5f + (volume * 0.875f);
203+
}
204+
205+
// Convert dB to register value (2dB steps) with proper bounds
206+
int8_t reg_val;
207+
if (dB <= -63.5f) {
208+
reg_val = -127; // Mute
209+
} else if (dB >= 24.0f) {
210+
reg_val = 48; // Max volume
211+
} else {
212+
reg_val = (int8_t)(dB * 2.0f);
213+
}
214+
215+
// Set DAC volume
216+
TLV_ASSERT(tlv_write_reg(address, TLV320DAC3100_REG_DAC_LVOL, reg_val), "Failed to set left volume", RESULT_FAIL);
217+
TLV_ASSERT(tlv_write_reg(address, TLV320DAC3100_REG_DAC_RVOL, reg_val), "Failed to set right volume", RESULT_FAIL);
218+
219+
// Also update headphone and speaker volumes
220+
TLV_ASSERT(tlv_set_page(1), "Failed to set page 1", RESULT_FAIL);
221+
TLV_ASSERT(tlv_write_reg(address, TLV320DAC3100_REG_HPL_VOL, 0x80 | reg_val), "Failed to set HPL volume", RESULT_FAIL);
222+
TLV_ASSERT(tlv_write_reg(address, TLV320DAC3100_REG_HPR_VOL, 0x80 | reg_val), "Failed to set HPR volume", RESULT_FAIL);
223+
TLV_ASSERT(tlv_write_reg(address, TLV320DAC3100_REG_SPK_VOL, 0x80 | reg_val), "Failed to set speaker volume", RESULT_FAIL);
224+
225+
return RESULT_OK;
226+
}
227+
228+
error_t tlv320dac3100_get_volume(int *volume) {
229+
AD_TRACED();
230+
TLV_ASSERT(tlv_set_page(0), "Failed to set page 0", RESULT_FAIL);
231+
232+
uint8_t reg_val;
233+
TLV_ASSERT(tlv_read_reg(TLV320DAC3100_REG_DAC_LVOL, &reg_val), "Failed to read volume", RESULT_FAIL);
234+
235+
// Convert register value to dB
236+
float dB = (int8_t)reg_val * 0.5f;
237+
238+
// Convert dB to 0-100 range
239+
if (dB <= -63.5f) {
240+
*volume = 0;
241+
} else if (dB >= 24.0f) {
242+
*volume = 100;
243+
} else {
244+
*volume = (int)((dB + 63.5f) / 0.875f);
245+
}
246+
247+
return RESULT_OK;
248+
}
249+
250+
error_t tlv320dac3100_set_mute(bool enable) {
251+
AD_TRACED();
252+
TLV_ASSERT(tlv_set_page(0), "Failed to set page 0", RESULT_FAIL);
253+
254+
uint8_t vol_ctrl = enable ? 0x0C : 0x00; // Set mute bits for both channels
255+
TLV_ASSERT(tlv_write_reg(address, TLV320DAC3100_REG_DAC_VOL_CTRL, vol_ctrl), "Failed to set mute", RESULT_FAIL);
256+
257+
return RESULT_OK;
258+
}

0 commit comments

Comments
 (0)