diff options
| author | jacqueline <me@jacqueline.id.au> | 2022-10-11 10:50:55 +1100 |
|---|---|---|
| committer | jacqueline <me@jacqueline.id.au> | 2022-10-11 10:50:55 +1100 |
| commit | a64fbc1cf513820fd83d0a7e80ca5acf0d7df795 (patch) | |
| tree | aa8423421e1dcd776226b0d13230f93e62cd2957 /main/dac.cpp | |
| parent | b9ee0eb88fd1c02aeb4cb1d1047e21119f8e2bf4 (diff) | |
| download | tangara-fw-a64fbc1cf513820fd83d0a7e80ca5acf0d7df795.tar.gz | |
the music plays!
Diffstat (limited to 'main/dac.cpp')
| -rw-r--r-- | main/dac.cpp | 223 |
1 files changed, 41 insertions, 182 deletions
diff --git a/main/dac.cpp b/main/dac.cpp index cf1ad4b9..513793db 100644 --- a/main/dac.cpp +++ b/main/dac.cpp @@ -1,5 +1,6 @@ #include "dac.h" +#include "esp_err.h" #include "i2c.h" #include "esp_log.h" #include "assert.h" @@ -18,175 +19,29 @@ AudioDac::AudioDac(GpioExpander *gpio) { AudioDac::~AudioDac() {}; -void AudioDac::Start(SampleRate sample_rate, BitDepth bit_depth) { - // First check that the arguments look okay. - - // Check that the bit clock (PLL input) is between 1MHz and 50MHz - uint32_t bckFreq = sample_rate * bit_depth * 2; - if (bckFreq < 1000000 || bckFreq > 50000000) { - return; - } - - // 24 bits is not supported for 44.1kHz and 48kHz. - if ((sample_rate == SAMPLE_RATE_44_1K || sample_rate == SAMPLE_RATE_48K) - && bit_depth == BIT_DEPTH_24) { - return; - } - - // Next, wait for the IC to boot up before we try configuring it. - bool is_booted = false; - for (int i=0; i<10; i++) { - uint8_t result = ReadPowerState(); - is_booted = result >> 7; - if (is_booted) { - break; - } else { - ESP_LOGI(TAG, "Waiting for boot..."); - vTaskDelay(pdMS_TO_TICKS(1)); - } - } - +esp_err_t AudioDac::Start() { + bool is_booted = WaitForPowerState([](bool booted, PowerState state){ return booted; }); if (!is_booted) { - // TODO: properly handle - ESP_LOGE(TAG, "Timed out waiting for boot!"); - return; - } - - // Now do all the math required to correctly set up the internal clocks. - int p, j, d, r; - int nmac, ndac, ncp, dosr, idac; - if (sample_rate == SAMPLE_RATE_11_025K || - sample_rate == SAMPLE_RATE_22_05K || - sample_rate == SAMPLE_RATE_44_1K) { - //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 (bit_depth == BIT_DEPTH_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 (sample_rate) { - case SAMPLE_RATE_16K: nmac = 6; break; - case SAMPLE_RATE_32K: nmac = 3; break; - default: nmac = 2; break; - }; - - ndac = 16; - ncp = 4; - dosr = 384000 / sample_rate; - idac = 98304000 / nmac / sample_rate; // DSP clock / sample rate - } - - // FS speed mode - int speedMode; - if (sample_rate <= SAMPLE_RATE_48K) { - speedMode = 0; - } else if (sample_rate <= SAMPLE_RATE_96K) { - speedMode = 1; - } else if (sample_rate <= SAMPLE_RATE_192K) { - speedMode = 2; - } else { - speedMode = 3; + ESP_LOGE(TAG, "Timed out waiting for boot"); + return ESP_ERR_TIMEOUT; } - // Set correct I2S config - uint8_t i2s_format = 0; - switch (bit_depth) { - case BIT_DEPTH_16: i2s_format = 0x00; break; - case BIT_DEPTH_24: i2s_format = 0x02; break; - case BIT_DEPTH_32: i2s_format = 0x03; break; - }; + WriteRegister(Register::DE_EMPHASIS, 1 << 4); + WriteVolume(100); - // We've calculated all of our data. Now assemble the big list of registers - // that we need to configure. - I2CTransaction transaction; - transaction - .start() - .write_addr(kPCM5122Address, I2C_MASTER_WRITE) - // All our registers are on the first page. - .write_ack(Register::PAGE_SELECT, 0) - // Disable clock autoset and ignore SCK detection - .write_ack(Register::IGNORE_ERRORS, 0x1A) - // Set PLL clock source to BCK - .write_ack(Register::PLL_CLOCK_SOURCE, 0x10) - // Set DAC clock source to PLL output - .write_ack(Register::DAC_CLOCK_SOURCE, 0x10) - - // Configure PLL - .write_ack(Register::PLL_P, p - 1) - .write_ack(Register::PLL_J, j) - .write_ack(Register::PLL_D_MSB, (d >> 8) & 0x3F) - .write_ack(Register::PLL_D_LSB, d & 0xFF) - .write_ack(Register::PLL_R, r - 1) - - // Clock dividers - .write_ack(Register::DSP_CLOCK_DIV, nmac - 1) - .write_ack(Register::DAC_CLOCK_DIV, ndac - 1) - .write_ack(Register::NCP_CLOCK_DIV, ncp - 1) - .write_ack(Register::OSR_CLOCK_DIV, dosr - 1) - - // IDAC (nb of DSP clock cycles per sample) - .write_ack(Register::IDAC_MSB, (idac >> 8) & 0xFF) - .write_ack(Register::IDAC_LSB, idac & 0xFF) - - .write_ack(Register::FS_SPEED_MODE, speedMode) - .write_ack(Register::I2S_FORMAT, i2s_format) + WaitForPowerState([](bool booted, PowerState state){ + return state == WAIT_FOR_CP || state == RAMP_UP || state == RUN || state == STANDBY; + }); - .stop(); - - ESP_LOGI(TAG, "Configuring DAC"); - // TODO: Handle this gracefully. - ESP_ERROR_CHECK(transaction.Execute()); - - // The DAC takes a moment to reconfigure itself. Give it some time before we - // start asking for its state. - vTaskDelay(pdMS_TO_TICKS(5)); - - // TODO: investigate why it's stuck waiting for CP voltage. - /* - bool is_configured = false; - for (int i=0; i<10; i++) { - uint8_t result = ReadPowerState(); - is_configured = (result & 0b1111) == 0b1001; - if (is_configured) { - break; - } else { - ESP_LOGI(TAG, "Waiting for configure..."); - vTaskDelay(pdMS_TO_TICKS(1)); - } - } + return ESP_OK; +} - if (!is_configured) { - // TODO: properly handle - ESP_LOGE(TAG, "Timed out waiting for configure!"); - return; - } - */ +void AudioDac::WriteVolume(uint8_t volume) { + WriteRegister(Register::DIGITAL_VOLUME_L, volume); + WriteRegister(Register::DIGITAL_VOLUME_R, volume); } -uint8_t AudioDac::ReadPowerState() { +std::pair<bool, AudioDac::PowerState> AudioDac::ReadPowerState() { uint8_t result = 0; I2CTransaction transaction; @@ -201,31 +56,35 @@ uint8_t AudioDac::ReadPowerState() { ESP_ERROR_CHECK(transaction.Execute()); - return result; + bool is_booted = result >> 7; + PowerState detail = (PowerState) (result & 0b1111); + return std::pair(is_booted, detail); } -void AudioDac::WriteVolume(uint8_t volume) { - I2CTransaction transaction; - transaction - .start() - .write_addr(kPCM5122Address, I2C_MASTER_WRITE) - .write_ack(Register::DIGITAL_VOLUME_L, volume) - .write_ack(Register::DIGITAL_VOLUME_R, volume) - .stop(); - - ESP_ERROR_CHECK(transaction.Execute()); -} - -void AudioDac::WritePowerMode(PowerMode mode) { - switch (mode) { - case ON: - case STANDBY: - // TODO: enable power switch. - break; - case OFF: - // TODO: disable power switch. +bool AudioDac::WaitForPowerState(std::function<bool(bool,AudioDac::PowerState)> predicate) { + bool has_matched = false; + for (int i=0; i<10; i++) { + std::pair<bool, PowerState> result = ReadPowerState(); + has_matched = predicate(result.first, result.second); + if (has_matched) { break; + } else { + ESP_LOGI(TAG, "Waiting for power state (was %d %x)", result.first, (uint8_t) result.second); + vTaskDelay(pdMS_TO_TICKS(1)); + } } + return has_matched; +} + +void AudioDac::WriteRegister(Register reg, uint8_t val) { + I2CTransaction transaction; + transaction + .start() + .write_addr(kPCM5122Address, I2C_MASTER_WRITE) + .write_ack(reg, val) + .stop(); + // TODO: Retry once? + ESP_ERROR_CHECK(transaction.Execute()); } } // namespace gay_ipod |
