From 2d95b637272f15ba2d74abc2371d708a08407d91 Mon Sep 17 00:00:00 2001 From: jacqueline Date: Thu, 20 Apr 2023 14:47:32 +1000 Subject: [PATCH] Working without big distortion :) --- src/audio/i2s_audio_output.cpp | 2 +- src/drivers/dac.cpp | 123 +++++++++++++++++++++++++++++++-- src/drivers/include/dac.hpp | 6 ++ 3 files changed, 124 insertions(+), 7 deletions(-) diff --git a/src/audio/i2s_audio_output.cpp b/src/audio/i2s_audio_output.cpp index 63563a10..e5c672f9 100644 --- a/src/audio/i2s_audio_output.cpp +++ b/src/audio/i2s_audio_output.cpp @@ -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(expander, std::move(dac)); } diff --git a/src/drivers/dac.cpp b/src/drivers/dac.cpp index dc9b51c8..9fdd7b94 100644 --- a/src/drivers/dac.cpp +++ b/src/drivers/dac.cpp @@ -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 diff --git a/src/drivers/include/dac.hpp b/src/drivers/include/dac.hpp index d27ed915..acdd1743 100644 --- a/src/drivers/include/dac.hpp +++ b/src/drivers/include/dac.hpp @@ -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?