Working without big distortion :)

custom
jacqueline 2 years ago
parent 731b2cfa77
commit 2d95b63727
  1. 2
      src/audio/i2s_audio_output.cpp
  2. 123
      src/drivers/dac.cpp
  3. 6
      src/drivers/include/dac.hpp

@ -31,7 +31,7 @@ auto I2SAudioOutput::create(drivers::GpioExpander* expander)
// Soft mute immediately, in order to minimise any clicks and pops caused by
// the initial output element and pipeline configuration.
// dac->WriteVolume(255);
dac->WriteVolume(120); // for testing
dac->WriteVolume(127); // for testing
return std::make_unique<I2SAudioOutput>(expander, std::move(dac));
}

@ -33,6 +33,12 @@ auto AudioDac::create(GpioExpander* expander)
i2s_chan_handle_t i2s_handle;
i2s_chan_config_t channel_config =
I2S_CHANNEL_DEFAULT_CONFIG(kI2SPort, I2S_ROLE_MASTER);
// Use the maximum possible DMA buffer size, since a smaller number of large
// copies is faster than a large number of small copies.
channel_config.dma_frame_num = 1024;
// Triple buffering should be enough to keep samples flowing smoothly.
// TODO(jacqueline): verify this with 192kHz 32bps.
channel_config.dma_desc_num = 8;
// channel_config.auto_clear = true;
ESP_ERROR_CHECK(i2s_new_channel(&channel_config, &i2s_handle, NULL));
@ -47,8 +53,7 @@ auto AudioDac::create(GpioExpander* expander)
i2s_std_config_t i2s_config = {
.clk_cfg = dac->clock_config_,
.slot_cfg = dac->slot_config_,
.gpio_cfg = {.mclk = GPIO_NUM_0,
//.mclk = I2S_GPIO_UNUSED,
.gpio_cfg = {.mclk = I2S_GPIO_UNUSED,
.bclk = GPIO_NUM_26,
.ws = GPIO_NUM_27,
.dout = GPIO_NUM_5,
@ -57,7 +62,7 @@ auto AudioDac::create(GpioExpander* expander)
{
.mclk_inv = false,
.bclk_inv = false,
.ws_inv = false,
.ws_inv = true,
}},
};
@ -115,7 +120,7 @@ AudioDac::AudioDac(GpioExpander* gpio, i2s_chan_handle_t i2s_handle)
i2s_active_(false),
active_page_(),
clock_config_(I2S_STD_CLK_DEFAULT_CONFIG(44100)),
slot_config_(I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT,
slot_config_(I2S_STD_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT,
I2S_SLOT_MODE_STEREO)) {
clock_config_.clk_src = I2S_CLK_SRC_PLL_160M;
gpio_->set_pin(GpioExpander::AMP_EN, true);
@ -192,8 +197,114 @@ auto AudioDac::Reconfigure(BitsPerSample bps, SampleRate rate) -> void {
bps == BPS_24 ? I2S_MCLK_MULTIPLE_384 : I2S_MCLK_MULTIPLE_256;
ESP_ERROR_CHECK(i2s_channel_reconfig_std_clock(i2s_handle_, &clock_config_));
WriteRegister(pcm512x::I2S_1, bps_bits);
// WriteRegister(pcm512x::I2S_2, 0);
// DAC reconfiguration.
//See here : https://e2e.ti.com/support/data_converters/audio_converters/f/64/t/428281
// for a config example
// Check that the bit clock (PLL input) is between 1MHz and 50MHz
uint32_t bckFreq = rate * bps * 2;
if (bckFreq < 1000000 || bckFreq > 50000000) {
ESP_LOGE(kTag, "bck freq out of range");
return;
}
// 24 bits is not supported for 44.1kHz and 48kHz.
if ((rate == SAMPLE_RATE_44_1 || rate == SAMPLE_RATE_48) && bps == BPS_24) {
// TODO(jacqueline): implement
ESP_LOGE(kTag, "sample rate and bps mismatch");
return;
}
//Initialize system clock from the I2S BCK input
WriteRegister(pcm512x::ERROR_DETECT, 0x1A); // Disable clock autoset and ignore SCK detection
WriteRegister(pcm512x::PLL_REF, 0x10); // Set PLL clock source to BCK
WriteRegister(pcm512x::DAC_REF, 0x10); // Set DAC clock source to PLL output
//PLL configuration
int p, j, d, r;
//Clock dividers
int nmac, ndac, ncp, dosr, idac;
if (rate == SAMPLE_RATE_11_025 || rate == SAMPLE_RATE_22_05 || rate == SAMPLE_RATE_44_1)
{
//44.1kHz and derivatives.
//P = 1, R = 2, D = 0 for all supported combinations.
//Set J to have PLL clk = 90.3168 MHz
p = 1;
r = 2;
j = 90316800 / bckFreq / r;
d = 0;
//Derive clocks from the 90.3168MHz PLL
nmac = 2;
ndac = 16;
ncp = 4;
dosr = 8;
idac = 1024; // DSP clock / sample rate
}
else
{
//8kHz and multiples.
//PLL config for a 98.304 MHz PLL clk
if (bps == BPS_24 && bckFreq > 1536000) {
p = 3;
} else if (bckFreq > 12288000) {
p = 2;
} else {
p = 1;
}
r = 2;
j = 98304000 / (bckFreq / p) / r;
d = 0;
//Derive clocks from the 98.304MHz PLL
switch (rate) {
case SAMPLE_RATE_16: nmac = 6; break;
case SAMPLE_RATE_32: nmac = 3; break;
default: nmac = 2; break;
}
ndac = 16;
ncp = 4;
dosr = 384000 / rate;
idac = 98304000 / nmac / rate; // DSP clock / sample rate
}
// Configure PLL
WriteRegister(pcm512x::PLL_COEFF_0, p - 1);
WriteRegister(pcm512x::PLL_COEFF_1, j);
WriteRegister(pcm512x::PLL_COEFF_2, (d >> 8) & 0x3F);
WriteRegister(pcm512x::PLL_COEFF_3, d & 0xFF);
WriteRegister(pcm512x::PLL_COEFF_4, r - 1);
// Clock dividers
WriteRegister(pcm512x::DSP_CLKDIV, nmac - 1);
WriteRegister(pcm512x::DAC_CLKDIV, ndac - 1);
WriteRegister(pcm512x::NCP_CLKDIV, ncp - 1);
WriteRegister(pcm512x::OSR_CLKDIV, dosr - 1);
// IDAC (nb of DSP clock cycles per sample)
WriteRegister(pcm512x::IDAC_1, (idac >> 8) & 0xFF);
WriteRegister(pcm512x::IDAC_2, idac & 0xFF);
// FS speed mode
int speedMode;
if (rate <= SAMPLE_RATE_48) {
speedMode = 0;
} else if (rate <= SAMPLE_RATE_96) {
speedMode = 1;
} else if (rate <= SAMPLE_RATE_192) {
speedMode = 2;
} else {
speedMode = 3;
}
WriteRegister(pcm512x::FS_SPEED_MODE, speedMode);
WriteRegister(pcm512x::I2S_1, (0b11 << 4) | bps_bits);
WriteRegister(pcm512x::I2S_2, 0);
// Configuration is all done, so we can now bring the DAC and I2S stream back
// up. I2S first, since otherwise the DAC will see that there's no clocks and

@ -150,8 +150,14 @@ class AudioDac {
BPS_32 = I2S_DATA_BIT_WIDTH_32BIT,
};
enum SampleRate {
SAMPLE_RATE_11_025 = 11025,
SAMPLE_RATE_16 = 16000,
SAMPLE_RATE_22_05 = 22050,
SAMPLE_RATE_32 = 32000,
SAMPLE_RATE_44_1 = 44100,
SAMPLE_RATE_48 = 48000,
SAMPLE_RATE_96 = 96000,
SAMPLE_RATE_192 = 192000,
};
// TODO(jacqueline): worth supporting channels here as well?

Loading…
Cancel
Save