#pragma once #include #include #include #include #include "driver/i2s_types.h" #include "esp_err.h" #include "freertos/portmacro.h" #include "result.hpp" #include "span.hpp" #include "driver/i2s_std.h" #include "gpio_expander.hpp" namespace drivers { /** * Interface for a PCM5122PWR DAC, configured over I2C. */ class AudioDac { public: enum Error { FAILED_TO_BOOT, FAILED_TO_CONFIGURE, FAILED_TO_INSTALL_I2S, }; static auto create(GpioExpander* expander) -> cpp::result, Error>; AudioDac(GpioExpander* gpio, i2s_chan_handle_t i2s_handle); ~AudioDac(); /** * Sets the volume on a scale from 0 (loudest) to 254 (quietest). A value of * 255 engages the soft mute function. */ void WriteVolume(uint8_t volume); 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, }; /* Returns the current boot-up status and internal state of the DAC */ std::pair ReadPowerState(); enum BitsPerSample { BPS_16 = I2S_DATA_BIT_WIDTH_16BIT, BPS_24 = I2S_DATA_BIT_WIDTH_24BIT, BPS_32 = I2S_DATA_BIT_WIDTH_32BIT, }; enum SampleRate { SAMPLE_RATE_44_1 = 44100, SAMPLE_RATE_48 = 48000, }; // TODO(jacqueline): worth supporting channels here as well? auto Reconfigure(BitsPerSample bps, SampleRate rate) -> bool; auto WriteData(const cpp::span& data, TickType_t max_wait) -> std::size_t; // Not copyable or movable. AudioDac(const AudioDac&) = delete; AudioDac& operator=(const AudioDac&) = delete; private: GpioExpander* gpio_; i2s_chan_handle_t i2s_handle_; i2s_std_clk_config_t clock_config_; i2s_std_slot_config_t slot_config_; /* * Pools the power state for up to 10ms, waiting for the given predicate to * be true. */ bool WaitForPowerState(std::function predicate); enum Register { PAGE_SELECT = 0, DE_EMPHASIS = 7, DIGITAL_VOLUME_L = 61, DIGITAL_VOLUME_R = 62, DSP_BOOT_POWER_STATE = 118, }; void WriteRegister(Register reg, uint8_t val); }; } // namespace drivers