summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--main/CMakeLists.txt2
-rw-r--r--main/dac.cpp223
-rw-r--r--main/dac.h73
-rw-r--r--main/gay-ipod-fw.cpp136
-rw-r--r--main/gpio-expander.h2
-rw-r--r--main/storage.h2
6 files changed, 186 insertions, 252 deletions
diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt
index b9409cb4..223c8ca2 100644
--- a/main/CMakeLists.txt
+++ b/main/CMakeLists.txt
@@ -1,4 +1,4 @@
idf_component_register(
SRCS "gay-ipod-fw.cpp" "dac.cpp" "gpio-expander.cpp" "battery.cpp" "storage.cpp" "i2c.cpp"
INCLUDE_DIRS "."
- REQUIRES "esp_adc_cal" "fatfs")
+ REQUIRES "esp_adc_cal" "fatfs" "audio_pipeline" "audio_stream")
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
diff --git a/main/dac.h b/main/dac.h
index bb06a0e7..bbc8485d 100644
--- a/main/dac.h
+++ b/main/dac.h
@@ -1,7 +1,9 @@
#pragma once
+#include "esp_err.h"
#include "gpio-expander.h"
#include <stdint.h>
+#include <functional>
namespace gay_ipod {
@@ -9,41 +11,18 @@ namespace gay_ipod {
static const uint8_t kPCM5122Timeout = 100 / portTICK_RATE_MS;
/**
- * PCM5122PWR
- *
- * Heavily inspired from the Arduino library here:
- * https://github.com/tommag/PCM51xx_Arduino/
+ * Interface for a PCM5122PWR DAC, configured over I2C.
*/
class AudioDac {
public:
AudioDac(GpioExpander *gpio);
~AudioDac();
- /** Supported sample rates */
- enum SampleRate {
- SAMPLE_RATE_8K = 8000,
- SAMPLE_RATE_11_025K = 11025,
- SAMPLE_RATE_16K = 16000,
- SAMPLE_RATE_22_05K = 22050,
- SAMPLE_RATE_32K = 32000,
- SAMPLE_RATE_44_1K = 44100,
- SAMPLE_RATE_48K = 48000,
- SAMPLE_RATE_96K = 96000,
- SAMPLE_RATE_192K = 192000,
- SAMPLE_RATE_384K = 384000
- };
-
- enum BitDepth {
- BIT_DEPTH_16 = 16,
- BIT_DEPTH_24 = 24,
- BIT_DEPTH_32 = 32,
- };
-
/**
* Performs initial configuration of the DAC and sets it to begin expecting
* I2S audio data.
*/
- void Start(SampleRate sample_rate, BitDepth bit_depth);
+ esp_err_t Start();
/**
* Sets the volume on a scale from 0 (loudest) to 254 (quietest). A value of
@@ -51,47 +30,43 @@ class AudioDac {
*/
void WriteVolume(uint8_t volume);
- enum PowerMode {
- ON = 0,
- STANDBY = 0x10,
- OFF = 0x01,
+ enum PowerState {
+ POWERDOWN = 0b0,
+ WAIT_FOR_CP = 0b1,
+ CALIBRATION_1 = 0b10,
+ CALIBRATION_2 = 0b11,
+ RAMP_UP = 0b100,
+ RUN = 0b101,
+ SHORT = 0b110,
+ RAMP_DOWN = 0b111,
+ STANDBY = 0b1000,
};
- void WritePowerMode(PowerMode state);
+ /* Returns the current boot-up status and internal state of the DAC */
+ std::pair<bool,PowerState> ReadPowerState();
// Not copyable or movable.
- // TODO: maybe this could be movable?
AudioDac(const AudioDac&) = delete;
AudioDac& operator=(const AudioDac&) = delete;
private:
GpioExpander *gpio_;
- PowerMode last_power_state_ = PowerMode::OFF;
+
+ /*
+ * Pools the power state for up to 10ms, waiting for the given predicate to
+ * be true.
+ */
+ bool WaitForPowerState(std::function<bool(bool,PowerState)> predicate);
enum Register {
PAGE_SELECT = 0,
- IGNORE_ERRORS = 37,
- PLL_CLOCK_SOURCE = 13,
- DAC_CLOCK_SOURCE = 14,
- PLL_P = 20,
- PLL_J = 21,
- PLL_D_MSB = 22,
- PLL_D_LSB = 23,
- PLL_R = 24,
- DSP_CLOCK_DIV = 27,
- DAC_CLOCK_DIV = 14,
- NCP_CLOCK_DIV = 29,
- OSR_CLOCK_DIV = 30,
- IDAC_MSB = 35,
- IDAC_LSB = 36,
- FS_SPEED_MODE = 34,
- I2S_FORMAT = 40,
+ DE_EMPHASIS = 7,
DIGITAL_VOLUME_L = 61,
DIGITAL_VOLUME_R = 62,
DSP_BOOT_POWER_STATE = 118,
};
- uint8_t ReadPowerState();
+ void WriteRegister(Register reg, uint8_t val);
};
} // namespace gay_ipod
diff --git a/main/gay-ipod-fw.cpp b/main/gay-ipod-fw.cpp
index 7d91d5b2..dfae9c7b 100644
--- a/main/gay-ipod-fw.cpp
+++ b/main/gay-ipod-fw.cpp
@@ -7,6 +7,7 @@
#include "driver/adc.h"
#include "driver/gpio.h"
#include "driver/i2c.h"
+#include "driver/i2s.h"
#include "driver/sdspi_host.h"
#include "driver/spi_common.h"
#include "driver/spi_master.h"
@@ -16,10 +17,18 @@
#include "gpio-expander.h"
#include "hal/adc_types.h"
#include "hal/gpio_types.h"
+#include "hal/i2s_types.h"
#include "hal/spi_types.h"
#include "storage.h"
-#define I2C_SDA_IO (GPIO_NUM_0)
+#include "audio_element.h"
+#include "audio_pipeline.h"
+#include "audio_common.h"
+#include "fatfs_stream.h"
+#include "i2s_stream.h"
+#include "mp3_decoder.h"
+
+#define I2C_SDA_IO (GPIO_NUM_2)
#define I2C_SCL_IO (GPIO_NUM_4)
#define I2C_CLOCK_HZ (400000)
@@ -114,31 +123,122 @@ extern "C" void app_main(void)
ESP_LOGI(TAG, "Trying to init DAC");
gay_ipod::AudioDac dac(&expander);
- dac.Start(
- gay_ipod::AudioDac::SAMPLE_RATE_48K,
- gay_ipod::AudioDac::BIT_DEPTH_16);
+ dac.Start();
vTaskDelay(pdMS_TO_TICKS(1000));
- ESP_LOGI(TAG, "Looks okay? Let's list some files!");
- vTaskDelay(pdMS_TO_TICKS(1000));
+ ESP_LOGI(TAG, "Looks okay? Let's play some music!");
+
+ i2s_port_t port = I2S_NUM_0;
+
+ i2s_config_t i2s_config = {
+ // Weird enum usage in ESP IDF.
+ .mode = static_cast<i2s_mode_t>(I2S_MODE_MASTER | I2S_MODE_TX),
+ .sample_rate = 44100,
+ .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
+ .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
+ .communication_format = I2S_COMM_FORMAT_STAND_I2S,
+ .intr_alloc_flags = ESP_INTR_FLAG_LOWMED,
+ .dma_buf_count = 8,
+ .dma_buf_len = 64,
+ .use_apll = false,
+ .tx_desc_auto_clear = false,
+ .fixed_mclk = 0,
+ .mclk_multiple = I2S_MCLK_MULTIPLE_DEFAULT,
+ .bits_per_chan = I2S_BITS_PER_CHAN_DEFAULT,
+ };
- DIR *d;
- struct dirent *dir;
- d = opendir(gay_ipod::kStoragePath);
- if (d) {
- while ((dir = readdir(d)) != NULL) {
- ESP_LOGI(TAG, "file! %s", dir->d_name);
- }
- closedir(d);
- } else {
- ESP_LOGI(TAG, "nope!");
- }
+ //ESP_ERROR_CHECK(i2s_driver_install(port, &i2s_config, 0, NULL));
+
+ audio_pipeline_handle_t pipeline;
+ audio_element_handle_t fatfs_stream_reader, i2s_stream_writer, audio_decoder;
+
+ audio_pipeline_cfg_t pipeline_config = audio_pipeline_cfg_t(DEFAULT_AUDIO_PIPELINE_CONFIG());
+ pipeline = audio_pipeline_init(&pipeline_config);
+ assert(pipeline != NULL);
+ ESP_LOGI(TAG, "Made pipeline okay.");
+
+ fatfs_stream_cfg_t fatfs_stream_config = fatfs_stream_cfg_t(FATFS_STREAM_CFG_DEFAULT());
+ fatfs_stream_config.type = AUDIO_STREAM_READER;
+ fatfs_stream_reader = fatfs_stream_init(&fatfs_stream_config);
+ assert(fatfs_stream_reader != NULL);
+ ESP_LOGI(TAG, "Made reader okay.");
+
+ i2s_stream_cfg_t i2s_stream_config = i2s_stream_cfg_t{
+ .type = AUDIO_STREAM_WRITER,
+ .i2s_config = i2s_config,
+ .i2s_port = port,
+ .use_alc = false,
+ .volume = 0, // Does nothing; use the dac
+ .out_rb_size = I2S_STREAM_RINGBUFFER_SIZE,
+ .task_stack = I2S_STREAM_TASK_STACK,
+ .task_core = I2S_STREAM_TASK_CORE,
+ .task_prio = I2S_STREAM_TASK_PRIO,
+ .stack_in_ext = false,
+ .multi_out_num = 0,
+ .uninstall_drv = true,
+ .need_expand = false,
+ .expand_src_bits = I2S_BITS_PER_SAMPLE_16BIT,
+ };
+ // TODO fix hardcoded mclk :(
+ i2s_stream_writer = i2s_stream_init(&i2s_stream_config);
+ assert(i2s_stream_writer != NULL);
+ ESP_LOGI(TAG, "Made i2s stream okay.");
+
+
+ ESP_LOGI(TAG, "Init i2s pins");
+ i2s_pin_config_t pin_config = {
+ .mck_io_num = GPIO_NUM_0,
+ .bck_io_num = GPIO_NUM_26,
+ .ws_io_num = GPIO_NUM_27,
+ .data_out_num = GPIO_NUM_5,
+ .data_in_num = I2S_PIN_NO_CHANGE
+ };
+
+ ESP_ERROR_CHECK(i2s_set_pin(port, &pin_config));
+
+ mp3_decoder_cfg_t decoder_config = mp3_decoder_cfg_t(DEFAULT_MP3_DECODER_CONFIG());
+ audio_decoder = mp3_decoder_init(&decoder_config);
+ assert(audio_decoder != NULL);
+ ESP_LOGI(TAG, "Made mp3 decoder okay.");
+
+ vTaskDelay(pdMS_TO_TICKS(500));
+ ESP_LOGI(TAG, "Putting it all together");
+
+ audio_pipeline_register(pipeline, fatfs_stream_reader, "file");
+ audio_pipeline_register(pipeline, audio_decoder, "dec");
+ audio_pipeline_register(pipeline, i2s_stream_writer, "i2s");
+
+ const char *link_tag[3] = {"file", "dec", "i2s"};
+ audio_pipeline_link(pipeline, &link_tag[0], 3);
+
+ ESP_LOGI(TAG, "Trying to play something??");
+ audio_element_set_uri(fatfs_stream_reader, "/sdcard/test.mp3");
+ audio_pipeline_run(pipeline);
vTaskDelay(pdMS_TO_TICKS(1000));
+ ESP_LOGI(TAG, "It could be playing? idk");
+
+ gay_ipod::AudioDac::PowerState state = dac.ReadPowerState().second;
+ ESP_LOGI(TAG, "dac power state: %x", (uint8_t)state);
+
+ vTaskDelay(pdMS_TO_TICKS(120000));
+
ESP_LOGI(TAG, "Time to deinit.");
+ audio_pipeline_stop(pipeline);
+ audio_pipeline_wait_for_stop(pipeline);
+ audio_pipeline_terminate(pipeline);
+
+ audio_pipeline_unregister(pipeline, fatfs_stream_reader);
+ audio_pipeline_unregister(pipeline, audio_decoder);
+ audio_pipeline_unregister(pipeline, i2s_stream_writer);
+
+ audio_pipeline_deinit(pipeline);
+ audio_element_deinit(fatfs_stream_reader);
+ audio_element_deinit(i2s_stream_writer);
+ audio_element_deinit(audio_decoder);
+
storage.Release();
ESP_LOGI(TAG, "Hooray!");
- vTaskDelay(pdMS_TO_TICKS(1000));
}
diff --git a/main/gpio-expander.h b/main/gpio-expander.h
index 93e2d3e0..f3b5b804 100644
--- a/main/gpio-expander.h
+++ b/main/gpio-expander.h
@@ -55,7 +55,7 @@ class GpioExpander {
// 6 - GPIO
// 7 - GPIO
// DAC mute output low, everything else is active-low inputs.
- static const uint8_t kPortBDefault = 0b11111101;
+ static const uint8_t kPortBDefault = 0b11111111;
/*
* Convenience mehod for packing the port a and b bytes into a single 16 bit
diff --git a/main/storage.h b/main/storage.h
index fc1f8f5d..c93a426b 100644
--- a/main/storage.h
+++ b/main/storage.h
@@ -22,7 +22,7 @@ namespace sdspi {
}
} // namespace sdspi
-static const char *kStoragePath = "/sd";
+static const char *kStoragePath = "/sdcard";
static const uint8_t kMaxOpenFiles = 8;
class SdStorage {