Redo pcm registers to include pages

custom
jacqueline 2 years ago
parent 3836768bb8
commit 40a9734b04
  1. 3
      src/audio/audio_decoder.cpp
  2. 9
      src/audio/audio_task.cpp
  3. 3
      src/audio/fatfs_audio_input.cpp
  4. 2
      src/codecs/mad.cpp
  5. 113
      src/drivers/dac.cpp
  6. 117
      src/drivers/include/dac.hpp

@ -107,9 +107,6 @@ auto AudioDecoder::Process(const std::vector<InputStream>& inputs,
}
auto write_res = current_codec_->WriteOutputSamples(output->data());
if (write_res.first > 0) {
ESP_LOGI(kTag, "wrote %u bytes of samples", write_res.first);
}
output->add(write_res.first);
has_samples_to_send_ = !write_res.second;

@ -162,6 +162,8 @@ void AudioTaskMain(void* args) {
vTaskDelete(NULL);
}
static std::byte sDrainBuf[1024];
void AudioDrainMain(void* args) {
{
AudioDrainArgs* real_args = reinterpret_cast<AudioDrainArgs*>(args);
@ -171,11 +173,10 @@ void AudioDrainMain(void* args) {
// TODO(jacqueline): implement PAUSE without busy-waiting.
while (*command != QUIT) {
std::byte buf[64];
std::size_t len =
xStreamBufferReceive(sink->buffer(), buf, sizeof(buf), portMAX_DELAY);
std::size_t len = xStreamBufferReceive(sink->buffer(), sDrainBuf,
sizeof(sDrainBuf), portMAX_DELAY);
if (len > 0) {
sink->Send({buf, len});
sink->Send({sDrainBuf, len});
}
}
}

@ -65,9 +65,6 @@ auto FatfsAudioInput::Process(const std::vector<InputStream>& inputs,
return;
}
if (size > 0) {
ESP_LOGI(kTag, "read %u bytes", size);
}
output->add(size);
if (size < max_size || f_eof(&current_file_)) {

@ -114,7 +114,7 @@ auto MadMp3Decoder::WriteOutputSamples(cpp::span<std::byte> output)
}
while (current_sample_ < synth_.pcm.length) {
if (output_byte + (3 * synth_.pcm.channels) >= output.size()) {
if (output_byte + (2 * synth_.pcm.channels) >= output.size()) {
return std::make_pair(output_byte, false);
}

@ -5,9 +5,7 @@
#include "assert.h"
#include "driver/i2c.h"
#include "driver/i2s_common.h"
#include "driver/i2s_std.h"
#include "driver/i2s_types.h"
#include "driver/i2s_types_legacy.h"
#include "esp_attr.h"
#include "esp_err.h"
#include "esp_log.h"
@ -19,6 +17,7 @@
#include "gpio_expander.hpp"
#include "hal/i2s_types.h"
#include "i2c.hpp"
#include "soc/clk_tree_defs.h"
#include "sys/_stdint.h"
namespace drivers {
@ -38,6 +37,7 @@ 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);
channel_config.auto_clear = true;
ESP_ERROR_CHECK(i2s_new_channel(&channel_config, &i2s_handle, NULL));
//
@ -84,20 +84,22 @@ auto AudioDac::create(GpioExpander* expander)
// The DAC should be booted but in power down mode, but it might not be if we
// didn't shut down cleanly. Reset it to ensure it is in a consistent state.
dac->WriteRegister(Register::POWER_MODE, 1 << 4);
dac->WriteRegister(Register::RESET, 0b10001);
dac->WriteRegister(pcm512x::POWER, 1 << 4);
dac->WriteRegister(pcm512x::RESET, 0b10001);
// Use BCK for the internal PLL.
// dac->WriteRegister(Register::PLL_CLOCK_SOURCE, 1 << 4);
// dac->WriteRegister(Register::DAC_CLOCK_SOURCE, 0b11 << 5);
//dac->WriteRegister(Register::PLL_ENABLE, 0);
dac->WriteRegister(Register::INTERPOLATION, 1 << 4);
//dac->WriteRegister(Register::DAC_CLOCK_SOURCE, 0b0110000);
//dac->WriteRegister(Register::CLOCK_ERRORS, 0b01000001);
//dac->WriteRegister(Register::I2S_FORMAT, 0b110000);
// dac->WriteRegister(Register::INTERPOLATION, 1 << 4);
dac->Reconfigure(BPS_16, SAMPLE_RATE_44_1);
dac->WriteRegister(Register::POWER_MODE, 0);
// Now configure the DAC for standard auto-clock SCK mode.
// dac->WriteRegister(Register::DAC_CLOCK_SOURCE, 0b11 << 5);
// Enable auto clocking, and do your best to carry on despite errors.
// dac->WriteRegister(Register::CLOCK_ERRORS, 0b1111101);
@ -115,9 +117,11 @@ AudioDac::AudioDac(GpioExpander* gpio, i2s_chan_handle_t i2s_handle)
: gpio_(gpio),
i2s_handle_(i2s_handle),
i2s_active_(false),
active_page_(),
clock_config_(I2S_STD_CLK_DEFAULT_CONFIG(44100)),
slot_config_(I2S_STD_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT,
slot_config_(I2S_STD_PHILIPS_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);
gpio_->Write();
}
@ -130,29 +134,14 @@ AudioDac::~AudioDac() {
}
void AudioDac::WriteVolume(uint8_t volume) {
WriteRegister(Register::DIGITAL_VOLUME_L, volume);
WriteRegister(Register::DIGITAL_VOLUME_R, volume);
// Left channel.
WriteRegister(pcm512x::DIGITAL_VOLUME_2, volume);
// Right channel.
WriteRegister(pcm512x::DIGITAL_VOLUME_3, volume);
}
std::pair<bool, AudioDac::PowerState> AudioDac::ReadPowerState() {
uint8_t result = 0;
I2CTransaction transaction;
transaction.start()
.write_addr(kPcm5122Address, I2C_MASTER_WRITE)
.write_ack(DSP_BOOT_POWER_STATE)
.start()
.write_addr(kPcm5122Address, I2C_MASTER_READ)
.read(&result, I2C_MASTER_NACK)
.stop();
esp_err_t err = transaction.Execute();
if (err == ESP_ERR_TIMEOUT) {
return std::pair(false, POWERDOWN);
} else {
}
ESP_ERROR_CHECK(err);
uint8_t result = ReadRegister(pcm512x::POWER_STATE);
bool is_booted = result >> 7;
PowerState detail = (PowerState)(result & 0b1111);
return std::pair(is_booted, detail);
@ -176,17 +165,17 @@ bool AudioDac::WaitForPowerState(
}
auto AudioDac::Reconfigure(BitsPerSample bps, SampleRate rate) -> void {
WriteRegister(Register::RESYNC_REQUEST, 1);
if (i2s_active_) {
WriteRegister(pcm512x::POWER, 1 << 4);
i2s_channel_disable(i2s_handle_);
}
// I2S reconfiguration.
slot_config_.slot_bit_width = (i2s_slot_bit_width_t)bps;
slot_config_.slot_bit_width = I2S_SLOT_BIT_WIDTH_16BIT;
ESP_ERROR_CHECK(i2s_channel_reconfig_std_slot(i2s_handle_, &slot_config_));
clock_config_.sample_rate_hz = rate;
clock_config_.sample_rate_hz = 44100;
// If we have an MCLK/SCK, then it must be a multiple of both the sample rate
// and the bit clock. At 24 BPS, we therefore have to change the MCLK multiple
// to avoid issues at some sample rates. (e.g. 48KHz)
@ -194,21 +183,19 @@ 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_));
// DAC reconfiguration.
if (rate == SAMPLE_RATE_44_1) {
WriteRegister(Register::DE_EMPHASIS, 1 << 4);
} else {
WriteRegister(Register::DE_EMPHASIS, 0);
}
// TODO: base on BPS
WriteRegister(Register::I2S_FORMAT, 0b00);
// WriteRegister(Register::I2S_FORMAT, 0b110000);
// 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
// shut itself down.
WriteRegister(Register::RESYNC_REQUEST, 0);
ESP_ERROR_CHECK(i2s_channel_enable(i2s_handle_));
WriteRegister(pcm512x::POWER, 0);
WriteRegister(pcm512x::SYNCHRONIZE, 1);
vTaskDelay(pdMS_TO_TICKS(10));
WriteRegister(pcm512x::SYNCHRONIZE, 0);
vTaskDelay(pdMS_TO_TICKS(10));
LogStatus();
i2s_active_ = true;
}
@ -223,7 +210,7 @@ auto AudioDac::WriteData(const cpp::span<const std::byte>& data) -> void {
auto AudioDac::Stop() -> void {
LogStatus();
WriteRegister(Register::POWER_MODE, 1 << 4);
WriteRegister(pcm512x::POWER, 1 << 4);
i2s_channel_disable(i2s_handle_);
}
@ -237,35 +224,47 @@ auto AudioDac::Stop() -> void {
auto AudioDac::LogStatus() -> void {
uint8_t res;
res = ReadRegister(Register::SAMPLE_RATE_DETECTION);
ESP_LOGI(kTag, "detected sample rate (want 3): %u", (res >> 4) && 0b111);
res = ReadRegister(pcm512x::RATE_DET_1);
ESP_LOGI(kTag, "detected sample rate (want 3): %u", (res & 0b01110000) >> 4);
ESP_LOGI(kTag, "detected SCK ratio (want 6): %u", res && 0b1111);
res = ReadRegister(Register::BCK_DETECTION);
res = ReadRegister(pcm512x::RATE_DET_3);
ESP_LOGI(kTag, "detected BCK (want... 16? 32?): %u", res);
res = ReadRegister(Register::CLOCK_ERROR_STATE);
res = ReadRegister(pcm512x::RATE_DET_4);
ESP_LOGI(kTag, "clock errors (want zeroes): ");
ESP_LOGI(kTag, BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(res & 0b1111111));
res = ReadRegister(Register::CLOCK_STATUS);
res = ReadRegister(pcm512x::CLOCK_STATUS);
ESP_LOGI(kTag, "clock status (want zeroes): ");
ESP_LOGI(kTag, BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(res & 0b10111));
res = ReadRegister(Register::AUTO_MUTE_STATE);
ESP_LOGI(kTag, "automute status (want 3): %u", res & 0b11);
res = ReadRegister(Register::SOFT_MUTE_STATE);
ESP_LOGI(kTag, "soft mute pin status (want 3): %u", res & 0b11);
res = ReadRegister(Register::SAMPLE_RATE_STATE);
ESP_LOGI(kTag, "detected sample speed mode (want 0): %u", res & 0b11);
res = ReadRegister(pcm512x::DIGITAL_MUTE_DET);
ESP_LOGI(kTag, "automute status (want 0): %u", res & 0b10001);
auto power = ReadPowerState();
ESP_LOGI(kTag, "current power state (want 5): %u", power.second);
}
void AudioDac::WriteRegister(Register reg, uint8_t val) {
void AudioDac::WriteRegister(pcm512x::Register r, uint8_t val) {
SelectPage(r.page);
WriteRegisterRaw(r.reg, val);
}
uint8_t AudioDac::ReadRegister(pcm512x::Register r) {
SelectPage(r.page);
return ReadRegisterRaw(r.reg);
}
void AudioDac::SelectPage(uint8_t page) {
if (active_page_ && active_page_ == page) {
return;
}
WriteRegisterRaw(0, page);
active_page_ = page;
}
void AudioDac::WriteRegisterRaw(uint8_t reg, uint8_t val) {
I2CTransaction transaction;
transaction.start()
.write_addr(kPcm5122Address, I2C_MASTER_WRITE)
@ -275,7 +274,7 @@ void AudioDac::WriteRegister(Register reg, uint8_t val) {
transaction.Execute();
}
uint8_t AudioDac::ReadRegister(Register reg) {
uint8_t AudioDac::ReadRegisterRaw(uint8_t reg) {
uint8_t result = 0;
I2CTransaction transaction;
transaction.start()

@ -20,6 +20,91 @@
namespace drivers {
namespace pcm512x {
class Register {
public:
uint8_t page;
uint8_t reg;
constexpr Register(uint8_t page, uint8_t reg) : page(page), reg(reg) {}
};
constexpr Register RESET(0, 1);
constexpr Register POWER(0, 2);
constexpr Register MUTE(0, 3);
constexpr Register PLL_EN(0, 4);
constexpr Register SPI_MISO_FUNCTION(0, 6);
constexpr Register DSP(0, 7);
constexpr Register GPIO_EN(0, 8);
constexpr Register BCLK_LRCLK_CFG(0, 9);
constexpr Register DSP_GPIO_INPUT(0, 10);
constexpr Register MASTER_MODE(0, 12);
constexpr Register PLL_REF(0, 13);
constexpr Register DAC_REF(0, 14);
constexpr Register GPIO_DACIN(0, 16);
constexpr Register GPIO_PLLIN(0, 18);
constexpr Register SYNCHRONIZE(0, 19);
constexpr Register PLL_COEFF_0(0, 20);
constexpr Register PLL_COEFF_1(0, 21);
constexpr Register PLL_COEFF_2(0, 22);
constexpr Register PLL_COEFF_3(0, 23);
constexpr Register PLL_COEFF_4(0, 24);
constexpr Register DSP_CLKDIV(0, 27);
constexpr Register DAC_CLKDIV(0, 28);
constexpr Register NCP_CLKDIV(0, 29);
constexpr Register OSR_CLKDIV(0, 30);
constexpr Register MASTER_CLKDIV_1(0, 32);
constexpr Register MASTER_CLKDIV_2(0, 33);
constexpr Register FS_SPEED_MODE(0, 34);
constexpr Register IDAC_1(0, 35);
constexpr Register IDAC_2(0, 36);
constexpr Register ERROR_DETECT(0, 37);
constexpr Register I2S_1(0, 40);
constexpr Register I2S_2(0, 41);
constexpr Register DAC_ROUTING(0, 42);
constexpr Register DSP_PROGRAM(0, 43);
constexpr Register CLKDET(0, 44);
constexpr Register AUTO_MUTE(0, 59);
constexpr Register DIGITAL_VOLUME_1(0, 60);
constexpr Register DIGITAL_VOLUME_2(0, 61);
constexpr Register DIGITAL_VOLUME_3(0, 62);
constexpr Register DIGITAL_MUTE_1(0, 63);
constexpr Register DIGITAL_MUTE_2(0, 64);
constexpr Register DIGITAL_MUTE_3(0, 65);
constexpr Register GPIO_OUTPUT_1(0, 80);
constexpr Register GPIO_OUTPUT_2(0, 81);
constexpr Register GPIO_OUTPUT_3(0, 82);
constexpr Register GPIO_OUTPUT_4(0, 83);
constexpr Register GPIO_OUTPUT_5(0, 84);
constexpr Register GPIO_OUTPUT_6(0, 85);
constexpr Register GPIO_CONTROL_1(0, 86);
constexpr Register GPIO_CONTROL_2(0, 87);
constexpr Register OVERFLOW(0, 90);
constexpr Register RATE_DET_1(0, 91);
constexpr Register RATE_DET_2(0, 92);
constexpr Register RATE_DET_3(0, 93);
constexpr Register RATE_DET_4(0, 94);
constexpr Register CLOCK_STATUS(0, 95);
constexpr Register ANALOG_MUTE_DET(0, 108);
constexpr Register POWER_STATE(0, 118);
constexpr Register GPIN(0, 119);
constexpr Register DIGITAL_MUTE_DET(0, 120);
constexpr Register OUTPUT_AMPLITUDE(1, 1);
constexpr Register ANALOG_GAIN_CTRL(1, 2);
constexpr Register UNDERVOLTAGE_PROT(1, 5);
constexpr Register ANALOG_MUTE_CTRL(1, 6);
constexpr Register ANALOG_GAIN_BOOST(1, 7);
constexpr Register VCOM_CTRL_1(1, 8);
constexpr Register VCOM_CTRL_2(1, 9);
constexpr Register CRAM_CTRL(44, 1);
constexpr Register FLEX_A(253, 63);
constexpr Register FLEX_B(253, 64);
} // namespace pcm512x
/**
* Interface for a PCM5122PWR DAC, configured over I2C.
*/
@ -84,6 +169,7 @@ class AudioDac {
GpioExpander* gpio_;
i2s_chan_handle_t i2s_handle_;
bool i2s_active_;
std::optional<uint8_t> active_page_;
i2s_std_clk_config_t clock_config_;
i2s_std_slot_config_t slot_config_;
@ -94,33 +180,12 @@ class AudioDac {
*/
bool WaitForPowerState(std::function<bool(bool, PowerState)> predicate);
enum Register {
PAGE_SELECT = 0,
RESET = 1,
POWER_MODE = 2,
PLL_ENABLE = 4,
DE_EMPHASIS = 7,
PLL_CLOCK_SOURCE = 13,
DAC_CLOCK_SOURCE = 14,
RESYNC_REQUEST = 19,
CLOCK_ERRORS = 37,
INTERPOLATION = 34,
I2S_FORMAT = 40,
DIGITAL_VOLUME_L = 61,
DIGITAL_VOLUME_R = 62,
SAMPLE_RATE_DETECTION = 91,
BCK_DETECTION = 93,
CLOCK_ERROR_STATE = 94,
CLOCK_STATUS = 95,
AUTO_MUTE_STATE = 108,
SOFT_MUTE_STATE = 114,
SAMPLE_RATE_STATE = 115,
DSP_BOOT_POWER_STATE = 118,
};
void WriteRegister(pcm512x::Register r, uint8_t val);
uint8_t ReadRegister(pcm512x::Register r);
void WriteRegister(Register reg, uint8_t val);
uint8_t ReadRegister(Register reg);
void SelectPage(uint8_t page);
void WriteRegisterRaw(uint8_t reg, uint8_t val);
uint8_t ReadRegisterRaw(uint8_t reg);
};
} // namespace drivers

Loading…
Cancel
Save