From c84bee1df03c5a06aa3dc62b0401f557bd10fe71 Mon Sep 17 00:00:00 2001 From: codingkarthik94 Date: Mon, 7 Jul 2025 19:26:55 +0530 Subject: [PATCH 1/8] version 0.0.1 Changelogs -Added: sine-wave geenerator under testing --- .../sinewave-generator/sinewave-generator.ino | 76 + src/Driver.h | 2961 +++++++++-------- src/Driver/nau8325/PCBCUPID_NAU8325.cpp | 615 ++++ src/Driver/nau8325/PCBCUPID_NAU8325.h | 473 +++ 4 files changed, 2817 insertions(+), 1308 deletions(-) create mode 100644 examples/custom/custom-nau8325/sinewave-generator/sinewave-generator.ino create mode 100644 src/Driver/nau8325/PCBCUPID_NAU8325.cpp create mode 100644 src/Driver/nau8325/PCBCUPID_NAU8325.h diff --git a/examples/custom/custom-nau8325/sinewave-generator/sinewave-generator.ino b/examples/custom/custom-nau8325/sinewave-generator/sinewave-generator.ino new file mode 100644 index 0000000..6fc7f0a --- /dev/null +++ b/examples/custom/custom-nau8325/sinewave-generator/sinewave-generator.ino @@ -0,0 +1,76 @@ +#include +#include +#include +#include +#include + +#include // https://github.com/pschatzmann/arduino-audio-tools +#include "Driver.h" // Your driver includes AudioDriverNAU8325Class + +// I2C settings for your ESP32 + NAU8325 +#define SDAPIN 4 +#define SCLPIN 5 +#define I2CSPEED 100000 +#define NAU8325ADDR 0x21 // or 0x10 depending on your board + +// I2S pin configuration for NAU8325 +#define MCLKPIN 22 +#define BCLKPIN 25 +#define WSPIN 24 +#define DOPIN 23 +#define DIPIN -1 // NAU8325 does not use input + +// Audio stream info +AudioInfo audio_info(44100, 2, 16); // Sample rate, channels, bit depth +SineWaveGenerator sine_wave(30000); // Amplitude +GeneratedSoundStream sound_stream(sine_wave);// Generates audio +DriverPins my_pins; // I2C/I2S pin setup + +// Use your custom NAU8325 class +audio_driver::AudioDriverNAU8325Class nau_driver; +AudioBoard audio_board(nau_driver, my_pins); + +// I2S codec stream (output to NAU8325) +I2SCodecStream sound_stream(audio_board); +StreamCopy copier(i2s_out_stream, sound_stream); // Copy sine → I2S + +// Optional custom I2C bus instance (if needed) +TwoWire myWire = TwoWire(0); + +void setup() { + Serial.begin(115200); + AudioLogger::instance().begin(Serial, AudioLogger::Info); + AudioDriverLogger.begin(Serial, AudioDriverLogLevel::Info); + + delay(1000); + Serial.println("Starting NAU8325 AudioBoard Setup..."); + + // Set I2C Pins for NAU8325 + my_pins.addI2C(PinFunction::CODEC, SCLPIN, SDAPIN, NAU8325ADDR, I2CSPEED, myWire); + + // Set I2S Pins + my_pins.addI2S(PinFunction::CODEC, MCLKPIN, BCLKPIN, WSPIN, DOPIN, DIPIN); + + // Apply pin settings + my_pins.begin(); + + // Init board + codec + if (!audio_board.begin()) { + Serial.println(" NAU8325 board begin failed"); + while (1); + } + + // Setup I2S codec stream + auto i2s_config = sound_stream.defaultConfig(); + i2s_config.copyFrom(audio_info); + sound_stream.begin(i2s_config); + + // Begin sine wave at 440 Hz + sine_wave.begin(audio_info, N_A4); + + Serial.println("Setup complete. Sine wave playing..."); +} + +void loop() { + copier.copy(); // Continuously stream sine wave to NAU8325 +} diff --git a/src/Driver.h b/src/Driver.h index aa24dab..6cc6efb 100644 --- a/src/Driver.h +++ b/src/Driver.h @@ -16,49 +16,57 @@ #include "Driver/wm8960/mtb_wm8960.h" #include "Driver/wm8978/WM8978.h" #include "Driver/wm8994/wm8994.h" +#include "Driver/nau8325/PCBCUPID_NAU8325.h" + #include "DriverCommon.h" #include "DriverPins.h" -namespace audio_driver { - -const int rate_num[14] = {8000, 11025, 16000, 22050, 24000, 32000, 44100, - 48000, 64000, 88200, 96000, 128000, 176400, 192000}; -const samplerate_t rate_code[14] = { - RATE_8K, RATE_11K, RATE_16K, RATE_22K, RATE_24K, RATE_32K, RATE_44K, - RATE_48K, RATE_64K, RATE_88K, RATE_96K, RATE_128K, RATE_176K, RATE_192K}; - -/** - * @brief I2S configuration and definition of input and output with default - * values - * @ingroup audio_driver - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class CodecConfig : public codec_config_t { - public: - /// @brief setup default values - CodecConfig() { - input_device = ADC_INPUT_LINE1; - output_device = DAC_OUTPUT_ALL; - i2s.bits = BIT_LENGTH_16BITS; - i2s.rate = RATE_44K; - i2s.channels = CHANNELS2; - i2s.fmt = I2S_NORMAL; - // codec is slave - microcontroller is master - i2s.mode = MODE_SLAVE; - } - - /// Compare all attributes but ignore sample rate - bool equalsExRate(CodecConfig alt) { - return (input_device == alt.input_device && - output_device == alt.output_device && i2s.bits == alt.i2s.bits && - i2s.channels == alt.i2s.channels && i2s.fmt == alt.i2s.fmt && - i2s.mode == alt.i2s.mode); - } - - /// Returns bits per sample as number - int getBitsNumeric() { - switch (i2s.bits) { +namespace audio_driver +{ + + const int rate_num[14] = {8000, 11025, 16000, 22050, 24000, 32000, 44100, + 48000, 64000, 88200, 96000, 128000, 176400, 192000}; + const samplerate_t rate_code[14] = { + RATE_8K, RATE_11K, RATE_16K, RATE_22K, RATE_24K, RATE_32K, RATE_44K, + RATE_48K, RATE_64K, RATE_88K, RATE_96K, RATE_128K, RATE_176K, RATE_192K}; + + /** + * @brief I2S configuration and definition of input and output with default + * values + * @ingroup audio_driver + * @author Phil Schatzmann + * @copyright GPLv3 + */ + class CodecConfig : public codec_config_t + { + public: + /// @brief setup default values + CodecConfig() + { + input_device = ADC_INPUT_LINE1; + output_device = DAC_OUTPUT_ALL; + i2s.bits = BIT_LENGTH_16BITS; + i2s.rate = RATE_44K; + i2s.channels = CHANNELS2; + i2s.fmt = I2S_NORMAL; + // codec is slave - microcontroller is master + i2s.mode = MODE_SLAVE; + } + + /// Compare all attributes but ignore sample rate + bool equalsExRate(CodecConfig alt) + { + return (input_device == alt.input_device && + output_device == alt.output_device && i2s.bits == alt.i2s.bits && + i2s.channels == alt.i2s.channels && i2s.fmt == alt.i2s.fmt && + i2s.mode == alt.i2s.mode); + } + + /// Returns bits per sample as number + int getBitsNumeric() + { + switch (i2s.bits) + { case BIT_LENGTH_16BITS: return 16; case BIT_LENGTH_24BITS: @@ -67,13 +75,15 @@ class CodecConfig : public codec_config_t { return 32; default: return 0; + } + return 0; } - return 0; - } - /// Sets the bits per sample with a numeric value - bool setBitsNumeric(int bits) { - switch (bits) { + /// Sets the bits per sample with a numeric value + bool setBitsNumeric(int bits) + { + switch (bits) + { case 16: i2s.bits = BIT_LENGTH_16BITS; return true; @@ -89,28 +99,33 @@ class CodecConfig : public codec_config_t { case 32: i2s.bits = BIT_LENGTH_32BITS; return true; + } + AD_LOGE("bits not supported: %d", bits); + return false; } - AD_LOGE("bits not supported: %d", bits); - return false; - } - /// Get the sample rate as number - int getRateNumeric() { - for (int j = 0; j < 14; j++) { - if (rate_code[j] == i2s.rate) { - AD_LOGD("-> %d", rate_num[j]); - return rate_num[j]; + /// Get the sample rate as number + int getRateNumeric() + { + for (int j = 0; j < 14; j++) + { + if (rate_code[j] == i2s.rate) + { + AD_LOGD("-> %d", rate_num[j]); + return rate_num[j]; + } } + return 0; } - return 0; - } - /// Returns the number of channels as number - int getChannelsNumeric() { return i2s.channels; } + /// Returns the number of channels as number + int getChannelsNumeric() { return i2s.channels; } - /// Defines the number of channels - bool setChannelsNumeric(int channels) { - switch (2) { + /// Defines the number of channels + bool setChannelsNumeric(int channels) + { + switch (2) + { case CHANNELS2: i2s.channels = (channels_t)channels; return true; @@ -127,353 +142,414 @@ class CodecConfig : public codec_config_t { AD_LOGE("Channels not supported: %d - using %d", channels, 2); i2s.channels = CHANNELS2; return false; + } } - } - - /// Sets the sample rate as number: returns the effectively set rate - int setRateNumeric(int requestedRate) { - int diff = 99999; - int result = 0; - for (int j = 0; j < 14; j++) { - if (rate_num[j] == requestedRate) { - AD_LOGD("-> %d", rate_num[j]); - i2s.rate = rate_code[j]; - return requestedRate; - } else { - int new_diff = abs(rate_code[j] - requestedRate); - if (new_diff < diff) { - result = j; - diff = new_diff; + + /// Sets the sample rate as number: returns the effectively set rate + int setRateNumeric(int requestedRate) + { + int diff = 99999; + int result = 0; + for (int j = 0; j < 14; j++) + { + if (rate_num[j] == requestedRate) + { + AD_LOGD("-> %d", rate_num[j]); + i2s.rate = rate_code[j]; + return requestedRate; + } + else + { + int new_diff = abs(rate_code[j] - requestedRate); + if (new_diff < diff) + { + result = j; + diff = new_diff; + } } } + AD_LOGE("Sample Rate not supported: %d - using %d", requestedRate, + rate_num[result]); + i2s.rate = rate_code[result]; + return rate_num[result]; } - AD_LOGE("Sample Rate not supported: %d - using %d", requestedRate, - rate_num[result]); - i2s.rate = rate_code[result]; - return rate_num[result]; - } - /// Determines the codec_mode_t dynamically based on the input and output - codec_mode_t get_mode() { - // codec_mode_t mode; - bool is_input = false; - bool is_output = false; + /// Determines the codec_mode_t dynamically based on the input and output + codec_mode_t get_mode() + { + // codec_mode_t mode; + bool is_input = false; + bool is_output = false; - if (input_device != ADC_INPUT_NONE) is_input = true; + if (input_device != ADC_INPUT_NONE) + is_input = true; - if (output_device != DAC_OUTPUT_NONE) is_output = true; + if (output_device != DAC_OUTPUT_NONE) + is_output = true; - if (is_input && is_output) { - AD_LOGD("mode->CODEC_MODE_BOTH"); - return CODEC_MODE_BOTH; - } + if (is_input && is_output) + { + AD_LOGD("mode->CODEC_MODE_BOTH"); + return CODEC_MODE_BOTH; + } - if (is_output) { - AD_LOGD("mode->CODEC_MODE_DECODE"); - return CODEC_MODE_DECODE; - } + if (is_output) + { + AD_LOGD("mode->CODEC_MODE_DECODE"); + return CODEC_MODE_DECODE; + } + + if (is_input) + { + AD_LOGD("mode->CODEC_MODE_ENCODE"); + return CODEC_MODE_ENCODE; + } - if (is_input) { - AD_LOGD("mode->CODEC_MODE_ENCODE"); - return CODEC_MODE_ENCODE; + AD_LOGD("mode->CODEC_MODE_NONE"); + return CODEC_MODE_NONE; } + // if sd active we setup SPI for the SD + bool sd_active = true; + }; - AD_LOGD("mode->CODEC_MODE_NONE"); - return CODEC_MODE_NONE; - } - // if sd active we setup SPI for the SD - bool sd_active = true; -}; + /** + * @brief Abstract Driver API for codec chips + * @ingroup audio_driver + * @author Phil Schatzmann + * @copyright GPLv3 + */ + class AudioDriver + { + public: + /// Starts the processing + virtual bool begin(CodecConfig codecCfg, DriverPins &pins) + { + AD_LOGI("AudioDriver::begin"); + p_pins = &pins; -/** - * @brief Abstract Driver API for codec chips - * @ingroup audio_driver - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class AudioDriver { - public: - /// Starts the processing - virtual bool begin(CodecConfig codecCfg, DriverPins &pins) { - AD_LOGI("AudioDriver::begin"); - p_pins = &pins; + // Store default i2c address to pins + setupI2CAddress(); - // Store default i2c address to pins - setupI2CAddress(); + p_pins->setSPIActiveForSD(codec_cfg.sd_active); + if (!p_pins->begin()) + { + AD_LOGE("AudioBoard::pins::begin failed"); + return false; + } - p_pins->setSPIActiveForSD(codec_cfg.sd_active); - if (!p_pins->begin()) { - AD_LOGE("AudioBoard::pins::begin failed"); + if (!setConfig(codecCfg)) + { + AD_LOGE("setConfig has failed"); + return false; + } + setPAPower(true); + // setup default volume + setVolume(DRIVER_DEFAULT_VOLUME); + return true; + } + /// changes the configuration + virtual bool setConfig(CodecConfig codecCfg) + { + AD_LOGI("AudioDriver::setConfig"); + codec_cfg = codecCfg; + if (!init(codec_cfg)) + { + AD_LOGE("AudioDriver init failed"); + return false; + } + codec_mode_t codec_mode = codec_cfg.get_mode(); + if (!controlState(codec_mode)) + { + AD_LOGE("AudioDriver controlState failed"); + return false; + } + bool result = configInterface(codec_mode, codec_cfg.i2s); + if (!result) + { + AD_LOGE("AudioDriver configInterface failed"); + return false; + } + return result; + } + /// Ends the processing: shut down dac and adc + virtual bool end(void) { return deinit(); } + /// Mutes all output lines + virtual bool setMute(bool enable) = 0; + /// Mute individual lines: only supported for some rare DACs + virtual bool setMute(bool mute, int line) + { + AD_LOGE("setMute not supported on line level"); return false; } - if (!setConfig(codecCfg)) { - AD_LOGE("setConfig has failed"); - return false; + /// Defines the Volume (in %) if volume is 0, mute is enabled,range is 0-100. + virtual bool setVolume(int volume) = 0; + /// Determines the actual volume (range: 0-100) + virtual int getVolume() = 0; + /// Defines the input volume (range: 0-100) if supported + virtual bool setInputVolume(int volume) { return false; } + /// Determines if setVolume() is suppored + virtual bool isVolumeSupported() { return true; } + /// Determines if setInputVolume() is supported + virtual bool isInputVolumeSupported() { return false; } + /// Provides the pin information + virtual DriverPins &pins() { return *p_pins; } + + void setPins(DriverPins &pins) { p_pins = &pins; } + + /// Sets the PA Power pin to active or inactive + bool setPAPower(bool enable) + { + if (p_pins == nullptr) + { + AD_LOGI("pins are null"); + return false; + } + GpioPin pin = pins().getPinID(PinFunction::PA); + if (pin == -1) + { + AD_LOGI("PinFunction::PA not defined"); + return false; + } + AD_LOGI("setPAPower pin %d -> %d", pin, enable); + digitalWrite(pin, enable ? HIGH : LOW); + return true; } - setPAPower(true); - // setup default volume - setVolume(DRIVER_DEFAULT_VOLUME); - return true; - } - /// changes the configuration - virtual bool setConfig(CodecConfig codecCfg) { - AD_LOGI("AudioDriver::setConfig"); - codec_cfg = codecCfg; - if (!init(codec_cfg)) { - AD_LOGE("AudioDriver init failed"); + + operator bool() { return p_pins != nullptr; } + + /// Defines the i2c address + virtual bool setI2CAddress(uint16_t adr) + { + if (p_pins == nullptr) + return false; + // look it up from the pin definition + auto i2c = pins().getI2CPins(PinFunction::CODEC); + if (i2c) + { + AD_LOGI("==> Updating address: 0x%x", adr); + PinsI2C val = i2c.value(); + val.address = adr; + pins().setI2C(val); + return true; + } + else + { + // we must have a codec defined! + assert(false); + } return false; } - codec_mode_t codec_mode = codec_cfg.get_mode(); - if (!controlState(codec_mode)) { - AD_LOGE("AudioDriver controlState failed"); - return false; + + /// Provides the i2c address + virtual int getI2CAddress() + { + if (p_pins == nullptr) + return i2c_default_address; + // look it up from the pin definition + auto i2c = pins().getI2CPins(PinFunction::CODEC); + if (i2c) + { + auto value = i2c.value(); + auto addr = value.address; + if (addr > -1) + return addr; + } + return i2c_default_address; } - bool result = configInterface(codec_mode, codec_cfg.i2s); - if (!result) { - AD_LOGE("AudioDriver configInterface failed"); - return false; + + /// If no address is defined in the pins we provide it here + void setupI2CAddress() + { + AD_LOGI("setupI2CAddress: 0x%x", i2c_default_address); + int adr = getI2CAddress(); + setI2CAddress(adr); } - return result; - } - /// Ends the processing: shut down dac and adc - virtual bool end(void) { return deinit(); } - /// Mutes all output lines - virtual bool setMute(bool enable) = 0; - /// Mute individual lines: only supported for some rare DACs - virtual bool setMute(bool mute, int line) { - AD_LOGE("setMute not supported on line level"); - return false; - } - - /// Defines the Volume (in %) if volume is 0, mute is enabled,range is 0-100. - virtual bool setVolume(int volume) = 0; - /// Determines the actual volume (range: 0-100) - virtual int getVolume() = 0; - /// Defines the input volume (range: 0-100) if supported - virtual bool setInputVolume(int volume) { return false; } - /// Determines if setVolume() is suppored - virtual bool isVolumeSupported() { return true; } - /// Determines if setInputVolume() is supported - virtual bool isInputVolumeSupported() { return false; } - /// Provides the pin information - virtual DriverPins &pins() { return *p_pins; } - - void setPins(DriverPins &pins) { p_pins = &pins; } - - /// Sets the PA Power pin to active or inactive - bool setPAPower(bool enable) { - if (p_pins == nullptr) { - AD_LOGI("pins are null"); - return false; + + protected: + CodecConfig codec_cfg; + DriverPins *p_pins = nullptr; + int i2c_default_address = -1; + + int mapVolume(int x, int in_min, int in_max, int out_min, int out_max) + { + return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; + } + + /// Determine the TwoWire object from the I2C config or use Wire + virtual i2c_bus_handle_t getI2C() + { + if (p_pins == nullptr) + return DEFAULT_WIRE; + auto i2c = pins().getI2CPins(PinFunction::CODEC); + if (!i2c) + { + return DEFAULT_WIRE; + } + i2c_bus_handle_t *result = (i2c_bus_handle_t *)i2c.value().p_wire; + return result; } - GpioPin pin = pins().getPinID(PinFunction::PA); - if (pin == -1) { - AD_LOGI("PinFunction::PA not defined"); + + virtual bool init(codec_config_t codec_cfg) { return false; } + virtual bool deinit() { return false; } + virtual bool controlState(codec_mode_t mode) { return false; }; + virtual bool configInterface(codec_mode_t mode, I2SDefinition iface) + { return false; + }; + + /// make sure that value is in range + /// @param volume + /// @return + int limitValue(int volume, int min = 0, int max = 100) + { + if (volume > max) + volume = max; + if (volume < min) + volume = min; + return volume; } - AD_LOGI("setPAPower pin %d -> %d", pin, enable); - digitalWrite(pin, enable ? HIGH : LOW); - return true; - } - - operator bool() { return p_pins != nullptr; } - - /// Defines the i2c address - virtual bool setI2CAddress(uint16_t adr) { - if (p_pins == nullptr) return false; - // look it up from the pin definition - auto i2c = pins().getI2CPins(PinFunction::CODEC); - if (i2c) { - AD_LOGI("==> Updating address: 0x%x", adr); - PinsI2C val = i2c.value(); - val.address = adr; - pins().setI2C(val); - return true; - } else { - // we must have a codec defined! - assert(false); - } - return false; - } - - /// Provides the i2c address - virtual int getI2CAddress() { - if (p_pins == nullptr) return i2c_default_address; - // look it up from the pin definition - auto i2c = pins().getI2CPins(PinFunction::CODEC); - if (i2c) { - auto value = i2c.value(); - auto addr = value.address; - if (addr > -1) return addr; - } - return i2c_default_address; - } - - /// If no address is defined in the pins we provide it here - void setupI2CAddress() { - AD_LOGI("setupI2CAddress: 0x%x", i2c_default_address); - int adr = getI2CAddress(); - setI2CAddress(adr); - } - - protected: - CodecConfig codec_cfg; - DriverPins *p_pins = nullptr; - int i2c_default_address = -1; - - int mapVolume(int x, int in_min, int in_max, int out_min, int out_max) { - return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; - } - - /// Determine the TwoWire object from the I2C config or use Wire - virtual i2c_bus_handle_t getI2C() { - if (p_pins == nullptr) return DEFAULT_WIRE; - auto i2c = pins().getI2CPins(PinFunction::CODEC); - if (!i2c) { - return DEFAULT_WIRE; - } - i2c_bus_handle_t *result = (i2c_bus_handle_t *)i2c.value().p_wire; - return result; - } - - virtual bool init(codec_config_t codec_cfg) { return false; } - virtual bool deinit() { return false; } - virtual bool controlState(codec_mode_t mode) { return false; }; - virtual bool configInterface(codec_mode_t mode, I2SDefinition iface) { - return false; }; - /// make sure that value is in range - /// @param volume - /// @return - int limitValue(int volume, int min = 0, int max = 100) { - if (volume > max) volume = max; - if (volume < min) volume = min; - return volume; - } -}; - -/** - * @brief Dummy Driver which does nothing. - * @ingroup audio_driver - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class NoDriverClass : public AudioDriver { - public: - virtual bool begin(CodecConfig codecCfg, DriverPins &pins) { - // codec_cfg = codecCfg; - // p_pins = &pins; - return true; - } - virtual bool end(void) { return true; } - virtual bool setMute(bool enable) { return false; } - virtual bool setVolume(int volume) { return false; } - virtual int getVolume() { return 100; } - virtual bool setInputVolume(int volume) { return false; } - virtual bool isVolumeSupported() { + /** + * @brief Dummy Driver which does nothing. + * @ingroup audio_driver + * @author Phil Schatzmann + * @copyright GPLv3 + */ + class NoDriverClass : public AudioDriver + { + public: + virtual bool begin(CodecConfig codecCfg, DriverPins &pins) { - return false; + // codec_cfg = codecCfg; + // p_pins = &pins; + return true; + } + virtual bool end(void) { return true; } + virtual bool setMute(bool enable) { return false; } + virtual bool setVolume(int volume) { return false; } + virtual int getVolume() { return 100; } + virtual bool setInputVolume(int volume) { return false; } + virtual bool isVolumeSupported() + { + { + return false; + } } - } - virtual bool isInputVolumeSupported() { return false; } -}; - -/** - * @brief Driver API for AC101 codec chip - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class AudioDriverAC101Class : public AudioDriver { - public: - AudioDriverAC101Class() { i2c_default_address = 0x1A; } - bool setMute(bool mute) { return ac101_set_voice_mute(mute); } - bool setVolume(int volume) { - return ac101_set_voice_volume(limitValue(volume, 0, 100)); + virtual bool isInputVolumeSupported() { return false; } }; - int getVolume() { - int vol; - ac101_get_voice_volume(&vol); - return vol; + + /** + * @brief Driver API for AC101 codec chip + * @author Phil Schatzmann + * @copyright GPLv3 + */ + class AudioDriverAC101Class : public AudioDriver + { + public: + AudioDriverAC101Class() { i2c_default_address = 0x1A; } + bool setMute(bool mute) { return ac101_set_voice_mute(mute); } + bool setVolume(int volume) + { + return ac101_set_voice_volume(limitValue(volume, 0, 100)); + }; + int getVolume() + { + int vol; + ac101_get_voice_volume(&vol); + return vol; + }; + + protected: + bool init(codec_config_t codec_cfg) + { + return ac101_init(&codec_cfg, getI2C(), getI2CAddress()) == RESULT_OK; + } + bool deinit() { return ac101_deinit() == RESULT_OK; } + bool controlState(codec_mode_t mode) + { + return ac101_ctrl_state_active(mode, true) == RESULT_OK; + } + bool configInterface(codec_mode_t mode, I2SDefinition iface) + { + return ac101_config_i2s(mode, &iface) == RESULT_OK; + } }; - protected: - bool init(codec_config_t codec_cfg) { - return ac101_init(&codec_cfg, getI2C(), getI2CAddress()) == RESULT_OK; - } - bool deinit() { return ac101_deinit() == RESULT_OK; } - bool controlState(codec_mode_t mode) { - return ac101_ctrl_state_active(mode, true) == RESULT_OK; - } - bool configInterface(codec_mode_t mode, I2SDefinition iface) { - return ac101_config_i2s(mode, &iface) == RESULT_OK; - } -}; - -/** - * @brief Driver API for the CS43l22 codec chip on 0x94 (0x4A<<1) - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class AudioDriverCS43l22Class : public AudioDriver { - public: - AudioDriverCS43l22Class(uint16_t deviceAddr = 0x4A) { - i2c_default_address = deviceAddr; - } - - virtual bool begin(CodecConfig codecCfg, DriverPins &pins) { - AD_LOGD("AudioDriverCS43l22Class::begin"); - p_pins = &pins; - codec_cfg = codecCfg; - // manage reset pin -> acive high - setPAPower(true); - // Setup enable pin for codec - delay(100); - uint32_t freq = getFrequency(codec_cfg.i2s.rate); - uint16_t outputDevice = getOutput(codec_cfg.output_device); - AD_LOGD("cs43l22_Init"); - bool result = - cs43l22_Init(deviceAddr, outputDevice, volume, freq, getI2C()) == 0; - if (!result) { - AD_LOGE("error: cs43l22_Init"); - } - cs43l22_Play(deviceAddr, nullptr, 0); - return result; - } - - virtual bool setConfig(CodecConfig codecCfg) { - codec_cfg = codecCfg; - uint32_t freq = getFrequency(codec_cfg.i2s.rate); - uint16_t outputDevice = getOutput(codec_cfg.output_device); - return cs43l22_Init(deviceAddr, outputDevice, this->volume, freq, - getI2C()) == 0; - } - - bool setMute(bool mute) { - uint32_t rc = mute ? cs43l22_Pause(deviceAddr) : cs43l22_Resume(deviceAddr); - return rc == 0; - } - - bool setVolume(int volume) { - this->volume = volume; - return cs43l22_SetVolume(deviceAddr, volume) == 0; - } - int getVolume() { return volume; } - - protected: - uint16_t deviceAddr; - int volume = 100; - - bool deinit() { - int cnt = cs43l22_Stop(deviceAddr, AUDIO_MUTE_ON); - cnt += cs43l22_Reset(deviceAddr); - setPAPower(false); - return cnt == 0; - } - - uint32_t getFrequency(samplerate_t rateNum) { - switch (rateNum) { + /** + * @brief Driver API for the CS43l22 codec chip on 0x94 (0x4A<<1) + * @author Phil Schatzmann + * @copyright GPLv3 + */ + class AudioDriverCS43l22Class : public AudioDriver + { + public: + AudioDriverCS43l22Class(uint16_t deviceAddr = 0x4A) + { + i2c_default_address = deviceAddr; + } + + virtual bool begin(CodecConfig codecCfg, DriverPins &pins) + { + AD_LOGD("AudioDriverCS43l22Class::begin"); + p_pins = &pins; + codec_cfg = codecCfg; + // manage reset pin -> acive high + setPAPower(true); + // Setup enable pin for codec + delay(100); + uint32_t freq = getFrequency(codec_cfg.i2s.rate); + uint16_t outputDevice = getOutput(codec_cfg.output_device); + AD_LOGD("cs43l22_Init"); + bool result = + cs43l22_Init(deviceAddr, outputDevice, volume, freq, getI2C()) == 0; + if (!result) + { + AD_LOGE("error: cs43l22_Init"); + } + cs43l22_Play(deviceAddr, nullptr, 0); + return result; + } + + virtual bool setConfig(CodecConfig codecCfg) + { + codec_cfg = codecCfg; + uint32_t freq = getFrequency(codec_cfg.i2s.rate); + uint16_t outputDevice = getOutput(codec_cfg.output_device); + return cs43l22_Init(deviceAddr, outputDevice, this->volume, freq, + getI2C()) == 0; + } + + bool setMute(bool mute) + { + uint32_t rc = mute ? cs43l22_Pause(deviceAddr) : cs43l22_Resume(deviceAddr); + return rc == 0; + } + + bool setVolume(int volume) + { + this->volume = volume; + return cs43l22_SetVolume(deviceAddr, volume) == 0; + } + int getVolume() { return volume; } + + protected: + uint16_t deviceAddr; + int volume = 100; + + bool deinit() + { + int cnt = cs43l22_Stop(deviceAddr, AUDIO_MUTE_ON); + cnt += cs43l22_Reset(deviceAddr); + setPAPower(false); + return cnt == 0; + } + + uint32_t getFrequency(samplerate_t rateNum) + { + switch (rateNum) + { case RATE_8K: return 8000; /*!< set to 8k samples per second */ case RATE_11K: @@ -492,12 +568,14 @@ class AudioDriverCS43l22Class : public AudioDriver { return 48000; /*!< set to 48k samples per second */ default: break; + } + return 44100; } - return 44100; - } - uint16_t getOutput(output_device_t output_device) { - switch (output_device) { + uint16_t getOutput(output_device_t output_device) + { + switch (output_device) + { case DAC_OUTPUT_NONE: return 0; case DAC_OUTPUT_LINE1: @@ -508,512 +586,596 @@ class AudioDriverCS43l22Class : public AudioDriver { return OUTPUT_DEVICE_BOTH; default: break; + } + return OUTPUT_DEVICE_BOTH; } - return OUTPUT_DEVICE_BOTH; - } -}; - -/** - * @brief Driver API for CS42448 TDS DAC/ADC - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class AudioDriverCS42448Class : public AudioDriver { - public: - AudioDriverCS42448Class() { i2c_default_address = 0x48; } - bool begin(CodecConfig codecCfg, DriverPins &pins) override { - cfg = codecCfg; - // setup pins - pins.begin(); - // setup cs42448 - cs42448.begin(cfg, getI2C(), getI2CAddress()); - cs42448.setMute(false); - return true; - } - virtual bool setConfig(CodecConfig codecCfg) { - bool result = true; - if (codecCfg.equalsExRate(cfg)) { - // just update the rate - if (cfg.i2s.rate != codecCfg.getRateNumeric()) { - cs42448.setMute(true); - cs42448.setSampleRate(codecCfg.getRateNumeric()); - cs42448.setMute(false); + }; + + /** + * @brief Driver API for CS42448 TDS DAC/ADC + * @author Phil Schatzmann + * @copyright GPLv3 + */ + class AudioDriverCS42448Class : public AudioDriver + { + public: + AudioDriverCS42448Class() { i2c_default_address = 0x48; } + bool begin(CodecConfig codecCfg, DriverPins &pins) override + { + cfg = codecCfg; + // setup pins + pins.begin(); + // setup cs42448 + cs42448.begin(cfg, getI2C(), getI2CAddress()); + cs42448.setMute(false); + return true; + } + virtual bool setConfig(CodecConfig codecCfg) + { + bool result = true; + if (codecCfg.equalsExRate(cfg)) + { + // just update the rate + if (cfg.i2s.rate != codecCfg.getRateNumeric()) + { + cs42448.setMute(true); + cs42448.setSampleRate(codecCfg.getRateNumeric()); + cs42448.setMute(false); + } } - } else { - assert(p_pins != nullptr); - result = begin(codecCfg, *p_pins); - } - return result; - } - bool end(void) override { return cs42448.end(); } - bool setMute(bool enable) override { return cs42448.setMute(enable); } - bool setMute(bool enable, int line) { - return cs42448.setMuteDAC(line, enable); - } - /// Defines the Volume (in %) if volume is 0, mute is enabled,range is 0-100. - bool setVolume(int volume) override { - this->volume = volume; - return cs42448.setVolumeDAC(2.55f * volume); - } - bool setVolume(int dac, int volume) { - return cs42448.setVolumeDAC(dac, 2.55f * volume); - } - - int getVolume() override { return volume; } - bool setInputVolume(int volume) override { - int vol = mapVolume(volume, 0, 100, -128, 127); - return cs42448.setVolumeADC(vol); - } - bool isVolumeSupported() override { return true; } - bool isInputVolumeSupported() override { return true; } - - DriverPins &pins() { return *p_pins; } - CS42448 &driver() { return cs42448; } - - protected: - CS42448 cs42448; - int volume = 100; - CodecConfig cfg; -}; - -/** - * @brief Driver API for ES7210 codec chip. This chip supports only input! - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class AudioDriverES7210Class : public AudioDriver { - public: - AudioDriverES7210Class() { i2c_default_address = ES7210_AD1_AD0_00 >> 1; } - bool setMute(bool mute) { return es7210_set_mute(mute) == RESULT_OK; } - bool setVolume(int volume) { - this->volume = volume; - return es7210_adc_set_volume(limitValue(volume, 0, 100)) == RESULT_OK; - } - int getVolume() { return volume; } - bool setInputVolume(int volume) { return setVolume(volume); } - bool isInputVolumeSupported() { return true; } - - protected: - int volume; - - bool init(codec_config_t codec_cfg) { - return es7210_adc_init(&codec_cfg, getI2C()) == RESULT_OK; - } - bool deinit() { return es7210_adc_deinit() == RESULT_OK; } - - bool controlState(codec_mode_t mode) { - return es7210_adc_ctrl_state_active(mode, true) == RESULT_OK; - } - bool configInterface(codec_mode_t mode, I2SDefinition iface) { - return es7210_adc_config_i2s(mode, &iface) == RESULT_OK; - } -}; - -/** - * @brief Driver API for Lyrat ES7243 codec chip - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class AudioDriverES7243Class : public AudioDriver { - public: - AudioDriverES7243Class() { i2c_default_address = 0x13; } - bool setMute(bool mute) { - return es7243_adc_set_voice_mute(mute) == RESULT_OK; - } - bool setVolume(int volume) { - return es7243_adc_set_voice_volume(limitValue(volume, 0, 100)) == RESULT_OK; - } - int getVolume() { - int vol; - es7243_adc_get_voice_volume(&vol); - return vol; - } - - protected: - bool init(codec_config_t codec_cfg) { - return es7243_adc_init(&codec_cfg, getI2C()) == RESULT_OK; - } - bool deinit() { return es7243_adc_deinit() == RESULT_OK; } - - bool controlState(codec_mode_t mode) { - return es7243_adc_ctrl_state_active(mode, true) == RESULT_OK; - } - bool configInterface(codec_mode_t mode, I2SDefinition iface) { - return es7243_adc_config_i2s(mode, &iface) == RESULT_OK; - } -}; - -/** - * @brief Driver API for ES7243e codec chip - * @author Phil Schatzmann - * @copyright GPLv3 - */ - -class AudioDriverES7243eClass : public AudioDriver { - public: - AudioDriverES7243eClass() { i2c_default_address = 0x13; } - bool setMute(bool mute) { - return mute ? setVolume(0) == RESULT_OK : setVolume(volume) == RESULT_OK; - } - bool setVolume(int volume) { - this->volume = volume; - return es7243e_adc_set_voice_volume(limitValue(volume, 0, 100)) == - RESULT_OK; - } - int getVolume() { - int vol; - es7243e_adc_get_voice_volume(&vol); - return vol; - } - - protected: - int volume = 0; - - bool init(codec_config_t codec_cfg) { - return es7243e_adc_init(&codec_cfg, getI2C()) == RESULT_OK; - } - bool deinit() { return es7243e_adc_deinit() == RESULT_OK; } - - bool controlState(codec_mode_t mode) { - return es7243e_adc_ctrl_state_active(mode, true) == RESULT_OK; - } - bool configInterface(codec_mode_t mode, I2SDefinition iface) { - return es7243e_adc_config_i2s(mode, &iface) == RESULT_OK; - } -}; - -/** - * @brief Driver API for ES8156 codec chip - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class AudioDriverES8156Class : public AudioDriver { - public: - AudioDriverES8156Class() { i2c_default_address = 0x8; } - bool setMute(bool mute) { - return es8156_codec_set_voice_mute(mute) == RESULT_OK; - } - bool setVolume(int volume) { - AD_LOGD("volume %d", volume); - return es8156_codec_set_voice_volume(limitValue(volume, 0, 100)) == - RESULT_OK; - } - int getVolume() { - int vol; - es8156_codec_get_voice_volume(&vol); - return vol; - } - - protected: - bool init(codec_config_t codec_cfg) { - return es8156_codec_init(&codec_cfg, getI2C()) == RESULT_OK; - } - bool deinit() { return es8156_codec_deinit() == RESULT_OK; } - - bool controlState(codec_mode_t mode) { - return es8156_codec_ctrl_state_active(mode, true) == RESULT_OK; - } - bool configInterface(codec_mode_t mode, I2SDefinition iface) { - return es8156_codec_config_i2s(mode, &iface) == RESULT_OK; - } -}; - -/** - * @brief Driver API for Lyrat ES8311 codec chip - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class AudioDriverES8311Class : public AudioDriver { - public: - AudioDriverES8311Class() { i2c_default_address = 0x18; } - bool setMute(bool mute) { return es8311_set_voice_mute(mute) == RESULT_OK; } - bool setVolume(int volume) { - return es8311_codec_set_voice_volume(limitValue(volume, 0, 100)) == - RESULT_OK; - } - int getVolume() { - int vol; - es8311_codec_get_voice_volume(&vol); - return vol; - } - - protected: - bool init(codec_config_t codec_cfg) { - int mclk_src = pins().getPinID(PinFunction::MCLK_SOURCE); - if (mclk_src == -1) { - mclk_src = 0; // = FROM_MCLK_PIN; - } - AD_LOGI("MCLK_SOURCE: %d", mclk_src); - - assert(getI2C() != nullptr); - return es8311_codec_init(&codec_cfg, getI2C(), mclk_src, getI2CAddress()) == - RESULT_OK; - } - bool deinit() { return es8311_codec_deinit() == RESULT_OK; } - - bool controlState(codec_mode_t mode) { - return es8311_codec_ctrl_state_active(mode, true) == RESULT_OK; - } - bool configInterface(codec_mode_t mode, I2SDefinition iface) { - return es8311_codec_config_i2s(mode, &iface) == RESULT_OK; - } -}; - -/** - * @brief Driver API for ES8374 codec chip - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class AudioDriverES8374Class : public AudioDriver { - public: - AudioDriverES8374Class() { i2c_default_address = 0x10; } - bool setMute(bool mute) { return es8374_set_voice_mute(mute) == RESULT_OK; } - bool setVolume(int volume) { - AD_LOGD("volume %d", volume); - return es8374_codec_set_voice_volume(limitValue(volume, 0, 100)) == - RESULT_OK; - } - int getVolume() { - int vol; - es8374_codec_get_voice_volume(&vol); - return vol; - } - - protected: - bool init(codec_config_t codec_cfg) { - auto codec_mode = this->codec_cfg.get_mode(); - return es8374_codec_init(&codec_cfg, codec_mode, getI2C(), - getI2CAddress()) == RESULT_OK; - } - bool deinit() { return es8374_codec_deinit() == RESULT_OK; } - - bool controlState(codec_mode_t mode) { - return es8374_codec_ctrl_state_active(mode, true) == RESULT_OK; - } - bool configInterface(codec_mode_t mode, I2SDefinition iface) { - return es8374_codec_config_i2s(mode, &iface) == RESULT_OK; - } -}; - -/** - * @brief Driver API for ES8388 codec chip - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class AudioDriverES8388Class : public AudioDriver { - public: - AudioDriverES8388Class(int volumeHack) { - i2c_default_address = 0x10; - volume_hack = volumeHack; - } - AudioDriverES8388Class() { i2c_default_address = 0x10; } - bool setMute(bool mute) { - line_active[0] = !mute; - line_active[1] = !mute; - return es8388_set_voice_mute(mute) == RESULT_OK; - } - // mute individual line: lines start at 0 (valid range 0:1) - bool setMute(bool mute, int line) { - if (line > 1) { - AD_LOGD("invalid line %d", line); - return false; + else + { + assert(p_pins != nullptr); + result = begin(codecCfg, *p_pins); + } + return result; } - // mute is managed on line level, so deactivate global mute - line_active[line] = !mute; - if (line_active[0] && line_active[1]) { - return es8388_config_output_device(DAC_OUTPUT_ALL) == RESULT_OK; - } else if (!line_active[0] && !line_active[1]) { - return es8388_config_output_device(DAC_OUTPUT_NONE) == RESULT_OK; - } else if (line_active[0]) { - return es8388_config_output_device(DAC_OUTPUT_LINE1) == RESULT_OK; - } else if (line_active[1]) { - return es8388_config_output_device(DAC_OUTPUT_LINE2) == RESULT_OK; - } - return false; - } - bool setVolume(int volume) { - AD_LOGD("volume %d", volume); - return es8388_set_voice_volume(limitValue(volume, 0, 100)) == RESULT_OK; - } - int getVolume() { - int vol; - es8388_get_voice_volume(&vol); - return vol; - } - - bool setInputVolume(int volume) { - // mapVolume values from 0 - 100 to 0 to 8 - - // es_mic_gain_t: MIC_GAIN_MIN = -1, 0,3,6,9,12,15,18,21,24 MIC_GAIN_MAX = - // 25 - - // Vol: 0, 12.5, 25, 37.5, 50, 62.5, 75, 87.5, 100 - // idx: 0, 1, 2, 3, 4, 5, 6, 7, 8 - // dB/gain: 0, 3, 6, 9, 12, 15, 18, 21, 24 - // factor: 1, 2, 4, 8, 16, 32, 63, 126, 252 - - // es8388 Register 9 – ADC Control 1 - // dB MicL MicR - // 0 0000 0000 - // 3 0001 0001 - // 6 0010 0010 - // 9 0011 0011 - // 12 0100 0100 - // 15 0101 0101 - // 18 0110 0110 - // 21 0111 0111 - // 24 1000 1000 - - es_mic_gain_t gains[] = {MIC_GAIN_0DB, MIC_GAIN_3DB, MIC_GAIN_6DB, - MIC_GAIN_9DB, MIC_GAIN_12DB, MIC_GAIN_15DB, - MIC_GAIN_18DB, MIC_GAIN_21DB, MIC_GAIN_24DB}; - - int vol = limitValue(volume, 0, 100); - int idx = mapVolume(vol, 0, 100, 0, 8); - - es_mic_gain_t gain = gains[idx]; - AD_LOGD("input volume: %d -> gain %d [dB] (idx: %d of 0..8)", volume, gain, - idx); - return setMicrophoneGain(gain); - } - - bool setMicrophoneGain(es_mic_gain_t gain) { - return es8388_set_mic_gain(gain) == RESULT_OK; - } - - bool isInputVolumeSupported() { return true; } - - void setVolumeHack(int volume_hack) { this->volume_hack = volume_hack; } - int getVolumeHack() { return volume_hack; } - - protected: - bool line_active[2] = {true}; - int volume_hack = AI_THINKER_ES8388_VOLUME_HACK; - - bool init(codec_config_t codec_cfg) { - return es8388_init(&codec_cfg, getI2C(), getI2CAddress(), volume_hack) == - RESULT_OK; - } - bool deinit() { return es8388_deinit() == RESULT_OK; } - - bool controlState(codec_mode_t mode) { - return es8388_ctrl_state_active(mode, true) == RESULT_OK; - } - bool configInterface(codec_mode_t mode, I2SDefinition iface) { - return es8388_config_i2s(mode, &iface) == RESULT_OK; - } -}; - -/** - * @brief Driver API for TAS5805M codec chip - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class AudioDriverTAS5805MClass : public AudioDriver { - public: - AudioDriverTAS5805MClass() { i2c_default_address = 0x2E; } - bool setMute(bool mute) { return tas5805m_set_mute(mute) == RESULT_OK; } - bool setVolume(int volume) { - AD_LOGD("volume %d", volume); - return tas5805m_set_volume(limitValue(volume, 0, 100)) == RESULT_OK; - } - int getVolume() { - int vol; - tas5805m_get_volume(&vol); - return vol; - } - - protected: - bool init(codec_config_t codec_cfg) { - return tas5805m_init(&codec_cfg, getI2C()) == RESULT_OK; - } - bool deinit() { return tas5805m_deinit() == RESULT_OK; } -}; - -/** - * @brief Driver API for WM8990 codec chip - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class AudioDriverWM8960Class : public AudioDriver { - public: - AudioDriverWM8960Class() { i2c_default_address = 0x1A; } - bool begin(CodecConfig codecCfg, DriverPins &pins) { - codec_cfg = codecCfg; - - // define wire object - mtb_wm8960_set_wire(getI2C()); - mtb_wm8960_set_write_retry_count(i2c_retry_count); - - // setup wm8960 - int features = getFeatures(codecCfg); - if (!mtb_wm8960_init(features)) { - AD_LOGE("mtb_wm8960_init"); - return false; + bool end(void) override { return cs42448.end(); } + bool setMute(bool enable) override { return cs42448.setMute(enable); } + bool setMute(bool enable, int line) + { + return cs42448.setMuteDAC(line, enable); } - setVolume(DRIVER_DEFAULT_VOLUME); - if (!mtb_wm8960_activate()) { - AD_LOGE("mtb_wm8960_activate"); - return false; + /// Defines the Volume (in %) if volume is 0, mute is enabled,range is 0-100. + bool setVolume(int volume) override + { + this->volume = volume; + return cs42448.setVolumeDAC(2.55f * volume); + } + bool setVolume(int dac, int volume) + { + return cs42448.setVolumeDAC(dac, 2.55f * volume); + } + + int getVolume() override { return volume; } + bool setInputVolume(int volume) override + { + int vol = mapVolume(volume, 0, 100, -128, 127); + return cs42448.setVolumeADC(vol); + } + bool isVolumeSupported() override { return true; } + bool isInputVolumeSupported() override { return true; } + + DriverPins &pins() { return *p_pins; } + CS42448 &driver() { return cs42448; } + + protected: + CS42448 cs42448; + int volume = 100; + CodecConfig cfg; + }; + + /** + * @brief Driver API for ES7210 codec chip. This chip supports only input! + * @author Phil Schatzmann + * @copyright GPLv3 + */ + class AudioDriverES7210Class : public AudioDriver + { + public: + AudioDriverES7210Class() { i2c_default_address = ES7210_AD1_AD0_00 >> 1; } + bool setMute(bool mute) { return es7210_set_mute(mute) == RESULT_OK; } + bool setVolume(int volume) + { + this->volume = volume; + return es7210_adc_set_volume(limitValue(volume, 0, 100)) == RESULT_OK; + } + int getVolume() { return volume; } + bool setInputVolume(int volume) { return setVolume(volume); } + bool isInputVolumeSupported() { return true; } + + protected: + int volume; + + bool init(codec_config_t codec_cfg) + { + return es7210_adc_init(&codec_cfg, getI2C()) == RESULT_OK; + } + bool deinit() { return es7210_adc_deinit() == RESULT_OK; } + + bool controlState(codec_mode_t mode) + { + return es7210_adc_ctrl_state_active(mode, true) == RESULT_OK; + } + bool configInterface(codec_mode_t mode, I2SDefinition iface) + { + return es7210_adc_config_i2s(mode, &iface) == RESULT_OK; + } + }; + + /** + * @brief Driver API for Lyrat ES7243 codec chip + * @author Phil Schatzmann + * @copyright GPLv3 + */ + class AudioDriverES7243Class : public AudioDriver + { + public: + AudioDriverES7243Class() { i2c_default_address = 0x13; } + bool setMute(bool mute) + { + return es7243_adc_set_voice_mute(mute) == RESULT_OK; + } + bool setVolume(int volume) + { + return es7243_adc_set_voice_volume(limitValue(volume, 0, 100)) == RESULT_OK; + } + int getVolume() + { + int vol; + es7243_adc_get_voice_volume(&vol); + return vol; + } + + protected: + bool init(codec_config_t codec_cfg) + { + return es7243_adc_init(&codec_cfg, getI2C()) == RESULT_OK; + } + bool deinit() { return es7243_adc_deinit() == RESULT_OK; } + + bool controlState(codec_mode_t mode) + { + return es7243_adc_ctrl_state_active(mode, true) == RESULT_OK; + } + bool configInterface(codec_mode_t mode, I2SDefinition iface) + { + return es7243_adc_config_i2s(mode, &iface) == RESULT_OK; + } + }; + + /** + * @brief Driver API for ES7243e codec chip + * @author Phil Schatzmann + * @copyright GPLv3 + */ + + class AudioDriverES7243eClass : public AudioDriver + { + public: + AudioDriverES7243eClass() { i2c_default_address = 0x13; } + bool setMute(bool mute) + { + return mute ? setVolume(0) == RESULT_OK : setVolume(volume) == RESULT_OK; + } + bool setVolume(int volume) + { + this->volume = volume; + return es7243e_adc_set_voice_volume(limitValue(volume, 0, 100)) == + RESULT_OK; + } + int getVolume() + { + int vol; + es7243e_adc_get_voice_volume(&vol); + return vol; + } + + protected: + int volume = 0; + + bool init(codec_config_t codec_cfg) + { + return es7243e_adc_init(&codec_cfg, getI2C()) == RESULT_OK; + } + bool deinit() { return es7243e_adc_deinit() == RESULT_OK; } + + bool controlState(codec_mode_t mode) + { + return es7243e_adc_ctrl_state_active(mode, true) == RESULT_OK; + } + bool configInterface(codec_mode_t mode, I2SDefinition iface) + { + return es7243e_adc_config_i2s(mode, &iface) == RESULT_OK; + } + }; + + /** + * @brief Driver API for ES8156 codec chip + * @author Phil Schatzmann + * @copyright GPLv3 + */ + class AudioDriverES8156Class : public AudioDriver + { + public: + AudioDriverES8156Class() { i2c_default_address = 0x8; } + bool setMute(bool mute) + { + return es8156_codec_set_voice_mute(mute) == RESULT_OK; } - if (!configure_clocking()) { - AD_LOGE("configure_clocking"); + bool setVolume(int volume) + { + AD_LOGD("volume %d", volume); + return es8156_codec_set_voice_volume(limitValue(volume, 0, 100)) == + RESULT_OK; + } + int getVolume() + { + int vol; + es8156_codec_get_voice_volume(&vol); + return vol; + } + + protected: + bool init(codec_config_t codec_cfg) + { + return es8156_codec_init(&codec_cfg, getI2C()) == RESULT_OK; + } + bool deinit() { return es8156_codec_deinit() == RESULT_OK; } + + bool controlState(codec_mode_t mode) + { + return es8156_codec_ctrl_state_active(mode, true) == RESULT_OK; + } + bool configInterface(codec_mode_t mode, I2SDefinition iface) + { + return es8156_codec_config_i2s(mode, &iface) == RESULT_OK; + } + }; + + /** + * @brief Driver API for Lyrat ES8311 codec chip + * @author Phil Schatzmann + * @copyright GPLv3 + */ + class AudioDriverES8311Class : public AudioDriver + { + public: + AudioDriverES8311Class() { i2c_default_address = 0x18; } + bool setMute(bool mute) { return es8311_set_voice_mute(mute) == RESULT_OK; } + bool setVolume(int volume) + { + return es8311_codec_set_voice_volume(limitValue(volume, 0, 100)) == + RESULT_OK; + } + int getVolume() + { + int vol; + es8311_codec_get_voice_volume(&vol); + return vol; + } + + protected: + bool init(codec_config_t codec_cfg) + { + int mclk_src = pins().getPinID(PinFunction::MCLK_SOURCE); + if (mclk_src == -1) + { + mclk_src = 0; // = FROM_MCLK_PIN; + } + AD_LOGI("MCLK_SOURCE: %d", mclk_src); + + assert(getI2C() != nullptr); + return es8311_codec_init(&codec_cfg, getI2C(), mclk_src, getI2CAddress()) == + RESULT_OK; + } + bool deinit() { return es8311_codec_deinit() == RESULT_OK; } + + bool controlState(codec_mode_t mode) + { + return es8311_codec_ctrl_state_active(mode, true) == RESULT_OK; + } + bool configInterface(codec_mode_t mode, I2SDefinition iface) + { + return es8311_codec_config_i2s(mode, &iface) == RESULT_OK; + } + }; + + /** + * @brief Driver API for ES8374 codec chip + * @author Phil Schatzmann + * @copyright GPLv3 + */ + class AudioDriverES8374Class : public AudioDriver + { + public: + AudioDriverES8374Class() { i2c_default_address = 0x10; } + bool setMute(bool mute) { return es8374_set_voice_mute(mute) == RESULT_OK; } + bool setVolume(int volume) + { + AD_LOGD("volume %d", volume); + return es8374_codec_set_voice_volume(limitValue(volume, 0, 100)) == + RESULT_OK; + } + int getVolume() + { + int vol; + es8374_codec_get_voice_volume(&vol); + return vol; + } + + protected: + bool init(codec_config_t codec_cfg) + { + auto codec_mode = this->codec_cfg.get_mode(); + return es8374_codec_init(&codec_cfg, codec_mode, getI2C(), + getI2CAddress()) == RESULT_OK; + } + bool deinit() { return es8374_codec_deinit() == RESULT_OK; } + + bool controlState(codec_mode_t mode) + { + return es8374_codec_ctrl_state_active(mode, true) == RESULT_OK; + } + bool configInterface(codec_mode_t mode, I2SDefinition iface) + { + return es8374_codec_config_i2s(mode, &iface) == RESULT_OK; + } + }; + + /** + * @brief Driver API for ES8388 codec chip + * @author Phil Schatzmann + * @copyright GPLv3 + */ + class AudioDriverES8388Class : public AudioDriver + { + public: + AudioDriverES8388Class(int volumeHack) + { + i2c_default_address = 0x10; + volume_hack = volumeHack; + } + AudioDriverES8388Class() { i2c_default_address = 0x10; } + bool setMute(bool mute) + { + line_active[0] = !mute; + line_active[1] = !mute; + return es8388_set_voice_mute(mute) == RESULT_OK; + } + // mute individual line: lines start at 0 (valid range 0:1) + bool setMute(bool mute, int line) + { + if (line > 1) + { + AD_LOGD("invalid line %d", line); + return false; + } + // mute is managed on line level, so deactivate global mute + line_active[line] = !mute; + if (line_active[0] && line_active[1]) + { + return es8388_config_output_device(DAC_OUTPUT_ALL) == RESULT_OK; + } + else if (!line_active[0] && !line_active[1]) + { + return es8388_config_output_device(DAC_OUTPUT_NONE) == RESULT_OK; + } + else if (line_active[0]) + { + return es8388_config_output_device(DAC_OUTPUT_LINE1) == RESULT_OK; + } + else if (line_active[1]) + { + return es8388_config_output_device(DAC_OUTPUT_LINE2) == RESULT_OK; + } return false; } - return true; - } - bool end(void) { - mtb_wm8960_deactivate(); - mtb_wm8960_free(); - return true; - } - virtual bool setConfig(CodecConfig codecCfg) { - codec_cfg = codecCfg; - return configure_clocking(); - } - - bool setMute(bool enable) { return setVolume(enable ? 0 : volume_out); } - - /// Defines the Volume (in %) if volume is 0, mute is enabled,range is 0-100. - bool setVolume(int volume) { - volume_out = limitValue(volume, 0, 100); - int vol_int = - volume_out == 0.0 ? 0 : mapVolume(volume_out, 0, 100, 30, 0x7F); - return mtb_wm8960_set_output_volume(vol_int); - } - - int getVolume() { return volume_out; } - - bool setInputVolume(int volume) { - volume_in = limitValue(volume, 0, 100); - int vol_int = mapVolume(volume_in, 0, 100, 0, 30); - return mtb_wm8960_adjust_input_volume(vol_int); - } - bool isVolumeSupported() { return true; } - - bool isInputVolumeSupported() { return true; } - - /// Configuration: define retry count (default : 0) - void setI2CRetryCount(int cnt) { i2c_retry_count = cnt; } - - /// Configuration: enable/disable PLL (active by default) - void setEnablePLL(bool active) { vs1053_enable_pll = active; } - - /// Configuration: define master clock frequency (default: 0) - void setMclkHz(uint32_t hz) { vs1053_mclk_hz = hz; } - - void dumpRegisters() { mtb_wm8960_dump(); } - - protected: - int volume_in = 100; - int volume_out = 100; - int i2c_retry_count = 0; - uint32_t vs1053_mclk_hz = 0; - bool vs1053_enable_pll = true; - - int getFeatures(CodecConfig cfg) { - int features = 0; - switch (cfg.output_device) { + bool setVolume(int volume) + { + AD_LOGD("volume %d", volume); + return es8388_set_voice_volume(limitValue(volume, 0, 100)) == RESULT_OK; + } + int getVolume() + { + int vol; + es8388_get_voice_volume(&vol); + return vol; + } + + bool setInputVolume(int volume) + { + // mapVolume values from 0 - 100 to 0 to 8 + + // es_mic_gain_t: MIC_GAIN_MIN = -1, 0,3,6,9,12,15,18,21,24 MIC_GAIN_MAX = + // 25 + + // Vol: 0, 12.5, 25, 37.5, 50, 62.5, 75, 87.5, 100 + // idx: 0, 1, 2, 3, 4, 5, 6, 7, 8 + // dB/gain: 0, 3, 6, 9, 12, 15, 18, 21, 24 + // factor: 1, 2, 4, 8, 16, 32, 63, 126, 252 + + // es8388 Register 9 – ADC Control 1 + // dB MicL MicR + // 0 0000 0000 + // 3 0001 0001 + // 6 0010 0010 + // 9 0011 0011 + // 12 0100 0100 + // 15 0101 0101 + // 18 0110 0110 + // 21 0111 0111 + // 24 1000 1000 + + es_mic_gain_t gains[] = {MIC_GAIN_0DB, MIC_GAIN_3DB, MIC_GAIN_6DB, + MIC_GAIN_9DB, MIC_GAIN_12DB, MIC_GAIN_15DB, + MIC_GAIN_18DB, MIC_GAIN_21DB, MIC_GAIN_24DB}; + + int vol = limitValue(volume, 0, 100); + int idx = mapVolume(vol, 0, 100, 0, 8); + + es_mic_gain_t gain = gains[idx]; + AD_LOGD("input volume: %d -> gain %d [dB] (idx: %d of 0..8)", volume, gain, + idx); + return setMicrophoneGain(gain); + } + + bool setMicrophoneGain(es_mic_gain_t gain) + { + return es8388_set_mic_gain(gain) == RESULT_OK; + } + + bool isInputVolumeSupported() { return true; } + + void setVolumeHack(int volume_hack) { this->volume_hack = volume_hack; } + int getVolumeHack() { return volume_hack; } + + protected: + bool line_active[2] = {true}; + int volume_hack = AI_THINKER_ES8388_VOLUME_HACK; + + bool init(codec_config_t codec_cfg) + { + return es8388_init(&codec_cfg, getI2C(), getI2CAddress(), volume_hack) == + RESULT_OK; + } + bool deinit() { return es8388_deinit() == RESULT_OK; } + + bool controlState(codec_mode_t mode) + { + return es8388_ctrl_state_active(mode, true) == RESULT_OK; + } + bool configInterface(codec_mode_t mode, I2SDefinition iface) + { + return es8388_config_i2s(mode, &iface) == RESULT_OK; + } + }; + + /** + * @brief Driver API for TAS5805M codec chip + * @author Phil Schatzmann + * @copyright GPLv3 + */ + class AudioDriverTAS5805MClass : public AudioDriver + { + public: + AudioDriverTAS5805MClass() { i2c_default_address = 0x2E; } + bool setMute(bool mute) { return tas5805m_set_mute(mute) == RESULT_OK; } + bool setVolume(int volume) + { + AD_LOGD("volume %d", volume); + return tas5805m_set_volume(limitValue(volume, 0, 100)) == RESULT_OK; + } + int getVolume() + { + int vol; + tas5805m_get_volume(&vol); + return vol; + } + + protected: + bool init(codec_config_t codec_cfg) + { + return tas5805m_init(&codec_cfg, getI2C()) == RESULT_OK; + } + bool deinit() { return tas5805m_deinit() == RESULT_OK; } + }; + + /** + * @brief Driver API for WM8990 codec chip + * @author Phil Schatzmann + * @copyright GPLv3 + */ + class AudioDriverWM8960Class : public AudioDriver + { + public: + AudioDriverWM8960Class() { i2c_default_address = 0x1A; } + bool begin(CodecConfig codecCfg, DriverPins &pins) + { + codec_cfg = codecCfg; + + // define wire object + mtb_wm8960_set_wire(getI2C()); + mtb_wm8960_set_write_retry_count(i2c_retry_count); + + // setup wm8960 + int features = getFeatures(codecCfg); + if (!mtb_wm8960_init(features)) + { + AD_LOGE("mtb_wm8960_init"); + return false; + } + setVolume(DRIVER_DEFAULT_VOLUME); + if (!mtb_wm8960_activate()) + { + AD_LOGE("mtb_wm8960_activate"); + return false; + } + if (!configure_clocking()) + { + AD_LOGE("configure_clocking"); + return false; + } + return true; + } + bool end(void) + { + mtb_wm8960_deactivate(); + mtb_wm8960_free(); + return true; + } + virtual bool setConfig(CodecConfig codecCfg) + { + codec_cfg = codecCfg; + return configure_clocking(); + } + + bool setMute(bool enable) { return setVolume(enable ? 0 : volume_out); } + + /// Defines the Volume (in %) if volume is 0, mute is enabled,range is 0-100. + bool setVolume(int volume) + { + volume_out = limitValue(volume, 0, 100); + int vol_int = + volume_out == 0.0 ? 0 : mapVolume(volume_out, 0, 100, 30, 0x7F); + return mtb_wm8960_set_output_volume(vol_int); + } + + int getVolume() { return volume_out; } + + bool setInputVolume(int volume) + { + volume_in = limitValue(volume, 0, 100); + int vol_int = mapVolume(volume_in, 0, 100, 0, 30); + return mtb_wm8960_adjust_input_volume(vol_int); + } + bool isVolumeSupported() { return true; } + + bool isInputVolumeSupported() { return true; } + + /// Configuration: define retry count (default : 0) + void setI2CRetryCount(int cnt) { i2c_retry_count = cnt; } + + /// Configuration: enable/disable PLL (active by default) + void setEnablePLL(bool active) { vs1053_enable_pll = active; } + + /// Configuration: define master clock frequency (default: 0) + void setMclkHz(uint32_t hz) { vs1053_mclk_hz = hz; } + + void dumpRegisters() { mtb_wm8960_dump(); } + + protected: + int volume_in = 100; + int volume_out = 100; + int i2c_retry_count = 0; + uint32_t vs1053_mclk_hz = 0; + bool vs1053_enable_pll = true; + + int getFeatures(CodecConfig cfg) + { + int features = 0; + switch (cfg.output_device) + { case DAC_OUTPUT_LINE1: features = features | WM8960_FEATURE_SPEAKER; break; @@ -1025,8 +1187,9 @@ class AudioDriverWM8960Class : public AudioDriver { break; default: break; - } - switch (cfg.input_device) { + } + switch (cfg.input_device) + { case ADC_INPUT_LINE1: features = features | WM8960_FEATURE_MICROPHONE1; break; @@ -1039,29 +1202,34 @@ class AudioDriverWM8960Class : public AudioDriver { break; default: break; + } + AD_LOGI("features: %d", features); + return features; } - AD_LOGI("features: %d", features); - return features; - } - - bool configure_clocking() { - if (vs1053_mclk_hz == 0) { - // just pick a multiple of the sample rate - vs1053_mclk_hz = 512 * codec_cfg.getRateNumeric(); - } - if (!mtb_wm8960_configure_clocking( - vs1053_mclk_hz, vs1053_enable_pll, - sampleRate(codec_cfg.getRateNumeric()), - wordLength(codec_cfg.getBitsNumeric()), - modeMasterSlave(codec_cfg.i2s.mode == MODE_MASTER))) { - AD_LOGE("mtb_wm8960_configure_clocking"); - return false; + + bool configure_clocking() + { + if (vs1053_mclk_hz == 0) + { + // just pick a multiple of the sample rate + vs1053_mclk_hz = 512 * codec_cfg.getRateNumeric(); + } + if (!mtb_wm8960_configure_clocking( + vs1053_mclk_hz, vs1053_enable_pll, + sampleRate(codec_cfg.getRateNumeric()), + wordLength(codec_cfg.getBitsNumeric()), + modeMasterSlave(codec_cfg.i2s.mode == MODE_MASTER))) + { + AD_LOGE("mtb_wm8960_configure_clocking"); + return false; + } + return true; } - return true; - } - mtb_wm8960_adc_dac_sample_rate_t sampleRate(int rate) { - switch (rate) { + mtb_wm8960_adc_dac_sample_rate_t sampleRate(int rate) + { + switch (rate) + { case 48000: return WM8960_ADC_DAC_SAMPLE_RATE_48_KHZ; case 44100: @@ -1085,11 +1253,13 @@ class AudioDriverWM8960Class : public AudioDriver { default: AD_LOGE("Unsupported rate: %d", rate); return WM8960_ADC_DAC_SAMPLE_RATE_44_1_KHZ; + } } - } - mtb_wm8960_word_length_t wordLength(int bits) { - switch (bits) { + mtb_wm8960_word_length_t wordLength(int bits) + { + switch (bits) + { case 16: return WM8960_WL_16BITS; case 20: @@ -1101,68 +1271,77 @@ class AudioDriverWM8960Class : public AudioDriver { default: AD_LOGE("Unsupported bits: %d", bits); return WM8960_WL_16BITS; + } } - } - - /// if microcontroller is master then module is slave - mtb_wm8960_mode_t modeMasterSlave(bool is_master) { - return is_master ? WM8960_MODE_MASTER : WM8960_MODE_SLAVE; - } -}; - -/** - * @brief Driver API for the wm8978 codec chip - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class AudioDriverWM8978Class : public AudioDriver { - public: - AudioDriverWM8978Class() = default; - - bool begin(CodecConfig codecCfg, DriverPins &pins) override { - bool rc = true; - rc = wm8078.begin(getI2C(), getI2CAddress()); - setConfig(codecCfg); - - // setup initial default volume - setVolume(DRIVER_DEFAULT_VOLUME); - - return rc; - } - - bool setConfig(CodecConfig codecCfg) override { - codec_cfg = codecCfg; - bool is_dac = codec_cfg.output_device != DAC_OUTPUT_NONE; - bool is_adc = codec_cfg.input_device != ADC_INPUT_NONE; - wm8078.cfgADDA(is_dac, is_adc); - - bool is_mic = codec_cfg.input_device == ADC_INPUT_LINE1 || - codec_cfg.input_device == ADC_INPUT_ALL; - bool is_linein = codec_cfg.input_device == ADC_INPUT_LINE2 || - codec_cfg.input_device == ADC_INPUT_ALL; - bool is_auxin = codec_cfg.input_device == ADC_INPUT_LINE3 || + + /// if microcontroller is master then module is slave + mtb_wm8960_mode_t modeMasterSlave(bool is_master) + { + return is_master ? WM8960_MODE_MASTER : WM8960_MODE_SLAVE; + } + }; + + /** + * @brief Driver API for the wm8978 codec chip + * @author Phil Schatzmann + * @copyright GPLv3 + */ + class AudioDriverWM8978Class : public AudioDriver + { + public: + AudioDriverWM8978Class() = default; + + bool begin(CodecConfig codecCfg, DriverPins &pins) override + { + bool rc = true; + rc = wm8078.begin(getI2C(), getI2CAddress()); + setConfig(codecCfg); + + // setup initial default volume + setVolume(DRIVER_DEFAULT_VOLUME); + + return rc; + } + + bool setConfig(CodecConfig codecCfg) override + { + codec_cfg = codecCfg; + bool is_dac = codec_cfg.output_device != DAC_OUTPUT_NONE; + bool is_adc = codec_cfg.input_device != ADC_INPUT_NONE; + wm8078.cfgADDA(is_dac, is_adc); + + bool is_mic = codec_cfg.input_device == ADC_INPUT_LINE1 || codec_cfg.input_device == ADC_INPUT_ALL; - wm8078.cfgInput(is_mic, is_linein, is_auxin); - wm8078.cfgOutput(is_dac, false); - - int bits = toBits(codecCfg.i2s.bits); - if (bits < 0) return false; - int i2s = toI2S(codecCfg.i2s.fmt); - if (i2s < 0) return false; - wm8078.cfgI2S(i2s, bits); - return true; - } - - bool end() override { - setVolume(0); - wm8078.cfgADDA(false, false); - return true; - } - - /// Mute line 0 = speaker, line 1 = headphones - bool setMute(bool mute, int line) override { - int scaled = mute ? 0 : mapVolume(volume, 0, 100, 0, 63); - switch (line) { + bool is_linein = codec_cfg.input_device == ADC_INPUT_LINE2 || + codec_cfg.input_device == ADC_INPUT_ALL; + bool is_auxin = codec_cfg.input_device == ADC_INPUT_LINE3 || + codec_cfg.input_device == ADC_INPUT_ALL; + wm8078.cfgInput(is_mic, is_linein, is_auxin); + wm8078.cfgOutput(is_dac, false); + + int bits = toBits(codecCfg.i2s.bits); + if (bits < 0) + return false; + int i2s = toI2S(codecCfg.i2s.fmt); + if (i2s < 0) + return false; + wm8078.cfgI2S(i2s, bits); + return true; + } + + bool end() override + { + setVolume(0); + wm8078.cfgADDA(false, false); + return true; + } + + /// Mute line 0 = speaker, line 1 = headphones + bool setMute(bool mute, int line) override + { + int scaled = mute ? 0 : mapVolume(volume, 0, 100, 0, 63); + switch (line) + { case 0: wm8078.setSPKvol(scaled); return true; @@ -1171,54 +1350,62 @@ class AudioDriverWM8978Class : public AudioDriver { return true; default: return false; + } + return false; + } + + bool setMute(bool mute) override + { + if (mute) + { + // set volume to 0 + wm8078.setSPKvol(0); + wm8078.setHPvol(0, 0); + } + else + { + // restore volume + setVolume(volume); + } + return true; + } + + bool setVolume(int volume) override + { + this->volume = volume; + int scaled = mapVolume(volume, 0, 100, 0, 63); + wm8078.setSPKvol(scaled); + wm8078.setHPvol(scaled, scaled); + return true; + } + + int getVolume() override { return volume; } + + bool setInputVolume(int volume) override + { + int scaled = mapVolume(volume, 0, 100, 0, 63); + wm8078.setMICgain(scaled); + wm8078.setAUXgain(scaled); + wm8078.setLINEINgain(scaled); + return true; } - return false; - } - - bool setMute(bool mute) override { - if (mute) { - // set volume to 0 - wm8078.setSPKvol(0); - wm8078.setHPvol(0, 0); - } else { - // restore volume - setVolume(volume); - } - return true; - } - - bool setVolume(int volume) override { - this->volume = volume; - int scaled = mapVolume(volume, 0, 100, 0, 63); - wm8078.setSPKvol(scaled); - wm8078.setHPvol(scaled, scaled); - return true; - } - - int getVolume() override { return volume; } - - bool setInputVolume(int volume) override { - int scaled = mapVolume(volume, 0, 100, 0, 63); - wm8078.setMICgain(scaled); - wm8078.setAUXgain(scaled); - wm8078.setLINEINgain(scaled); - return true; - } - - bool isVolumeSupported() override { return true; } - - bool isInputVolumeSupported() override { return true; } - - WM8978 &driver() { return wm8078; } - - protected: - WM8978 wm8078; - int volume = 0; - - /// fmt:0,LSB(right-aligned);1,MSB(left-aligned);2,Philips standard, - /// I2S;3,PCM/DSP; - int toI2S(i2s_format_t fmt) { - switch (fmt) { + + bool isVolumeSupported() override { return true; } + + bool isInputVolumeSupported() override { return true; } + + WM8978 &driver() { return wm8078; } + + protected: + WM8978 wm8078; + int volume = 0; + + /// fmt:0,LSB(right-aligned);1,MSB(left-aligned);2,Philips standard, + /// I2S;3,PCM/DSP; + int toI2S(i2s_format_t fmt) + { + switch (fmt) + { case I2S_NORMAL: return 2; case I2S_LEFT: @@ -1229,13 +1416,15 @@ class AudioDriverWM8978Class : public AudioDriver { return 3; default: break; + } + return -1; } - return -1; - } - // len: 0, 16 digits; 1, 20 digits; 2, 24 digits; 3, 32 digits; - int toBits(sample_bits_t bits) { - switch (bits) { + // len: 0, 16 digits; 1, 20 digits; 2, 24 digits; 3, 32 digits; + int toBits(sample_bits_t bits) + { + switch (bits) + { case BIT_LENGTH_16BITS: return 0; case BIT_LENGTH_20BITS: @@ -1246,60 +1435,68 @@ class AudioDriverWM8978Class : public AudioDriver { return 3; default: break; + } + return -1; + } + }; + + /** + * @brief Driver API for the wm8994 codec chip + * @author Phil Schatzmann + * @copyright GPLv3 + */ + class AudioDriverWM8994Class : public AudioDriver + { + public: + AudioDriverWM8994Class(uint16_t deviceAddr = 0x1A) + { + this->i2c_default_address = deviceAddr; + } + + virtual bool begin(CodecConfig codecCfg, DriverPins &pins) + { + codec_cfg = codecCfg; + // manage reset pin -> active high + setPAPower(true); + delay(10); + p_pins = &pins; + int vol = mapVolume(volume, 0, 100, DEFAULT_VOLMIN, DEFAULT_VOLMAX); + uint32_t freq = codecCfg.getRateNumeric(); + uint16_t outputDevice = getOutput(codec_cfg.output_device); + + return wm8994_Init(getI2CAddress(), outputDevice, vol, freq, getI2C()) == 0; + } + + bool setMute(bool mute) + { + uint32_t rc = + mute ? wm8994_Pause(getI2CAddress()) : wm8994_Resume(getI2CAddress()); + return rc == 0; + } + + bool setVolume(int volume) + { + this->volume = volume; + int vol = mapVolume(volume, 0, 100, DEFAULT_VOLMIN, DEFAULT_VOLMAX); + return wm8994_SetVolume(getI2CAddress(), vol) == 0; + } + int getVolume() { return volume; } + + protected: + int volume = 100; + + bool deinit() + { + int cnt = wm8994_Stop(getI2CAddress(), AUDIO_MUTE_ON); + cnt += wm8994_Reset(getI2CAddress()); + setPAPower(false); + return cnt == 0; } - return -1; - } -}; - -/** - * @brief Driver API for the wm8994 codec chip - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class AudioDriverWM8994Class : public AudioDriver { - public: - AudioDriverWM8994Class(uint16_t deviceAddr = 0x1A) { - this->i2c_default_address = deviceAddr; - } - - virtual bool begin(CodecConfig codecCfg, DriverPins &pins) { - codec_cfg = codecCfg; - // manage reset pin -> active high - setPAPower(true); - delay(10); - p_pins = &pins; - int vol = mapVolume(volume, 0, 100, DEFAULT_VOLMIN, DEFAULT_VOLMAX); - uint32_t freq = codecCfg.getRateNumeric(); - uint16_t outputDevice = getOutput(codec_cfg.output_device); - - return wm8994_Init(getI2CAddress(), outputDevice, vol, freq, getI2C()) == 0; - } - - bool setMute(bool mute) { - uint32_t rc = - mute ? wm8994_Pause(getI2CAddress()) : wm8994_Resume(getI2CAddress()); - return rc == 0; - } - - bool setVolume(int volume) { - this->volume = volume; - int vol = mapVolume(volume, 0, 100, DEFAULT_VOLMIN, DEFAULT_VOLMAX); - return wm8994_SetVolume(getI2CAddress(), vol) == 0; - } - int getVolume() { return volume; } - - protected: - int volume = 100; - - bool deinit() { - int cnt = wm8994_Stop(getI2CAddress(), AUDIO_MUTE_ON); - cnt += wm8994_Reset(getI2CAddress()); - setPAPower(false); - return cnt == 0; - } - - uint16_t getOutput(output_device_t output_device) { - switch (output_device) { + + uint16_t getOutput(output_device_t output_device) + { + switch (output_device) + { case DAC_OUTPUT_NONE: return 0; case DAC_OUTPUT_LINE1: @@ -1308,275 +1505,423 @@ class AudioDriverWM8994Class : public AudioDriver { return OUTPUT_DEVICE_HEADPHONE; case DAC_OUTPUT_ALL: return OUTPUT_DEVICE_BOTH; + } + return OUTPUT_DEVICE_BOTH; } - return OUTPUT_DEVICE_BOTH; - } -}; - -/** - * @brief Driver API for the CS43l22 codec chip on 0x94 (0x4A<<1) - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class AudioDriverPCM3168Class : public AudioDriver { - public: - AudioDriverPCM3168Class() { i2c_default_address = 0x44; }; - - bool setMute(bool mute) { return driver.setMute(mute); } - - bool setMute(bool mute, int line) { return driver.setMute(line, mute); } - - bool setVolume(int vol) { - volume = vol; - return driver.setVolume(100.0 * vol); - } - int getVolume() { return volume; } - - protected: - int volume; - PCM3168 driver; - - bool init(codec_config_t codec_cfg) { - driver.setWire(getI2C()); - driver.setAddress(getI2CAddress()); - return true; - } - bool deinit() { return driver.end(); } - bool controlState(codec_mode_t mode) { return true; } - bool configInterface(codec_mode_t mode, I2SDefinition iface) { - if (iface.mode == MODE_MASTER) { - AD_LOGE("Only slave is supported: MCU must be master"); - return false; + }; + + /** + * @brief Driver API for the CS43l22 codec chip on 0x94 (0x4A<<1) + * @author Phil Schatzmann + * @copyright GPLv3 + */ + class AudioDriverPCM3168Class : public AudioDriver + { + public: + AudioDriverPCM3168Class() { i2c_default_address = 0x44; }; + + bool setMute(bool mute) { return driver.setMute(mute); } + + bool setMute(bool mute, int line) { return driver.setMute(line, mute); } + + bool setVolume(int vol) + { + volume = vol; + return driver.setVolume(100.0 * vol); + } + int getVolume() { return volume; } + + protected: + int volume; + PCM3168 driver; + + bool init(codec_config_t codec_cfg) + { + driver.setWire(getI2C()); + driver.setAddress(getI2CAddress()); + return true; } - PCM3168::FMT fmt = PCM3168::FMT::I2SHighSpeedTDM24bit; - switch (iface.bits) { + bool deinit() { return driver.end(); } + bool controlState(codec_mode_t mode) { return true; } + bool configInterface(codec_mode_t mode, I2SDefinition iface) + { + if (iface.mode == MODE_MASTER) + { + AD_LOGE("Only slave is supported: MCU must be master"); + return false; + } + PCM3168::FMT fmt = PCM3168::FMT::I2SHighSpeedTDM24bit; + switch (iface.bits) + { case BIT_LENGTH_16BITS: - if (iface.fmt != I2S_RIGHT) { + if (iface.fmt != I2S_RIGHT) + { AD_LOGW("Only I2S_RIGHT is supported for 16 bits"); } fmt = PCM3168::FMT::Right16bit; break; case BIT_LENGTH_32BITS: case BIT_LENGTH_24BITS: - if (iface.signal_type == SIGNAL_TDM) { - switch (iface.fmt) { - case I2S_NORMAL: - fmt = PCM3168::FMT::I2STDM24bit; - break; - case I2S_LEFT: - fmt = PCM3168::FMT::LeftTDM24bit; - break; - default: - AD_LOGE("Unsupported fmt for tdm"); - return false; + if (iface.signal_type == SIGNAL_TDM) + { + switch (iface.fmt) + { + case I2S_NORMAL: + fmt = PCM3168::FMT::I2STDM24bit; + break; + case I2S_LEFT: + fmt = PCM3168::FMT::LeftTDM24bit; + break; + default: + AD_LOGE("Unsupported fmt for tdm"); + return false; } - } else { - switch (iface.fmt) { - case I2S_NORMAL: - fmt = PCM3168::FMT::I2S24bit; - break; - case I2S_LEFT: - fmt = PCM3168::FMT::Left24bit; - break; - case I2S_RIGHT: - fmt = PCM3168::FMT::Right24bit; - break; - case I2S_DSP: - fmt = PCM3168::FMT::LeftDSP24bit; - break; - default: - AD_LOGE("Unsupported fmt"); - return false; + } + else + { + switch (iface.fmt) + { + case I2S_NORMAL: + fmt = PCM3168::FMT::I2S24bit; + break; + case I2S_LEFT: + fmt = PCM3168::FMT::Left24bit; + break; + case I2S_RIGHT: + fmt = PCM3168::FMT::Right24bit; + break; + case I2S_DSP: + fmt = PCM3168::FMT::LeftDSP24bit; + break; + default: + AD_LOGE("Unsupported fmt"); + return false; } } break; default: AD_LOGE("Unsupported bits"); return false; - } + } - return driver.begin(fmt); - } -}; - -/** - * @brief Driver API for Lyrat Mini with a ES8311 and a ES7243 codec chip - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class AudioDriverLyratMiniClass : public AudioDriver { - public: - bool begin(CodecConfig codecCfg, DriverPins &pins) { - AD_LOGI("AudioDriverLyratMiniClass::begin"); - p_pins = &pins; - codec_cfg = codecCfg; - - // setup SPI for SD - // pins.setSPIActiveForSD(codecCfg.sd_active); - - // Start ES8311 - AD_LOGI("starting ES8311"); - pins.begin(); - dac.setPins(this->pins()); - if (!dac.setConfig(codecCfg)) { - AD_LOGE("setConfig failed"); - return false; + return driver.begin(fmt); } - setPAPower(true); - setVolume(DRIVER_DEFAULT_VOLUME); + }; - // Start ES7243 - if (codecCfg.input_device != ADC_INPUT_NONE) { - AD_LOGI("starting ES7243"); - adc.setPins(this->pins()); - if (!adc.setConfig(codecCfg)) { - AD_LOGE("adc.begin failed"); + /** + * @brief Driver API for Lyrat Mini with a ES8311 and a ES7243 codec chip + * @author Phil Schatzmann + * @copyright GPLv3 + */ + class AudioDriverLyratMiniClass : public AudioDriver + { + public: + bool begin(CodecConfig codecCfg, DriverPins &pins) + { + AD_LOGI("AudioDriverLyratMiniClass::begin"); + p_pins = &pins; + codec_cfg = codecCfg; + + // setup SPI for SD + // pins.setSPIActiveForSD(codecCfg.sd_active); + + // Start ES8311 + AD_LOGI("starting ES8311"); + pins.begin(); + dac.setPins(this->pins()); + if (!dac.setConfig(codecCfg)) + { + AD_LOGE("setConfig failed"); return false; } + setPAPower(true); + setVolume(DRIVER_DEFAULT_VOLUME); + + // Start ES7243 + if (codecCfg.input_device != ADC_INPUT_NONE) + { + AD_LOGI("starting ES7243"); + adc.setPins(this->pins()); + if (!adc.setConfig(codecCfg)) + { + AD_LOGE("adc.begin failed"); + return false; + } + } + + return true; + } + bool end(void) + { + int rc = 0; + rc += dac.end(); + rc += adc.end(); + return rc == 2; } + bool setMute(bool enable) override { return dac.setMute(enable); } + bool setVolume(int volume) override { return dac.setVolume(volume); } + int getVolume() override { return dac.getVolume(); } + bool setInputVolume(int volume) override { return adc.setVolume(volume); } + int getInputVolume() { return adc.getVolume(); } + bool isInputVolumeSupported() override { return true; } + + protected: + AudioDriverES8311Class dac; + AudioDriverES7243Class adc; + }; + + /** + * @brief Driver API for NAU8325 CODEC WITH AMPLIFIER + * @author + * @copyright MIT Licence + */ + + class AudioDriverNAU8325Class : public AudioDriver + { + public: + AudioDriverNAU8325Class() { i2c_default_address = 0x21; } - return true; - } - bool end(void) { - int rc = 0; - rc += dac.end(); - rc += adc.end(); - return rc == 2; - } - bool setMute(bool enable) override { return dac.setMute(enable); } - bool setVolume(int volume) override { return dac.setVolume(volume); } - int getVolume() override { return dac.getVolume(); } - bool setInputVolume(int volume) override { return adc.setVolume(volume); } - int getInputVolume() { return adc.getVolume(); } - bool isInputVolumeSupported() override { return true; } - - protected: - AudioDriverES8311Class dac; - AudioDriverES7243Class adc; -}; + bool setMute(bool mute) override + { + return nau.softMute(mute); + } + + bool setVolume(int volume) override + { + int vol = mapVolume(volume, 0, 100, 0x00, 0xff); + nau.setVolume(vol, vol); + return true; + } + + int getVolume() override + { + return volume; + } + + protected: + PCBCUPID_NAU8325 nau = PCBCUPID_NAU8325(wire, i2c_default_address); + int volume = 100; + + bool init_config(codec_config_t codec_cfg) override + { + int fs = getRate(codec_cfg.i2s.rate); + int bits = getBits(codec_cfg.i2s.bits); + return nau.begin(fs, bits); + } + + bool Deinit() override + { + return true; + } + + bool controlState(codec_mode_t mode) override + { + return mode == CODEC_MODE_DECODE || mode == CODEC_MODE_BOTH; + } + + bool configureInterface(codec_mode_t mode, I2SDefinition iface) override + { + NAU8325_I2SFormat fmt = I2S_STD; + switch (iface.fmt) + { + case I2S_NORMAL: + /* code */ + fmt = I2S_STD; + break; + case I2S_LEFT_JUSTIFIED: + fmt = I2S_LEFT; + break; + case I2S_RIGHT_JUSTIFIED: + fmt = I2S_RIGHT; + break; + case I2S_DSP: + fmt = I2S_DSP; + break; + + default: + break; + } + + return nau.setI2SFormat(fmt, NORMAL_BCLK); + } + + private: + int getRate(samplerate_t rate) + { + switch (rate) + { + case RATE_8K: + return 8000; + case RATE_16K: + return 16000; + case RATE_24K: + return 24000; + case RATE_32K: + return 32000; + case RATE_48K: + return 48000; + case RATE_44K: + return 44000; + default: + return 44100; + } + } + + int getBits(sample_bits_t bits) + { + switch (bits) + { + case BIT_LENGTH_16BITS + return 16; + case BIT_LENGTH_24BITS: + return 24; + case BIT_LENGTH_32BITS: + return 32; + default: + return 16; + } + } + }; #ifdef ARDUINO -// currently only supported in Arduino because we do not have a platform -// independent SPI API -/** - * @brief Driver API for AD1938 TDS DAC/ADC - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class AudioDriverAD1938Class : public AudioDriver { - public: - bool begin(CodecConfig codecCfg, DriverPins &pins) override { - int clatch = pins.getPinID(PinFunction::LATCH); - if (clatch < 0) return false; - int reset = pins.getPinID(PinFunction::RESET); - if (reset < 0) return false; - auto spi_opt = pins.getSPIPins(PinFunction::CODEC); - SPIClass *p_spi = nullptr; - if (spi_opt) { - p_spi = (SPIClass *)spi_opt.value().p_spi; - } else { - p_spi = &SPI; - p_spi->begin(); - } - // setup pins - pins.begin(); - // setup ad1938 - ad1938.begin(codecCfg, clatch, reset, *p_spi); - ad1938.enable(); - ad1938.setMute(false); - return true; - } - virtual bool setConfig(CodecConfig codecCfg) { - assert(p_pins != nullptr); - bool result = begin(codecCfg, *p_pins); - return result; - } - bool end(void) override { return ad1938.end(); } - bool setMute(bool mute) override { return ad1938.setMute(mute); } - // mutes an individual DAC: valid range (0:3) - bool setMute(bool mute, int line) { - if (line > 3) return false; - return ad1938.setVolumeDAC( - line, mute ? 0.0 : (static_cast(volumes[line]) / 100.0f)); - } - - /// Defines the Volume (in %) if volume is 0, mute is enabled,range is 0-100. - bool setVolume(int volume) override { - this->volume = volume; - for (int j = 0; j < 8; j++) { - volumes[j] = volume; - } - return ad1938.setVolume(static_cast(volume) / 100.0f); - } - /// Defines the Volume per DAC (in %) if volume is 0, mute is enabled,range is - /// 0-100. - bool setVolume(int volume, int line) { - if (line > 7) return false; - volumes[line] = volume; - return ad1938.setVolumeDAC(static_cast(volume) / 100.0f, line); - } - - int getVolume() override { return volume; } - bool setInputVolume(int volume) override { return false; } - bool isVolumeSupported() override { return true; } - bool isInputVolumeSupported() override { return false; } - - DriverPins &pins() { return *p_pins; } - AD1938 &driver() { return ad1938; } - - protected: - AD1938 ad1938; - int volume = 100; - int volumes[8] = {100}; -}; + // currently only supported in Arduino because we do not have a platform + // independent SPI API + /** + * @brief Driver API for AD1938 TDS DAC/ADC + * @author Phil Schatzmann + * @copyright GPLv3 + */ + class AudioDriverAD1938Class : public AudioDriver + { + public: + bool begin(CodecConfig codecCfg, DriverPins &pins) override + { + int clatch = pins.getPinID(PinFunction::LATCH); + if (clatch < 0) + return false; + int reset = pins.getPinID(PinFunction::RESET); + if (reset < 0) + return false; + auto spi_opt = pins.getSPIPins(PinFunction::CODEC); + SPIClass *p_spi = nullptr; + if (spi_opt) + { + p_spi = (SPIClass *)spi_opt.value().p_spi; + } + else + { + p_spi = &SPI; + p_spi->begin(); + } + // setup pins + pins.begin(); + // setup ad1938 + ad1938.begin(codecCfg, clatch, reset, *p_spi); + ad1938.enable(); + ad1938.setMute(false); + return true; + } + virtual bool setConfig(CodecConfig codecCfg) + { + assert(p_pins != nullptr); + bool result = begin(codecCfg, *p_pins); + return result; + } + bool end(void) override { return ad1938.end(); } + bool setMute(bool mute) override { return ad1938.setMute(mute); } + // mutes an individual DAC: valid range (0:3) + bool setMute(bool mute, int line) + { + if (line > 3) + return false; + return ad1938.setVolumeDAC( + line, mute ? 0.0 : (static_cast(volumes[line]) / 100.0f)); + } + + /// Defines the Volume (in %) if volume is 0, mute is enabled,range is 0-100. + bool setVolume(int volume) override + { + this->volume = volume; + for (int j = 0; j < 8; j++) + { + volumes[j] = volume; + } + return ad1938.setVolume(static_cast(volume) / 100.0f); + } + /// Defines the Volume per DAC (in %) if volume is 0, mute is enabled,range is + /// 0-100. + bool setVolume(int volume, int line) + { + if (line > 7) + return false; + volumes[line] = volume; + return ad1938.setVolumeDAC(static_cast(volume) / 100.0f, line); + } + + int getVolume() override { return volume; } + bool setInputVolume(int volume) override { return false; } + bool isVolumeSupported() override { return true; } + bool isInputVolumeSupported() override { return false; } + + DriverPins &pins() { return *p_pins; } + AD1938 &driver() { return ad1938; } + + protected: + AD1938 ad1938; + int volume = 100; + int volumes[8] = {100}; + }; #endif -// -- Drivers -/// @ingroup audio_driver -static AudioDriverAC101Class AudioDriverAC101; -/// @ingroup audio_driver -static AudioDriverCS43l22Class AudioDriverCS43l22; -/// @ingroup audio_driver -static AudioDriverES7210Class AudioDriverES7210; -/// @ingroup audio_driver -static AudioDriverES7243Class AudioDriverES7243; -/// @ingroup audio_driver -static AudioDriverES7243eClass AudioDriverES7243e; -/// @ingroup audio_driver -static AudioDriverES8156Class AudioDriverES8156; -/// @ingroup audio_driver -static AudioDriverES8311Class AudioDriverES8311; -/// @ingroup audio_driver -static AudioDriverES8374Class AudioDriverES8374; -/// @ingroup audio_driver -static AudioDriverES8388Class AudioDriverES8388; -/// @ingroup audio_driver -static AudioDriverES8388Class AudioDriverES8388H0{0}; -/// @ingroup audio_driver -static AudioDriverES8388Class AudioDriverES8388H1{1}; -/// @ingroup audio_driver -static AudioDriverES8388Class AudioDriverES8388H2{2}; -/// @ingroup audio_driver -static AudioDriverES8388Class AudioDriverES8388H3{3}; -/// @ingroup audio_driver -static AudioDriverWM8960Class AudioDriverWM8960; -/// @ingroup audio_driver -static AudioDriverWM8978Class AudioDriverWM8978; -/// @ingroup audio_driver -static AudioDriverWM8994Class AudioDriverWM8994; -/// @ingroup audio_driver -static AudioDriverLyratMiniClass AudioDriverLyratMini; -/// @ingroup audio_driver -static NoDriverClass NoDriver; -/// @ingroup audio_driver -static AudioDriverCS42448Class AudioDriverCS42448; -/// @ingroup audio_driver -static AudioDriverPCM3168Class AudioDriverPCM3168; + // -- Drivers + /// @ingroup audio_driver + static AudioDriverAC101Class AudioDriverAC101; + /// @ingroup audio_driver + static AudioDriverCS43l22Class AudioDriverCS43l22; + /// @ingroup audio_driver + static AudioDriverES7210Class AudioDriverES7210; + /// @ingroup audio_driver + static AudioDriverES7243Class AudioDriverES7243; + /// @ingroup audio_driver + static AudioDriverES7243eClass AudioDriverES7243e; + /// @ingroup audio_driver + static AudioDriverES8156Class AudioDriverES8156; + /// @ingroup audio_driver + static AudioDriverES8311Class AudioDriverES8311; + /// @ingroup audio_driver + static AudioDriverES8374Class AudioDriverES8374; + /// @ingroup audio_driver + static AudioDriverES8388Class AudioDriverES8388; + /// @ingroup audio_driver + static AudioDriverES8388Class AudioDriverES8388H0{0}; + /// @ingroup audio_driver + static AudioDriverES8388Class AudioDriverES8388H1{1}; + /// @ingroup audio_driver + static AudioDriverES8388Class AudioDriverES8388H2{2}; + /// @ingroup audio_driver + static AudioDriverES8388Class AudioDriverES8388H3{3}; + /// @ingroup audio_driver + static AudioDriverWM8960Class AudioDriverWM8960; + /// @ingroup audio_driver + static AudioDriverWM8978Class AudioDriverWM8978; + /// @ingroup audio_driver + static AudioDriverWM8994Class AudioDriverWM8994; + /// @ingroup audio_driver + static AudioDriverLyratMiniClass AudioDriverLyratMini; + /// @ingroup audio_driver + static NoDriverClass NoDriver; + /// @ingroup audio_driver + static AudioDriverCS42448Class AudioDriverCS42448; + /// @ingroup audio_driver + static AudioDriverPCM3168Class AudioDriverPCM3168; + /// @ingroup audio_driver + static AudioDriverNAU8325Class AudioDriverNAU8325; + #ifdef ARDUINO -/// @ingroup audio_driver -static AudioDriverAD1938Class AudioDriverAD1938; + /// @ingroup audio_driver + static AudioDriverAD1938Class AudioDriverAD1938; #endif -} // namespace audio_driver \ No newline at end of file +} // namespace audio_driver \ No newline at end of file diff --git a/src/Driver/nau8325/PCBCUPID_NAU8325.cpp b/src/Driver/nau8325/PCBCUPID_NAU8325.cpp new file mode 100644 index 0000000..c643ea3 --- /dev/null +++ b/src/Driver/nau8325/PCBCUPID_NAU8325.cpp @@ -0,0 +1,615 @@ +#include "PCBCUPID_NAU8325.h" + +#define MASTER_CLK_MIN 2048000 +#define MASTER_CLK_MAX 49152000 + +uint16_t set_ratio = 0; //declared as global + +bool alc_enable = true; +bool clock_detection = true; +bool clock_det_data = true; +uint32_t dac_vref_microvolt = 2880000; +uint32_t vref_impedance_ohms = 125000; + +PCBCUPID_NAU8325::PCBCUPID_NAU8325(TwoWire &wire, uint8_t i2c_addr) + : i2c(wire), i2c_addr(i2c_addr) {} + +bool PCBCUPID_NAU8325::begin(uint32_t fs, uint8_t bits_per_sample, uint16_t ratio) { + Serial.println("[NAU8325] beginDynamic() starting..."); + Serial.printf(" fs = %lu Hz\n", fs); + Serial.printf(" ratio = %u Hz\n", ratio); + Serial.printf(" bits = %u\n", bits_per_sample); + + if (bits_per_sample != 16 && bits_per_sample != 24 && bits_per_sample != 32) { + Serial.printf("[NAU8325] Unsupported bit width: %u\n", bits_per_sample); + return false; + } + + set_ratio = ratio; + uint32_t mclk = fs * ratio; + resetChip(); + delay(5); + + if (!setSysClock(mclk)) + return false; + + if (!configureAudio(fs, mclk, bits_per_sample)) + return false; + + if (!setI2SFormat(I2S_STD, NORMAL_BCLK)) + return false; + + // ***********Analog + ALC + Vref Config *********** + initRegisters(); + + // Wait for analog to settle + delay(50); + + // Enable Analog/Output Blocks (DAPM Equivalent) + enableDAPMBlocks(); + + // Set default volume AFTER analog path is ready + setVolume(0xFD, 0xFD); + + softMute(false); + + /*default powerOn*/ + powerOn(); + + Serial.println("[NAU8325] beginDynamic() complete - sound should play"); + return true; +} + +/*If the user didn't provide the ratio it will call this function with default ratio*/ +bool PCBCUPID_NAU8325::begin(uint32_t fs, uint8_t bits) { + uint16_t ratio = 256; + return begin(fs, bits, ratio); +} + +/*if the user didn't pass any of the parameter like fs,bits,ratio*/ +bool PCBCUPID_NAU8325::begin() { + uint32_t fs = 44100; + uint16_t ratio = 256; + uint8_t bits = 16; + return begin(fs, bits, ratio); +} + +void PCBCUPID_NAU8325::enableDAPMBlocks() { + // Enable DAC L/R in ENA_CTRL (R04) + writeRegisterBits(NAU8325_R04_ENA_CTRL, + NAU8325_ENA_CTRL_DAC_EN_MASK, + NAU8325_ENA_CTRL_DAC_EN_MASK); + + // Enable analog power-up blocks (e.g., VMID, Class-D) in ANALOG_CONTROL_1 (R61) + writeRegisterBits(NAU8325_R61_ANALOG_CONTROL_1, + NAU8325_ANALOG_CTRL1_EN_MASK, + NAU8325_ANALOG_CTRL1_EN_MASK); + + // Enable speaker path routing (ANALOG_CONTROL_6 R66) + writeRegisterBits(NAU8325_R66_ANALOG_CONTROL_6, + NAU8325_ANALOG_CTRL6_EN_MASK, + NAU8325_ANALOG_CTRL6_EN_MASK); +} + +void PCBCUPID_NAU8325::resetChip() { + writeRegister(0x0000, 0x0001); + delay(2); + writeRegister(0x0000, 0x0000); + delay(2); +} + +uint16_t PCBCUPID_NAU8325::readDeviceId() { + uint16_t id; + if (readRegister(NAU8325_R02_DEVICE_ID, id)) { + return id; + } else { + return 0xFFFF; // Invalid or failed read + } +} + +// N1 divider options +const SrcAttr PCBCUPID_NAU8325::mclk_n1_div[] = { + { 1, 0x0 }, + { 2, 0x1 }, + { 3, 0x2 } +}; + +// N2 divider options +const SrcAttr PCBCUPID_NAU8325::mclk_n2_div[] = { + { 0, 0x0 }, + { 1, 0x1 }, + { 2, 0x2 }, + { 3, 0x3 }, + { 4, 0x4 } +}; + +// N3 multiplier options +const SrcAttr PCBCUPID_NAU8325::mclk_n3_mult[] = { + { 0, 0x1 }, + { 1, 0x2 }, + { 2, 0x3 }, + { 3, 0x4 } +}; + +// DAC oversampling options +const OsrAttr PCBCUPID_NAU8325::osr_dac_sel[] = { + { 64, 2 }, // OSR 64, SRC 1/4 + { 256, 0 }, // OSR 256, SRC 1 + { 128, 1 }, // OSR 128, SRC 1/2 + { 0, 0 }, + { 32, 3 } // OSR 32, SRC 1/8 +}; + +// accessing the table as expected based oon sample rate and mclk +const SRateAttr PCBCUPID_NAU8325::target_srate_table[] = { + { 48000, 2, true, { 12288000, 19200000, 24000000 } }, + { 16000, 1, false, { 4096000, 6400000, 8000000 } }, + { 8000, 0, false, { 2048000, 3200000, 4000000 } }, + { 44100, 2, true, { 11289600, 17640000, 22050000 } }, + { 64000, 3, false, { 16384000, 25600000, 32000000 } }, + { 96000, 3, true, { 24576000, 38400000, 48000000 } }, + { 12000, 0, true, { 3072000, 4800000, 6000000 } }, + { 24000, 1, true, { 6144000, 9600000, 12000000 } }, + { 32000, 2, false, { 8192000, 12800000, 16000000 } } +}; + +bool PCBCUPID_NAU8325::applySampleRateClocks(const SRateAttr *srate, int n1_sel, int mclk_mult_sel, int n2_sel) { + if (!srate || n1_sel < 0 || n1_sel >= 3 || n2_sel < 0 || n2_sel >= 5) { + Serial.println("[NAU8325] Invalid N1/N2 divider index."); + return false; + } + + // --- Debug --- + Serial.println("--- Debugging clock dividers ---"); + Serial.printf("n1_sel=%d (val=%d)\n", n1_sel, mclk_n1_div[n1_sel].val); + Serial.printf("n2_sel=%d (val=%d)\n", n2_sel, mclk_n2_div[n2_sel].val); + Serial.printf("mult_sel=%d\n", mclk_mult_sel); + + // Set sample rate range and max mode + writeRegisterBits(NAU8325_R40_CLK_DET_CTRL, + NAU8325_REG_SRATE_MASK | NAU8325_REG_DIV_MAX, + (srate->range << NAU8325_REG_SRATE_SFT) | (srate->max ? NAU8325_REG_DIV_MAX : 0)); + // Batch update CLK_CTRL (0x03) + uint16_t clk_ctrl_val = 0; + clk_ctrl_val |= mclk_n2_div[n2_sel].val; // MCLK_SRC + clk_ctrl_val |= mclk_n1_div[n1_sel].val << NAU8325_CLK_MUL_SRC_SFT; // N1 + if (mclk_mult_sel >= 0 && mclk_mult_sel <= 3) + clk_ctrl_val |= mclk_n3_mult[mclk_mult_sel].val << NAU8325_MCLK_SEL_SFT; // N3 + + clk_ctrl_val |= 0x1000; // DAC_CLK = MCLK/2 (required!) + + writeRegister(NAU8325_R03_CLK_CTRL, clk_ctrl_val); + + writeRegister(NAU8325_R03_CLK_CTRL, clk_ctrl_val); // Now written only once + + // Configure ANALOG_CONTROL_5 for 4x/8x clock enable + switch (mclk_mult_sel) { + case 2: + writeRegisterBits(NAU8325_R65_ANALOG_CONTROL_5, + NAU8325_MCLK4XEN_EN, + NAU8325_MCLK4XEN_EN); + break; + case 3: + writeRegisterBits(NAU8325_R65_ANALOG_CONTROL_5, + NAU8325_MCLK4XEN_EN | NAU8325_MCLK8XEN_EN, + NAU8325_MCLK4XEN_EN | NAU8325_MCLK8XEN_EN); + break; + default: + writeRegisterBits(NAU8325_R65_ANALOG_CONTROL_5, + NAU8325_MCLK4XEN_EN | NAU8325_MCLK8XEN_EN, + 0); + break; + } + + return true; +} + +int PCBCUPID_NAU8325::getMclkRatioAndN2Index(const SRateAttr *srate, int mclk_hz, int &n2_sel_out) { + int ratio = NAU8325_MCLK_FS_RATIO_NUM; // Default: not found + + for (int i = 0; i < 5; i++) { // mclk_n2_div[] has 5 entries + int div = mclk_n2_div[i].param; + int mclk_src = mclk_hz >> div; + + if (srate->mclk_src[NAU8325_MCLK_FS_RATIO_256] == mclk_src) { + ratio = NAU8325_MCLK_FS_RATIO_256; + n2_sel_out = i; + break; + } + if (srate->mclk_src[NAU8325_MCLK_FS_RATIO_400] == mclk_src) { + ratio = NAU8325_MCLK_FS_RATIO_400; + n2_sel_out = i; + break; + } + if (srate->mclk_src[NAU8325_MCLK_FS_RATIO_500] == mclk_src) { + ratio = NAU8325_MCLK_FS_RATIO_500; + n2_sel_out = i; + break; + } + } + + return ratio; +} + +const SRateAttr *PCBCUPID_NAU8325::getSRateAttr(int fs) + +{ + for (size_t i = 0; i < sizeof(target_srate_table) / sizeof(target_srate_table[0]); ++i) { + if (target_srate_table[i].fs == fs) { + return &target_srate_table[i]; + } + } + return nullptr; +} + +bool PCBCUPID_NAU8325::chooseClockSource(int fs, int mclk, + const SRateAttr *&srate, + int &n1_sel, int &mult_sel, int &n2_sel) { + if (fs <= 0 || mclk <= 0) { + Serial.println("[NAU8325] Invalid fs or mclk."); + return false; + } + + srate = getSRateAttr(fs); + if (!srate) { + Serial.printf("[NAU8325] Unsupported fs: %d\n", fs); + return false; + } + + // First try direct N2 mapping + int ratio = getMclkRatioAndN2Index(srate, mclk, n2_sel); + if (ratio != NAU8325_MCLK_FS_RATIO_NUM) { + n1_sel = 0; + mult_sel = -1; // Bypass + Serial.printf("[NAU8325] Direct match: fs=%d, mclk=%d → N2=%d (Ratio=%d)\n", + fs, mclk, n2_sel, ratio); + return true; + } + + // Try all N1 and N3 combinations + int mclk_max = 0; + int best_n1 = -1, best_mult = -1, best_n2 = -1; + + for (int i = 0; i < 3; ++i) // N1 options + { + for (int j = 0; j < 4; ++j) // N3 multiplier options + { + int m = (mclk << mclk_n3_mult[j].param) / mclk_n1_div[i].param; + int r = getMclkRatioAndN2Index(srate, m, n2_sel); + + if (r != NAU8325_MCLK_FS_RATIO_NUM && (m > mclk_max || best_n1 < i)) { + mclk_max = m; + best_n1 = i; + best_mult = j; + best_n2 = n2_sel; + } + } + } + + if (mclk_max > 0) { + n1_sel = best_n1; + mult_sel = best_mult; + n2_sel = best_n2; + + Serial.printf("[NAU8325] Matched via N1/N3 combo: fs=%d, mclk=%d → N1=%d, N3(mult)=%d, N2=%d\n", + fs, mclk, n1_sel, mult_sel, n2_sel); + Serial.printf("[NAU8325] chooseClockSource(): fs=%d, mclk=%d\n", fs, mclk); + + return true; + } + + Serial.printf("[NAU8325] Failed to match MCLK %d Hz with fs %d Hz\n", mclk, fs); + return false; +} + +bool PCBCUPID_NAU8325::configureClocks(int fs, int mclk) { + const SRateAttr *srate = nullptr; + int n1_sel = 0, mult_sel = -1, n2_sel = 0; + + if (!chooseClockSource(fs, mclk, srate, n1_sel, mult_sel, n2_sel)) { + Serial.printf("[NAU8325] Failed to choose clock source for fs=%d, mclk=%d\n", fs, mclk); + return false; + } + + if (!applySampleRateClocks(srate, n1_sel, mult_sel, n2_sel)) { + Serial.println("[NAU8325] Failed to apply sample rate clocks."); + return false; + } + Serial.printf("[NAU8325] configureClocks(): fs=%u, mclk=%u\n", fs, mclk); + + return true; +} + +const OsrAttr *PCBCUPID_NAU8325::getCurrentOSR() { + uint16_t value; + if (!readRegister(NAU8325_R29_DAC_CTRL1, value)) { + return nullptr; + } + + uint8_t osr_index = value & NAU8325_DAC_OVERSAMPLE_MASK; + + if (osr_index >= sizeof(osr_dac_sel) / sizeof(osr_dac_sel[0])) { + return nullptr; + } + + return &osr_dac_sel[osr_index]; +} + +bool PCBCUPID_NAU8325::configureAudio(uint32_t fs, uint32_t mclk, uint8_t bits_per_sample) { + this->fs = fs; + this->mclk = mclk; + + // Configure clock tree + if (!configureClocks(fs, mclk)) { + return false; + } + + // Optional: set oversampling mode explicitly (OSR = 128) + writeRegisterBits(NAU8325_R29_DAC_CTRL1, NAU8325_DAC_OVERSAMPLE_MASK, NAU8325_DAC_OVERSAMPLE_128); + + const OsrAttr *osr = getCurrentOSR(); + if (!osr || osr->osr == 0 || (fs * osr->osr > CLK_DA_AD_MAX)) { + Serial.println("[NAU8325] Invalid OSR or fs × OSR exceeds max."); + return false; + } + + // Set DAC clock source + writeRegisterBits(NAU8325_R03_CLK_CTRL, + NAU8325_CLK_DAC_SRC_MASK, + osr->clk_src << NAU8325_CLK_DAC_SRC_SFT); + + // Configure I2S word length + uint16_t val_len = 0; + switch (bits_per_sample) { + case 16: + val_len = NAU8325_I2S_DL_16; + break; + case 20: + val_len = NAU8325_I2S_DL_20; + break; + case 24: + val_len = NAU8325_I2S_DL_24; + break; + case 32: + val_len = NAU8325_I2S_DL_32; + break; + default: + Serial.printf("[NAU8325] Invalid bit width: %u\n", bits_per_sample); + return false; + } + + writeRegisterBits(NAU8325_R0D_I2S_PCM_CTRL1, + NAU8325_I2S_DL_MASK, val_len); + + Serial.printf("[NAU8325] configureAudio(): fs=%u, mclk=%u, bits=%u\n", fs, mclk, bits_per_sample); + + return true; +} + +bool PCBCUPID_NAU8325::setI2SFormat(NAU8325_I2SFormat format, NAU8325_ClockPolarity polarity) { + uint16_t ctrl1_val = 0; + + // Set BCLK polarity + if (polarity == INVERTED_BCLK) { + ctrl1_val |= NAU8325_I2S_BP_INV; + } + + // Set data format + switch (format) { + case I2S_STD: + ctrl1_val |= NAU8325_I2S_DF_I2S; + break; + case LEFT_JUSTIFIED: + ctrl1_val |= NAU8325_I2S_DF_LEFT; + break; + case RIGHT_JUSTIFIED: + ctrl1_val |= NAU8325_I2S_DF_RIGTH; + break; + case DSP_A: + ctrl1_val |= NAU8325_I2S_DF_PCM_AB; + break; + case DSP_B: + ctrl1_val |= NAU8325_I2S_DF_PCM_AB | NAU8325_I2S_PCMB_EN; + break; + default: + return false; + } + + // Apply to I2S_PCM_CTRL1 + return writeRegisterBits(NAU8325_R0D_I2S_PCM_CTRL1, + NAU8325_I2S_DF_MASK | NAU8325_I2S_BP_MASK | NAU8325_I2S_PCMB_EN, + ctrl1_val); +} + +bool PCBCUPID_NAU8325::setSysClock(uint32_t freq) { + if (freq < MASTER_CLK_MIN || freq > MASTER_CLK_MAX) { + Serial.printf("[NAU8325] Invalid MCLK: %u Hz (allowed: %u - %u)\n", freq, MASTER_CLK_MIN, MASTER_CLK_MAX); + return false; + } + + this->mclk = freq; + Serial.printf("[NAU8325] MCLK set to %u Hz\n", mclk); + return true; +} + +void PCBCUPID_NAU8325::setPowerUpDefault(bool enable) { + writeRegisterBits(NAU8325_R40_CLK_DET_CTRL, NAU8325_PWRUP_DFT, + enable ? NAU8325_PWRUP_DFT : 0); +} + +void PCBCUPID_NAU8325::setOversampling(OversamplingMode mode) { + writeRegisterBits(NAU8325_R29_DAC_CTRL1, NAU8325_DAC_OVERSAMPLE_MASK, mode); +} + +void PCBCUPID_NAU8325::setVolume(uint8_t left, uint8_t right) { + uint16_t value = ((left & 0xFF) << 8) | (right & 0xFF); + writeRegister(NAU8325_R13_DAC_VOLUME, value); +} + +void PCBCUPID_NAU8325::softMute(bool enable) // under testing +{ + writeRegisterBits(NAU8325_R12_MUTE_CTRL, NAU8325_SOFT_MUTE, + enable ? NAU8325_SOFT_MUTE : 0); + delay(30); // Same as msleep(30) +} + +void PCBCUPID_NAU8325::initRegisters() { + // Set ALC parameters default timing and max gain + writeRegisterBits(NAU8325_R2C_ALC_CTRL1, NAU8325_ALC_MAXGAIN_MASK, 0x7 << NAU8325_ALC_MAXGAIN_SFT); + writeRegisterBits(NAU8325_R2D_ALC_CTRL2, + NAU8325_ALC_DCY_MASK | NAU8325_ALC_ATK_MASK | NAU8325_ALC_HLD_MASK, + (0x5 << NAU8325_ALC_DCY_SFT) | (0x3 << NAU8325_ALC_ATK_SFT) | (0x5 << NAU8325_ALC_HLD_SFT)); + + /* Enable ALC to avoid signal distortion when battery low. */ + if (alc_enable) { + writeRegisterBits(NAU8325_R2E_ALC_CTRL3, NAU8325_ALC_EN, NAU8325_ALC_EN); + } + + // Clock detection bits + uint16_t clk_det_mask = NAU8325_CLKPWRUP_DIS | NAU8325_PWRUP_DFT; + + if (clock_detection) + writeRegisterBits(NAU8325_R40_CLK_DET_CTRL, clk_det_mask, 0); + else + writeRegisterBits(NAU8325_R40_CLK_DET_CTRL, clk_det_mask, NAU8325_CLKPWRUP_DIS); + + if (clock_det_data) + writeRegisterBits(NAU8325_R40_CLK_DET_CTRL, NAU8325_APWRUP_EN, NAU8325_APWRUP_EN); + else + writeRegisterBits(NAU8325_R40_CLK_DET_CTRL, NAU8325_APWRUP_EN, 0); + + // DAC Vref configuration + uint16_t vref_val = 0xFFFF; + if (dac_vref_microvolt == 1800000) + vref_val = 0 << NAU8325_DACVREFSEL_SFT; + else if (dac_vref_microvolt == 2700000) + vref_val = 1 << NAU8325_DACVREFSEL_SFT; + else if (dac_vref_microvolt == 2880000) + vref_val = 2 << NAU8325_DACVREFSEL_SFT; + else if (dac_vref_microvolt == 3060000) + vref_val = 3 << NAU8325_DACVREFSEL_SFT; + + if (vref_val != 0xFFFF) { + writeRegisterBits(NAU8325_R73_RDAC, NAU8325_DACVREFSEL_MASK, vref_val); + } else { + Serial.printf("[NAU8325] Invalid DAC Vref: %lu uV\n", dac_vref_microvolt); + } + + /* DAC Reference Voltage Decoupling Capacitors. */ + writeRegisterBits(NAU8325_R63_ANALOG_CONTROL_3, NAU8325_CLASSD_COARSE_GAIN_MASK, 0x4); + /* Auto-Att Min Gain 0dB, Class-D N Driver Slew Rate -25%. */ + writeRegisterBits(NAU8325_R64_ANALOG_CONTROL_4, NAU8325_CLASSD_SLEWN_MASK, 0x7); + + // VMID resistor selection + uint16_t vref_imp_val = 0xFFFF; + if (vref_impedance_ohms == 0) + vref_imp_val = 0 << NAU8325_BIAS_VMID_SEL_SFT; + else if (vref_impedance_ohms == 25000) + vref_imp_val = 1 << NAU8325_BIAS_VMID_SEL_SFT; + else if (vref_impedance_ohms == 125000) + vref_imp_val = 2 << NAU8325_BIAS_VMID_SEL_SFT; + else if (vref_impedance_ohms == 2500) + vref_imp_val = 3 << NAU8325_BIAS_VMID_SEL_SFT; + + if (vref_imp_val != 0xFFFF) { + writeRegisterBits(NAU8325_R60_BIAS_ADJ, NAU8325_BIAS_VMID_SEL_MASK, vref_imp_val); + } else { + Serial.printf("[NAU8325] Invalid VMID impedance: %lu ohms\n", vref_impedance_ohms); + } + + // Enable VMID, BIAS, DACs, etc., Voltage / current Amps + writeRegisterBits(NAU8325_R61_ANALOG_CONTROL_1, + NAU8325_DACEN_MASK | NAU8325_DACCLKEN_MASK | NAU8325_DACEN_R_MASK | NAU8325_DACCLKEN_R_MASK | NAU8325_CLASSDEN_MASK | NAU8325_VMDFSTENB_MASK | NAU8325_BIASEN_MASK | NAU8325_VMIDEN_MASK, + (0x1 << NAU8325_DACEN_SFT) | (0x1 << NAU8325_DACCLKEN_SFT) | (0x1 << NAU8325_DACEN_R_SFT) | (0x1 << NAU8325_DACCLKEN_R_SFT) | (0x1 << NAU8325_CLASSDEN_SFT) | (0x1 << NAU8325_VMDFSTENB_SFT) | (0x1 << NAU8325_BIASEN_SFT) | (1 << NAU8325_VMIDEN_SFT)); + + /* Enable ALC to avoid signal distortion when battery low. */ + if (alc_enable) { + writeRegisterBits(NAU8325_R2E_ALC_CTRL3, NAU8325_ALC_EN, NAU8325_ALC_EN); + } + + // Clock detection bits + uint16_t clk_det_mask1 = NAU8325_CLKPWRUP_DIS | NAU8325_PWRUP_DFT; + + if (clock_detection) + writeRegisterBits(NAU8325_R40_CLK_DET_CTRL, clk_det_mask1, 0); + else + writeRegisterBits(NAU8325_R40_CLK_DET_CTRL, clk_det_mask1, NAU8325_CLKPWRUP_DIS); + + if (clock_det_data) + writeRegisterBits(NAU8325_R40_CLK_DET_CTRL, NAU8325_APWRUP_EN, NAU8325_APWRUP_EN); + else + writeRegisterBits(NAU8325_R40_CLK_DET_CTRL, NAU8325_APWRUP_EN, 0); + + // Set default OSR = 128 + writeRegisterBits(NAU8325_R29_DAC_CTRL1, + NAU8325_DAC_OVERSAMPLE_MASK, + NAU8325_DAC_OVERSAMPLE_128); +} + +bool PCBCUPID_NAU8325::writeRegister(uint16_t reg, uint16_t val) { + Serial.printf("[WRITE] reg=0x%04X val=0x%04X\n", reg, val); + i2c.beginTransmission(i2c_addr); + i2c.write((reg >> 8) & 0xFF); + i2c.write(reg & 0xFF); + i2c.write((val >> 8) & 0xFF); + i2c.write(val & 0xFF); + return i2c.endTransmission() == 0; +} + +bool PCBCUPID_NAU8325::writeRegisterBits(uint16_t reg, uint16_t mask, uint16_t value) // mask ---> is to set the the particular bit renaining bit untouched. +{ + uint16_t current; + if (!readRegister(reg, current)) + return false; + + uint16_t new_value = (current & ~mask) | (value & mask); + return writeRegister(reg, new_value); +} + +bool PCBCUPID_NAU8325::readRegister(uint16_t reg, uint16_t &value) { + i2c.beginTransmission(i2c_addr); + i2c.write((reg >> 8) & 0xFF); + i2c.write(reg & 0xFF); + if (i2c.endTransmission(false) != 0) { + return false; + } + + if (i2c.requestFrom(i2c_addr, (uint8_t)2) != 2) { + return false; + } + + uint8_t high = i2c.read(); + uint8_t low = i2c.read(); + value = ((uint16_t)high << 8) | low; + return true; +} + +void PCBCUPID_NAU8325::powerOn() { + // Clear soft mute to allow audio output + softMute(false); + + // Enable DACs, clocks, bias, class-D + writeRegisterBits(NAU8325_R61_ANALOG_CONTROL_1, + NAU8325_DACEN_MASK | NAU8325_DACCLKEN_MASK | NAU8325_DACEN_R_MASK | NAU8325_DACCLKEN_R_MASK | NAU8325_CLASSDEN_MASK | NAU8325_VMDFSTENB_MASK | NAU8325_BIASEN_MASK | NAU8325_VMIDEN_MASK, + (3 << NAU8325_DACEN_SFT) | (3 << NAU8325_DACCLKEN_SFT) | (3 << NAU8325_DACEN_R_SFT) | (3 << NAU8325_DACCLKEN_R_SFT) | (3 << NAU8325_CLASSDEN_SFT) | (3 << NAU8325_VMDFSTENB_SFT) | (3 << NAU8325_BIASEN_SFT) | 0x03); // VMID_EN + + // If clock detection is disabled, enable PWRUP_DFT + setPowerUpDefault(true); + + delay(30); // Let analog circuitry stabilize +} + +void PCBCUPID_NAU8325::powerOff() { + // Soft mute first to prevent pop + softMute(true); + + // Disable analog and DAC power + writeRegisterBits(NAU8325_R61_ANALOG_CONTROL_1, + NAU8325_DACEN_MASK | NAU8325_DACCLKEN_MASK | NAU8325_DACEN_R_MASK | NAU8325_DACCLKEN_R_MASK | NAU8325_CLASSDEN_MASK | NAU8325_VMDFSTENB_MASK | NAU8325_BIASEN_MASK | NAU8325_VMIDEN_MASK, + 0x0000); + + // If clock detection is disabled, clear PWRUP_DFT + setPowerUpDefault(false); + + delay(30); // Let output fade out +} diff --git a/src/Driver/nau8325/PCBCUPID_NAU8325.h b/src/Driver/nau8325/PCBCUPID_NAU8325.h new file mode 100644 index 0000000..0f53a49 --- /dev/null +++ b/src/Driver/nau8325/PCBCUPID_NAU8325.h @@ -0,0 +1,473 @@ +#ifndef PCBCUPID_NAU8325_H +#define PCBCUPID_NAU8325_H + +#include +#include + +#define NAU8325_R00_HARDWARE_RST 0x00 +#define NAU8325_R01_SOFTWARE_RST 0x01 +#define NAU8325_R02_DEVICE_ID 0x02 +#define NAU8325_R03_CLK_CTRL 0x03 +#define NAU8325_R04_ENA_CTRL 0x04 +#define NAU8325_R05_INTERRUPT_CTRL 0x05 +#define NAU8325_R06_INT_CLR_STATUS 0x06 +#define NAU8325_R09_IRQOUT 0x09 +#define NAU8325_R0A_IO_CTRL 0x0a +#define NAU8325_R0B_PDM_CTRL 0x0b +#define NAU8325_R0C_TDM_CTRL 0x0c +#define NAU8325_R0D_I2S_PCM_CTRL1 0x0d +#define NAU8325_R0E_I2S_PCM_CTRL2 0x0e +#define NAU8325_R0F_L_TIME_SLOT 0x0f +#define NAU8325_R10_R_TIME_SLOT 0x10 +#define NAU8325_R11_HPF_CTRL 0x11 +#define NAU8325_R12_MUTE_CTRL 0x12 +#define NAU8325_R13_DAC_VOLUME 0x13 +#define NAU8325_R1D_DEBUG_READ1 0x1d +#define NAU8325_R1F_DEBUG_READ2 0x1f +#define NAU8325_R22_DEBUG_READ3 0x22 +#define NAU8325_R29_DAC_CTRL1 0x29 +#define NAU8325_R2A_DAC_CTRL2 0x2a +#define NAU8325_R2C_ALC_CTRL1 0x2c +#define NAU8325_R2D_ALC_CTRL2 0x2d +#define NAU8325_R2E_ALC_CTRL3 0x2e +#define NAU8325_R2F_ALC_CTRL4 0x2f +#define NAU8325_R40_CLK_DET_CTRL 0x40 +#define NAU8325_R49_TEST_STATUS 0x49 +#define NAU8325_R4A_ANALOG_READ 0x4a +#define NAU8325_R50_MIXER_CTRL 0x50 +#define NAU8325_R55_MISC_CTRL 0x55 +#define NAU8325_R60_BIAS_ADJ 0x60 +#define NAU8325_R61_ANALOG_CONTROL_1 0x61 +#define NAU8325_R62_ANALOG_CONTROL_2 0x62 +#define NAU8325_R63_ANALOG_CONTROL_3 0x63 +#define NAU8325_R64_ANALOG_CONTROL_4 0x64 +#define NAU8325_R65_ANALOG_CONTROL_5 0x65 +#define NAU8325_R66_ANALOG_CONTROL_6 0x66 +#define NAU8325_R69_CLIP_CTRL 0x69 +#define NAU8325_R73_RDAC 0x73 +#define NAU8325_REG_MAX NAU8325_R73_RDAC + +#define NAU8325_VMIDEN_SFT 0x03 + +/* 16-bit control register address, and 16-bits control register data */ +#define NAU8325_REG_ADDR_LEN 16 +#define NAU8325_REG_DATA_LEN 16 + +/*Below are bit masking of individual register*/ + +/* CLK_CTRL (0x03) */ +#define NAU8325_CLK_DAC_SRC_SFT 12 +#define NAU8325_CLK_DAC_SRC_MASK (0x3 << NAU8325_CLK_DAC_SRC_SFT) +#define NAU8325_CLK_MUL_SRC_SFT 6 +#define NAU8325_CLK_MUL_SRC_MASK (0x3 << NAU8325_CLK_MUL_SRC_SFT) +#define NAU8325_MCLK_SEL_SFT 3 +#define NAU8325_MCLK_SEL_MASK (0x7 << NAU8325_MCLK_SEL_SFT) +#define NAU8325_MCLK_SRC_MASK 0x7 + +/* ENA_CTRL (0x04) */ +#define NAU8325_DAC_LEFT_CH_EN_SFT 3 +#define NAU8325_DAC_LEFT_CH_EN (0x1 << NAU8325_DAC_LEFT_CH_EN_SFT) +#define NAU8325_DAC_RIGHT_CH_EN_SFT 2 +#define NAU8325_DAC_RIGHT_CH_EN (0x1 << NAU8325_DAC_RIGHT_CH_EN_SFT) + +// R04: ENA_CTRL +#define NAU8325_ENA_CTRL_DACR_EN 0x0008 // Bit 3 +#define NAU8325_ENA_CTRL_DACL_EN 0x0004 // Bit 2 +#define NAU8325_ENA_CTRL_DAC_EN_MASK 0x000C // Bits 3:2 + +/* INTERRUPT_CTRL (0x05) */ +#define NAU8325_ARP_DWN_INT_SFT 12 +#define NAU8325_ARP_DWN_INT_MASK (0x1 << NAU8325_ARP_DWN_INT_SFT) +#define NAU8325_CLIP_INT_SFT 11 +#define NAU8325_CLIP_INT_MASK (0x1 << NAU8325_CLIP_INT_SFT) +#define NAU8325_LVD_INT_SFT 10 +#define NAU8325_LVD_INT_MASK (0x1 << NAU8325_LVD_INT_SFT) +#define NAU8325_PWR_INT_DIS_SFT 8 +#define NAU8325_PWR_INT_DIS (0x1 << NAU8325_PWR_INT_DIS_SFT) +#define NAU8325_OCP_OTP_SHTDWN_INT_SFT 4 +#define NAU8325_OCP_OTP_SHTDWN_INT_MASK (0x1 << NAU8325_OCP_OTP_SHTDWN_INT_SFT) +#define NAU8325_CLIP_INT_DIS_SFT 3 +#define NAU8325_CLIP_INT_DIS (0x1 << NAU8325_CLIP_INT_DIS_SFT) +#define NAU8325_LVD_INT_DIS_SFT 2 +#define NAU8325_LVD_INT_DIS (0x1 << NAU8325_LVD_INT_DIS_SFT) +#define NAU8325_PWR_INT_MASK 0x1 + +/* INT_CLR_STATUS (0x06) */ +#define NAU8325_INT_STATUS_MASK 0x7f + +/* IRQOUT (0x9) */ +#define NAU8325_IRQOUT_SEL_SEF 12 +#define NAU8325_IRQOUT_SEL_MASK (0xf << NAU8325_IRQOUT_SEL_SEF) +#define NAU8325_DEM_DITH_SFT 7 +#define NAU8325_DEM_DITH_EN (0x1 << NAU8325_DEM_DITH_SFT) +#define NAU8325_GAINZI3_SFT 5 +#define NAU8325_GAINZI3_MASK (0x1 << NAU8325_GAINZI3_SFT) +#define NAU8325_GAINZI2_MASK 0x1f + +/* IO_CTRL (0x0a) */ +#define NAU8325_IRQ_PL_SFT 15 +#define NAU8325_IRQ_PL_ACT_HIGH (0x1 << NAU8325_IRQ_PL_SFT) +#define NAU8325_IRQ_PS_SFT 14 +#define NAU8325_IRQ_PS_UP (0x1 << NAU8325_IRQ_PS_SFT) +#define NAU8325_IRQ_PE_SFT 13 +#define NAU8325_IRQ_PE_EN (0x1 << NAU8325_IRQ_PE_SFT) +#define NAU8325_IRQ_DS_SFT 12 +#define NAU8325_IRQ_DS_HIGH (0x1 << NAU8325_IRQ_DS_SFT) +#define NAU8325_IRQ_OUTPUT_SFT 11 +#define NAU8325_IRQ_OUTPUT_EN (0x1 << NAU8325_IRQ_OUTPUT_SFT) +#define NAU8325_IRQ_PIN_DEBUG_SFT 10 +#define NAU8325_IRQ_PIN_DEBUG_EN (0x1 << NAU8325_IRQ_PIN_DEBUG_SFT) + +/* PDM_CTRL (0x0b) */ +#define NAU8325_PDM_LCH_EDGE_SFT 1 +#define NAU8325_PDM_LCH_EDGE__MASK (0x1 << NAU8325_PDM_LCH_EDGE_SFT) +#define NAU8325_PDM_MODE_EN 0x1 + +/* TDM_CTRL (0x0c) */ +#define NAU8325_TDM_SFT 15 +#define NAU8325_TDM_EN (0x1 << NAU8325_TDM_SFT) +#define NAU8325_PCM_OFFSET_CTRL_SFT 14 +#define NAU8325_PCM_OFFSET_CTRL_EN (0x1 << NAU8325_PCM_OFFSET_CTRL_SFT) +#define NAU8325_DAC_LEFT_SFT 6 +#define NAU8325_NAU8325_DAC_LEFT_MASK (0x7 << NAU8325_DAC_LEFT_SFT) +#define NAU8325_DAC_RIGHT_SFT 3 +#define NAU8325_DAC_RIGHT_MASK (0x7 << NAU8325_DAC_RIGHT_SFT) + +/* I2S_PCM_CTRL1 (0x0d) */ +#define NAU8325_DACCM_CTL_SFT 14 +#define NAU8325_DACCM_CTL_MASK (0x3 << NAU8325_DACCM_CTL_SFT) +#define NAU8325_CMB8_0_SFT 10 +#define NAU8325_CMB8_0_MASK (0x1 << NAU8325_CMB8_0_SFT) +#define NAU8325_UA_OFFSET_SFT 9 +#define NAU8325_UA_OFFSET_MASK (0x1 << NAU8325_UA_OFFSET_SFT) +#define NAU8325_I2S_BP_SFT 7 +#define NAU8325_I2S_BP_MASK (0x1 << NAU8325_I2S_BP_SFT) +#define NAU8325_I2S_BP_INV (0x1 << NAU8325_I2S_BP_SFT) +#define NAU8325_I2S_PCMB_SFT 6 +#define NAU8325_I2S_PCMB_EN (0x1 << NAU8325_I2S_PCMB_SFT) +#define NAU8325_I2S_DACPSHS0_SFT 5 +#define NAU8325_I2S_DACPSHS0_MASK (0x1 << NAU8325_I2S_DACPSHS0_SFT) +#define NAU8325_I2S_DL_SFT 2 +#define NAU8325_I2S_DL_MASK (0x3 << NAU8325_I2S_DL_SFT) +#define NAU8325_I2S_DL_32 (0x3 << NAU8325_I2S_DL_SFT) +#define NAU8325_I2S_DL_24 (0x2 << NAU8325_I2S_DL_SFT) +#define NAU8325_I2S_DL_20 (0x1 << NAU8325_I2S_DL_SFT) +#define NAU8325_I2S_DL_16 (0x0 << NAU8325_I2S_DL_SFT) +#define NAU8325_I2S_DF_MASK 0x3 +#define NAU8325_I2S_DF_RIGTH 0x0 +#define NAU8325_I2S_DF_LEFT 0x1 +#define NAU8325_I2S_DF_I2S 0x2 +#define NAU8325_I2S_DF_PCM_AB 0x3 + +/* I2S_PCM_CTRL2 (0x0e) */ +#define NAU8325_PCM_TS_SFT 10 +#define NAU8325_PCM_TS_EN (0x1 << NAU8325_PCM_TS_SFT) +#define NAU8325_PCM8BIT0_SFT 8 +#define NAU8325_PCM8BIT0_MASK (0x1 << NAU8325_PCM8BIT0_SFT) + +/* L_TIME_SLOT (0x0f)*/ +#define NAU8325_SHORT_FS_DET_SFT 13 +#define NAU8325_SHORT_FS_DET_DIS (0x1 << NAU8325_SHORT_FS_DET_SFT) +#define NAU8325_TSLOT_L0_MASK 0x3ff + +/* R_TIME_SLOT (0x10)*/ +#define NAU8325_TSLOT_R0_MASK 0x3ff + +/* HPF_CTRL (0x11)*/ +#define NAU8325_DAC_HPF_SFT 15 +#define NAU8325_DAC_HPF_EN (0x1 << NAU8325_DAC_HPF_SFT) +#define NAU8325_DAC_HPF_APP_SFT 14 +#define NAU8325_DAC_HPF_APP_MASK (0x1 << NAU8325_DAC_HPF_APP_SFT) +#define NAU8325_DAC_HPF_FCUT_SFT 11 +#define NAU8325_DAC_HPF_FCUT_MASK (0x7 << NAU8325_DAC_HPF_FCUT_SFT) + +/* MUTE_CTRL (0x12)*/ +#define NAU8325_SOFT_MUTE_SFT 15 +#define NAU8325_SOFT_MUTE (0x1 << NAU8325_SOFT_MUTE_SFT) +#define NAU8325_DAC_ZC_SFT 8 +#define NAU8325_DAC_ZC_EN (0x1 << NAU8325_DAC_ZC_SFT) +#define NAU8325_UNMUTE_CTL_SFT 6 +#define NAU8325_UNMUTE_CTL_MASK (0x3 << NAU8325_UNMUTE_CTL_SFT) +#define NAU8325_ANA_MUTE_SFT 4 +#define NAU8325_ANA_MUTE_MASK (0x3 << NAU8325_ANA_MUTE_SFT) +#define NAU8325_AUTO_MUTE_SFT 3 +#define NAU8325_AUTO_MUTE_DIS (0x1 << NAU8325_AUTO_MUTE_SFT) + +/* DAC_VOLUME (0x13) */ +#define NAU8325_DAC_VOLUME_L_SFT 8 +#define NAU8325_DAC_VOLUME_L_EN (0xff << NAU8325_DAC_VOLUME_L_SFT) +#define NAU8325_DAC_VOLUME_R_SFT 0 +#define NAU8325_DAC_VOLUME_R_EN (0xff << NAU8325_DAC_VOLUME_R_SFT) +#define NAU8325_DAC_VOL_MAX 0xff + +/* DEBUG_READ1 (0x1d)*/ +#define NAU8325_OSR100_MASK (0x1 << 6) +#define NAU8325_MIPS500_MASK (0x1 << 5) +#define NAU8325_SHUTDWNDRVR_R_MASK (0x1 << 4) +#define NAU8325_SHUTDWNDRVR_L_MASK (0x1 << 3) +#define NAU8325_MUTEB_MASK (0x1 << 2) +#define NAU8325_PDOSCB_MASK (0x1 << 1) +#define NAU8325_POWERDOWN1B_D_MASK 0x1 + +/* DEBUG_READ2 (0x1f)*/ +#define NAU8325_R_CHANNEL_Vol_SFT 8 +#define NAU8325_R_CHANNEL_Vol_MASK (0xff << NAU8325_R_CHANNEL_Vol_SFT) +#define NAU8325_L_CHANNEL_Vol_MASK 0xff + +/* DEBUG_READ3(0x22)*/ +#define NAU8325_PGAL_GAIN_MASK (0x3f << 7) +#define NAU8325_CLIP_MASK (0x1 << 6) +#define NAU8325_SCAN_MODE_MASK (0x1 << 5) +#define NAU8325_SDB_MASK (0x1 << 4) +#define NAU8325_TALARM_MASK (0x1 << 3) +#define NAU8325_SHORTR_MASK (0x1 << 2) +#define NAU8325_SHORTL_MASK (0x1 << 1) +#define NAU8325_TMDET_MASK 0x1 + +/* DAC_CTRL1 (0x29) */ +#define NAU8325_DAC_OVERSAMPLE_SFT 0 +#define NAU8325_DAC_OVERSAMPLE_MASK 0x7 +#define NAU8325_DAC_OVERSAMPLE_256 1 +#define NAU8325_DAC_OVERSAMPLE_128 2 +#define NAU8325_DAC_OVERSAMPLE_64 0 +#define NAU8325_DAC_OVERSAMPLE_32 4 + +/* ALC_CTRL1 (0x2c) */ +#define NAU8325_ALC_MAXGAIN_SFT 5 +#define NAU8325_ALC_MAXGAIN_MAX 0x7 +#define NAU8325_ALC_MAXGAIN_MASK (0x7 << NAU8325_ALC_MAXGAIN_SFT) +#define NAU8325_ALC_MINGAIN_MAX 4 +#define NAU8325_ALC_MINGAIN_SFT 1 +#define NAU8325_ALC_MINGAIN_MASK (0x7 << NAU8325_ALC_MINGAIN_SFT) + +/* ALC_CTRL2 (0x2d) */ +#define NAU8325_ALC_DCY_SFT 12 +#define NAU8325_ALC_DCY_MAX 0xb +#define NAU8325_ALC_DCY_MASK (0xf << NAU8325_ALC_DCY_SFT) +#define NAU8325_ALC_ATK_SFT 8 +#define NAU8325_ALC_ATK_MAX 0xb +#define NAU8325_ALC_ATK_MASK (0xf << NAU8325_ALC_ATK_SFT) +#define NAU8325_ALC_HLD_SFT 4 +#define NAU8325_ALC_HLD_MAX 0xa +#define NAU8325_ALC_HLD_MASK (0xf << NAU8325_ALC_HLD_SFT) +#define NAU8325_ALC_LVL_SFT 0 +#define NAU8325_ALC_LVL_MAX 0xf +#define NAU8325_ALC_LVL_MASK 0xf + +/* ALC_CTRL3 (0x2e) */ +#define NAU8325_ALC_EN_SFT 15 +#define NAU8325_ALC_EN (0x1 << NAU8325_ALC_EN_SFT) + +/* TEMP_COMP_CTRL (0x30) */ +#define NAU8325_TEMP_COMP_ACT2_MASK 0xff + +/* LPF_CTRL (0x33) */ +#define NAU8325_LPF_IN1_EN_SFT 15 +#define NAU8325_LPF_IN1_EN (0x1 << NAU8325_LPF_IN1_EN_SFT) +#define NAU8325_LPF_IN1_TC_SFT 11 +#define NAU8325_LPF_IN1_TC_MASK (0xf << NAU8325_LPF_IN1_TC_SFT) +#define NAU8325_LPF_IN2_EN_SFT 10 +#define NAU8325_LPF_IN2_EN (0x1 << NAU8325_LPF_IN2_EN_SFT) +#define NAU8325_LPF_IN2_TC_SFT 6 +#define NAU8325_LPF_IN2_TC_MASK (0xf << NAU8325_LPF_IN2_TC_SFT) + +/* CLK_DET_CTRL (0x40) */ +#define NAU8325_APWRUP_SFT 15 +#define NAU8325_APWRUP_EN (0x1 << NAU8325_APWRUP_SFT) +#define NAU8325_CLKPWRUP_SFT 14 +#define NAU8325_CLKPWRUP_DIS (0x1 << NAU8325_CLKPWRUP_SFT) +#define NAU8325_PWRUP_DFT_SFT 13 +#define NAU8325_PWRUP_DFT (0x1 << NAU8325_PWRUP_DFT_SFT) +#define NAU8325_REG_SRATE_SFT 10 +#define NAU8325_REG_SRATE_MASK (0x7 << NAU8325_REG_SRATE_SFT) +#define NAU8325_REG_ALT_SRATE_SFT 9 +#define NAU8325_REG_ALT_SRATE_EN (0x1 << NAU8325_REG_ALT_SRATE_SFT) +#define NAU8325_REG_DIV_MAX 0x1 + +/* BIAS_ADJ (0x60) */ +#define NAU8325_BIAS_VMID_SEL_SFT 4 +#define NAU8325_BIAS_VMID_SEL_MASK (0x3 << NAU8325_BIAS_VMID_SEL_SFT) + +/* ANALOG_CONTROL_1 (0x61) */ +#define NAU8325_VMDFSTENB_SFT 14 +#define NAU8325_VMDFSTENB_MASK (0x3 << NAU8325_VMDFSTENB_SFT) +#define NAU8325_CLASSDEN_SFT 12 +#define NAU8325_CLASSDEN_MASK (0x3 << NAU8325_CLASSDEN_SFT) +#define NAU8325_DACCLKEN_R_SFT 10 +#define NAU8325_DACCLKEN_R_MASK (0x3 << NAU8325_DACCLKEN_R_SFT) +#define NAU8325_DACEN_R_SFT 8 +#define NAU8325_DACEN_R_MASK (0x3 << NAU8325_DACEN_R_SFT) +#define NAU8325_DACCLKEN_SFT 6 +#define NAU8325_DACCLKEN_MASK (0x3 << NAU8325_DACCLKEN_SFT) +#define NAU8325_DACEN_SFT 4 +#define NAU8325_DACEN_MASK (0x3 << NAU8325_DACEN_SFT) +#define NAU8325_BIASEN_SFT 2 +#define NAU8325_BIASEN_MASK (0x3 << NAU8325_BIASEN_SFT) +#define NAU8325_VMIDEN_MASK 0x3 + +// R61: ANALOG_CONTROL_1 +#define NAU8325_ANALOG_CTRL1_VMID_EN 0x0001 // Bit 0 +#define NAU8325_ANALOG_CTRL1_CLASSD_EN 0x0002 // Bit 1 +#define NAU8325_ANALOG_CTRL1_EN_MASK 0x0003 // Bits 1:0 + +/* ANALOG_CONTROL_2 (0x62) */ +#define NAU8325_PWMMOD_SFT 14 +#define NAU8325_PWMMOD_MASK (0x1 << NAU8325_PWMMOD_SFT) +#define NAU8325_DACTEST_SFT 6 +#define NAU8325_DACTEST_MASK (0x3 << NAU8325_DACTEST_SFT) +#define NAU8325_DACREFCAP_SFT 4 +#define NAU8325_DACREFCAP_MASK (0x3 << NAU8325_DACREFCAP_SFT) + +/* ANALOG_CONTROL_3 (0x63) */ +#define NAU8325_POWER_DOWN_L_SFT 12 +#define NAU8325_POWER_DOWN_L_MASK (0x3 << NAU8325_POWER_DOWN_L_SFT) +#define NAU8325_POWER_DOWN_R_SFT 11 +#define NAU8325_POWER_DOWN_R_MASK (0x3 << NAU8325_DACREFCAP_SFT) +#define NAU8325_CLASSD_FINE_SFT 5 +#define NAU8325_CLASSD_FINE_MASK (0x3 << NAU8325_CLASSD_FINE_SFT) +#define NAU8325_CLASSD_COARSE_GAIN_MASK 0xf + +/* ANALOG_CONTROL_4 (0x64) */ +#define NAU8325_CLASSD_OCPN_SFT 12 +#define NAU8325_CLASSD_OCPN_MASK (0xf << NAU8325_CLASSD_OCPN_SFT) +#define NAU8325_CLASSD_OCPP_SFT 8 +#define NAU8325_CLASSD_OCPP_MASK (0xf << NAU8325_CLASSD_OCPP_SFT) +#define NAU8325_CLASSD_SLEWN_MASK 0xff + +/* ANALOG_CONTROL_5 (0x65) */ +#define NAU8325_MCLK_RANGE_SFT 2 +#define NAU8325_MCLK_RANGE_EN (0x1 << NAU8325_MCLK_RANGE_SFT) +#define NAU8325_MCLK8XEN_SFT 1 +#define NAU8325_MCLK8XEN_EN (0x1 << NAU8325_MCLK8XEN_SFT) +#define NAU8325_MCLK4XEN_EN 0x1 + +/* ANALOG_CONTROL_6 (0x66) */ +#define NAU8325_VBATLOW_SFT 4 +#define NAU8325_VBATLOW_MASK (0x1 << NAU8325_VBATLOW_SFT) +#define NAU8325_VDDSPK_LIM_SFT 3 +#define NAU8325_VDDSPK_LIM_EN (0x1 << NAU8325_VDDSPK_LIM_SFT) +#define NAU8325_VDDSPK_LIM_MASK 0x7 + +// R66: ANALOG_CONTROL_6 +#define NAU8325_ANALOG_CTRL6_SPKL_EN 0x0020 // Bit 5 +#define NAU8325_ANALOG_CTRL6_SPKR_EN 0x0010 // Bit 4 +#define NAU8325_ANALOG_CTRL6_EN_MASK 0x0030 // Bits 5:4 + +/* CLIP_CTRL (0x69)*/ +#define NAU8325_ANTI_CLIP_SFT 4 +#define NAU8325_ANTI_CLIP_EN (0x1 << NAU8325_ANTI_CLIP_SFT) + +/* RDAC (0x73) */ +#define NAU8325_CLK_DAC_DELAY_SFT 4 +#define NAU8325_CLK_DAC_DELAY_EN (0x7 << NAU8325_CLK_DAC_DELAY_SFT) +#define NAU8325_DACVREFSEL_SFT 2 +#define NAU8325_DACVREFSEL_MASK (0x3 << NAU8325_DACVREFSEL_SFT) + +#define CLK_DA_AD_MAX 6144000UL // or another max as per datasheet + +struct SrcAttr { + int param; + uint8_t val; +}; + +struct OsrAttr { + int osr; + uint8_t clk_src; +}; + +struct SRateAttr { + int fs; + uint8_t range; + bool max; + uint32_t mclk_src[3]; +}; + +struct RegDefault { + uint16_t reg; + uint16_t val; +}; + +enum NAU8325_I2SFormat { + I2S_STD, + LEFT_JUSTIFIED, + RIGHT_JUSTIFIED, + DSP_A, + DSP_B +}; +enum NAU8325_ClockPolarity { + NORMAL_BCLK, + INVERTED_BCLK +}; +enum OversamplingMode { + OSR_64 = 2, + OSR_128 = 1, + OSR_256 = 0, + OSR_32 = 4 +}; + +enum { + NAU8325_MCLK_FS_RATIO_256 = 0, + NAU8325_MCLK_FS_RATIO_400 = 1, + NAU8325_MCLK_FS_RATIO_500 = 2, + NAU8325_MCLK_FS_RATIO_NUM = 3 +}; + +class PCBCUPID_NAU8325 { +public: + PCBCUPID_NAU8325(TwoWire &wire, uint8_t i2c_addr); + + static const RegDefault reg_defaults[]; + + // Static constant lookup tables + static const SrcAttr mclk_n1_div[]; + static const SrcAttr mclk_n2_div[]; + static const SrcAttr mclk_n3_mult[]; + static const OsrAttr osr_dac_sel[]; + static const SRateAttr target_srate_table[]; + + bool begin(uint32_t fs, uint8_t bits_per_sample, uint16_t ratio); + bool begin(uint32_t fs, uint8_t bits_per_sample); + bool begin(); + + uint16_t readDeviceId(); + void resetChip(); + + void softMute(bool enable); + void powerOn(); + void powerOff(); + + void setPowerUpDefault(bool enable); + void setOversampling(OversamplingMode mode); + void setVolume(uint8_t left, uint8_t right); // public + + + +private: + // Add private members (Wire, I2C address, etc.) here later + TwoWire &i2c; + uint8_t i2c_addr; + uint32_t fs; + uint32_t mclk; + + bool clock_detection; + + void initRegisters(); // Initialize codec + + bool setSysClock(uint32_t freq); + bool configureAudio(uint32_t fs, uint32_t mclk, uint8_t bits_per_sample); + bool chooseClockSource(int fs, int mclk, const SRateAttr *&srate, int &n1_sel, int &mult_sel, int &n2_sel); + bool configureClocks(int fs, int mclk); + bool applySampleRateClocks(const SRateAttr *srate, int n1_sel, int mclk_mult_sel, int n2_sel); + int getMclkRatioAndN2Index(const SRateAttr *srate, int mclk_hz, int &n2_sel_out); + const SRateAttr *getSRateAttr(int fs); + const OsrAttr *getCurrentOSR(); + bool setI2SFormat(NAU8325_I2SFormat format, NAU8325_ClockPolarity polarity); + + void enableDAPMBlocks(); + + bool readRegister(uint16_t reg, uint16_t &value); + bool writeRegister(uint16_t reg, uint16_t value); + bool writeRegisterBits(uint16_t reg, uint16_t mask, uint16_t value); +}; + +#endif // PCBCUPID_NAU8325_H From d8d0e7c7aac2b8bf599e5dc8e4751593f7477504 Mon Sep 17 00:00:00 2001 From: codingkarthik94 Date: Wed, 9 Jul 2025 01:04:06 +0530 Subject: [PATCH 2/8] version 0.0.2 Changelogs -Added: custom-nau8325 sinewave-generator by integrating the driver class with PCBCUPID_NAU8325 driver -Tested: working , it is generating sine wave tone for N_A4 --- .../sinewave-generator/sinewave-generator.ino | 67 + src/AudioBoard.h | 3 + src/Driver.h | 2714 +++++++++-------- src/Driver/nau8325/PCBCUPID_NAU8325.cpp | 615 ++++ src/Driver/nau8325/PCBCUPID_NAU8325.h | 481 +++ src/DriverPins.h | 3 + 6 files changed, 2686 insertions(+), 1197 deletions(-) create mode 100644 examples/custom/custom-nau8325/sinewave-generator/sinewave-generator.ino create mode 100644 src/Driver/nau8325/PCBCUPID_NAU8325.cpp create mode 100644 src/Driver/nau8325/PCBCUPID_NAU8325.h diff --git a/examples/custom/custom-nau8325/sinewave-generator/sinewave-generator.ino b/examples/custom/custom-nau8325/sinewave-generator/sinewave-generator.ino new file mode 100644 index 0000000..e10c6f4 --- /dev/null +++ b/examples/custom/custom-nau8325/sinewave-generator/sinewave-generator.ino @@ -0,0 +1,67 @@ +#include +#include +#include +#include "Driver.h" + +using namespace audio_tools; +using namespace audio_driver; + +// === Pins === +#define SDAPIN 4 +#define SCLPIN 5 +#define I2CSPEED 100000 +#define NAU8325ADDR 0x21 +#define MCLKPIN 22 +#define BCLKPIN 25 +#define WSPIN 24 +#define DOPIN 23 +#define DIPIN -1 + +// === Audio Setup === +AudioInfo audio_info(44100, 2, 16); +SineWaveGenerator sine_wave(30000); +GeneratedSoundStream sound_stream(sine_wave); +StreamCopy copier; + +TwoWire myWire = TwoWire(0); +DriverPins pins; +AudioBoard board(AudioDriverNAU8325, pins); +I2SCodecStream i2s_out(board); + +void setup() { + Serial.begin(115200); + delay(1000); // Allow time for serial monitor to open + AudioLogger::instance().begin(Serial, AudioLogger::Info); + AudioDriverLogger.begin(Serial, AudioDriverLogLevel::Debug); + + // Setup I2C and I2S pins + pins.addI2C(PinFunction::CODEC, SCLPIN, SDAPIN, NAU8325ADDR, I2CSPEED, myWire); + pins.addI2S(PinFunction::CODEC, MCLKPIN, BCLKPIN, WSPIN, DOPIN, DIPIN); + pins.begin(); + + // Codec config + CodecConfig config; + config.i2s.rate = RATE_44K; + config.i2s.bits = BIT_LENGTH_16BITS; + config.i2s.channels = CHANNELS2; + + // Start codec board + if (!board.begin(config)) { + Serial.println("[NAU8325] board.begin() failed!"); + while (1); + } + + // Configure I2S output + auto cfg = i2s_out.defaultConfig(); + cfg.copyFrom(audio_info); + i2s_out.begin(cfg); + copier.begin(i2s_out, sound_stream); + + // Start sine wave + sine_wave.begin(audio_info, N_A4); + Serial.println("Sinewave output started."); +} + +void loop() { + copier.copy(); +} diff --git a/src/AudioBoard.h b/src/AudioBoard.h index 2e16d29..d36d2dc 100644 --- a/src/AudioBoard.h +++ b/src/AudioBoard.h @@ -131,6 +131,9 @@ static AudioBoard NoBoard{NoDriver, NoPins}; static AudioBoard GenericWM8960{AudioDriverWM8960, NoPins}; /// @ingroup audio_driver static AudioBoard GenericCS43l22{AudioDriverCS43l22, NoPins}; +/// @ingroup audio_driver +static AudioBoard NAU8325Board{AudioDriverNAU8325, NoPins}; + #if defined(ARDUINO_GENERIC_F411VETX) /// @ingroup audio_driver static AudioBoard STM32F411Disco{AudioDriverCS43l22, PinsSTM32F411Disco}; diff --git a/src/Driver.h b/src/Driver.h index aa24dab..eb87910 100644 --- a/src/Driver.h +++ b/src/Driver.h @@ -16,49 +16,58 @@ #include "Driver/wm8960/mtb_wm8960.h" #include "Driver/wm8978/WM8978.h" #include "Driver/wm8994/wm8994.h" +#include "Driver/nau8325/PCBCUPID_NAU8325.h" +#include "ConfigAudioDriver.h" // CodecConfig, Enums + #include "DriverCommon.h" #include "DriverPins.h" -namespace audio_driver { - -const int rate_num[14] = {8000, 11025, 16000, 22050, 24000, 32000, 44100, - 48000, 64000, 88200, 96000, 128000, 176400, 192000}; -const samplerate_t rate_code[14] = { - RATE_8K, RATE_11K, RATE_16K, RATE_22K, RATE_24K, RATE_32K, RATE_44K, - RATE_48K, RATE_64K, RATE_88K, RATE_96K, RATE_128K, RATE_176K, RATE_192K}; - -/** - * @brief I2S configuration and definition of input and output with default - * values - * @ingroup audio_driver - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class CodecConfig : public codec_config_t { - public: - /// @brief setup default values - CodecConfig() { - input_device = ADC_INPUT_LINE1; - output_device = DAC_OUTPUT_ALL; - i2s.bits = BIT_LENGTH_16BITS; - i2s.rate = RATE_44K; - i2s.channels = CHANNELS2; - i2s.fmt = I2S_NORMAL; - // codec is slave - microcontroller is master - i2s.mode = MODE_SLAVE; - } +namespace audio_driver +{ + + const int rate_num[14] = {8000, 11025, 16000, 22050, 24000, 32000, 44100, + 48000, 64000, 88200, 96000, 128000, 176400, 192000}; + const samplerate_t rate_code[14] = { + RATE_8K, RATE_11K, RATE_16K, RATE_22K, RATE_24K, RATE_32K, RATE_44K, + RATE_48K, RATE_64K, RATE_88K, RATE_96K, RATE_128K, RATE_176K, RATE_192K}; + + /** + * @brief I2S configuration and definition of input and output with default + * values + * @ingroup audio_driver + * @author Phil Schatzmann + * @copyright GPLv3 + */ + class CodecConfig : public codec_config_t + { + public: + /// @brief setup default values + CodecConfig() + { + input_device = ADC_INPUT_LINE1; + output_device = DAC_OUTPUT_ALL; + i2s.bits = BIT_LENGTH_16BITS; + i2s.rate = RATE_44K; + i2s.channels = CHANNELS2; + i2s.fmt = I2S_NORMAL; + // codec is slave - microcontroller is master + i2s.mode = MODE_SLAVE; + } - /// Compare all attributes but ignore sample rate - bool equalsExRate(CodecConfig alt) { - return (input_device == alt.input_device && - output_device == alt.output_device && i2s.bits == alt.i2s.bits && - i2s.channels == alt.i2s.channels && i2s.fmt == alt.i2s.fmt && - i2s.mode == alt.i2s.mode); - } + /// Compare all attributes but ignore sample rate + bool equalsExRate(CodecConfig alt) + { + return (input_device == alt.input_device && + output_device == alt.output_device && i2s.bits == alt.i2s.bits && + i2s.channels == alt.i2s.channels && i2s.fmt == alt.i2s.fmt && + i2s.mode == alt.i2s.mode); + } - /// Returns bits per sample as number - int getBitsNumeric() { - switch (i2s.bits) { + /// Returns bits per sample as number + int getBitsNumeric() + { + switch (i2s.bits) + { case BIT_LENGTH_16BITS: return 16; case BIT_LENGTH_24BITS: @@ -67,13 +76,15 @@ class CodecConfig : public codec_config_t { return 32; default: return 0; + } + return 0; } - return 0; - } - /// Sets the bits per sample with a numeric value - bool setBitsNumeric(int bits) { - switch (bits) { + /// Sets the bits per sample with a numeric value + bool setBitsNumeric(int bits) + { + switch (bits) + { case 16: i2s.bits = BIT_LENGTH_16BITS; return true; @@ -89,28 +100,33 @@ class CodecConfig : public codec_config_t { case 32: i2s.bits = BIT_LENGTH_32BITS; return true; + } + AD_LOGE("bits not supported: %d", bits); + return false; } - AD_LOGE("bits not supported: %d", bits); - return false; - } - /// Get the sample rate as number - int getRateNumeric() { - for (int j = 0; j < 14; j++) { - if (rate_code[j] == i2s.rate) { - AD_LOGD("-> %d", rate_num[j]); - return rate_num[j]; + /// Get the sample rate as number + int getRateNumeric() + { + for (int j = 0; j < 14; j++) + { + if (rate_code[j] == i2s.rate) + { + AD_LOGD("-> %d", rate_num[j]); + return rate_num[j]; + } } + return 0; } - return 0; - } - /// Returns the number of channels as number - int getChannelsNumeric() { return i2s.channels; } + /// Returns the number of channels as number + int getChannelsNumeric() { return i2s.channels; } - /// Defines the number of channels - bool setChannelsNumeric(int channels) { - switch (2) { + /// Defines the number of channels + bool setChannelsNumeric(int channels) + { + switch (2) + { case CHANNELS2: i2s.channels = (channels_t)channels; return true; @@ -127,353 +143,414 @@ class CodecConfig : public codec_config_t { AD_LOGE("Channels not supported: %d - using %d", channels, 2); i2s.channels = CHANNELS2; return false; + } } - } - /// Sets the sample rate as number: returns the effectively set rate - int setRateNumeric(int requestedRate) { - int diff = 99999; - int result = 0; - for (int j = 0; j < 14; j++) { - if (rate_num[j] == requestedRate) { - AD_LOGD("-> %d", rate_num[j]); - i2s.rate = rate_code[j]; - return requestedRate; - } else { - int new_diff = abs(rate_code[j] - requestedRate); - if (new_diff < diff) { - result = j; - diff = new_diff; + /// Sets the sample rate as number: returns the effectively set rate + int setRateNumeric(int requestedRate) + { + int diff = 99999; + int result = 0; + for (int j = 0; j < 14; j++) + { + if (rate_num[j] == requestedRate) + { + AD_LOGD("-> %d", rate_num[j]); + i2s.rate = rate_code[j]; + return requestedRate; + } + else + { + int new_diff = abs(rate_code[j] - requestedRate); + if (new_diff < diff) + { + result = j; + diff = new_diff; + } } } + AD_LOGE("Sample Rate not supported: %d - using %d", requestedRate, + rate_num[result]); + i2s.rate = rate_code[result]; + return rate_num[result]; } - AD_LOGE("Sample Rate not supported: %d - using %d", requestedRate, - rate_num[result]); - i2s.rate = rate_code[result]; - return rate_num[result]; - } - /// Determines the codec_mode_t dynamically based on the input and output - codec_mode_t get_mode() { - // codec_mode_t mode; - bool is_input = false; - bool is_output = false; + /// Determines the codec_mode_t dynamically based on the input and output + codec_mode_t get_mode() + { + // codec_mode_t mode; + bool is_input = false; + bool is_output = false; - if (input_device != ADC_INPUT_NONE) is_input = true; + if (input_device != ADC_INPUT_NONE) + is_input = true; - if (output_device != DAC_OUTPUT_NONE) is_output = true; + if (output_device != DAC_OUTPUT_NONE) + is_output = true; - if (is_input && is_output) { - AD_LOGD("mode->CODEC_MODE_BOTH"); - return CODEC_MODE_BOTH; - } + if (is_input && is_output) + { + AD_LOGD("mode->CODEC_MODE_BOTH"); + return CODEC_MODE_BOTH; + } - if (is_output) { - AD_LOGD("mode->CODEC_MODE_DECODE"); - return CODEC_MODE_DECODE; - } + if (is_output) + { + AD_LOGD("mode->CODEC_MODE_DECODE"); + return CODEC_MODE_DECODE; + } + + if (is_input) + { + AD_LOGD("mode->CODEC_MODE_ENCODE"); + return CODEC_MODE_ENCODE; + } - if (is_input) { - AD_LOGD("mode->CODEC_MODE_ENCODE"); - return CODEC_MODE_ENCODE; + AD_LOGD("mode->CODEC_MODE_NONE"); + return CODEC_MODE_NONE; } + // if sd active we setup SPI for the SD + bool sd_active = true; + }; - AD_LOGD("mode->CODEC_MODE_NONE"); - return CODEC_MODE_NONE; - } - // if sd active we setup SPI for the SD - bool sd_active = true; -}; + /** + * @brief Abstract Driver API for codec chips + * @ingroup audio_driver + * @author Phil Schatzmann + * @copyright GPLv3 + */ + class AudioDriver + { + public: + /// Starts the processing + virtual bool begin(CodecConfig codecCfg, DriverPins &pins) + { + AD_LOGI("AudioDriver::begin"); + p_pins = &pins; -/** - * @brief Abstract Driver API for codec chips - * @ingroup audio_driver - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class AudioDriver { - public: - /// Starts the processing - virtual bool begin(CodecConfig codecCfg, DriverPins &pins) { - AD_LOGI("AudioDriver::begin"); - p_pins = &pins; - - // Store default i2c address to pins - setupI2CAddress(); - - p_pins->setSPIActiveForSD(codec_cfg.sd_active); - if (!p_pins->begin()) { - AD_LOGE("AudioBoard::pins::begin failed"); - return false; - } + // Store default i2c address to pins + setupI2CAddress(); - if (!setConfig(codecCfg)) { - AD_LOGE("setConfig has failed"); - return false; + p_pins->setSPIActiveForSD(codec_cfg.sd_active); + if (!p_pins->begin()) + { + AD_LOGE("AudioBoard::pins::begin failed"); + return false; + } + + if (!setConfig(codecCfg)) + { + AD_LOGE("setConfig has failed"); + return false; + } + setPAPower(true); + // setup default volume + setVolume(DRIVER_DEFAULT_VOLUME); + return true; } - setPAPower(true); - // setup default volume - setVolume(DRIVER_DEFAULT_VOLUME); - return true; - } - /// changes the configuration - virtual bool setConfig(CodecConfig codecCfg) { - AD_LOGI("AudioDriver::setConfig"); - codec_cfg = codecCfg; - if (!init(codec_cfg)) { - AD_LOGE("AudioDriver init failed"); - return false; + /// changes the configuration + virtual bool setConfig(CodecConfig codecCfg) + { + AD_LOGI("AudioDriver::setConfig"); + codec_cfg = codecCfg; + if (!init(codec_cfg)) + { + AD_LOGE("AudioDriver init failed"); + return false; + } + codec_mode_t codec_mode = codec_cfg.get_mode(); + if (!controlState(codec_mode)) + { + AD_LOGE("AudioDriver controlState failed"); + return false; + } + bool result = configInterface(codec_mode, codec_cfg.i2s); + if (!result) + { + AD_LOGE("AudioDriver configInterface failed"); + return false; + } + return result; } - codec_mode_t codec_mode = codec_cfg.get_mode(); - if (!controlState(codec_mode)) { - AD_LOGE("AudioDriver controlState failed"); + /// Ends the processing: shut down dac and adc + virtual bool end(void) { return deinit(); } + /// Mutes all output lines + virtual bool setMute(bool enable) = 0; + /// Mute individual lines: only supported for some rare DACs + virtual bool setMute(bool mute, int line) + { + AD_LOGE("setMute not supported on line level"); return false; } - bool result = configInterface(codec_mode, codec_cfg.i2s); - if (!result) { - AD_LOGE("AudioDriver configInterface failed"); - return false; + + /// Defines the Volume (in %) if volume is 0, mute is enabled,range is 0-100. + virtual bool setVolume(int volume) = 0; + /// Determines the actual volume (range: 0-100) + virtual int getVolume() = 0; + /// Defines the input volume (range: 0-100) if supported + virtual bool setInputVolume(int volume) { return false; } + /// Determines if setVolume() is suppored + virtual bool isVolumeSupported() { return true; } + /// Determines if setInputVolume() is supported + virtual bool isInputVolumeSupported() { return false; } + /// Provides the pin information + virtual DriverPins &pins() { return *p_pins; } + + void setPins(DriverPins &pins) { p_pins = &pins; } + + /// Sets the PA Power pin to active or inactive + bool setPAPower(bool enable) + { + if (p_pins == nullptr) + { + AD_LOGI("pins are null"); + return false; + } + GpioPin pin = pins().getPinID(PinFunction::PA); + if (pin == -1) + { + AD_LOGI("PinFunction::PA not defined"); + return false; + } + AD_LOGI("setPAPower pin %d -> %d", pin, enable); + digitalWrite(pin, enable ? HIGH : LOW); + return true; } - return result; - } - /// Ends the processing: shut down dac and adc - virtual bool end(void) { return deinit(); } - /// Mutes all output lines - virtual bool setMute(bool enable) = 0; - /// Mute individual lines: only supported for some rare DACs - virtual bool setMute(bool mute, int line) { - AD_LOGE("setMute not supported on line level"); - return false; - } - /// Defines the Volume (in %) if volume is 0, mute is enabled,range is 0-100. - virtual bool setVolume(int volume) = 0; - /// Determines the actual volume (range: 0-100) - virtual int getVolume() = 0; - /// Defines the input volume (range: 0-100) if supported - virtual bool setInputVolume(int volume) { return false; } - /// Determines if setVolume() is suppored - virtual bool isVolumeSupported() { return true; } - /// Determines if setInputVolume() is supported - virtual bool isInputVolumeSupported() { return false; } - /// Provides the pin information - virtual DriverPins &pins() { return *p_pins; } - - void setPins(DriverPins &pins) { p_pins = &pins; } - - /// Sets the PA Power pin to active or inactive - bool setPAPower(bool enable) { - if (p_pins == nullptr) { - AD_LOGI("pins are null"); + operator bool() { return p_pins != nullptr; } + + /// Defines the i2c address + virtual bool setI2CAddress(uint16_t adr) + { + if (p_pins == nullptr) + return false; + // look it up from the pin definition + auto i2c = pins().getI2CPins(PinFunction::CODEC); + if (i2c) + { + AD_LOGI("==> Updating address: 0x%x", adr); + PinsI2C val = i2c.value(); + val.address = adr; + pins().setI2C(val); + return true; + } + else + { + // we must have a codec defined! + assert(false); + } return false; } - GpioPin pin = pins().getPinID(PinFunction::PA); - if (pin == -1) { - AD_LOGI("PinFunction::PA not defined"); - return false; + + /// Provides the i2c address + virtual int getI2CAddress() + { + if (p_pins == nullptr) + return i2c_default_address; + // look it up from the pin definition + auto i2c = pins().getI2CPins(PinFunction::CODEC); + if (i2c) + { + auto value = i2c.value(); + auto addr = value.address; + if (addr > -1) + return addr; + } + return i2c_default_address; } - AD_LOGI("setPAPower pin %d -> %d", pin, enable); - digitalWrite(pin, enable ? HIGH : LOW); - return true; - } - operator bool() { return p_pins != nullptr; } - - /// Defines the i2c address - virtual bool setI2CAddress(uint16_t adr) { - if (p_pins == nullptr) return false; - // look it up from the pin definition - auto i2c = pins().getI2CPins(PinFunction::CODEC); - if (i2c) { - AD_LOGI("==> Updating address: 0x%x", adr); - PinsI2C val = i2c.value(); - val.address = adr; - pins().setI2C(val); - return true; - } else { - // we must have a codec defined! - assert(false); + /// If no address is defined in the pins we provide it here + void setupI2CAddress() + { + AD_LOGI("setupI2CAddress: 0x%x", i2c_default_address); + int adr = getI2CAddress(); + setI2CAddress(adr); } - return false; - } - /// Provides the i2c address - virtual int getI2CAddress() { - if (p_pins == nullptr) return i2c_default_address; - // look it up from the pin definition - auto i2c = pins().getI2CPins(PinFunction::CODEC); - if (i2c) { - auto value = i2c.value(); - auto addr = value.address; - if (addr > -1) return addr; - } - return i2c_default_address; - } + protected: + CodecConfig codec_cfg; + DriverPins *p_pins = nullptr; + int i2c_default_address = -1; - /// If no address is defined in the pins we provide it here - void setupI2CAddress() { - AD_LOGI("setupI2CAddress: 0x%x", i2c_default_address); - int adr = getI2CAddress(); - setI2CAddress(adr); - } + int mapVolume(int x, int in_min, int in_max, int out_min, int out_max) + { + return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; + } - protected: - CodecConfig codec_cfg; - DriverPins *p_pins = nullptr; - int i2c_default_address = -1; + /// Determine the TwoWire object from the I2C config or use Wire + virtual i2c_bus_handle_t getI2C() + { + if (p_pins == nullptr) + return DEFAULT_WIRE; + auto i2c = pins().getI2CPins(PinFunction::CODEC); + if (!i2c) + { + return DEFAULT_WIRE; + } + i2c_bus_handle_t *result = (i2c_bus_handle_t *)i2c.value().p_wire; + return result; + } - int mapVolume(int x, int in_min, int in_max, int out_min, int out_max) { - return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; - } + virtual bool init(codec_config_t codec_cfg) { return false; } + virtual bool deinit() { return false; } + virtual bool controlState(codec_mode_t mode) { return false; }; + virtual bool configInterface(codec_mode_t mode, I2SDefinition iface) + { + return false; + }; - /// Determine the TwoWire object from the I2C config or use Wire - virtual i2c_bus_handle_t getI2C() { - if (p_pins == nullptr) return DEFAULT_WIRE; - auto i2c = pins().getI2CPins(PinFunction::CODEC); - if (!i2c) { - return DEFAULT_WIRE; + /// make sure that value is in range + /// @param volume + /// @return + int limitValue(int volume, int min = 0, int max = 100) + { + if (volume > max) + volume = max; + if (volume < min) + volume = min; + return volume; } - i2c_bus_handle_t *result = (i2c_bus_handle_t *)i2c.value().p_wire; - return result; - } + }; - virtual bool init(codec_config_t codec_cfg) { return false; } - virtual bool deinit() { return false; } - virtual bool controlState(codec_mode_t mode) { return false; }; - virtual bool configInterface(codec_mode_t mode, I2SDefinition iface) { - return false; + /** + * @brief Dummy Driver which does nothing. + * @ingroup audio_driver + * @author Phil Schatzmann + * @copyright GPLv3 + */ + class NoDriverClass : public AudioDriver + { + public: + virtual bool begin(CodecConfig codecCfg, DriverPins &pins) + { + // codec_cfg = codecCfg; + // p_pins = &pins; + return true; + } + virtual bool end(void) { return true; } + virtual bool setMute(bool enable) { return false; } + virtual bool setVolume(int volume) { return false; } + virtual int getVolume() { return 100; } + virtual bool setInputVolume(int volume) { return false; } + virtual bool isVolumeSupported() + { + { + return false; + } + } + virtual bool isInputVolumeSupported() { return false; } }; - /// make sure that value is in range - /// @param volume - /// @return - int limitValue(int volume, int min = 0, int max = 100) { - if (volume > max) volume = max; - if (volume < min) volume = min; - return volume; - } -}; + /** + * @brief Driver API for AC101 codec chip + * @author Phil Schatzmann + * @copyright GPLv3 + */ + class AudioDriverAC101Class : public AudioDriver + { + public: + AudioDriverAC101Class() { i2c_default_address = 0x1A; } + bool setMute(bool mute) { return ac101_set_voice_mute(mute); } + bool setVolume(int volume) + { + return ac101_set_voice_volume(limitValue(volume, 0, 100)); + }; + int getVolume() + { + int vol; + ac101_get_voice_volume(&vol); + return vol; + }; -/** - * @brief Dummy Driver which does nothing. - * @ingroup audio_driver - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class NoDriverClass : public AudioDriver { - public: - virtual bool begin(CodecConfig codecCfg, DriverPins &pins) { - // codec_cfg = codecCfg; - // p_pins = &pins; - return true; - } - virtual bool end(void) { return true; } - virtual bool setMute(bool enable) { return false; } - virtual bool setVolume(int volume) { return false; } - virtual int getVolume() { return 100; } - virtual bool setInputVolume(int volume) { return false; } - virtual bool isVolumeSupported() { + protected: + bool init(codec_config_t codec_cfg) { - return false; + return ac101_init(&codec_cfg, getI2C(), getI2CAddress()) == RESULT_OK; + } + bool deinit() { return ac101_deinit() == RESULT_OK; } + bool controlState(codec_mode_t mode) + { + return ac101_ctrl_state_active(mode, true) == RESULT_OK; + } + bool configInterface(codec_mode_t mode, I2SDefinition iface) + { + return ac101_config_i2s(mode, &iface) == RESULT_OK; } - } - virtual bool isInputVolumeSupported() { return false; } -}; - -/** - * @brief Driver API for AC101 codec chip - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class AudioDriverAC101Class : public AudioDriver { - public: - AudioDriverAC101Class() { i2c_default_address = 0x1A; } - bool setMute(bool mute) { return ac101_set_voice_mute(mute); } - bool setVolume(int volume) { - return ac101_set_voice_volume(limitValue(volume, 0, 100)); - }; - int getVolume() { - int vol; - ac101_get_voice_volume(&vol); - return vol; }; - protected: - bool init(codec_config_t codec_cfg) { - return ac101_init(&codec_cfg, getI2C(), getI2CAddress()) == RESULT_OK; - } - bool deinit() { return ac101_deinit() == RESULT_OK; } - bool controlState(codec_mode_t mode) { - return ac101_ctrl_state_active(mode, true) == RESULT_OK; - } - bool configInterface(codec_mode_t mode, I2SDefinition iface) { - return ac101_config_i2s(mode, &iface) == RESULT_OK; - } -}; - -/** - * @brief Driver API for the CS43l22 codec chip on 0x94 (0x4A<<1) - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class AudioDriverCS43l22Class : public AudioDriver { - public: - AudioDriverCS43l22Class(uint16_t deviceAddr = 0x4A) { - i2c_default_address = deviceAddr; - } + /** + * @brief Driver API for the CS43l22 codec chip on 0x94 (0x4A<<1) + * @author Phil Schatzmann + * @copyright GPLv3 + */ + class AudioDriverCS43l22Class : public AudioDriver + { + public: + AudioDriverCS43l22Class(uint16_t deviceAddr = 0x4A) + { + i2c_default_address = deviceAddr; + } - virtual bool begin(CodecConfig codecCfg, DriverPins &pins) { - AD_LOGD("AudioDriverCS43l22Class::begin"); - p_pins = &pins; - codec_cfg = codecCfg; - // manage reset pin -> acive high - setPAPower(true); - // Setup enable pin for codec - delay(100); - uint32_t freq = getFrequency(codec_cfg.i2s.rate); - uint16_t outputDevice = getOutput(codec_cfg.output_device); - AD_LOGD("cs43l22_Init"); - bool result = - cs43l22_Init(deviceAddr, outputDevice, volume, freq, getI2C()) == 0; - if (!result) { - AD_LOGE("error: cs43l22_Init"); - } - cs43l22_Play(deviceAddr, nullptr, 0); - return result; - } + virtual bool begin(CodecConfig codecCfg, DriverPins &pins) + { + AD_LOGD("AudioDriverCS43l22Class::begin"); + p_pins = &pins; + codec_cfg = codecCfg; + // manage reset pin -> acive high + setPAPower(true); + // Setup enable pin for codec + delay(100); + uint32_t freq = getFrequency(codec_cfg.i2s.rate); + uint16_t outputDevice = getOutput(codec_cfg.output_device); + AD_LOGD("cs43l22_Init"); + bool result = + cs43l22_Init(deviceAddr, outputDevice, volume, freq, getI2C()) == 0; + if (!result) + { + AD_LOGE("error: cs43l22_Init"); + } + cs43l22_Play(deviceAddr, nullptr, 0); + return result; + } - virtual bool setConfig(CodecConfig codecCfg) { - codec_cfg = codecCfg; - uint32_t freq = getFrequency(codec_cfg.i2s.rate); - uint16_t outputDevice = getOutput(codec_cfg.output_device); - return cs43l22_Init(deviceAddr, outputDevice, this->volume, freq, - getI2C()) == 0; - } + virtual bool setConfig(CodecConfig codecCfg) + { + codec_cfg = codecCfg; + uint32_t freq = getFrequency(codec_cfg.i2s.rate); + uint16_t outputDevice = getOutput(codec_cfg.output_device); + return cs43l22_Init(deviceAddr, outputDevice, this->volume, freq, + getI2C()) == 0; + } - bool setMute(bool mute) { - uint32_t rc = mute ? cs43l22_Pause(deviceAddr) : cs43l22_Resume(deviceAddr); - return rc == 0; - } + bool setMute(bool mute) + { + uint32_t rc = mute ? cs43l22_Pause(deviceAddr) : cs43l22_Resume(deviceAddr); + return rc == 0; + } - bool setVolume(int volume) { - this->volume = volume; - return cs43l22_SetVolume(deviceAddr, volume) == 0; - } - int getVolume() { return volume; } + bool setVolume(int volume) + { + this->volume = volume; + return cs43l22_SetVolume(deviceAddr, volume) == 0; + } + int getVolume() { return volume; } - protected: - uint16_t deviceAddr; - int volume = 100; + protected: + uint16_t deviceAddr; + int volume = 100; - bool deinit() { - int cnt = cs43l22_Stop(deviceAddr, AUDIO_MUTE_ON); - cnt += cs43l22_Reset(deviceAddr); - setPAPower(false); - return cnt == 0; - } + bool deinit() + { + int cnt = cs43l22_Stop(deviceAddr, AUDIO_MUTE_ON); + cnt += cs43l22_Reset(deviceAddr); + setPAPower(false); + return cnt == 0; + } - uint32_t getFrequency(samplerate_t rateNum) { - switch (rateNum) { + uint32_t getFrequency(samplerate_t rateNum) + { + switch (rateNum) + { case RATE_8K: return 8000; /*!< set to 8k samples per second */ case RATE_11K: @@ -492,12 +569,14 @@ class AudioDriverCS43l22Class : public AudioDriver { return 48000; /*!< set to 48k samples per second */ default: break; + } + return 44100; } - return 44100; - } - uint16_t getOutput(output_device_t output_device) { - switch (output_device) { + uint16_t getOutput(output_device_t output_device) + { + switch (output_device) + { case DAC_OUTPUT_NONE: return 0; case DAC_OUTPUT_LINE1: @@ -508,512 +587,596 @@ class AudioDriverCS43l22Class : public AudioDriver { return OUTPUT_DEVICE_BOTH; default: break; + } + return OUTPUT_DEVICE_BOTH; } - return OUTPUT_DEVICE_BOTH; - } -}; + }; -/** - * @brief Driver API for CS42448 TDS DAC/ADC - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class AudioDriverCS42448Class : public AudioDriver { - public: - AudioDriverCS42448Class() { i2c_default_address = 0x48; } - bool begin(CodecConfig codecCfg, DriverPins &pins) override { - cfg = codecCfg; - // setup pins - pins.begin(); - // setup cs42448 - cs42448.begin(cfg, getI2C(), getI2CAddress()); - cs42448.setMute(false); - return true; - } - virtual bool setConfig(CodecConfig codecCfg) { - bool result = true; - if (codecCfg.equalsExRate(cfg)) { - // just update the rate - if (cfg.i2s.rate != codecCfg.getRateNumeric()) { - cs42448.setMute(true); - cs42448.setSampleRate(codecCfg.getRateNumeric()); - cs42448.setMute(false); + /** + * @brief Driver API for CS42448 TDS DAC/ADC + * @author Phil Schatzmann + * @copyright GPLv3 + */ + class AudioDriverCS42448Class : public AudioDriver + { + public: + AudioDriverCS42448Class() { i2c_default_address = 0x48; } + bool begin(CodecConfig codecCfg, DriverPins &pins) override + { + cfg = codecCfg; + // setup pins + pins.begin(); + // setup cs42448 + cs42448.begin(cfg, getI2C(), getI2CAddress()); + cs42448.setMute(false); + return true; + } + virtual bool setConfig(CodecConfig codecCfg) + { + bool result = true; + if (codecCfg.equalsExRate(cfg)) + { + // just update the rate + if (cfg.i2s.rate != codecCfg.getRateNumeric()) + { + cs42448.setMute(true); + cs42448.setSampleRate(codecCfg.getRateNumeric()); + cs42448.setMute(false); + } } - } else { - assert(p_pins != nullptr); - result = begin(codecCfg, *p_pins); + else + { + assert(p_pins != nullptr); + result = begin(codecCfg, *p_pins); + } + return result; + } + bool end(void) override { return cs42448.end(); } + bool setMute(bool enable) override { return cs42448.setMute(enable); } + bool setMute(bool enable, int line) + { + return cs42448.setMuteDAC(line, enable); + } + /// Defines the Volume (in %) if volume is 0, mute is enabled,range is 0-100. + bool setVolume(int volume) override + { + this->volume = volume; + return cs42448.setVolumeDAC(2.55f * volume); + } + bool setVolume(int dac, int volume) + { + return cs42448.setVolumeDAC(dac, 2.55f * volume); } - return result; - } - bool end(void) override { return cs42448.end(); } - bool setMute(bool enable) override { return cs42448.setMute(enable); } - bool setMute(bool enable, int line) { - return cs42448.setMuteDAC(line, enable); - } - /// Defines the Volume (in %) if volume is 0, mute is enabled,range is 0-100. - bool setVolume(int volume) override { - this->volume = volume; - return cs42448.setVolumeDAC(2.55f * volume); - } - bool setVolume(int dac, int volume) { - return cs42448.setVolumeDAC(dac, 2.55f * volume); - } - int getVolume() override { return volume; } - bool setInputVolume(int volume) override { - int vol = mapVolume(volume, 0, 100, -128, 127); - return cs42448.setVolumeADC(vol); - } - bool isVolumeSupported() override { return true; } - bool isInputVolumeSupported() override { return true; } + int getVolume() override { return volume; } + bool setInputVolume(int volume) override + { + int vol = mapVolume(volume, 0, 100, -128, 127); + return cs42448.setVolumeADC(vol); + } + bool isVolumeSupported() override { return true; } + bool isInputVolumeSupported() override { return true; } - DriverPins &pins() { return *p_pins; } - CS42448 &driver() { return cs42448; } + DriverPins &pins() { return *p_pins; } + CS42448 &driver() { return cs42448; } - protected: - CS42448 cs42448; - int volume = 100; - CodecConfig cfg; -}; + protected: + CS42448 cs42448; + int volume = 100; + CodecConfig cfg; + }; -/** - * @brief Driver API for ES7210 codec chip. This chip supports only input! - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class AudioDriverES7210Class : public AudioDriver { - public: - AudioDriverES7210Class() { i2c_default_address = ES7210_AD1_AD0_00 >> 1; } - bool setMute(bool mute) { return es7210_set_mute(mute) == RESULT_OK; } - bool setVolume(int volume) { - this->volume = volume; - return es7210_adc_set_volume(limitValue(volume, 0, 100)) == RESULT_OK; - } - int getVolume() { return volume; } - bool setInputVolume(int volume) { return setVolume(volume); } - bool isInputVolumeSupported() { return true; } + /** + * @brief Driver API for ES7210 codec chip. This chip supports only input! + * @author Phil Schatzmann + * @copyright GPLv3 + */ + class AudioDriverES7210Class : public AudioDriver + { + public: + AudioDriverES7210Class() { i2c_default_address = ES7210_AD1_AD0_00 >> 1; } + bool setMute(bool mute) { return es7210_set_mute(mute) == RESULT_OK; } + bool setVolume(int volume) + { + this->volume = volume; + return es7210_adc_set_volume(limitValue(volume, 0, 100)) == RESULT_OK; + } + int getVolume() { return volume; } + bool setInputVolume(int volume) { return setVolume(volume); } + bool isInputVolumeSupported() { return true; } - protected: - int volume; + protected: + int volume; - bool init(codec_config_t codec_cfg) { - return es7210_adc_init(&codec_cfg, getI2C()) == RESULT_OK; - } - bool deinit() { return es7210_adc_deinit() == RESULT_OK; } + bool init(codec_config_t codec_cfg) + { + return es7210_adc_init(&codec_cfg, getI2C()) == RESULT_OK; + } + bool deinit() { return es7210_adc_deinit() == RESULT_OK; } - bool controlState(codec_mode_t mode) { - return es7210_adc_ctrl_state_active(mode, true) == RESULT_OK; - } - bool configInterface(codec_mode_t mode, I2SDefinition iface) { - return es7210_adc_config_i2s(mode, &iface) == RESULT_OK; - } -}; + bool controlState(codec_mode_t mode) + { + return es7210_adc_ctrl_state_active(mode, true) == RESULT_OK; + } + bool configInterface(codec_mode_t mode, I2SDefinition iface) + { + return es7210_adc_config_i2s(mode, &iface) == RESULT_OK; + } + }; -/** - * @brief Driver API for Lyrat ES7243 codec chip - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class AudioDriverES7243Class : public AudioDriver { - public: - AudioDriverES7243Class() { i2c_default_address = 0x13; } - bool setMute(bool mute) { - return es7243_adc_set_voice_mute(mute) == RESULT_OK; - } - bool setVolume(int volume) { - return es7243_adc_set_voice_volume(limitValue(volume, 0, 100)) == RESULT_OK; - } - int getVolume() { - int vol; - es7243_adc_get_voice_volume(&vol); - return vol; - } + /** + * @brief Driver API for Lyrat ES7243 codec chip + * @author Phil Schatzmann + * @copyright GPLv3 + */ + class AudioDriverES7243Class : public AudioDriver + { + public: + AudioDriverES7243Class() { i2c_default_address = 0x13; } + bool setMute(bool mute) + { + return es7243_adc_set_voice_mute(mute) == RESULT_OK; + } + bool setVolume(int volume) + { + return es7243_adc_set_voice_volume(limitValue(volume, 0, 100)) == RESULT_OK; + } + int getVolume() + { + int vol; + es7243_adc_get_voice_volume(&vol); + return vol; + } - protected: - bool init(codec_config_t codec_cfg) { - return es7243_adc_init(&codec_cfg, getI2C()) == RESULT_OK; - } - bool deinit() { return es7243_adc_deinit() == RESULT_OK; } + protected: + bool init(codec_config_t codec_cfg) + { + return es7243_adc_init(&codec_cfg, getI2C()) == RESULT_OK; + } + bool deinit() { return es7243_adc_deinit() == RESULT_OK; } - bool controlState(codec_mode_t mode) { - return es7243_adc_ctrl_state_active(mode, true) == RESULT_OK; - } - bool configInterface(codec_mode_t mode, I2SDefinition iface) { - return es7243_adc_config_i2s(mode, &iface) == RESULT_OK; - } -}; + bool controlState(codec_mode_t mode) + { + return es7243_adc_ctrl_state_active(mode, true) == RESULT_OK; + } + bool configInterface(codec_mode_t mode, I2SDefinition iface) + { + return es7243_adc_config_i2s(mode, &iface) == RESULT_OK; + } + }; -/** - * @brief Driver API for ES7243e codec chip - * @author Phil Schatzmann - * @copyright GPLv3 - */ + /** + * @brief Driver API for ES7243e codec chip + * @author Phil Schatzmann + * @copyright GPLv3 + */ + + class AudioDriverES7243eClass : public AudioDriver + { + public: + AudioDriverES7243eClass() { i2c_default_address = 0x13; } + bool setMute(bool mute) + { + return mute ? setVolume(0) == RESULT_OK : setVolume(volume) == RESULT_OK; + } + bool setVolume(int volume) + { + this->volume = volume; + return es7243e_adc_set_voice_volume(limitValue(volume, 0, 100)) == + RESULT_OK; + } + int getVolume() + { + int vol; + es7243e_adc_get_voice_volume(&vol); + return vol; + } -class AudioDriverES7243eClass : public AudioDriver { - public: - AudioDriverES7243eClass() { i2c_default_address = 0x13; } - bool setMute(bool mute) { - return mute ? setVolume(0) == RESULT_OK : setVolume(volume) == RESULT_OK; - } - bool setVolume(int volume) { - this->volume = volume; - return es7243e_adc_set_voice_volume(limitValue(volume, 0, 100)) == - RESULT_OK; - } - int getVolume() { - int vol; - es7243e_adc_get_voice_volume(&vol); - return vol; - } + protected: + int volume = 0; - protected: - int volume = 0; + bool init(codec_config_t codec_cfg) + { + return es7243e_adc_init(&codec_cfg, getI2C()) == RESULT_OK; + } + bool deinit() { return es7243e_adc_deinit() == RESULT_OK; } - bool init(codec_config_t codec_cfg) { - return es7243e_adc_init(&codec_cfg, getI2C()) == RESULT_OK; - } - bool deinit() { return es7243e_adc_deinit() == RESULT_OK; } + bool controlState(codec_mode_t mode) + { + return es7243e_adc_ctrl_state_active(mode, true) == RESULT_OK; + } + bool configInterface(codec_mode_t mode, I2SDefinition iface) + { + return es7243e_adc_config_i2s(mode, &iface) == RESULT_OK; + } + }; - bool controlState(codec_mode_t mode) { - return es7243e_adc_ctrl_state_active(mode, true) == RESULT_OK; - } - bool configInterface(codec_mode_t mode, I2SDefinition iface) { - return es7243e_adc_config_i2s(mode, &iface) == RESULT_OK; - } -}; + /** + * @brief Driver API for ES8156 codec chip + * @author Phil Schatzmann + * @copyright GPLv3 + */ + class AudioDriverES8156Class : public AudioDriver + { + public: + AudioDriverES8156Class() { i2c_default_address = 0x8; } + bool setMute(bool mute) + { + return es8156_codec_set_voice_mute(mute) == RESULT_OK; + } + bool setVolume(int volume) + { + AD_LOGD("volume %d", volume); + return es8156_codec_set_voice_volume(limitValue(volume, 0, 100)) == + RESULT_OK; + } + int getVolume() + { + int vol; + es8156_codec_get_voice_volume(&vol); + return vol; + } -/** - * @brief Driver API for ES8156 codec chip - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class AudioDriverES8156Class : public AudioDriver { - public: - AudioDriverES8156Class() { i2c_default_address = 0x8; } - bool setMute(bool mute) { - return es8156_codec_set_voice_mute(mute) == RESULT_OK; - } - bool setVolume(int volume) { - AD_LOGD("volume %d", volume); - return es8156_codec_set_voice_volume(limitValue(volume, 0, 100)) == - RESULT_OK; - } - int getVolume() { - int vol; - es8156_codec_get_voice_volume(&vol); - return vol; - } + protected: + bool init(codec_config_t codec_cfg) + { + return es8156_codec_init(&codec_cfg, getI2C()) == RESULT_OK; + } + bool deinit() { return es8156_codec_deinit() == RESULT_OK; } - protected: - bool init(codec_config_t codec_cfg) { - return es8156_codec_init(&codec_cfg, getI2C()) == RESULT_OK; - } - bool deinit() { return es8156_codec_deinit() == RESULT_OK; } + bool controlState(codec_mode_t mode) + { + return es8156_codec_ctrl_state_active(mode, true) == RESULT_OK; + } + bool configInterface(codec_mode_t mode, I2SDefinition iface) + { + return es8156_codec_config_i2s(mode, &iface) == RESULT_OK; + } + }; - bool controlState(codec_mode_t mode) { - return es8156_codec_ctrl_state_active(mode, true) == RESULT_OK; - } - bool configInterface(codec_mode_t mode, I2SDefinition iface) { - return es8156_codec_config_i2s(mode, &iface) == RESULT_OK; - } -}; + /** + * @brief Driver API for Lyrat ES8311 codec chip + * @author Phil Schatzmann + * @copyright GPLv3 + */ + class AudioDriverES8311Class : public AudioDriver + { + public: + AudioDriverES8311Class() { i2c_default_address = 0x18; } + bool setMute(bool mute) { return es8311_set_voice_mute(mute) == RESULT_OK; } + bool setVolume(int volume) + { + return es8311_codec_set_voice_volume(limitValue(volume, 0, 100)) == + RESULT_OK; + } + int getVolume() + { + int vol; + es8311_codec_get_voice_volume(&vol); + return vol; + } -/** - * @brief Driver API for Lyrat ES8311 codec chip - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class AudioDriverES8311Class : public AudioDriver { - public: - AudioDriverES8311Class() { i2c_default_address = 0x18; } - bool setMute(bool mute) { return es8311_set_voice_mute(mute) == RESULT_OK; } - bool setVolume(int volume) { - return es8311_codec_set_voice_volume(limitValue(volume, 0, 100)) == - RESULT_OK; - } - int getVolume() { - int vol; - es8311_codec_get_voice_volume(&vol); - return vol; - } + protected: + bool init(codec_config_t codec_cfg) + { + int mclk_src = pins().getPinID(PinFunction::MCLK_SOURCE); + if (mclk_src == -1) + { + mclk_src = 0; // = FROM_MCLK_PIN; + } + AD_LOGI("MCLK_SOURCE: %d", mclk_src); - protected: - bool init(codec_config_t codec_cfg) { - int mclk_src = pins().getPinID(PinFunction::MCLK_SOURCE); - if (mclk_src == -1) { - mclk_src = 0; // = FROM_MCLK_PIN; + assert(getI2C() != nullptr); + return es8311_codec_init(&codec_cfg, getI2C(), mclk_src, getI2CAddress()) == + RESULT_OK; } - AD_LOGI("MCLK_SOURCE: %d", mclk_src); + bool deinit() { return es8311_codec_deinit() == RESULT_OK; } - assert(getI2C() != nullptr); - return es8311_codec_init(&codec_cfg, getI2C(), mclk_src, getI2CAddress()) == - RESULT_OK; - } - bool deinit() { return es8311_codec_deinit() == RESULT_OK; } + bool controlState(codec_mode_t mode) + { + return es8311_codec_ctrl_state_active(mode, true) == RESULT_OK; + } + bool configInterface(codec_mode_t mode, I2SDefinition iface) + { + return es8311_codec_config_i2s(mode, &iface) == RESULT_OK; + } + }; - bool controlState(codec_mode_t mode) { - return es8311_codec_ctrl_state_active(mode, true) == RESULT_OK; - } - bool configInterface(codec_mode_t mode, I2SDefinition iface) { - return es8311_codec_config_i2s(mode, &iface) == RESULT_OK; - } -}; + /** + * @brief Driver API for ES8374 codec chip + * @author Phil Schatzmann + * @copyright GPLv3 + */ + class AudioDriverES8374Class : public AudioDriver + { + public: + AudioDriverES8374Class() { i2c_default_address = 0x10; } + bool setMute(bool mute) { return es8374_set_voice_mute(mute) == RESULT_OK; } + bool setVolume(int volume) + { + AD_LOGD("volume %d", volume); + return es8374_codec_set_voice_volume(limitValue(volume, 0, 100)) == + RESULT_OK; + } + int getVolume() + { + int vol; + es8374_codec_get_voice_volume(&vol); + return vol; + } -/** - * @brief Driver API for ES8374 codec chip - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class AudioDriverES8374Class : public AudioDriver { - public: - AudioDriverES8374Class() { i2c_default_address = 0x10; } - bool setMute(bool mute) { return es8374_set_voice_mute(mute) == RESULT_OK; } - bool setVolume(int volume) { - AD_LOGD("volume %d", volume); - return es8374_codec_set_voice_volume(limitValue(volume, 0, 100)) == - RESULT_OK; - } - int getVolume() { - int vol; - es8374_codec_get_voice_volume(&vol); - return vol; - } + protected: + bool init(codec_config_t codec_cfg) + { + auto codec_mode = this->codec_cfg.get_mode(); + return es8374_codec_init(&codec_cfg, codec_mode, getI2C(), + getI2CAddress()) == RESULT_OK; + } + bool deinit() { return es8374_codec_deinit() == RESULT_OK; } - protected: - bool init(codec_config_t codec_cfg) { - auto codec_mode = this->codec_cfg.get_mode(); - return es8374_codec_init(&codec_cfg, codec_mode, getI2C(), - getI2CAddress()) == RESULT_OK; - } - bool deinit() { return es8374_codec_deinit() == RESULT_OK; } + bool controlState(codec_mode_t mode) + { + return es8374_codec_ctrl_state_active(mode, true) == RESULT_OK; + } + bool configInterface(codec_mode_t mode, I2SDefinition iface) + { + return es8374_codec_config_i2s(mode, &iface) == RESULT_OK; + } + }; + + /** + * @brief Driver API for ES8388 codec chip + * @author Phil Schatzmann + * @copyright GPLv3 + */ + class AudioDriverES8388Class : public AudioDriver + { + public: + AudioDriverES8388Class(int volumeHack) + { + i2c_default_address = 0x10; + volume_hack = volumeHack; + } + AudioDriverES8388Class() { i2c_default_address = 0x10; } + bool setMute(bool mute) + { + line_active[0] = !mute; + line_active[1] = !mute; + return es8388_set_voice_mute(mute) == RESULT_OK; + } + // mute individual line: lines start at 0 (valid range 0:1) + bool setMute(bool mute, int line) + { + if (line > 1) + { + AD_LOGD("invalid line %d", line); + return false; + } + // mute is managed on line level, so deactivate global mute + line_active[line] = !mute; + if (line_active[0] && line_active[1]) + { + return es8388_config_output_device(DAC_OUTPUT_ALL) == RESULT_OK; + } + else if (!line_active[0] && !line_active[1]) + { + return es8388_config_output_device(DAC_OUTPUT_NONE) == RESULT_OK; + } + else if (line_active[0]) + { + return es8388_config_output_device(DAC_OUTPUT_LINE1) == RESULT_OK; + } + else if (line_active[1]) + { + return es8388_config_output_device(DAC_OUTPUT_LINE2) == RESULT_OK; + } + return false; + } + bool setVolume(int volume) + { + AD_LOGD("volume %d", volume); + return es8388_set_voice_volume(limitValue(volume, 0, 100)) == RESULT_OK; + } + int getVolume() + { + int vol; + es8388_get_voice_volume(&vol); + return vol; + } - bool controlState(codec_mode_t mode) { - return es8374_codec_ctrl_state_active(mode, true) == RESULT_OK; - } - bool configInterface(codec_mode_t mode, I2SDefinition iface) { - return es8374_codec_config_i2s(mode, &iface) == RESULT_OK; - } -}; + bool setInputVolume(int volume) + { + // mapVolume values from 0 - 100 to 0 to 8 + + // es_mic_gain_t: MIC_GAIN_MIN = -1, 0,3,6,9,12,15,18,21,24 MIC_GAIN_MAX = + // 25 + + // Vol: 0, 12.5, 25, 37.5, 50, 62.5, 75, 87.5, 100 + // idx: 0, 1, 2, 3, 4, 5, 6, 7, 8 + // dB/gain: 0, 3, 6, 9, 12, 15, 18, 21, 24 + // factor: 1, 2, 4, 8, 16, 32, 63, 126, 252 + + // es8388 Register 9 – ADC Control 1 + // dB MicL MicR + // 0 0000 0000 + // 3 0001 0001 + // 6 0010 0010 + // 9 0011 0011 + // 12 0100 0100 + // 15 0101 0101 + // 18 0110 0110 + // 21 0111 0111 + // 24 1000 1000 + + es_mic_gain_t gains[] = {MIC_GAIN_0DB, MIC_GAIN_3DB, MIC_GAIN_6DB, + MIC_GAIN_9DB, MIC_GAIN_12DB, MIC_GAIN_15DB, + MIC_GAIN_18DB, MIC_GAIN_21DB, MIC_GAIN_24DB}; + + int vol = limitValue(volume, 0, 100); + int idx = mapVolume(vol, 0, 100, 0, 8); + + es_mic_gain_t gain = gains[idx]; + AD_LOGD("input volume: %d -> gain %d [dB] (idx: %d of 0..8)", volume, gain, + idx); + return setMicrophoneGain(gain); + } -/** - * @brief Driver API for ES8388 codec chip - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class AudioDriverES8388Class : public AudioDriver { - public: - AudioDriverES8388Class(int volumeHack) { - i2c_default_address = 0x10; - volume_hack = volumeHack; - } - AudioDriverES8388Class() { i2c_default_address = 0x10; } - bool setMute(bool mute) { - line_active[0] = !mute; - line_active[1] = !mute; - return es8388_set_voice_mute(mute) == RESULT_OK; - } - // mute individual line: lines start at 0 (valid range 0:1) - bool setMute(bool mute, int line) { - if (line > 1) { - AD_LOGD("invalid line %d", line); - return false; + bool setMicrophoneGain(es_mic_gain_t gain) + { + return es8388_set_mic_gain(gain) == RESULT_OK; } - // mute is managed on line level, so deactivate global mute - line_active[line] = !mute; - if (line_active[0] && line_active[1]) { - return es8388_config_output_device(DAC_OUTPUT_ALL) == RESULT_OK; - } else if (!line_active[0] && !line_active[1]) { - return es8388_config_output_device(DAC_OUTPUT_NONE) == RESULT_OK; - } else if (line_active[0]) { - return es8388_config_output_device(DAC_OUTPUT_LINE1) == RESULT_OK; - } else if (line_active[1]) { - return es8388_config_output_device(DAC_OUTPUT_LINE2) == RESULT_OK; - } - return false; - } - bool setVolume(int volume) { - AD_LOGD("volume %d", volume); - return es8388_set_voice_volume(limitValue(volume, 0, 100)) == RESULT_OK; - } - int getVolume() { - int vol; - es8388_get_voice_volume(&vol); - return vol; - } - bool setInputVolume(int volume) { - // mapVolume values from 0 - 100 to 0 to 8 - - // es_mic_gain_t: MIC_GAIN_MIN = -1, 0,3,6,9,12,15,18,21,24 MIC_GAIN_MAX = - // 25 - - // Vol: 0, 12.5, 25, 37.5, 50, 62.5, 75, 87.5, 100 - // idx: 0, 1, 2, 3, 4, 5, 6, 7, 8 - // dB/gain: 0, 3, 6, 9, 12, 15, 18, 21, 24 - // factor: 1, 2, 4, 8, 16, 32, 63, 126, 252 - - // es8388 Register 9 – ADC Control 1 - // dB MicL MicR - // 0 0000 0000 - // 3 0001 0001 - // 6 0010 0010 - // 9 0011 0011 - // 12 0100 0100 - // 15 0101 0101 - // 18 0110 0110 - // 21 0111 0111 - // 24 1000 1000 - - es_mic_gain_t gains[] = {MIC_GAIN_0DB, MIC_GAIN_3DB, MIC_GAIN_6DB, - MIC_GAIN_9DB, MIC_GAIN_12DB, MIC_GAIN_15DB, - MIC_GAIN_18DB, MIC_GAIN_21DB, MIC_GAIN_24DB}; - - int vol = limitValue(volume, 0, 100); - int idx = mapVolume(vol, 0, 100, 0, 8); - - es_mic_gain_t gain = gains[idx]; - AD_LOGD("input volume: %d -> gain %d [dB] (idx: %d of 0..8)", volume, gain, - idx); - return setMicrophoneGain(gain); - } + bool isInputVolumeSupported() { return true; } - bool setMicrophoneGain(es_mic_gain_t gain) { - return es8388_set_mic_gain(gain) == RESULT_OK; - } + void setVolumeHack(int volume_hack) { this->volume_hack = volume_hack; } + int getVolumeHack() { return volume_hack; } - bool isInputVolumeSupported() { return true; } + protected: + bool line_active[2] = {true}; + int volume_hack = AI_THINKER_ES8388_VOLUME_HACK; - void setVolumeHack(int volume_hack) { this->volume_hack = volume_hack; } - int getVolumeHack() { return volume_hack; } + bool init(codec_config_t codec_cfg) + { + return es8388_init(&codec_cfg, getI2C(), getI2CAddress(), volume_hack) == + RESULT_OK; + } + bool deinit() { return es8388_deinit() == RESULT_OK; } - protected: - bool line_active[2] = {true}; - int volume_hack = AI_THINKER_ES8388_VOLUME_HACK; + bool controlState(codec_mode_t mode) + { + return es8388_ctrl_state_active(mode, true) == RESULT_OK; + } + bool configInterface(codec_mode_t mode, I2SDefinition iface) + { + return es8388_config_i2s(mode, &iface) == RESULT_OK; + } + }; - bool init(codec_config_t codec_cfg) { - return es8388_init(&codec_cfg, getI2C(), getI2CAddress(), volume_hack) == - RESULT_OK; - } - bool deinit() { return es8388_deinit() == RESULT_OK; } + /** + * @brief Driver API for TAS5805M codec chip + * @author Phil Schatzmann + * @copyright GPLv3 + */ + class AudioDriverTAS5805MClass : public AudioDriver + { + public: + AudioDriverTAS5805MClass() { i2c_default_address = 0x2E; } + bool setMute(bool mute) { return tas5805m_set_mute(mute) == RESULT_OK; } + bool setVolume(int volume) + { + AD_LOGD("volume %d", volume); + return tas5805m_set_volume(limitValue(volume, 0, 100)) == RESULT_OK; + } + int getVolume() + { + int vol; + tas5805m_get_volume(&vol); + return vol; + } - bool controlState(codec_mode_t mode) { - return es8388_ctrl_state_active(mode, true) == RESULT_OK; - } - bool configInterface(codec_mode_t mode, I2SDefinition iface) { - return es8388_config_i2s(mode, &iface) == RESULT_OK; - } -}; + protected: + bool init(codec_config_t codec_cfg) + { + return tas5805m_init(&codec_cfg, getI2C()) == RESULT_OK; + } + bool deinit() { return tas5805m_deinit() == RESULT_OK; } + }; -/** - * @brief Driver API for TAS5805M codec chip - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class AudioDriverTAS5805MClass : public AudioDriver { - public: - AudioDriverTAS5805MClass() { i2c_default_address = 0x2E; } - bool setMute(bool mute) { return tas5805m_set_mute(mute) == RESULT_OK; } - bool setVolume(int volume) { - AD_LOGD("volume %d", volume); - return tas5805m_set_volume(limitValue(volume, 0, 100)) == RESULT_OK; - } - int getVolume() { - int vol; - tas5805m_get_volume(&vol); - return vol; - } + /** + * @brief Driver API for WM8990 codec chip + * @author Phil Schatzmann + * @copyright GPLv3 + */ + class AudioDriverWM8960Class : public AudioDriver + { + public: + AudioDriverWM8960Class() { i2c_default_address = 0x1A; } + bool begin(CodecConfig codecCfg, DriverPins &pins) + { + codec_cfg = codecCfg; - protected: - bool init(codec_config_t codec_cfg) { - return tas5805m_init(&codec_cfg, getI2C()) == RESULT_OK; - } - bool deinit() { return tas5805m_deinit() == RESULT_OK; } -}; + // define wire object + mtb_wm8960_set_wire(getI2C()); + mtb_wm8960_set_write_retry_count(i2c_retry_count); -/** - * @brief Driver API for WM8990 codec chip - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class AudioDriverWM8960Class : public AudioDriver { - public: - AudioDriverWM8960Class() { i2c_default_address = 0x1A; } - bool begin(CodecConfig codecCfg, DriverPins &pins) { - codec_cfg = codecCfg; - - // define wire object - mtb_wm8960_set_wire(getI2C()); - mtb_wm8960_set_write_retry_count(i2c_retry_count); - - // setup wm8960 - int features = getFeatures(codecCfg); - if (!mtb_wm8960_init(features)) { - AD_LOGE("mtb_wm8960_init"); - return false; + // setup wm8960 + int features = getFeatures(codecCfg); + if (!mtb_wm8960_init(features)) + { + AD_LOGE("mtb_wm8960_init"); + return false; + } + setVolume(DRIVER_DEFAULT_VOLUME); + if (!mtb_wm8960_activate()) + { + AD_LOGE("mtb_wm8960_activate"); + return false; + } + if (!configure_clocking()) + { + AD_LOGE("configure_clocking"); + return false; + } + return true; } - setVolume(DRIVER_DEFAULT_VOLUME); - if (!mtb_wm8960_activate()) { - AD_LOGE("mtb_wm8960_activate"); - return false; + bool end(void) + { + mtb_wm8960_deactivate(); + mtb_wm8960_free(); + return true; } - if (!configure_clocking()) { - AD_LOGE("configure_clocking"); - return false; + virtual bool setConfig(CodecConfig codecCfg) + { + codec_cfg = codecCfg; + return configure_clocking(); } - return true; - } - bool end(void) { - mtb_wm8960_deactivate(); - mtb_wm8960_free(); - return true; - } - virtual bool setConfig(CodecConfig codecCfg) { - codec_cfg = codecCfg; - return configure_clocking(); - } - bool setMute(bool enable) { return setVolume(enable ? 0 : volume_out); } + bool setMute(bool enable) { return setVolume(enable ? 0 : volume_out); } - /// Defines the Volume (in %) if volume is 0, mute is enabled,range is 0-100. - bool setVolume(int volume) { - volume_out = limitValue(volume, 0, 100); - int vol_int = - volume_out == 0.0 ? 0 : mapVolume(volume_out, 0, 100, 30, 0x7F); - return mtb_wm8960_set_output_volume(vol_int); - } + /// Defines the Volume (in %) if volume is 0, mute is enabled,range is 0-100. + bool setVolume(int volume) + { + volume_out = limitValue(volume, 0, 100); + int vol_int = + volume_out == 0.0 ? 0 : mapVolume(volume_out, 0, 100, 30, 0x7F); + return mtb_wm8960_set_output_volume(vol_int); + } - int getVolume() { return volume_out; } + int getVolume() { return volume_out; } - bool setInputVolume(int volume) { - volume_in = limitValue(volume, 0, 100); - int vol_int = mapVolume(volume_in, 0, 100, 0, 30); - return mtb_wm8960_adjust_input_volume(vol_int); - } - bool isVolumeSupported() { return true; } + bool setInputVolume(int volume) + { + volume_in = limitValue(volume, 0, 100); + int vol_int = mapVolume(volume_in, 0, 100, 0, 30); + return mtb_wm8960_adjust_input_volume(vol_int); + } + bool isVolumeSupported() { return true; } - bool isInputVolumeSupported() { return true; } + bool isInputVolumeSupported() { return true; } - /// Configuration: define retry count (default : 0) - void setI2CRetryCount(int cnt) { i2c_retry_count = cnt; } + /// Configuration: define retry count (default : 0) + void setI2CRetryCount(int cnt) { i2c_retry_count = cnt; } - /// Configuration: enable/disable PLL (active by default) - void setEnablePLL(bool active) { vs1053_enable_pll = active; } + /// Configuration: enable/disable PLL (active by default) + void setEnablePLL(bool active) { vs1053_enable_pll = active; } - /// Configuration: define master clock frequency (default: 0) - void setMclkHz(uint32_t hz) { vs1053_mclk_hz = hz; } + /// Configuration: define master clock frequency (default: 0) + void setMclkHz(uint32_t hz) { vs1053_mclk_hz = hz; } - void dumpRegisters() { mtb_wm8960_dump(); } + void dumpRegisters() { mtb_wm8960_dump(); } - protected: - int volume_in = 100; - int volume_out = 100; - int i2c_retry_count = 0; - uint32_t vs1053_mclk_hz = 0; - bool vs1053_enable_pll = true; + protected: + int volume_in = 100; + int volume_out = 100; + int i2c_retry_count = 0; + uint32_t vs1053_mclk_hz = 0; + bool vs1053_enable_pll = true; - int getFeatures(CodecConfig cfg) { - int features = 0; - switch (cfg.output_device) { + int getFeatures(CodecConfig cfg) + { + int features = 0; + switch (cfg.output_device) + { case DAC_OUTPUT_LINE1: features = features | WM8960_FEATURE_SPEAKER; break; @@ -1025,8 +1188,9 @@ class AudioDriverWM8960Class : public AudioDriver { break; default: break; - } - switch (cfg.input_device) { + } + switch (cfg.input_device) + { case ADC_INPUT_LINE1: features = features | WM8960_FEATURE_MICROPHONE1; break; @@ -1039,29 +1203,34 @@ class AudioDriverWM8960Class : public AudioDriver { break; default: break; + } + AD_LOGI("features: %d", features); + return features; } - AD_LOGI("features: %d", features); - return features; - } - bool configure_clocking() { - if (vs1053_mclk_hz == 0) { - // just pick a multiple of the sample rate - vs1053_mclk_hz = 512 * codec_cfg.getRateNumeric(); - } - if (!mtb_wm8960_configure_clocking( - vs1053_mclk_hz, vs1053_enable_pll, - sampleRate(codec_cfg.getRateNumeric()), - wordLength(codec_cfg.getBitsNumeric()), - modeMasterSlave(codec_cfg.i2s.mode == MODE_MASTER))) { - AD_LOGE("mtb_wm8960_configure_clocking"); - return false; + bool configure_clocking() + { + if (vs1053_mclk_hz == 0) + { + // just pick a multiple of the sample rate + vs1053_mclk_hz = 512 * codec_cfg.getRateNumeric(); + } + if (!mtb_wm8960_configure_clocking( + vs1053_mclk_hz, vs1053_enable_pll, + sampleRate(codec_cfg.getRateNumeric()), + wordLength(codec_cfg.getBitsNumeric()), + modeMasterSlave(codec_cfg.i2s.mode == MODE_MASTER))) + { + AD_LOGE("mtb_wm8960_configure_clocking"); + return false; + } + return true; } - return true; - } - mtb_wm8960_adc_dac_sample_rate_t sampleRate(int rate) { - switch (rate) { + mtb_wm8960_adc_dac_sample_rate_t sampleRate(int rate) + { + switch (rate) + { case 48000: return WM8960_ADC_DAC_SAMPLE_RATE_48_KHZ; case 44100: @@ -1085,11 +1254,13 @@ class AudioDriverWM8960Class : public AudioDriver { default: AD_LOGE("Unsupported rate: %d", rate); return WM8960_ADC_DAC_SAMPLE_RATE_44_1_KHZ; + } } - } - mtb_wm8960_word_length_t wordLength(int bits) { - switch (bits) { + mtb_wm8960_word_length_t wordLength(int bits) + { + switch (bits) + { case 16: return WM8960_WL_16BITS; case 20: @@ -1101,68 +1272,77 @@ class AudioDriverWM8960Class : public AudioDriver { default: AD_LOGE("Unsupported bits: %d", bits); return WM8960_WL_16BITS; + } } - } - /// if microcontroller is master then module is slave - mtb_wm8960_mode_t modeMasterSlave(bool is_master) { - return is_master ? WM8960_MODE_MASTER : WM8960_MODE_SLAVE; - } -}; + /// if microcontroller is master then module is slave + mtb_wm8960_mode_t modeMasterSlave(bool is_master) + { + return is_master ? WM8960_MODE_MASTER : WM8960_MODE_SLAVE; + } + }; -/** - * @brief Driver API for the wm8978 codec chip - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class AudioDriverWM8978Class : public AudioDriver { - public: - AudioDriverWM8978Class() = default; + /** + * @brief Driver API for the wm8978 codec chip + * @author Phil Schatzmann + * @copyright GPLv3 + */ + class AudioDriverWM8978Class : public AudioDriver + { + public: + AudioDriverWM8978Class() = default; + + bool begin(CodecConfig codecCfg, DriverPins &pins) override + { + bool rc = true; + rc = wm8078.begin(getI2C(), getI2CAddress()); + setConfig(codecCfg); - bool begin(CodecConfig codecCfg, DriverPins &pins) override { - bool rc = true; - rc = wm8078.begin(getI2C(), getI2CAddress()); - setConfig(codecCfg); + // setup initial default volume + setVolume(DRIVER_DEFAULT_VOLUME); - // setup initial default volume - setVolume(DRIVER_DEFAULT_VOLUME); + return rc; + } - return rc; - } + bool setConfig(CodecConfig codecCfg) override + { + codec_cfg = codecCfg; + bool is_dac = codec_cfg.output_device != DAC_OUTPUT_NONE; + bool is_adc = codec_cfg.input_device != ADC_INPUT_NONE; + wm8078.cfgADDA(is_dac, is_adc); - bool setConfig(CodecConfig codecCfg) override { - codec_cfg = codecCfg; - bool is_dac = codec_cfg.output_device != DAC_OUTPUT_NONE; - bool is_adc = codec_cfg.input_device != ADC_INPUT_NONE; - wm8078.cfgADDA(is_dac, is_adc); - - bool is_mic = codec_cfg.input_device == ADC_INPUT_LINE1 || - codec_cfg.input_device == ADC_INPUT_ALL; - bool is_linein = codec_cfg.input_device == ADC_INPUT_LINE2 || - codec_cfg.input_device == ADC_INPUT_ALL; - bool is_auxin = codec_cfg.input_device == ADC_INPUT_LINE3 || + bool is_mic = codec_cfg.input_device == ADC_INPUT_LINE1 || codec_cfg.input_device == ADC_INPUT_ALL; - wm8078.cfgInput(is_mic, is_linein, is_auxin); - wm8078.cfgOutput(is_dac, false); - - int bits = toBits(codecCfg.i2s.bits); - if (bits < 0) return false; - int i2s = toI2S(codecCfg.i2s.fmt); - if (i2s < 0) return false; - wm8078.cfgI2S(i2s, bits); - return true; - } + bool is_linein = codec_cfg.input_device == ADC_INPUT_LINE2 || + codec_cfg.input_device == ADC_INPUT_ALL; + bool is_auxin = codec_cfg.input_device == ADC_INPUT_LINE3 || + codec_cfg.input_device == ADC_INPUT_ALL; + wm8078.cfgInput(is_mic, is_linein, is_auxin); + wm8078.cfgOutput(is_dac, false); + + int bits = toBits(codecCfg.i2s.bits); + if (bits < 0) + return false; + int i2s = toI2S(codecCfg.i2s.fmt); + if (i2s < 0) + return false; + wm8078.cfgI2S(i2s, bits); + return true; + } - bool end() override { - setVolume(0); - wm8078.cfgADDA(false, false); - return true; - } + bool end() override + { + setVolume(0); + wm8078.cfgADDA(false, false); + return true; + } - /// Mute line 0 = speaker, line 1 = headphones - bool setMute(bool mute, int line) override { - int scaled = mute ? 0 : mapVolume(volume, 0, 100, 0, 63); - switch (line) { + /// Mute line 0 = speaker, line 1 = headphones + bool setMute(bool mute, int line) override + { + int scaled = mute ? 0 : mapVolume(volume, 0, 100, 0, 63); + switch (line) + { case 0: wm8078.setSPKvol(scaled); return true; @@ -1171,54 +1351,62 @@ class AudioDriverWM8978Class : public AudioDriver { return true; default: return false; + } + return false; } - return false; - } - bool setMute(bool mute) override { - if (mute) { - // set volume to 0 - wm8078.setSPKvol(0); - wm8078.setHPvol(0, 0); - } else { - // restore volume - setVolume(volume); + bool setMute(bool mute) override + { + if (mute) + { + // set volume to 0 + wm8078.setSPKvol(0); + wm8078.setHPvol(0, 0); + } + else + { + // restore volume + setVolume(volume); + } + return true; } - return true; - } - bool setVolume(int volume) override { - this->volume = volume; - int scaled = mapVolume(volume, 0, 100, 0, 63); - wm8078.setSPKvol(scaled); - wm8078.setHPvol(scaled, scaled); - return true; - } + bool setVolume(int volume) override + { + this->volume = volume; + int scaled = mapVolume(volume, 0, 100, 0, 63); + wm8078.setSPKvol(scaled); + wm8078.setHPvol(scaled, scaled); + return true; + } - int getVolume() override { return volume; } + int getVolume() override { return volume; } - bool setInputVolume(int volume) override { - int scaled = mapVolume(volume, 0, 100, 0, 63); - wm8078.setMICgain(scaled); - wm8078.setAUXgain(scaled); - wm8078.setLINEINgain(scaled); - return true; - } + bool setInputVolume(int volume) override + { + int scaled = mapVolume(volume, 0, 100, 0, 63); + wm8078.setMICgain(scaled); + wm8078.setAUXgain(scaled); + wm8078.setLINEINgain(scaled); + return true; + } - bool isVolumeSupported() override { return true; } + bool isVolumeSupported() override { return true; } - bool isInputVolumeSupported() override { return true; } + bool isInputVolumeSupported() override { return true; } - WM8978 &driver() { return wm8078; } + WM8978 &driver() { return wm8078; } - protected: - WM8978 wm8078; - int volume = 0; + protected: + WM8978 wm8078; + int volume = 0; - /// fmt:0,LSB(right-aligned);1,MSB(left-aligned);2,Philips standard, - /// I2S;3,PCM/DSP; - int toI2S(i2s_format_t fmt) { - switch (fmt) { + /// fmt:0,LSB(right-aligned);1,MSB(left-aligned);2,Philips standard, + /// I2S;3,PCM/DSP; + int toI2S(i2s_format_t fmt) + { + switch (fmt) + { case I2S_NORMAL: return 2; case I2S_LEFT: @@ -1229,13 +1417,15 @@ class AudioDriverWM8978Class : public AudioDriver { return 3; default: break; + } + return -1; } - return -1; - } - // len: 0, 16 digits; 1, 20 digits; 2, 24 digits; 3, 32 digits; - int toBits(sample_bits_t bits) { - switch (bits) { + // len: 0, 16 digits; 1, 20 digits; 2, 24 digits; 3, 32 digits; + int toBits(sample_bits_t bits) + { + switch (bits) + { case BIT_LENGTH_16BITS: return 0; case BIT_LENGTH_20BITS: @@ -1246,60 +1436,68 @@ class AudioDriverWM8978Class : public AudioDriver { return 3; default: break; + } + return -1; } - return -1; - } -}; + }; -/** - * @brief Driver API for the wm8994 codec chip - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class AudioDriverWM8994Class : public AudioDriver { - public: - AudioDriverWM8994Class(uint16_t deviceAddr = 0x1A) { - this->i2c_default_address = deviceAddr; - } + /** + * @brief Driver API for the wm8994 codec chip + * @author Phil Schatzmann + * @copyright GPLv3 + */ + class AudioDriverWM8994Class : public AudioDriver + { + public: + AudioDriverWM8994Class(uint16_t deviceAddr = 0x1A) + { + this->i2c_default_address = deviceAddr; + } - virtual bool begin(CodecConfig codecCfg, DriverPins &pins) { - codec_cfg = codecCfg; - // manage reset pin -> active high - setPAPower(true); - delay(10); - p_pins = &pins; - int vol = mapVolume(volume, 0, 100, DEFAULT_VOLMIN, DEFAULT_VOLMAX); - uint32_t freq = codecCfg.getRateNumeric(); - uint16_t outputDevice = getOutput(codec_cfg.output_device); - - return wm8994_Init(getI2CAddress(), outputDevice, vol, freq, getI2C()) == 0; - } + virtual bool begin(CodecConfig codecCfg, DriverPins &pins) + { + codec_cfg = codecCfg; + // manage reset pin -> active high + setPAPower(true); + delay(10); + p_pins = &pins; + int vol = mapVolume(volume, 0, 100, DEFAULT_VOLMIN, DEFAULT_VOLMAX); + uint32_t freq = codecCfg.getRateNumeric(); + uint16_t outputDevice = getOutput(codec_cfg.output_device); + + return wm8994_Init(getI2CAddress(), outputDevice, vol, freq, getI2C()) == 0; + } - bool setMute(bool mute) { - uint32_t rc = - mute ? wm8994_Pause(getI2CAddress()) : wm8994_Resume(getI2CAddress()); - return rc == 0; - } + bool setMute(bool mute) + { + uint32_t rc = + mute ? wm8994_Pause(getI2CAddress()) : wm8994_Resume(getI2CAddress()); + return rc == 0; + } - bool setVolume(int volume) { - this->volume = volume; - int vol = mapVolume(volume, 0, 100, DEFAULT_VOLMIN, DEFAULT_VOLMAX); - return wm8994_SetVolume(getI2CAddress(), vol) == 0; - } - int getVolume() { return volume; } + bool setVolume(int volume) + { + this->volume = volume; + int vol = mapVolume(volume, 0, 100, DEFAULT_VOLMIN, DEFAULT_VOLMAX); + return wm8994_SetVolume(getI2CAddress(), vol) == 0; + } + int getVolume() { return volume; } - protected: - int volume = 100; + protected: + int volume = 100; - bool deinit() { - int cnt = wm8994_Stop(getI2CAddress(), AUDIO_MUTE_ON); - cnt += wm8994_Reset(getI2CAddress()); - setPAPower(false); - return cnt == 0; - } + bool deinit() + { + int cnt = wm8994_Stop(getI2CAddress(), AUDIO_MUTE_ON); + cnt += wm8994_Reset(getI2CAddress()); + setPAPower(false); + return cnt == 0; + } - uint16_t getOutput(output_device_t output_device) { - switch (output_device) { + uint16_t getOutput(output_device_t output_device) + { + switch (output_device) + { case DAC_OUTPUT_NONE: return 0; case DAC_OUTPUT_LINE1: @@ -1308,275 +1506,397 @@ class AudioDriverWM8994Class : public AudioDriver { return OUTPUT_DEVICE_HEADPHONE; case DAC_OUTPUT_ALL: return OUTPUT_DEVICE_BOTH; + } + return OUTPUT_DEVICE_BOTH; } - return OUTPUT_DEVICE_BOTH; - } -}; + }; -/** - * @brief Driver API for the CS43l22 codec chip on 0x94 (0x4A<<1) - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class AudioDriverPCM3168Class : public AudioDriver { - public: - AudioDriverPCM3168Class() { i2c_default_address = 0x44; }; + /** + * @brief Driver API for the CS43l22 codec chip on 0x94 (0x4A<<1) + * @author Phil Schatzmann + * @copyright GPLv3 + */ + class AudioDriverPCM3168Class : public AudioDriver + { + public: + AudioDriverPCM3168Class() { i2c_default_address = 0x44; }; - bool setMute(bool mute) { return driver.setMute(mute); } + bool setMute(bool mute) { return driver.setMute(mute); } - bool setMute(bool mute, int line) { return driver.setMute(line, mute); } + bool setMute(bool mute, int line) { return driver.setMute(line, mute); } - bool setVolume(int vol) { - volume = vol; - return driver.setVolume(100.0 * vol); - } - int getVolume() { return volume; } + bool setVolume(int vol) + { + volume = vol; + return driver.setVolume(100.0 * vol); + } + int getVolume() { return volume; } - protected: - int volume; - PCM3168 driver; + protected: + int volume; + PCM3168 driver; - bool init(codec_config_t codec_cfg) { - driver.setWire(getI2C()); - driver.setAddress(getI2CAddress()); - return true; - } - bool deinit() { return driver.end(); } - bool controlState(codec_mode_t mode) { return true; } - bool configInterface(codec_mode_t mode, I2SDefinition iface) { - if (iface.mode == MODE_MASTER) { - AD_LOGE("Only slave is supported: MCU must be master"); - return false; + bool init(codec_config_t codec_cfg) + { + driver.setWire(getI2C()); + driver.setAddress(getI2CAddress()); + return true; } - PCM3168::FMT fmt = PCM3168::FMT::I2SHighSpeedTDM24bit; - switch (iface.bits) { + bool deinit() { return driver.end(); } + bool controlState(codec_mode_t mode) { return true; } + bool configInterface(codec_mode_t mode, I2SDefinition iface) + { + if (iface.mode == MODE_MASTER) + { + AD_LOGE("Only slave is supported: MCU must be master"); + return false; + } + PCM3168::FMT fmt = PCM3168::FMT::I2SHighSpeedTDM24bit; + switch (iface.bits) + { case BIT_LENGTH_16BITS: - if (iface.fmt != I2S_RIGHT) { + if (iface.fmt != I2S_RIGHT) + { AD_LOGW("Only I2S_RIGHT is supported for 16 bits"); } fmt = PCM3168::FMT::Right16bit; break; case BIT_LENGTH_32BITS: case BIT_LENGTH_24BITS: - if (iface.signal_type == SIGNAL_TDM) { - switch (iface.fmt) { - case I2S_NORMAL: - fmt = PCM3168::FMT::I2STDM24bit; - break; - case I2S_LEFT: - fmt = PCM3168::FMT::LeftTDM24bit; - break; - default: - AD_LOGE("Unsupported fmt for tdm"); - return false; + if (iface.signal_type == SIGNAL_TDM) + { + switch (iface.fmt) + { + case I2S_NORMAL: + fmt = PCM3168::FMT::I2STDM24bit; + break; + case I2S_LEFT: + fmt = PCM3168::FMT::LeftTDM24bit; + break; + default: + AD_LOGE("Unsupported fmt for tdm"); + return false; } - } else { - switch (iface.fmt) { - case I2S_NORMAL: - fmt = PCM3168::FMT::I2S24bit; - break; - case I2S_LEFT: - fmt = PCM3168::FMT::Left24bit; - break; - case I2S_RIGHT: - fmt = PCM3168::FMT::Right24bit; - break; - case I2S_DSP: - fmt = PCM3168::FMT::LeftDSP24bit; - break; - default: - AD_LOGE("Unsupported fmt"); - return false; + } + else + { + switch (iface.fmt) + { + case I2S_NORMAL: + fmt = PCM3168::FMT::I2S24bit; + break; + case I2S_LEFT: + fmt = PCM3168::FMT::Left24bit; + break; + case I2S_RIGHT: + fmt = PCM3168::FMT::Right24bit; + break; + case I2S_DSP: + fmt = PCM3168::FMT::LeftDSP24bit; + break; + default: + AD_LOGE("Unsupported fmt"); + return false; } } break; default: AD_LOGE("Unsupported bits"); return false; + } + + return driver.begin(fmt); } + }; - return driver.begin(fmt); - } -}; + /** + * @brief Driver API for Lyrat Mini with a ES8311 and a ES7243 codec chip + * @author Phil Schatzmann + * @copyright GPLv3 + */ + class AudioDriverLyratMiniClass : public AudioDriver + { + public: + bool begin(CodecConfig codecCfg, DriverPins &pins) + { + AD_LOGI("AudioDriverLyratMiniClass::begin"); + p_pins = &pins; + codec_cfg = codecCfg; + + // setup SPI for SD + // pins.setSPIActiveForSD(codecCfg.sd_active); + + // Start ES8311 + AD_LOGI("starting ES8311"); + pins.begin(); + dac.setPins(this->pins()); + if (!dac.setConfig(codecCfg)) + { + AD_LOGE("setConfig failed"); + return false; + } + setPAPower(true); + setVolume(DRIVER_DEFAULT_VOLUME); + + // Start ES7243 + if (codecCfg.input_device != ADC_INPUT_NONE) + { + AD_LOGI("starting ES7243"); + adc.setPins(this->pins()); + if (!adc.setConfig(codecCfg)) + { + AD_LOGE("adc.begin failed"); + return false; + } + } + + return true; + } + bool end(void) + { + int rc = 0; + rc += dac.end(); + rc += adc.end(); + return rc == 2; + } + bool setMute(bool enable) override { return dac.setMute(enable); } + bool setVolume(int volume) override { return dac.setVolume(volume); } + int getVolume() override { return dac.getVolume(); } + bool setInputVolume(int volume) override { return adc.setVolume(volume); } + int getInputVolume() { return adc.getVolume(); } + bool isInputVolumeSupported() override { return true; } + + protected: + AudioDriverES8311Class dac; + AudioDriverES7243Class adc; + }; + + /** + * @brief Driver API for NAU8325 CODEC WITH AMPLIFIER + * @author + * @copyright MIT Licence + */ + // *************** NAU8325 DRIVER WRAPPER ************ /** - * @brief Driver API for Lyrat Mini with a ES8311 and a ES7243 codec chip - * @author Phil Schatzmann - * @copyright GPLv3 + * @brief Driver API for NAU8325 CODEC WITH AMPLIFIER + * @author You + * @copyright MIT License */ -class AudioDriverLyratMiniClass : public AudioDriver { - public: - bool begin(CodecConfig codecCfg, DriverPins &pins) { - AD_LOGI("AudioDriverLyratMiniClass::begin"); - p_pins = &pins; - codec_cfg = codecCfg; - - // setup SPI for SD - // pins.setSPIActiveForSD(codecCfg.sd_active); - - // Start ES8311 - AD_LOGI("starting ES8311"); - pins.begin(); - dac.setPins(this->pins()); - if (!dac.setConfig(codecCfg)) { - AD_LOGE("setConfig failed"); +// -- NAU8325 Driver Class +class AudioDriverNAU8325Class : public AudioDriver { +public: + PCBCUPID_NAU8325 *nau = nullptr; // pointer to construct later + + ~AudioDriverNAU8325Class() { + if (nau) delete nau; + } + + bool begin(CodecConfig cfg, DriverPins &pins) override { + AD_LOGI("AudioDriverNAU8325Class::begin"); + + this->p_pins = &pins; + + // Get I2C config + auto i2c_opt = pins.getI2CPins(PinFunction::CODEC); + if (!i2c_opt) { + AD_LOGE("No I2C pins defined for codec"); return false; } - setPAPower(true); - setVolume(DRIVER_DEFAULT_VOLUME); + PinsI2C val = i2c_opt.value(); - // Start ES7243 - if (codecCfg.input_device != ADC_INPUT_NONE) { - AD_LOGI("starting ES7243"); - adc.setPins(this->pins()); - if (!adc.setConfig(codecCfg)) { - AD_LOGE("adc.begin failed"); - return false; - } + // Create instance with required params + nau = new PCBCUPID_NAU8325(*((TwoWire *)val.p_wire), val.address); + + // Set MCLK if available + int mclk = pins.getPinID(PinFunction::MCLK_SOURCE); + if (mclk > 0) { + pinMode(mclk, OUTPUT); + digitalWrite(mclk, HIGH); + delay(10); // optional small delay to stabilize + } + + // Begin your driver using known public methods + int fs = cfg.getRateNumeric(); + int bits = cfg.getBitsNumeric(); + + AD_LOGI("Calling nau->begin(fs=%d, bits=%d)", fs, bits); + if (!nau->begin(fs, bits)) { + AD_LOGE("NAU8325 beginDynamic failed"); + return false; } return true; } - bool end(void) { - int rc = 0; - rc += dac.end(); - rc += adc.end(); - return rc == 2; - } - bool setMute(bool enable) override { return dac.setMute(enable); } - bool setVolume(int volume) override { return dac.setVolume(volume); } - int getVolume() override { return dac.getVolume(); } - bool setInputVolume(int volume) override { return adc.setVolume(volume); } - int getInputVolume() { return adc.getVolume(); } - bool isInputVolumeSupported() override { return true; } - - protected: - AudioDriverES8311Class dac; - AudioDriverES7243Class adc; -}; -#ifdef ARDUINO -// currently only supported in Arduino because we do not have a platform -// independent SPI API -/** - * @brief Driver API for AD1938 TDS DAC/ADC - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class AudioDriverAD1938Class : public AudioDriver { - public: - bool begin(CodecConfig codecCfg, DriverPins &pins) override { - int clatch = pins.getPinID(PinFunction::LATCH); - if (clatch < 0) return false; - int reset = pins.getPinID(PinFunction::RESET); - if (reset < 0) return false; - auto spi_opt = pins.getSPIPins(PinFunction::CODEC); - SPIClass *p_spi = nullptr; - if (spi_opt) { - p_spi = (SPIClass *)spi_opt.value().p_spi; - } else { - p_spi = &SPI; - p_spi->begin(); - } - // setup pins - pins.begin(); - // setup ad1938 - ad1938.begin(codecCfg, clatch, reset, *p_spi); - ad1938.enable(); - ad1938.setMute(false); + bool end() override { + if (nau) { + nau->powerOff(); // if it's void, just call directly + } return true; } - virtual bool setConfig(CodecConfig codecCfg) { - assert(p_pins != nullptr); - bool result = begin(codecCfg, *p_pins); - return result; - } - bool end(void) override { return ad1938.end(); } - bool setMute(bool mute) override { return ad1938.setMute(mute); } - // mutes an individual DAC: valid range (0:3) - bool setMute(bool mute, int line) { - if (line > 3) return false; - return ad1938.setVolumeDAC( - line, mute ? 0.0 : (static_cast(volumes[line]) / 100.0f)); + + bool setMute(bool enable) override { + if (!nau) return false; + nau->softMute(enable); // assumed void return + return true; } - /// Defines the Volume (in %) if volume is 0, mute is enabled,range is 0-100. bool setVolume(int volume) override { - this->volume = volume; - for (int j = 0; j < 8; j++) { - volumes[j] = volume; - } - return ad1938.setVolume(static_cast(volume) / 100.0f); + if (!nau) return false; + // Mapping volume (0–100) to 0–255 (NAU range) + uint8_t val = map(volume, 0, 100, 0, 255); + nau->setVolume(val, val); // assuming stereo + return true; } - /// Defines the Volume per DAC (in %) if volume is 0, mute is enabled,range is - /// 0-100. - bool setVolume(int volume, int line) { - if (line > 7) return false; - volumes[line] = volume; - return ad1938.setVolumeDAC(static_cast(volume) / 100.0f, line); + + int getVolume() override { + // You can return dummy volume since NAU8325 doesn't have a getVolume + return 100; } +}; - int getVolume() override { return volume; } - bool setInputVolume(int volume) override { return false; } - bool isVolumeSupported() override { return true; } - bool isInputVolumeSupported() override { return false; } - DriverPins &pins() { return *p_pins; } - AD1938 &driver() { return ad1938; } - protected: - AD1938 ad1938; - int volume = 100; - int volumes[8] = {100}; -}; + +#ifdef ARDUINO + // currently only supported in Arduino because we do not have a platform + // independent SPI API + /** + * @brief Driver API for AD1938 TDS DAC/ADC + * @author Phil Schatzmann + * @copyright GPLv3 + */ + class AudioDriverAD1938Class : public AudioDriver + { + public: + bool begin(CodecConfig codecCfg, DriverPins &pins) override + { + int clatch = pins.getPinID(PinFunction::LATCH); + if (clatch < 0) + return false; + int reset = pins.getPinID(PinFunction::RESET); + if (reset < 0) + return false; + auto spi_opt = pins.getSPIPins(PinFunction::CODEC); + SPIClass *p_spi = nullptr; + if (spi_opt) + { + p_spi = (SPIClass *)spi_opt.value().p_spi; + } + else + { + p_spi = &SPI; + p_spi->begin(); + } + // setup pins + pins.begin(); + // setup ad1938 + ad1938.begin(codecCfg, clatch, reset, *p_spi); + ad1938.enable(); + ad1938.setMute(false); + return true; + } + virtual bool setConfig(CodecConfig codecCfg) + { + assert(p_pins != nullptr); + bool result = begin(codecCfg, *p_pins); + return result; + } + bool end(void) override { return ad1938.end(); } + bool setMute(bool mute) override { return ad1938.setMute(mute); } + // mutes an individual DAC: valid range (0:3) + bool setMute(bool mute, int line) + { + if (line > 3) + return false; + return ad1938.setVolumeDAC( + line, mute ? 0.0 : (static_cast(volumes[line]) / 100.0f)); + } + + /// Defines the Volume (in %) if volume is 0, mute is enabled,range is 0-100. + bool setVolume(int volume) override + { + this->volume = volume; + for (int j = 0; j < 8; j++) + { + volumes[j] = volume; + } + return ad1938.setVolume(static_cast(volume) / 100.0f); + } + /// Defines the Volume per DAC (in %) if volume is 0, mute is enabled,range is + /// 0-100. + bool setVolume(int volume, int line) + { + if (line > 7) + return false; + volumes[line] = volume; + return ad1938.setVolumeDAC(static_cast(volume) / 100.0f, line); + } + + int getVolume() override { return volume; } + bool setInputVolume(int volume) override { return false; } + bool isVolumeSupported() override { return true; } + bool isInputVolumeSupported() override { return false; } + + DriverPins &pins() { return *p_pins; } + AD1938 &driver() { return ad1938; } + + protected: + AD1938 ad1938; + int volume = 100; + int volumes[8] = {100}; + }; #endif -// -- Drivers -/// @ingroup audio_driver -static AudioDriverAC101Class AudioDriverAC101; -/// @ingroup audio_driver -static AudioDriverCS43l22Class AudioDriverCS43l22; -/// @ingroup audio_driver -static AudioDriverES7210Class AudioDriverES7210; -/// @ingroup audio_driver -static AudioDriverES7243Class AudioDriverES7243; -/// @ingroup audio_driver -static AudioDriverES7243eClass AudioDriverES7243e; -/// @ingroup audio_driver -static AudioDriverES8156Class AudioDriverES8156; -/// @ingroup audio_driver -static AudioDriverES8311Class AudioDriverES8311; -/// @ingroup audio_driver -static AudioDriverES8374Class AudioDriverES8374; -/// @ingroup audio_driver -static AudioDriverES8388Class AudioDriverES8388; -/// @ingroup audio_driver -static AudioDriverES8388Class AudioDriverES8388H0{0}; -/// @ingroup audio_driver -static AudioDriverES8388Class AudioDriverES8388H1{1}; -/// @ingroup audio_driver -static AudioDriverES8388Class AudioDriverES8388H2{2}; -/// @ingroup audio_driver -static AudioDriverES8388Class AudioDriverES8388H3{3}; -/// @ingroup audio_driver -static AudioDriverWM8960Class AudioDriverWM8960; -/// @ingroup audio_driver -static AudioDriverWM8978Class AudioDriverWM8978; -/// @ingroup audio_driver -static AudioDriverWM8994Class AudioDriverWM8994; -/// @ingroup audio_driver -static AudioDriverLyratMiniClass AudioDriverLyratMini; -/// @ingroup audio_driver -static NoDriverClass NoDriver; -/// @ingroup audio_driver -static AudioDriverCS42448Class AudioDriverCS42448; -/// @ingroup audio_driver -static AudioDriverPCM3168Class AudioDriverPCM3168; + // -- Drivers + /// @ingroup audio_driver + static AudioDriverAC101Class AudioDriverAC101; + /// @ingroup audio_driver + static AudioDriverCS43l22Class AudioDriverCS43l22; + /// @ingroup audio_driver + static AudioDriverES7210Class AudioDriverES7210; + /// @ingroup audio_driver + static AudioDriverES7243Class AudioDriverES7243; + /// @ingroup audio_driver + static AudioDriverES7243eClass AudioDriverES7243e; + /// @ingroup audio_driver + static AudioDriverES8156Class AudioDriverES8156; + /// @ingroup audio_driver + static AudioDriverES8311Class AudioDriverES8311; + /// @ingroup audio_driver + static AudioDriverES8374Class AudioDriverES8374; + /// @ingroup audio_driver + static AudioDriverES8388Class AudioDriverES8388; + /// @ingroup audio_driver + static AudioDriverES8388Class AudioDriverES8388H0{0}; + /// @ingroup audio_driver + static AudioDriverES8388Class AudioDriverES8388H1{1}; + /// @ingroup audio_driver + static AudioDriverES8388Class AudioDriverES8388H2{2}; + /// @ingroup audio_driver + static AudioDriverES8388Class AudioDriverES8388H3{3}; + /// @ingroup audio_driver + static AudioDriverWM8960Class AudioDriverWM8960; + /// @ingroup audio_driver + static AudioDriverWM8978Class AudioDriverWM8978; + /// @ingroup audio_driver + static AudioDriverWM8994Class AudioDriverWM8994; + /// @ingroup audio_driver + static AudioDriverLyratMiniClass AudioDriverLyratMini; + /// @ingroup audio_driver + static NoDriverClass NoDriver; + /// @ingroup audio_driver + static AudioDriverCS42448Class AudioDriverCS42448; + /// @ingroup audio_driver + static AudioDriverPCM3168Class AudioDriverPCM3168; + /// @ingroup audio_driver + static AudioDriverNAU8325Class AudioDriverNAU8325; #ifdef ARDUINO -/// @ingroup audio_driver -static AudioDriverAD1938Class AudioDriverAD1938; + /// @ingroup audio_driver + static AudioDriverAD1938Class AudioDriverAD1938; #endif -} // namespace audio_driver \ No newline at end of file +} // namespace audio_driver \ No newline at end of file diff --git a/src/Driver/nau8325/PCBCUPID_NAU8325.cpp b/src/Driver/nau8325/PCBCUPID_NAU8325.cpp new file mode 100644 index 0000000..c643ea3 --- /dev/null +++ b/src/Driver/nau8325/PCBCUPID_NAU8325.cpp @@ -0,0 +1,615 @@ +#include "PCBCUPID_NAU8325.h" + +#define MASTER_CLK_MIN 2048000 +#define MASTER_CLK_MAX 49152000 + +uint16_t set_ratio = 0; //declared as global + +bool alc_enable = true; +bool clock_detection = true; +bool clock_det_data = true; +uint32_t dac_vref_microvolt = 2880000; +uint32_t vref_impedance_ohms = 125000; + +PCBCUPID_NAU8325::PCBCUPID_NAU8325(TwoWire &wire, uint8_t i2c_addr) + : i2c(wire), i2c_addr(i2c_addr) {} + +bool PCBCUPID_NAU8325::begin(uint32_t fs, uint8_t bits_per_sample, uint16_t ratio) { + Serial.println("[NAU8325] beginDynamic() starting..."); + Serial.printf(" fs = %lu Hz\n", fs); + Serial.printf(" ratio = %u Hz\n", ratio); + Serial.printf(" bits = %u\n", bits_per_sample); + + if (bits_per_sample != 16 && bits_per_sample != 24 && bits_per_sample != 32) { + Serial.printf("[NAU8325] Unsupported bit width: %u\n", bits_per_sample); + return false; + } + + set_ratio = ratio; + uint32_t mclk = fs * ratio; + resetChip(); + delay(5); + + if (!setSysClock(mclk)) + return false; + + if (!configureAudio(fs, mclk, bits_per_sample)) + return false; + + if (!setI2SFormat(I2S_STD, NORMAL_BCLK)) + return false; + + // ***********Analog + ALC + Vref Config *********** + initRegisters(); + + // Wait for analog to settle + delay(50); + + // Enable Analog/Output Blocks (DAPM Equivalent) + enableDAPMBlocks(); + + // Set default volume AFTER analog path is ready + setVolume(0xFD, 0xFD); + + softMute(false); + + /*default powerOn*/ + powerOn(); + + Serial.println("[NAU8325] beginDynamic() complete - sound should play"); + return true; +} + +/*If the user didn't provide the ratio it will call this function with default ratio*/ +bool PCBCUPID_NAU8325::begin(uint32_t fs, uint8_t bits) { + uint16_t ratio = 256; + return begin(fs, bits, ratio); +} + +/*if the user didn't pass any of the parameter like fs,bits,ratio*/ +bool PCBCUPID_NAU8325::begin() { + uint32_t fs = 44100; + uint16_t ratio = 256; + uint8_t bits = 16; + return begin(fs, bits, ratio); +} + +void PCBCUPID_NAU8325::enableDAPMBlocks() { + // Enable DAC L/R in ENA_CTRL (R04) + writeRegisterBits(NAU8325_R04_ENA_CTRL, + NAU8325_ENA_CTRL_DAC_EN_MASK, + NAU8325_ENA_CTRL_DAC_EN_MASK); + + // Enable analog power-up blocks (e.g., VMID, Class-D) in ANALOG_CONTROL_1 (R61) + writeRegisterBits(NAU8325_R61_ANALOG_CONTROL_1, + NAU8325_ANALOG_CTRL1_EN_MASK, + NAU8325_ANALOG_CTRL1_EN_MASK); + + // Enable speaker path routing (ANALOG_CONTROL_6 R66) + writeRegisterBits(NAU8325_R66_ANALOG_CONTROL_6, + NAU8325_ANALOG_CTRL6_EN_MASK, + NAU8325_ANALOG_CTRL6_EN_MASK); +} + +void PCBCUPID_NAU8325::resetChip() { + writeRegister(0x0000, 0x0001); + delay(2); + writeRegister(0x0000, 0x0000); + delay(2); +} + +uint16_t PCBCUPID_NAU8325::readDeviceId() { + uint16_t id; + if (readRegister(NAU8325_R02_DEVICE_ID, id)) { + return id; + } else { + return 0xFFFF; // Invalid or failed read + } +} + +// N1 divider options +const SrcAttr PCBCUPID_NAU8325::mclk_n1_div[] = { + { 1, 0x0 }, + { 2, 0x1 }, + { 3, 0x2 } +}; + +// N2 divider options +const SrcAttr PCBCUPID_NAU8325::mclk_n2_div[] = { + { 0, 0x0 }, + { 1, 0x1 }, + { 2, 0x2 }, + { 3, 0x3 }, + { 4, 0x4 } +}; + +// N3 multiplier options +const SrcAttr PCBCUPID_NAU8325::mclk_n3_mult[] = { + { 0, 0x1 }, + { 1, 0x2 }, + { 2, 0x3 }, + { 3, 0x4 } +}; + +// DAC oversampling options +const OsrAttr PCBCUPID_NAU8325::osr_dac_sel[] = { + { 64, 2 }, // OSR 64, SRC 1/4 + { 256, 0 }, // OSR 256, SRC 1 + { 128, 1 }, // OSR 128, SRC 1/2 + { 0, 0 }, + { 32, 3 } // OSR 32, SRC 1/8 +}; + +// accessing the table as expected based oon sample rate and mclk +const SRateAttr PCBCUPID_NAU8325::target_srate_table[] = { + { 48000, 2, true, { 12288000, 19200000, 24000000 } }, + { 16000, 1, false, { 4096000, 6400000, 8000000 } }, + { 8000, 0, false, { 2048000, 3200000, 4000000 } }, + { 44100, 2, true, { 11289600, 17640000, 22050000 } }, + { 64000, 3, false, { 16384000, 25600000, 32000000 } }, + { 96000, 3, true, { 24576000, 38400000, 48000000 } }, + { 12000, 0, true, { 3072000, 4800000, 6000000 } }, + { 24000, 1, true, { 6144000, 9600000, 12000000 } }, + { 32000, 2, false, { 8192000, 12800000, 16000000 } } +}; + +bool PCBCUPID_NAU8325::applySampleRateClocks(const SRateAttr *srate, int n1_sel, int mclk_mult_sel, int n2_sel) { + if (!srate || n1_sel < 0 || n1_sel >= 3 || n2_sel < 0 || n2_sel >= 5) { + Serial.println("[NAU8325] Invalid N1/N2 divider index."); + return false; + } + + // --- Debug --- + Serial.println("--- Debugging clock dividers ---"); + Serial.printf("n1_sel=%d (val=%d)\n", n1_sel, mclk_n1_div[n1_sel].val); + Serial.printf("n2_sel=%d (val=%d)\n", n2_sel, mclk_n2_div[n2_sel].val); + Serial.printf("mult_sel=%d\n", mclk_mult_sel); + + // Set sample rate range and max mode + writeRegisterBits(NAU8325_R40_CLK_DET_CTRL, + NAU8325_REG_SRATE_MASK | NAU8325_REG_DIV_MAX, + (srate->range << NAU8325_REG_SRATE_SFT) | (srate->max ? NAU8325_REG_DIV_MAX : 0)); + // Batch update CLK_CTRL (0x03) + uint16_t clk_ctrl_val = 0; + clk_ctrl_val |= mclk_n2_div[n2_sel].val; // MCLK_SRC + clk_ctrl_val |= mclk_n1_div[n1_sel].val << NAU8325_CLK_MUL_SRC_SFT; // N1 + if (mclk_mult_sel >= 0 && mclk_mult_sel <= 3) + clk_ctrl_val |= mclk_n3_mult[mclk_mult_sel].val << NAU8325_MCLK_SEL_SFT; // N3 + + clk_ctrl_val |= 0x1000; // DAC_CLK = MCLK/2 (required!) + + writeRegister(NAU8325_R03_CLK_CTRL, clk_ctrl_val); + + writeRegister(NAU8325_R03_CLK_CTRL, clk_ctrl_val); // Now written only once + + // Configure ANALOG_CONTROL_5 for 4x/8x clock enable + switch (mclk_mult_sel) { + case 2: + writeRegisterBits(NAU8325_R65_ANALOG_CONTROL_5, + NAU8325_MCLK4XEN_EN, + NAU8325_MCLK4XEN_EN); + break; + case 3: + writeRegisterBits(NAU8325_R65_ANALOG_CONTROL_5, + NAU8325_MCLK4XEN_EN | NAU8325_MCLK8XEN_EN, + NAU8325_MCLK4XEN_EN | NAU8325_MCLK8XEN_EN); + break; + default: + writeRegisterBits(NAU8325_R65_ANALOG_CONTROL_5, + NAU8325_MCLK4XEN_EN | NAU8325_MCLK8XEN_EN, + 0); + break; + } + + return true; +} + +int PCBCUPID_NAU8325::getMclkRatioAndN2Index(const SRateAttr *srate, int mclk_hz, int &n2_sel_out) { + int ratio = NAU8325_MCLK_FS_RATIO_NUM; // Default: not found + + for (int i = 0; i < 5; i++) { // mclk_n2_div[] has 5 entries + int div = mclk_n2_div[i].param; + int mclk_src = mclk_hz >> div; + + if (srate->mclk_src[NAU8325_MCLK_FS_RATIO_256] == mclk_src) { + ratio = NAU8325_MCLK_FS_RATIO_256; + n2_sel_out = i; + break; + } + if (srate->mclk_src[NAU8325_MCLK_FS_RATIO_400] == mclk_src) { + ratio = NAU8325_MCLK_FS_RATIO_400; + n2_sel_out = i; + break; + } + if (srate->mclk_src[NAU8325_MCLK_FS_RATIO_500] == mclk_src) { + ratio = NAU8325_MCLK_FS_RATIO_500; + n2_sel_out = i; + break; + } + } + + return ratio; +} + +const SRateAttr *PCBCUPID_NAU8325::getSRateAttr(int fs) + +{ + for (size_t i = 0; i < sizeof(target_srate_table) / sizeof(target_srate_table[0]); ++i) { + if (target_srate_table[i].fs == fs) { + return &target_srate_table[i]; + } + } + return nullptr; +} + +bool PCBCUPID_NAU8325::chooseClockSource(int fs, int mclk, + const SRateAttr *&srate, + int &n1_sel, int &mult_sel, int &n2_sel) { + if (fs <= 0 || mclk <= 0) { + Serial.println("[NAU8325] Invalid fs or mclk."); + return false; + } + + srate = getSRateAttr(fs); + if (!srate) { + Serial.printf("[NAU8325] Unsupported fs: %d\n", fs); + return false; + } + + // First try direct N2 mapping + int ratio = getMclkRatioAndN2Index(srate, mclk, n2_sel); + if (ratio != NAU8325_MCLK_FS_RATIO_NUM) { + n1_sel = 0; + mult_sel = -1; // Bypass + Serial.printf("[NAU8325] Direct match: fs=%d, mclk=%d → N2=%d (Ratio=%d)\n", + fs, mclk, n2_sel, ratio); + return true; + } + + // Try all N1 and N3 combinations + int mclk_max = 0; + int best_n1 = -1, best_mult = -1, best_n2 = -1; + + for (int i = 0; i < 3; ++i) // N1 options + { + for (int j = 0; j < 4; ++j) // N3 multiplier options + { + int m = (mclk << mclk_n3_mult[j].param) / mclk_n1_div[i].param; + int r = getMclkRatioAndN2Index(srate, m, n2_sel); + + if (r != NAU8325_MCLK_FS_RATIO_NUM && (m > mclk_max || best_n1 < i)) { + mclk_max = m; + best_n1 = i; + best_mult = j; + best_n2 = n2_sel; + } + } + } + + if (mclk_max > 0) { + n1_sel = best_n1; + mult_sel = best_mult; + n2_sel = best_n2; + + Serial.printf("[NAU8325] Matched via N1/N3 combo: fs=%d, mclk=%d → N1=%d, N3(mult)=%d, N2=%d\n", + fs, mclk, n1_sel, mult_sel, n2_sel); + Serial.printf("[NAU8325] chooseClockSource(): fs=%d, mclk=%d\n", fs, mclk); + + return true; + } + + Serial.printf("[NAU8325] Failed to match MCLK %d Hz with fs %d Hz\n", mclk, fs); + return false; +} + +bool PCBCUPID_NAU8325::configureClocks(int fs, int mclk) { + const SRateAttr *srate = nullptr; + int n1_sel = 0, mult_sel = -1, n2_sel = 0; + + if (!chooseClockSource(fs, mclk, srate, n1_sel, mult_sel, n2_sel)) { + Serial.printf("[NAU8325] Failed to choose clock source for fs=%d, mclk=%d\n", fs, mclk); + return false; + } + + if (!applySampleRateClocks(srate, n1_sel, mult_sel, n2_sel)) { + Serial.println("[NAU8325] Failed to apply sample rate clocks."); + return false; + } + Serial.printf("[NAU8325] configureClocks(): fs=%u, mclk=%u\n", fs, mclk); + + return true; +} + +const OsrAttr *PCBCUPID_NAU8325::getCurrentOSR() { + uint16_t value; + if (!readRegister(NAU8325_R29_DAC_CTRL1, value)) { + return nullptr; + } + + uint8_t osr_index = value & NAU8325_DAC_OVERSAMPLE_MASK; + + if (osr_index >= sizeof(osr_dac_sel) / sizeof(osr_dac_sel[0])) { + return nullptr; + } + + return &osr_dac_sel[osr_index]; +} + +bool PCBCUPID_NAU8325::configureAudio(uint32_t fs, uint32_t mclk, uint8_t bits_per_sample) { + this->fs = fs; + this->mclk = mclk; + + // Configure clock tree + if (!configureClocks(fs, mclk)) { + return false; + } + + // Optional: set oversampling mode explicitly (OSR = 128) + writeRegisterBits(NAU8325_R29_DAC_CTRL1, NAU8325_DAC_OVERSAMPLE_MASK, NAU8325_DAC_OVERSAMPLE_128); + + const OsrAttr *osr = getCurrentOSR(); + if (!osr || osr->osr == 0 || (fs * osr->osr > CLK_DA_AD_MAX)) { + Serial.println("[NAU8325] Invalid OSR or fs × OSR exceeds max."); + return false; + } + + // Set DAC clock source + writeRegisterBits(NAU8325_R03_CLK_CTRL, + NAU8325_CLK_DAC_SRC_MASK, + osr->clk_src << NAU8325_CLK_DAC_SRC_SFT); + + // Configure I2S word length + uint16_t val_len = 0; + switch (bits_per_sample) { + case 16: + val_len = NAU8325_I2S_DL_16; + break; + case 20: + val_len = NAU8325_I2S_DL_20; + break; + case 24: + val_len = NAU8325_I2S_DL_24; + break; + case 32: + val_len = NAU8325_I2S_DL_32; + break; + default: + Serial.printf("[NAU8325] Invalid bit width: %u\n", bits_per_sample); + return false; + } + + writeRegisterBits(NAU8325_R0D_I2S_PCM_CTRL1, + NAU8325_I2S_DL_MASK, val_len); + + Serial.printf("[NAU8325] configureAudio(): fs=%u, mclk=%u, bits=%u\n", fs, mclk, bits_per_sample); + + return true; +} + +bool PCBCUPID_NAU8325::setI2SFormat(NAU8325_I2SFormat format, NAU8325_ClockPolarity polarity) { + uint16_t ctrl1_val = 0; + + // Set BCLK polarity + if (polarity == INVERTED_BCLK) { + ctrl1_val |= NAU8325_I2S_BP_INV; + } + + // Set data format + switch (format) { + case I2S_STD: + ctrl1_val |= NAU8325_I2S_DF_I2S; + break; + case LEFT_JUSTIFIED: + ctrl1_val |= NAU8325_I2S_DF_LEFT; + break; + case RIGHT_JUSTIFIED: + ctrl1_val |= NAU8325_I2S_DF_RIGTH; + break; + case DSP_A: + ctrl1_val |= NAU8325_I2S_DF_PCM_AB; + break; + case DSP_B: + ctrl1_val |= NAU8325_I2S_DF_PCM_AB | NAU8325_I2S_PCMB_EN; + break; + default: + return false; + } + + // Apply to I2S_PCM_CTRL1 + return writeRegisterBits(NAU8325_R0D_I2S_PCM_CTRL1, + NAU8325_I2S_DF_MASK | NAU8325_I2S_BP_MASK | NAU8325_I2S_PCMB_EN, + ctrl1_val); +} + +bool PCBCUPID_NAU8325::setSysClock(uint32_t freq) { + if (freq < MASTER_CLK_MIN || freq > MASTER_CLK_MAX) { + Serial.printf("[NAU8325] Invalid MCLK: %u Hz (allowed: %u - %u)\n", freq, MASTER_CLK_MIN, MASTER_CLK_MAX); + return false; + } + + this->mclk = freq; + Serial.printf("[NAU8325] MCLK set to %u Hz\n", mclk); + return true; +} + +void PCBCUPID_NAU8325::setPowerUpDefault(bool enable) { + writeRegisterBits(NAU8325_R40_CLK_DET_CTRL, NAU8325_PWRUP_DFT, + enable ? NAU8325_PWRUP_DFT : 0); +} + +void PCBCUPID_NAU8325::setOversampling(OversamplingMode mode) { + writeRegisterBits(NAU8325_R29_DAC_CTRL1, NAU8325_DAC_OVERSAMPLE_MASK, mode); +} + +void PCBCUPID_NAU8325::setVolume(uint8_t left, uint8_t right) { + uint16_t value = ((left & 0xFF) << 8) | (right & 0xFF); + writeRegister(NAU8325_R13_DAC_VOLUME, value); +} + +void PCBCUPID_NAU8325::softMute(bool enable) // under testing +{ + writeRegisterBits(NAU8325_R12_MUTE_CTRL, NAU8325_SOFT_MUTE, + enable ? NAU8325_SOFT_MUTE : 0); + delay(30); // Same as msleep(30) +} + +void PCBCUPID_NAU8325::initRegisters() { + // Set ALC parameters default timing and max gain + writeRegisterBits(NAU8325_R2C_ALC_CTRL1, NAU8325_ALC_MAXGAIN_MASK, 0x7 << NAU8325_ALC_MAXGAIN_SFT); + writeRegisterBits(NAU8325_R2D_ALC_CTRL2, + NAU8325_ALC_DCY_MASK | NAU8325_ALC_ATK_MASK | NAU8325_ALC_HLD_MASK, + (0x5 << NAU8325_ALC_DCY_SFT) | (0x3 << NAU8325_ALC_ATK_SFT) | (0x5 << NAU8325_ALC_HLD_SFT)); + + /* Enable ALC to avoid signal distortion when battery low. */ + if (alc_enable) { + writeRegisterBits(NAU8325_R2E_ALC_CTRL3, NAU8325_ALC_EN, NAU8325_ALC_EN); + } + + // Clock detection bits + uint16_t clk_det_mask = NAU8325_CLKPWRUP_DIS | NAU8325_PWRUP_DFT; + + if (clock_detection) + writeRegisterBits(NAU8325_R40_CLK_DET_CTRL, clk_det_mask, 0); + else + writeRegisterBits(NAU8325_R40_CLK_DET_CTRL, clk_det_mask, NAU8325_CLKPWRUP_DIS); + + if (clock_det_data) + writeRegisterBits(NAU8325_R40_CLK_DET_CTRL, NAU8325_APWRUP_EN, NAU8325_APWRUP_EN); + else + writeRegisterBits(NAU8325_R40_CLK_DET_CTRL, NAU8325_APWRUP_EN, 0); + + // DAC Vref configuration + uint16_t vref_val = 0xFFFF; + if (dac_vref_microvolt == 1800000) + vref_val = 0 << NAU8325_DACVREFSEL_SFT; + else if (dac_vref_microvolt == 2700000) + vref_val = 1 << NAU8325_DACVREFSEL_SFT; + else if (dac_vref_microvolt == 2880000) + vref_val = 2 << NAU8325_DACVREFSEL_SFT; + else if (dac_vref_microvolt == 3060000) + vref_val = 3 << NAU8325_DACVREFSEL_SFT; + + if (vref_val != 0xFFFF) { + writeRegisterBits(NAU8325_R73_RDAC, NAU8325_DACVREFSEL_MASK, vref_val); + } else { + Serial.printf("[NAU8325] Invalid DAC Vref: %lu uV\n", dac_vref_microvolt); + } + + /* DAC Reference Voltage Decoupling Capacitors. */ + writeRegisterBits(NAU8325_R63_ANALOG_CONTROL_3, NAU8325_CLASSD_COARSE_GAIN_MASK, 0x4); + /* Auto-Att Min Gain 0dB, Class-D N Driver Slew Rate -25%. */ + writeRegisterBits(NAU8325_R64_ANALOG_CONTROL_4, NAU8325_CLASSD_SLEWN_MASK, 0x7); + + // VMID resistor selection + uint16_t vref_imp_val = 0xFFFF; + if (vref_impedance_ohms == 0) + vref_imp_val = 0 << NAU8325_BIAS_VMID_SEL_SFT; + else if (vref_impedance_ohms == 25000) + vref_imp_val = 1 << NAU8325_BIAS_VMID_SEL_SFT; + else if (vref_impedance_ohms == 125000) + vref_imp_val = 2 << NAU8325_BIAS_VMID_SEL_SFT; + else if (vref_impedance_ohms == 2500) + vref_imp_val = 3 << NAU8325_BIAS_VMID_SEL_SFT; + + if (vref_imp_val != 0xFFFF) { + writeRegisterBits(NAU8325_R60_BIAS_ADJ, NAU8325_BIAS_VMID_SEL_MASK, vref_imp_val); + } else { + Serial.printf("[NAU8325] Invalid VMID impedance: %lu ohms\n", vref_impedance_ohms); + } + + // Enable VMID, BIAS, DACs, etc., Voltage / current Amps + writeRegisterBits(NAU8325_R61_ANALOG_CONTROL_1, + NAU8325_DACEN_MASK | NAU8325_DACCLKEN_MASK | NAU8325_DACEN_R_MASK | NAU8325_DACCLKEN_R_MASK | NAU8325_CLASSDEN_MASK | NAU8325_VMDFSTENB_MASK | NAU8325_BIASEN_MASK | NAU8325_VMIDEN_MASK, + (0x1 << NAU8325_DACEN_SFT) | (0x1 << NAU8325_DACCLKEN_SFT) | (0x1 << NAU8325_DACEN_R_SFT) | (0x1 << NAU8325_DACCLKEN_R_SFT) | (0x1 << NAU8325_CLASSDEN_SFT) | (0x1 << NAU8325_VMDFSTENB_SFT) | (0x1 << NAU8325_BIASEN_SFT) | (1 << NAU8325_VMIDEN_SFT)); + + /* Enable ALC to avoid signal distortion when battery low. */ + if (alc_enable) { + writeRegisterBits(NAU8325_R2E_ALC_CTRL3, NAU8325_ALC_EN, NAU8325_ALC_EN); + } + + // Clock detection bits + uint16_t clk_det_mask1 = NAU8325_CLKPWRUP_DIS | NAU8325_PWRUP_DFT; + + if (clock_detection) + writeRegisterBits(NAU8325_R40_CLK_DET_CTRL, clk_det_mask1, 0); + else + writeRegisterBits(NAU8325_R40_CLK_DET_CTRL, clk_det_mask1, NAU8325_CLKPWRUP_DIS); + + if (clock_det_data) + writeRegisterBits(NAU8325_R40_CLK_DET_CTRL, NAU8325_APWRUP_EN, NAU8325_APWRUP_EN); + else + writeRegisterBits(NAU8325_R40_CLK_DET_CTRL, NAU8325_APWRUP_EN, 0); + + // Set default OSR = 128 + writeRegisterBits(NAU8325_R29_DAC_CTRL1, + NAU8325_DAC_OVERSAMPLE_MASK, + NAU8325_DAC_OVERSAMPLE_128); +} + +bool PCBCUPID_NAU8325::writeRegister(uint16_t reg, uint16_t val) { + Serial.printf("[WRITE] reg=0x%04X val=0x%04X\n", reg, val); + i2c.beginTransmission(i2c_addr); + i2c.write((reg >> 8) & 0xFF); + i2c.write(reg & 0xFF); + i2c.write((val >> 8) & 0xFF); + i2c.write(val & 0xFF); + return i2c.endTransmission() == 0; +} + +bool PCBCUPID_NAU8325::writeRegisterBits(uint16_t reg, uint16_t mask, uint16_t value) // mask ---> is to set the the particular bit renaining bit untouched. +{ + uint16_t current; + if (!readRegister(reg, current)) + return false; + + uint16_t new_value = (current & ~mask) | (value & mask); + return writeRegister(reg, new_value); +} + +bool PCBCUPID_NAU8325::readRegister(uint16_t reg, uint16_t &value) { + i2c.beginTransmission(i2c_addr); + i2c.write((reg >> 8) & 0xFF); + i2c.write(reg & 0xFF); + if (i2c.endTransmission(false) != 0) { + return false; + } + + if (i2c.requestFrom(i2c_addr, (uint8_t)2) != 2) { + return false; + } + + uint8_t high = i2c.read(); + uint8_t low = i2c.read(); + value = ((uint16_t)high << 8) | low; + return true; +} + +void PCBCUPID_NAU8325::powerOn() { + // Clear soft mute to allow audio output + softMute(false); + + // Enable DACs, clocks, bias, class-D + writeRegisterBits(NAU8325_R61_ANALOG_CONTROL_1, + NAU8325_DACEN_MASK | NAU8325_DACCLKEN_MASK | NAU8325_DACEN_R_MASK | NAU8325_DACCLKEN_R_MASK | NAU8325_CLASSDEN_MASK | NAU8325_VMDFSTENB_MASK | NAU8325_BIASEN_MASK | NAU8325_VMIDEN_MASK, + (3 << NAU8325_DACEN_SFT) | (3 << NAU8325_DACCLKEN_SFT) | (3 << NAU8325_DACEN_R_SFT) | (3 << NAU8325_DACCLKEN_R_SFT) | (3 << NAU8325_CLASSDEN_SFT) | (3 << NAU8325_VMDFSTENB_SFT) | (3 << NAU8325_BIASEN_SFT) | 0x03); // VMID_EN + + // If clock detection is disabled, enable PWRUP_DFT + setPowerUpDefault(true); + + delay(30); // Let analog circuitry stabilize +} + +void PCBCUPID_NAU8325::powerOff() { + // Soft mute first to prevent pop + softMute(true); + + // Disable analog and DAC power + writeRegisterBits(NAU8325_R61_ANALOG_CONTROL_1, + NAU8325_DACEN_MASK | NAU8325_DACCLKEN_MASK | NAU8325_DACEN_R_MASK | NAU8325_DACCLKEN_R_MASK | NAU8325_CLASSDEN_MASK | NAU8325_VMDFSTENB_MASK | NAU8325_BIASEN_MASK | NAU8325_VMIDEN_MASK, + 0x0000); + + // If clock detection is disabled, clear PWRUP_DFT + setPowerUpDefault(false); + + delay(30); // Let output fade out +} diff --git a/src/Driver/nau8325/PCBCUPID_NAU8325.h b/src/Driver/nau8325/PCBCUPID_NAU8325.h new file mode 100644 index 0000000..594c5d7 --- /dev/null +++ b/src/Driver/nau8325/PCBCUPID_NAU8325.h @@ -0,0 +1,481 @@ +#ifndef PCBCUPID_NAU8325_H +#define PCBCUPID_NAU8325_H + +#include +#include + +#define NAU8325_R00_HARDWARE_RST 0x00 +#define NAU8325_R01_SOFTWARE_RST 0x01 +#define NAU8325_R02_DEVICE_ID 0x02 +#define NAU8325_R03_CLK_CTRL 0x03 +#define NAU8325_R04_ENA_CTRL 0x04 +#define NAU8325_R05_INTERRUPT_CTRL 0x05 +#define NAU8325_R06_INT_CLR_STATUS 0x06 +#define NAU8325_R09_IRQOUT 0x09 +#define NAU8325_R0A_IO_CTRL 0x0a +#define NAU8325_R0B_PDM_CTRL 0x0b +#define NAU8325_R0C_TDM_CTRL 0x0c +#define NAU8325_R0D_I2S_PCM_CTRL1 0x0d +#define NAU8325_R0E_I2S_PCM_CTRL2 0x0e +#define NAU8325_R0F_L_TIME_SLOT 0x0f +#define NAU8325_R10_R_TIME_SLOT 0x10 +#define NAU8325_R11_HPF_CTRL 0x11 +#define NAU8325_R12_MUTE_CTRL 0x12 +#define NAU8325_R13_DAC_VOLUME 0x13 +#define NAU8325_R1D_DEBUG_READ1 0x1d +#define NAU8325_R1F_DEBUG_READ2 0x1f +#define NAU8325_R22_DEBUG_READ3 0x22 +#define NAU8325_R29_DAC_CTRL1 0x29 +#define NAU8325_R2A_DAC_CTRL2 0x2a +#define NAU8325_R2C_ALC_CTRL1 0x2c +#define NAU8325_R2D_ALC_CTRL2 0x2d +#define NAU8325_R2E_ALC_CTRL3 0x2e +#define NAU8325_R2F_ALC_CTRL4 0x2f +#define NAU8325_R40_CLK_DET_CTRL 0x40 +#define NAU8325_R49_TEST_STATUS 0x49 +#define NAU8325_R4A_ANALOG_READ 0x4a +#define NAU8325_R50_MIXER_CTRL 0x50 +#define NAU8325_R55_MISC_CTRL 0x55 +#define NAU8325_R60_BIAS_ADJ 0x60 +#define NAU8325_R61_ANALOG_CONTROL_1 0x61 +#define NAU8325_R62_ANALOG_CONTROL_2 0x62 +#define NAU8325_R63_ANALOG_CONTROL_3 0x63 +#define NAU8325_R64_ANALOG_CONTROL_4 0x64 +#define NAU8325_R65_ANALOG_CONTROL_5 0x65 +#define NAU8325_R66_ANALOG_CONTROL_6 0x66 +#define NAU8325_R69_CLIP_CTRL 0x69 +#define NAU8325_R73_RDAC 0x73 +#define NAU8325_REG_MAX NAU8325_R73_RDAC + +#define NAU8325_VMIDEN_SFT 0x03 + +/* 16-bit control register address, and 16-bits control register data */ +#define NAU8325_REG_ADDR_LEN 16 +#define NAU8325_REG_DATA_LEN 16 + +/*Below are bit masking of individual register*/ + +/* CLK_CTRL (0x03) */ +#define NAU8325_CLK_DAC_SRC_SFT 12 +#define NAU8325_CLK_DAC_SRC_MASK (0x3 << NAU8325_CLK_DAC_SRC_SFT) +#define NAU8325_CLK_MUL_SRC_SFT 6 +#define NAU8325_CLK_MUL_SRC_MASK (0x3 << NAU8325_CLK_MUL_SRC_SFT) +#define NAU8325_MCLK_SEL_SFT 3 +#define NAU8325_MCLK_SEL_MASK (0x7 << NAU8325_MCLK_SEL_SFT) +#define NAU8325_MCLK_SRC_MASK 0x7 + +/* ENA_CTRL (0x04) */ +#define NAU8325_DAC_LEFT_CH_EN_SFT 3 +#define NAU8325_DAC_LEFT_CH_EN (0x1 << NAU8325_DAC_LEFT_CH_EN_SFT) +#define NAU8325_DAC_RIGHT_CH_EN_SFT 2 +#define NAU8325_DAC_RIGHT_CH_EN (0x1 << NAU8325_DAC_RIGHT_CH_EN_SFT) + +// R04: ENA_CTRL +#define NAU8325_ENA_CTRL_DACR_EN 0x0008 // Bit 3 +#define NAU8325_ENA_CTRL_DACL_EN 0x0004 // Bit 2 +#define NAU8325_ENA_CTRL_DAC_EN_MASK 0x000C // Bits 3:2 + +/* INTERRUPT_CTRL (0x05) */ +#define NAU8325_ARP_DWN_INT_SFT 12 +#define NAU8325_ARP_DWN_INT_MASK (0x1 << NAU8325_ARP_DWN_INT_SFT) +#define NAU8325_CLIP_INT_SFT 11 +#define NAU8325_CLIP_INT_MASK (0x1 << NAU8325_CLIP_INT_SFT) +#define NAU8325_LVD_INT_SFT 10 +#define NAU8325_LVD_INT_MASK (0x1 << NAU8325_LVD_INT_SFT) +#define NAU8325_PWR_INT_DIS_SFT 8 +#define NAU8325_PWR_INT_DIS (0x1 << NAU8325_PWR_INT_DIS_SFT) +#define NAU8325_OCP_OTP_SHTDWN_INT_SFT 4 +#define NAU8325_OCP_OTP_SHTDWN_INT_MASK (0x1 << NAU8325_OCP_OTP_SHTDWN_INT_SFT) +#define NAU8325_CLIP_INT_DIS_SFT 3 +#define NAU8325_CLIP_INT_DIS (0x1 << NAU8325_CLIP_INT_DIS_SFT) +#define NAU8325_LVD_INT_DIS_SFT 2 +#define NAU8325_LVD_INT_DIS (0x1 << NAU8325_LVD_INT_DIS_SFT) +#define NAU8325_PWR_INT_MASK 0x1 + +/* INT_CLR_STATUS (0x06) */ +#define NAU8325_INT_STATUS_MASK 0x7f + +/* IRQOUT (0x9) */ +#define NAU8325_IRQOUT_SEL_SEF 12 +#define NAU8325_IRQOUT_SEL_MASK (0xf << NAU8325_IRQOUT_SEL_SEF) +#define NAU8325_DEM_DITH_SFT 7 +#define NAU8325_DEM_DITH_EN (0x1 << NAU8325_DEM_DITH_SFT) +#define NAU8325_GAINZI3_SFT 5 +#define NAU8325_GAINZI3_MASK (0x1 << NAU8325_GAINZI3_SFT) +#define NAU8325_GAINZI2_MASK 0x1f + +/* IO_CTRL (0x0a) */ +#define NAU8325_IRQ_PL_SFT 15 +#define NAU8325_IRQ_PL_ACT_HIGH (0x1 << NAU8325_IRQ_PL_SFT) +#define NAU8325_IRQ_PS_SFT 14 +#define NAU8325_IRQ_PS_UP (0x1 << NAU8325_IRQ_PS_SFT) +#define NAU8325_IRQ_PE_SFT 13 +#define NAU8325_IRQ_PE_EN (0x1 << NAU8325_IRQ_PE_SFT) +#define NAU8325_IRQ_DS_SFT 12 +#define NAU8325_IRQ_DS_HIGH (0x1 << NAU8325_IRQ_DS_SFT) +#define NAU8325_IRQ_OUTPUT_SFT 11 +#define NAU8325_IRQ_OUTPUT_EN (0x1 << NAU8325_IRQ_OUTPUT_SFT) +#define NAU8325_IRQ_PIN_DEBUG_SFT 10 +#define NAU8325_IRQ_PIN_DEBUG_EN (0x1 << NAU8325_IRQ_PIN_DEBUG_SFT) + +/* PDM_CTRL (0x0b) */ +#define NAU8325_PDM_LCH_EDGE_SFT 1 +#define NAU8325_PDM_LCH_EDGE__MASK (0x1 << NAU8325_PDM_LCH_EDGE_SFT) +#define NAU8325_PDM_MODE_EN 0x1 + +/* TDM_CTRL (0x0c) */ +#define NAU8325_TDM_SFT 15 +#define NAU8325_TDM_EN (0x1 << NAU8325_TDM_SFT) +#define NAU8325_PCM_OFFSET_CTRL_SFT 14 +#define NAU8325_PCM_OFFSET_CTRL_EN (0x1 << NAU8325_PCM_OFFSET_CTRL_SFT) +#define NAU8325_DAC_LEFT_SFT 6 +#define NAU8325_NAU8325_DAC_LEFT_MASK (0x7 << NAU8325_DAC_LEFT_SFT) +#define NAU8325_DAC_RIGHT_SFT 3 +#define NAU8325_DAC_RIGHT_MASK (0x7 << NAU8325_DAC_RIGHT_SFT) + +/* I2S_PCM_CTRL1 (0x0d) */ +#define NAU8325_DACCM_CTL_SFT 14 +#define NAU8325_DACCM_CTL_MASK (0x3 << NAU8325_DACCM_CTL_SFT) +#define NAU8325_CMB8_0_SFT 10 +#define NAU8325_CMB8_0_MASK (0x1 << NAU8325_CMB8_0_SFT) +#define NAU8325_UA_OFFSET_SFT 9 +#define NAU8325_UA_OFFSET_MASK (0x1 << NAU8325_UA_OFFSET_SFT) +#define NAU8325_I2S_BP_SFT 7 +#define NAU8325_I2S_BP_MASK (0x1 << NAU8325_I2S_BP_SFT) +#define NAU8325_I2S_BP_INV (0x1 << NAU8325_I2S_BP_SFT) +#define NAU8325_I2S_PCMB_SFT 6 +#define NAU8325_I2S_PCMB_EN (0x1 << NAU8325_I2S_PCMB_SFT) +#define NAU8325_I2S_DACPSHS0_SFT 5 +#define NAU8325_I2S_DACPSHS0_MASK (0x1 << NAU8325_I2S_DACPSHS0_SFT) +#define NAU8325_I2S_DL_SFT 2 +#define NAU8325_I2S_DL_MASK (0x3 << NAU8325_I2S_DL_SFT) +#define NAU8325_I2S_DL_32 (0x3 << NAU8325_I2S_DL_SFT) +#define NAU8325_I2S_DL_24 (0x2 << NAU8325_I2S_DL_SFT) +#define NAU8325_I2S_DL_20 (0x1 << NAU8325_I2S_DL_SFT) +#define NAU8325_I2S_DL_16 (0x0 << NAU8325_I2S_DL_SFT) +#define NAU8325_I2S_DF_MASK 0x3 +#define NAU8325_I2S_DF_RIGTH 0x0 +#define NAU8325_I2S_DF_LEFT 0x1 +#define NAU8325_I2S_DF_I2S 0x2 +#define NAU8325_I2S_DF_PCM_AB 0x3 + +/* I2S_PCM_CTRL2 (0x0e) */ +#define NAU8325_PCM_TS_SFT 10 +#define NAU8325_PCM_TS_EN (0x1 << NAU8325_PCM_TS_SFT) +#define NAU8325_PCM8BIT0_SFT 8 +#define NAU8325_PCM8BIT0_MASK (0x1 << NAU8325_PCM8BIT0_SFT) + +/* L_TIME_SLOT (0x0f)*/ +#define NAU8325_SHORT_FS_DET_SFT 13 +#define NAU8325_SHORT_FS_DET_DIS (0x1 << NAU8325_SHORT_FS_DET_SFT) +#define NAU8325_TSLOT_L0_MASK 0x3ff + +/* R_TIME_SLOT (0x10)*/ +#define NAU8325_TSLOT_R0_MASK 0x3ff + +/* HPF_CTRL (0x11)*/ +#define NAU8325_DAC_HPF_SFT 15 +#define NAU8325_DAC_HPF_EN (0x1 << NAU8325_DAC_HPF_SFT) +#define NAU8325_DAC_HPF_APP_SFT 14 +#define NAU8325_DAC_HPF_APP_MASK (0x1 << NAU8325_DAC_HPF_APP_SFT) +#define NAU8325_DAC_HPF_FCUT_SFT 11 +#define NAU8325_DAC_HPF_FCUT_MASK (0x7 << NAU8325_DAC_HPF_FCUT_SFT) + +/* MUTE_CTRL (0x12)*/ +#define NAU8325_SOFT_MUTE_SFT 15 +#define NAU8325_SOFT_MUTE (0x1 << NAU8325_SOFT_MUTE_SFT) +#define NAU8325_DAC_ZC_SFT 8 +#define NAU8325_DAC_ZC_EN (0x1 << NAU8325_DAC_ZC_SFT) +#define NAU8325_UNMUTE_CTL_SFT 6 +#define NAU8325_UNMUTE_CTL_MASK (0x3 << NAU8325_UNMUTE_CTL_SFT) +#define NAU8325_ANA_MUTE_SFT 4 +#define NAU8325_ANA_MUTE_MASK (0x3 << NAU8325_ANA_MUTE_SFT) +#define NAU8325_AUTO_MUTE_SFT 3 +#define NAU8325_AUTO_MUTE_DIS (0x1 << NAU8325_AUTO_MUTE_SFT) + +/* DAC_VOLUME (0x13) */ +#define NAU8325_DAC_VOLUME_L_SFT 8 +#define NAU8325_DAC_VOLUME_L_EN (0xff << NAU8325_DAC_VOLUME_L_SFT) +#define NAU8325_DAC_VOLUME_R_SFT 0 +#define NAU8325_DAC_VOLUME_R_EN (0xff << NAU8325_DAC_VOLUME_R_SFT) +#define NAU8325_DAC_VOL_MAX 0xff + +/* DEBUG_READ1 (0x1d)*/ +#define NAU8325_OSR100_MASK (0x1 << 6) +#define NAU8325_MIPS500_MASK (0x1 << 5) +#define NAU8325_SHUTDWNDRVR_R_MASK (0x1 << 4) +#define NAU8325_SHUTDWNDRVR_L_MASK (0x1 << 3) +#define NAU8325_MUTEB_MASK (0x1 << 2) +#define NAU8325_PDOSCB_MASK (0x1 << 1) +#define NAU8325_POWERDOWN1B_D_MASK 0x1 + +/* DEBUG_READ2 (0x1f)*/ +#define NAU8325_R_CHANNEL_Vol_SFT 8 +#define NAU8325_R_CHANNEL_Vol_MASK (0xff << NAU8325_R_CHANNEL_Vol_SFT) +#define NAU8325_L_CHANNEL_Vol_MASK 0xff + +/* DEBUG_READ3(0x22)*/ +#define NAU8325_PGAL_GAIN_MASK (0x3f << 7) +#define NAU8325_CLIP_MASK (0x1 << 6) +#define NAU8325_SCAN_MODE_MASK (0x1 << 5) +#define NAU8325_SDB_MASK (0x1 << 4) +#define NAU8325_TALARM_MASK (0x1 << 3) +#define NAU8325_SHORTR_MASK (0x1 << 2) +#define NAU8325_SHORTL_MASK (0x1 << 1) +#define NAU8325_TMDET_MASK 0x1 + +/* DAC_CTRL1 (0x29) */ +#define NAU8325_DAC_OVERSAMPLE_SFT 0 +#define NAU8325_DAC_OVERSAMPLE_MASK 0x7 +#define NAU8325_DAC_OVERSAMPLE_256 1 +#define NAU8325_DAC_OVERSAMPLE_128 2 +#define NAU8325_DAC_OVERSAMPLE_64 0 +#define NAU8325_DAC_OVERSAMPLE_32 4 + +/* ALC_CTRL1 (0x2c) */ +#define NAU8325_ALC_MAXGAIN_SFT 5 +#define NAU8325_ALC_MAXGAIN_MAX 0x7 +#define NAU8325_ALC_MAXGAIN_MASK (0x7 << NAU8325_ALC_MAXGAIN_SFT) +#define NAU8325_ALC_MINGAIN_MAX 4 +#define NAU8325_ALC_MINGAIN_SFT 1 +#define NAU8325_ALC_MINGAIN_MASK (0x7 << NAU8325_ALC_MINGAIN_SFT) + +/* ALC_CTRL2 (0x2d) */ +#define NAU8325_ALC_DCY_SFT 12 +#define NAU8325_ALC_DCY_MAX 0xb +#define NAU8325_ALC_DCY_MASK (0xf << NAU8325_ALC_DCY_SFT) +#define NAU8325_ALC_ATK_SFT 8 +#define NAU8325_ALC_ATK_MAX 0xb +#define NAU8325_ALC_ATK_MASK (0xf << NAU8325_ALC_ATK_SFT) +#define NAU8325_ALC_HLD_SFT 4 +#define NAU8325_ALC_HLD_MAX 0xa +#define NAU8325_ALC_HLD_MASK (0xf << NAU8325_ALC_HLD_SFT) +#define NAU8325_ALC_LVL_SFT 0 +#define NAU8325_ALC_LVL_MAX 0xf +#define NAU8325_ALC_LVL_MASK 0xf + +/* ALC_CTRL3 (0x2e) */ +#define NAU8325_ALC_EN_SFT 15 +#define NAU8325_ALC_EN (0x1 << NAU8325_ALC_EN_SFT) + +/* TEMP_COMP_CTRL (0x30) */ +#define NAU8325_TEMP_COMP_ACT2_MASK 0xff + +/* LPF_CTRL (0x33) */ +#define NAU8325_LPF_IN1_EN_SFT 15 +#define NAU8325_LPF_IN1_EN (0x1 << NAU8325_LPF_IN1_EN_SFT) +#define NAU8325_LPF_IN1_TC_SFT 11 +#define NAU8325_LPF_IN1_TC_MASK (0xf << NAU8325_LPF_IN1_TC_SFT) +#define NAU8325_LPF_IN2_EN_SFT 10 +#define NAU8325_LPF_IN2_EN (0x1 << NAU8325_LPF_IN2_EN_SFT) +#define NAU8325_LPF_IN2_TC_SFT 6 +#define NAU8325_LPF_IN2_TC_MASK (0xf << NAU8325_LPF_IN2_TC_SFT) + +/* CLK_DET_CTRL (0x40) */ +#define NAU8325_APWRUP_SFT 15 +#define NAU8325_APWRUP_EN (0x1 << NAU8325_APWRUP_SFT) +#define NAU8325_CLKPWRUP_SFT 14 +#define NAU8325_CLKPWRUP_DIS (0x1 << NAU8325_CLKPWRUP_SFT) +#define NAU8325_PWRUP_DFT_SFT 13 +#define NAU8325_PWRUP_DFT (0x1 << NAU8325_PWRUP_DFT_SFT) +#define NAU8325_REG_SRATE_SFT 10 +#define NAU8325_REG_SRATE_MASK (0x7 << NAU8325_REG_SRATE_SFT) +#define NAU8325_REG_ALT_SRATE_SFT 9 +#define NAU8325_REG_ALT_SRATE_EN (0x1 << NAU8325_REG_ALT_SRATE_SFT) +#define NAU8325_REG_DIV_MAX 0x1 + +/* BIAS_ADJ (0x60) */ +#define NAU8325_BIAS_VMID_SEL_SFT 4 +#define NAU8325_BIAS_VMID_SEL_MASK (0x3 << NAU8325_BIAS_VMID_SEL_SFT) + +/* ANALOG_CONTROL_1 (0x61) */ +#define NAU8325_VMDFSTENB_SFT 14 +#define NAU8325_VMDFSTENB_MASK (0x3 << NAU8325_VMDFSTENB_SFT) +#define NAU8325_CLASSDEN_SFT 12 +#define NAU8325_CLASSDEN_MASK (0x3 << NAU8325_CLASSDEN_SFT) +#define NAU8325_DACCLKEN_R_SFT 10 +#define NAU8325_DACCLKEN_R_MASK (0x3 << NAU8325_DACCLKEN_R_SFT) +#define NAU8325_DACEN_R_SFT 8 +#define NAU8325_DACEN_R_MASK (0x3 << NAU8325_DACEN_R_SFT) +#define NAU8325_DACCLKEN_SFT 6 +#define NAU8325_DACCLKEN_MASK (0x3 << NAU8325_DACCLKEN_SFT) +#define NAU8325_DACEN_SFT 4 +#define NAU8325_DACEN_MASK (0x3 << NAU8325_DACEN_SFT) +#define NAU8325_BIASEN_SFT 2 +#define NAU8325_BIASEN_MASK (0x3 << NAU8325_BIASEN_SFT) +#define NAU8325_VMIDEN_MASK 0x3 + +// R61: ANALOG_CONTROL_1 +#define NAU8325_ANALOG_CTRL1_VMID_EN 0x0001 // Bit 0 +#define NAU8325_ANALOG_CTRL1_CLASSD_EN 0x0002 // Bit 1 +#define NAU8325_ANALOG_CTRL1_EN_MASK 0x0003 // Bits 1:0 + +/* ANALOG_CONTROL_2 (0x62) */ +#define NAU8325_PWMMOD_SFT 14 +#define NAU8325_PWMMOD_MASK (0x1 << NAU8325_PWMMOD_SFT) +#define NAU8325_DACTEST_SFT 6 +#define NAU8325_DACTEST_MASK (0x3 << NAU8325_DACTEST_SFT) +#define NAU8325_DACREFCAP_SFT 4 +#define NAU8325_DACREFCAP_MASK (0x3 << NAU8325_DACREFCAP_SFT) + +/* ANALOG_CONTROL_3 (0x63) */ +#define NAU8325_POWER_DOWN_L_SFT 12 +#define NAU8325_POWER_DOWN_L_MASK (0x3 << NAU8325_POWER_DOWN_L_SFT) +#define NAU8325_POWER_DOWN_R_SFT 11 +#define NAU8325_POWER_DOWN_R_MASK (0x3 << NAU8325_DACREFCAP_SFT) +#define NAU8325_CLASSD_FINE_SFT 5 +#define NAU8325_CLASSD_FINE_MASK (0x3 << NAU8325_CLASSD_FINE_SFT) +#define NAU8325_CLASSD_COARSE_GAIN_MASK 0xf + +/* ANALOG_CONTROL_4 (0x64) */ +#define NAU8325_CLASSD_OCPN_SFT 12 +#define NAU8325_CLASSD_OCPN_MASK (0xf << NAU8325_CLASSD_OCPN_SFT) +#define NAU8325_CLASSD_OCPP_SFT 8 +#define NAU8325_CLASSD_OCPP_MASK (0xf << NAU8325_CLASSD_OCPP_SFT) +#define NAU8325_CLASSD_SLEWN_MASK 0xff + +/* ANALOG_CONTROL_5 (0x65) */ +#define NAU8325_MCLK_RANGE_SFT 2 +#define NAU8325_MCLK_RANGE_EN (0x1 << NAU8325_MCLK_RANGE_SFT) +#define NAU8325_MCLK8XEN_SFT 1 +#define NAU8325_MCLK8XEN_EN (0x1 << NAU8325_MCLK8XEN_SFT) +#define NAU8325_MCLK4XEN_EN 0x1 + +/* ANALOG_CONTROL_6 (0x66) */ +#define NAU8325_VBATLOW_SFT 4 +#define NAU8325_VBATLOW_MASK (0x1 << NAU8325_VBATLOW_SFT) +#define NAU8325_VDDSPK_LIM_SFT 3 +#define NAU8325_VDDSPK_LIM_EN (0x1 << NAU8325_VDDSPK_LIM_SFT) +#define NAU8325_VDDSPK_LIM_MASK 0x7 + +// R66: ANALOG_CONTROL_6 +#define NAU8325_ANALOG_CTRL6_SPKL_EN 0x0020 // Bit 5 +#define NAU8325_ANALOG_CTRL6_SPKR_EN 0x0010 // Bit 4 +#define NAU8325_ANALOG_CTRL6_EN_MASK 0x0030 // Bits 5:4 + +/* CLIP_CTRL (0x69)*/ +#define NAU8325_ANTI_CLIP_SFT 4 +#define NAU8325_ANTI_CLIP_EN (0x1 << NAU8325_ANTI_CLIP_SFT) + +/* RDAC (0x73) */ +#define NAU8325_CLK_DAC_DELAY_SFT 4 +#define NAU8325_CLK_DAC_DELAY_EN (0x7 << NAU8325_CLK_DAC_DELAY_SFT) +#define NAU8325_DACVREFSEL_SFT 2 +#define NAU8325_DACVREFSEL_MASK (0x3 << NAU8325_DACVREFSEL_SFT) + +#define CLK_DA_AD_MAX 6144000UL // or another max as per datasheet + +struct SrcAttr +{ + int param; + uint8_t val; +}; + +struct OsrAttr +{ + int osr; + uint8_t clk_src; +}; + +struct SRateAttr +{ + int fs; + uint8_t range; + bool max; + uint32_t mclk_src[3]; +}; + +struct RegDefault +{ + uint16_t reg; + uint16_t val; +}; + +enum NAU8325_I2SFormat +{ + I2S_STD, + LEFT_JUSTIFIED, + RIGHT_JUSTIFIED, + DSP_A, + DSP_B +}; +enum NAU8325_ClockPolarity +{ + NORMAL_BCLK, + INVERTED_BCLK +}; +enum OversamplingMode +{ + OSR_64 = 2, + OSR_128 = 1, + OSR_256 = 0, + OSR_32 = 4 +}; + +enum +{ + NAU8325_MCLK_FS_RATIO_256 = 0, + NAU8325_MCLK_FS_RATIO_400 = 1, + NAU8325_MCLK_FS_RATIO_500 = 2, + NAU8325_MCLK_FS_RATIO_NUM = 3 +}; + +class PCBCUPID_NAU8325 +{ +public: + PCBCUPID_NAU8325(TwoWire &wire, uint8_t i2c_addr); + + static const RegDefault reg_defaults[]; + + // Static constant lookup tables + static const SrcAttr mclk_n1_div[]; + static const SrcAttr mclk_n2_div[]; + static const SrcAttr mclk_n3_mult[]; + static const OsrAttr osr_dac_sel[]; + static const SRateAttr target_srate_table[]; + + bool begin(uint32_t fs, uint8_t bits_per_sample, uint16_t ratio); + bool begin(uint32_t fs, uint8_t bits_per_sample); + bool begin(); + + uint16_t readDeviceId(); + void resetChip(); + + void softMute(bool enable); + void powerOn(); + void powerOff(); + + void setPowerUpDefault(bool enable); + void setOversampling(OversamplingMode mode); + void setVolume(uint8_t left, uint8_t right); // public + + bool setI2SFormat(NAU8325_I2SFormat format, NAU8325_ClockPolarity polarity); + +private: + // Add private members (Wire, I2C address, etc.) here later + TwoWire &i2c; + uint8_t i2c_addr; + uint32_t fs; + uint32_t mclk; + + bool clock_detection; + + void initRegisters(); // Initialize codec + + bool setSysClock(uint32_t freq); + bool configureAudio(uint32_t fs, uint32_t mclk, uint8_t bits_per_sample); + bool chooseClockSource(int fs, int mclk, const SRateAttr *&srate, int &n1_sel, int &mult_sel, int &n2_sel); + bool configureClocks(int fs, int mclk); + bool applySampleRateClocks(const SRateAttr *srate, int n1_sel, int mclk_mult_sel, int n2_sel); + int getMclkRatioAndN2Index(const SRateAttr *srate, int mclk_hz, int &n2_sel_out); + const SRateAttr *getSRateAttr(int fs); + const OsrAttr *getCurrentOSR(); + + void enableDAPMBlocks(); + + bool readRegister(uint16_t reg, uint16_t &value); + bool writeRegister(uint16_t reg, uint16_t value); + bool writeRegisterBits(uint16_t reg, uint16_t mask, uint16_t value); +}; + +#endif // PCBCUPID_NAU8325_H diff --git a/src/DriverPins.h b/src/DriverPins.h index 53acf66..b57cf92 100644 --- a/src/DriverPins.h +++ b/src/DriverPins.h @@ -808,5 +808,8 @@ static PinsAudioKitEs8388v1Class PinsAudioKitEs8388v1; static PinsAudioKitEs8388v2Class PinsAudioKitEs8388v2; /// @ingroup audio_driver static PinsAudioKitAC101Class PinsAudioKitAC101; +/// @ingroup audio_driver +static DriverPins PinsNAU8325; + } // namespace audio_driver \ No newline at end of file From 551e5d9ab1f77062e7256167c460cb4449c37690 Mon Sep 17 00:00:00 2001 From: codingkarthik94 Date: Wed, 9 Jul 2025 11:09:40 +0530 Subject: [PATCH 3/8] version 0.0.2 Changelogs -Added: sinewave generator, which has been integrated with our PCBCUPID_NAU8325 driver from the custom Audiodriver class -Tested: working as expected , I have tested for two frequency sine tone N_A4 and N_B4. --- .../custom-nau8325/sinewave-generator/sinewave-generator.ino | 2 +- src/Driver.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/custom/custom-nau8325/sinewave-generator/sinewave-generator.ino b/examples/custom/custom-nau8325/sinewave-generator/sinewave-generator.ino index e10c6f4..cc62edd 100644 --- a/examples/custom/custom-nau8325/sinewave-generator/sinewave-generator.ino +++ b/examples/custom/custom-nau8325/sinewave-generator/sinewave-generator.ino @@ -58,7 +58,7 @@ void setup() { copier.begin(i2s_out, sound_stream); // Start sine wave - sine_wave.begin(audio_info, N_A4); + sine_wave.begin(audio_info, N_B4); Serial.println("Sinewave output started."); } diff --git a/src/Driver.h b/src/Driver.h index eb87910..42021ff 100644 --- a/src/Driver.h +++ b/src/Driver.h @@ -1738,7 +1738,7 @@ class AudioDriverNAU8325Class : public AudioDriver { bool setMute(bool enable) override { if (!nau) return false; - nau->softMute(enable); // assumed void return + nau->softMute(enable); // void return return true; } From 0a0ad58dfc558b41c38d3b2f962dd68149cc90d6 Mon Sep 17 00:00:00 2001 From: codingkarthik94 Date: Wed, 9 Jul 2025 11:52:32 +0530 Subject: [PATCH 4/8] version 0.0.3 Changelogs -MOdified: SetI2SFormat function added to private , before it was in public. --- src/Driver/nau8325/PCBCUPID_NAU8325.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Driver/nau8325/PCBCUPID_NAU8325.h b/src/Driver/nau8325/PCBCUPID_NAU8325.h index 594c5d7..54f87f9 100644 --- a/src/Driver/nau8325/PCBCUPID_NAU8325.h +++ b/src/Driver/nau8325/PCBCUPID_NAU8325.h @@ -449,7 +449,7 @@ class PCBCUPID_NAU8325 void setOversampling(OversamplingMode mode); void setVolume(uint8_t left, uint8_t right); // public - bool setI2SFormat(NAU8325_I2SFormat format, NAU8325_ClockPolarity polarity); + private: // Add private members (Wire, I2C address, etc.) here later @@ -470,6 +470,7 @@ class PCBCUPID_NAU8325 int getMclkRatioAndN2Index(const SRateAttr *srate, int mclk_hz, int &n2_sel_out); const SRateAttr *getSRateAttr(int fs); const OsrAttr *getCurrentOSR(); + bool setI2SFormat(NAU8325_I2SFormat format, NAU8325_ClockPolarity polarity); void enableDAPMBlocks(); From 1a972b6220a036ec0526d76985afac61177d1447 Mon Sep 17 00:00:00 2001 From: srini Date: Thu, 31 Jul 2025 12:33:20 +0530 Subject: [PATCH 5/8] reviewed --- .../sinewave-generator/sinewave-generator.ino | 67 ------------------- src/AudioBoard.h | 2 +- src/Driver.h | 40 ++++------- src/DriverPins.h | 2 +- 4 files changed, 16 insertions(+), 95 deletions(-) delete mode 100644 examples/custom/custom-nau8325/sinewave-generator/sinewave-generator.ino diff --git a/examples/custom/custom-nau8325/sinewave-generator/sinewave-generator.ino b/examples/custom/custom-nau8325/sinewave-generator/sinewave-generator.ino deleted file mode 100644 index cc62edd..0000000 --- a/examples/custom/custom-nau8325/sinewave-generator/sinewave-generator.ino +++ /dev/null @@ -1,67 +0,0 @@ -#include -#include -#include -#include "Driver.h" - -using namespace audio_tools; -using namespace audio_driver; - -// === Pins === -#define SDAPIN 4 -#define SCLPIN 5 -#define I2CSPEED 100000 -#define NAU8325ADDR 0x21 -#define MCLKPIN 22 -#define BCLKPIN 25 -#define WSPIN 24 -#define DOPIN 23 -#define DIPIN -1 - -// === Audio Setup === -AudioInfo audio_info(44100, 2, 16); -SineWaveGenerator sine_wave(30000); -GeneratedSoundStream sound_stream(sine_wave); -StreamCopy copier; - -TwoWire myWire = TwoWire(0); -DriverPins pins; -AudioBoard board(AudioDriverNAU8325, pins); -I2SCodecStream i2s_out(board); - -void setup() { - Serial.begin(115200); - delay(1000); // Allow time for serial monitor to open - AudioLogger::instance().begin(Serial, AudioLogger::Info); - AudioDriverLogger.begin(Serial, AudioDriverLogLevel::Debug); - - // Setup I2C and I2S pins - pins.addI2C(PinFunction::CODEC, SCLPIN, SDAPIN, NAU8325ADDR, I2CSPEED, myWire); - pins.addI2S(PinFunction::CODEC, MCLKPIN, BCLKPIN, WSPIN, DOPIN, DIPIN); - pins.begin(); - - // Codec config - CodecConfig config; - config.i2s.rate = RATE_44K; - config.i2s.bits = BIT_LENGTH_16BITS; - config.i2s.channels = CHANNELS2; - - // Start codec board - if (!board.begin(config)) { - Serial.println("[NAU8325] board.begin() failed!"); - while (1); - } - - // Configure I2S output - auto cfg = i2s_out.defaultConfig(); - cfg.copyFrom(audio_info); - i2s_out.begin(cfg); - copier.begin(i2s_out, sound_stream); - - // Start sine wave - sine_wave.begin(audio_info, N_B4); - Serial.println("Sinewave output started."); -} - -void loop() { - copier.copy(); -} diff --git a/src/AudioBoard.h b/src/AudioBoard.h index d36d2dc..c03fd44 100644 --- a/src/AudioBoard.h +++ b/src/AudioBoard.h @@ -132,7 +132,7 @@ static AudioBoard GenericWM8960{AudioDriverWM8960, NoPins}; /// @ingroup audio_driver static AudioBoard GenericCS43l22{AudioDriverCS43l22, NoPins}; /// @ingroup audio_driver -static AudioBoard NAU8325Board{AudioDriverNAU8325, NoPins}; +static AudioBoard NAU8325Board{AudioDriverNAU8325, NoPins}; //change name to GmodNAU8325 #if defined(ARDUINO_GENERIC_F411VETX) /// @ingroup audio_driver diff --git a/src/Driver.h b/src/Driver.h index 98baff9..e0e01e2 100644 --- a/src/Driver.h +++ b/src/Driver.h @@ -1671,25 +1671,14 @@ namespace audio_driver AudioDriverES7243Class adc; }; - /** - * @brief Driver API for NAU8325 CODEC WITH AMPLIFIER - * @author - * @copyright MIT Licence - */ - // *************** NAU8325 DRIVER WRAPPER ************ - -/** - * @brief Driver API for NAU8325 CODEC WITH AMPLIFIER - * @author You - * @copyright MIT License - */ -// -- NAU8325 Driver Class +// *************** NAU8325 DRIVER WRAPPER ************ + class AudioDriverNAU8325Class : public AudioDriver { public: - PCBCUPID_NAU8325 *nau = nullptr; // pointer to construct later + PCBCUPID_NAU8325 *nau8325 = nullptr; ~AudioDriverNAU8325Class() { - if (nau) delete nau; + if (nau8325) delete nau8325; } bool begin(CodecConfig cfg, DriverPins &pins) override { @@ -1705,8 +1694,7 @@ class AudioDriverNAU8325Class : public AudioDriver { } PinsI2C val = i2c_opt.value(); - // Create instance with required params - nau = new PCBCUPID_NAU8325(*((TwoWire *)val.p_wire), val.address); + nau8325 = new PCBCUPID_NAU8325(*((TwoWire *)val.p_wire), val.address); // Set MCLK if available int mclk = pins.getPinID(PinFunction::MCLK_SOURCE); @@ -1720,8 +1708,8 @@ class AudioDriverNAU8325Class : public AudioDriver { int fs = cfg.getRateNumeric(); int bits = cfg.getBitsNumeric(); - AD_LOGI("Calling nau->begin(fs=%d, bits=%d)", fs, bits); - if (!nau->begin(fs, bits)) { + AD_LOGI("Calling nau8325->begin(fs=%d, bits=%d)", fs, bits); + if (!nau8325->begin(fs, bits)) { AD_LOGE("NAU8325 beginDynamic failed"); return false; } @@ -1730,23 +1718,23 @@ class AudioDriverNAU8325Class : public AudioDriver { } bool end() override { - if (nau) { - nau->powerOff(); // if it's void, just call directly + if (nau8325) { + nau8325->powerOff(); // if it's void, just call directly } return true; } bool setMute(bool enable) override { - if (!nau) return false; - nau->softMute(enable); // void return + if (!nau8325) return false; + nau8325->softMute(enable); // void return return true; } bool setVolume(int volume) override { - if (!nau) return false; - // Mapping volume (0–100) to 0–255 (NAU range) + if (!nau8325) return false; + // Mapping volume (0–100) to 0–255 (nau8325 range) uint8_t val = map(volume, 0, 100, 0, 255); - nau->setVolume(val, val); // assuming stereo + nau8325->setVolume(val, val); // assuming stereo return true; } diff --git a/src/DriverPins.h b/src/DriverPins.h index b57cf92..6b37637 100644 --- a/src/DriverPins.h +++ b/src/DriverPins.h @@ -809,7 +809,7 @@ static PinsAudioKitEs8388v2Class PinsAudioKitEs8388v2; /// @ingroup audio_driver static PinsAudioKitAC101Class PinsAudioKitAC101; /// @ingroup audio_driver -static DriverPins PinsNAU8325; +static DriverPins PinsNAU8325; //changeTo PinsGmodNAU8325 } // namespace audio_driver \ No newline at end of file From 3537c3bf0e9520d0d223d09ae1194b9cbcdf1c67 Mon Sep 17 00:00:00 2001 From: codingkarthik94 Date: Thu, 31 Jul 2025 16:53:19 +0530 Subject: [PATCH 6/8] version 0.0.4 Changelogs -Modified: the driver - > nau8325 --- README.md | 2 + src/AudioBoard.h | 2 +- src/Driver.h | 4 +- src/Driver/nau8325/PCBCUPID_NAU8325.cpp | 512 +++++++++++++----------- src/Driver/nau8325/PCBCUPID_NAU8325.h | 50 ++- src/DriverPins.h | 2 +- 6 files changed, 328 insertions(+), 244 deletions(-) diff --git a/README.md b/README.md index 3ede962..12f8ddc 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,7 @@ This library privides the following drivers: - AudioDriverWM8960 - AudioDriverWM8978 - AudioDriverWM8994 +- AudioDriverNAU8325 And it supports the following boards: @@ -45,6 +46,7 @@ And it supports the following boards: - LyratV42  - LyratV43  - NoBoard  +- GmodNAU8325 ## Logging diff --git a/src/AudioBoard.h b/src/AudioBoard.h index c03fd44..fa90c9e 100644 --- a/src/AudioBoard.h +++ b/src/AudioBoard.h @@ -132,7 +132,7 @@ static AudioBoard GenericWM8960{AudioDriverWM8960, NoPins}; /// @ingroup audio_driver static AudioBoard GenericCS43l22{AudioDriverCS43l22, NoPins}; /// @ingroup audio_driver -static AudioBoard NAU8325Board{AudioDriverNAU8325, NoPins}; //change name to GmodNAU8325 +static AudioBoard GmodNAU8325{AudioDriverNAU8325, NoPins}; //change name to GmodNAU8325 #if defined(ARDUINO_GENERIC_F411VETX) /// @ingroup audio_driver diff --git a/src/Driver.h b/src/Driver.h index e0e01e2..2cd9cc4 100644 --- a/src/Driver.h +++ b/src/Driver.h @@ -1671,7 +1671,7 @@ namespace audio_driver AudioDriverES7243Class adc; }; -// *************** NAU8325 DRIVER WRAPPER ************ +/* *************** NAU8325 DRIVER WRAPPER ************ */ class AudioDriverNAU8325Class : public AudioDriver { public: @@ -1694,7 +1694,7 @@ class AudioDriverNAU8325Class : public AudioDriver { } PinsI2C val = i2c_opt.value(); - nau8325 = new PCBCUPID_NAU8325(*((TwoWire *)val.p_wire), val.address); + nau8325 = new PCBCUPID_NAU8325(*((TwoWire *)val.p_wire)); // Set MCLK if available int mclk = pins.getPinID(PinFunction::MCLK_SOURCE); diff --git a/src/Driver/nau8325/PCBCUPID_NAU8325.cpp b/src/Driver/nau8325/PCBCUPID_NAU8325.cpp index c643ea3..d7c01ad 100644 --- a/src/Driver/nau8325/PCBCUPID_NAU8325.cpp +++ b/src/Driver/nau8325/PCBCUPID_NAU8325.cpp @@ -1,9 +1,52 @@ #include "PCBCUPID_NAU8325.h" +#include "esp_log.h" #define MASTER_CLK_MIN 2048000 #define MASTER_CLK_MAX 49152000 -uint16_t set_ratio = 0; //declared as global +// N1 divider options +const SrcAttr PCBCUPID_NAU8325::mclk_n1_div[] = { + {1, 0x0}, + {2, 0x1}, + {3, 0x2}}; + +// N2 divider options +const SrcAttr PCBCUPID_NAU8325::mclk_n2_div[] = { + {0, 0x0}, + {1, 0x1}, + {2, 0x2}, + {3, 0x3}, + {4, 0x4}}; + +// N3 multiplier options +const SrcAttr PCBCUPID_NAU8325::mclk_n3_mult[] = { + {0, 0x1}, + {1, 0x2}, + {2, 0x3}, + {3, 0x4}}; + +// DAC oversampling options +const OsrAttr PCBCUPID_NAU8325::osr_dac_sel[] = { + {64, 2}, // OSR 64, SRC 1/4 + {256, 0}, // OSR 256, SRC 1 + {128, 1}, // OSR 128, SRC 1/2 + {0, 0}, + {32, 3} // OSR 32, SRC 1/8 +}; + +// accessing the table as expected based oon sample rate and mclk +const SRateAttr PCBCUPID_NAU8325::target_srate_table[] = { + {48000, 2, true, {12288000, 19200000, 24000000}}, + {16000, 1, false, {4096000, 6400000, 8000000}}, + {8000, 0, false, {2048000, 3200000, 4000000}}, + {44100, 2, true, {11289600, 17640000, 22050000}}, + {64000, 3, false, {16384000, 25600000, 32000000}}, + {96000, 3, true, {24576000, 38400000, 48000000}}, + {12000, 0, true, {3072000, 4800000, 6000000}}, + {24000, 1, true, {6144000, 9600000, 12000000}}, + {32000, 2, false, {8192000, 12800000, 16000000}}}; + +uint16_t set_ratio = 0; // Clock ratio for enabling the I2S device bool alc_enable = true; bool clock_detection = true; @@ -11,17 +54,21 @@ bool clock_det_data = true; uint32_t dac_vref_microvolt = 2880000; uint32_t vref_impedance_ohms = 125000; -PCBCUPID_NAU8325::PCBCUPID_NAU8325(TwoWire &wire, uint8_t i2c_addr) - : i2c(wire), i2c_addr(i2c_addr) {} +PCBCUPID_NAU8325::PCBCUPID_NAU8325(TwoWire &wire) + : i2c(wire), i2c_addr(NAU8325_I2C_ADDR) {} + +bool PCBCUPID_NAU8325::begin(uint32_t fs, uint8_t bits_per_sample, uint16_t ratio) +{ + + log_i("[NAU8325] beginDynamic() starting..."); + log_i(" fs = %lu Hz", fs); + log_i(" ratio = %u Hz", ratio); + log_i(" bits = %u", bits_per_sample); -bool PCBCUPID_NAU8325::begin(uint32_t fs, uint8_t bits_per_sample, uint16_t ratio) { - Serial.println("[NAU8325] beginDynamic() starting..."); - Serial.printf(" fs = %lu Hz\n", fs); - Serial.printf(" ratio = %u Hz\n", ratio); - Serial.printf(" bits = %u\n", bits_per_sample); + if (bits_per_sample != 16 && bits_per_sample != 24 && bits_per_sample != 32) + { - if (bits_per_sample != 16 && bits_per_sample != 24 && bits_per_sample != 32) { - Serial.printf("[NAU8325] Unsupported bit width: %u\n", bits_per_sample); + log_i("[NAU8325] Unsupported bit width: %u\n", bits_per_sample); return false; } @@ -56,25 +103,24 @@ bool PCBCUPID_NAU8325::begin(uint32_t fs, uint8_t bits_per_sample, uint16_t rati /*default powerOn*/ powerOn(); - Serial.println("[NAU8325] beginDynamic() complete - sound should play"); + log_i("[NAU8325] beginDynamic() complete - sound should play"); return true; } /*If the user didn't provide the ratio it will call this function with default ratio*/ -bool PCBCUPID_NAU8325::begin(uint32_t fs, uint8_t bits) { - uint16_t ratio = 256; - return begin(fs, bits, ratio); +bool PCBCUPID_NAU8325::begin(uint32_t fs, uint8_t bits) +{ + return begin(fs, bits, 256); // Passing 256 as the default ratio for clock division } /*if the user didn't pass any of the parameter like fs,bits,ratio*/ -bool PCBCUPID_NAU8325::begin() { - uint32_t fs = 44100; - uint16_t ratio = 256; - uint8_t bits = 16; - return begin(fs, bits, ratio); +bool PCBCUPID_NAU8325::begin() +{ + return begin(44100, 16, 256); // FS - 44100, BitRate - 16, Ratio 256 } -void PCBCUPID_NAU8325::enableDAPMBlocks() { +void PCBCUPID_NAU8325::enableDAPMBlocks() +{ // Enable DAC L/R in ENA_CTRL (R04) writeRegisterBits(NAU8325_R04_ENA_CTRL, NAU8325_ENA_CTRL_DAC_EN_MASK, @@ -91,79 +137,40 @@ void PCBCUPID_NAU8325::enableDAPMBlocks() { NAU8325_ANALOG_CTRL6_EN_MASK); } -void PCBCUPID_NAU8325::resetChip() { +void PCBCUPID_NAU8325::resetChip() +{ writeRegister(0x0000, 0x0001); delay(2); writeRegister(0x0000, 0x0000); delay(2); } -uint16_t PCBCUPID_NAU8325::readDeviceId() { +uint16_t PCBCUPID_NAU8325::readDeviceId() +{ uint16_t id; - if (readRegister(NAU8325_R02_DEVICE_ID, id)) { + if (readRegister(NAU8325_R02_DEVICE_ID, id)) + { return id; - } else { - return 0xFFFF; // Invalid or failed read + } + else + { + return 0xFFFF; // Invalid or failed read } } -// N1 divider options -const SrcAttr PCBCUPID_NAU8325::mclk_n1_div[] = { - { 1, 0x0 }, - { 2, 0x1 }, - { 3, 0x2 } -}; - -// N2 divider options -const SrcAttr PCBCUPID_NAU8325::mclk_n2_div[] = { - { 0, 0x0 }, - { 1, 0x1 }, - { 2, 0x2 }, - { 3, 0x3 }, - { 4, 0x4 } -}; - -// N3 multiplier options -const SrcAttr PCBCUPID_NAU8325::mclk_n3_mult[] = { - { 0, 0x1 }, - { 1, 0x2 }, - { 2, 0x3 }, - { 3, 0x4 } -}; - -// DAC oversampling options -const OsrAttr PCBCUPID_NAU8325::osr_dac_sel[] = { - { 64, 2 }, // OSR 64, SRC 1/4 - { 256, 0 }, // OSR 256, SRC 1 - { 128, 1 }, // OSR 128, SRC 1/2 - { 0, 0 }, - { 32, 3 } // OSR 32, SRC 1/8 -}; - -// accessing the table as expected based oon sample rate and mclk -const SRateAttr PCBCUPID_NAU8325::target_srate_table[] = { - { 48000, 2, true, { 12288000, 19200000, 24000000 } }, - { 16000, 1, false, { 4096000, 6400000, 8000000 } }, - { 8000, 0, false, { 2048000, 3200000, 4000000 } }, - { 44100, 2, true, { 11289600, 17640000, 22050000 } }, - { 64000, 3, false, { 16384000, 25600000, 32000000 } }, - { 96000, 3, true, { 24576000, 38400000, 48000000 } }, - { 12000, 0, true, { 3072000, 4800000, 6000000 } }, - { 24000, 1, true, { 6144000, 9600000, 12000000 } }, - { 32000, 2, false, { 8192000, 12800000, 16000000 } } -}; - -bool PCBCUPID_NAU8325::applySampleRateClocks(const SRateAttr *srate, int n1_sel, int mclk_mult_sel, int n2_sel) { - if (!srate || n1_sel < 0 || n1_sel >= 3 || n2_sel < 0 || n2_sel >= 5) { - Serial.println("[NAU8325] Invalid N1/N2 divider index."); +bool PCBCUPID_NAU8325::applySampleRateClocks(const SRateAttr *srate, int n1_sel, int mclk_mult_sel, int n2_sel) +{ + if (!srate || n1_sel < 0 || n1_sel >= 3 || n2_sel < 0 || n2_sel >= 5) + { + log_i("[NAU8325] Invalid N1/N2 divider index."); return false; } // --- Debug --- - Serial.println("--- Debugging clock dividers ---"); - Serial.printf("n1_sel=%d (val=%d)\n", n1_sel, mclk_n1_div[n1_sel].val); - Serial.printf("n2_sel=%d (val=%d)\n", n2_sel, mclk_n2_div[n2_sel].val); - Serial.printf("mult_sel=%d\n", mclk_mult_sel); + log_i("--- Debugging clock dividers ---"); + log_i("n1_sel=%d (val=%d)", n1_sel, mclk_n1_div[n1_sel].val); + log_i("n2_sel=%d (val=%d)", n2_sel, mclk_n2_div[n2_sel].val); + log_i("mult_sel=%d", mclk_mult_sel); // Set sample rate range and max mode writeRegisterBits(NAU8325_R40_CLK_DET_CTRL, @@ -171,57 +178,63 @@ bool PCBCUPID_NAU8325::applySampleRateClocks(const SRateAttr *srate, int n1_sel, (srate->range << NAU8325_REG_SRATE_SFT) | (srate->max ? NAU8325_REG_DIV_MAX : 0)); // Batch update CLK_CTRL (0x03) uint16_t clk_ctrl_val = 0; - clk_ctrl_val |= mclk_n2_div[n2_sel].val; // MCLK_SRC - clk_ctrl_val |= mclk_n1_div[n1_sel].val << NAU8325_CLK_MUL_SRC_SFT; // N1 + clk_ctrl_val |= mclk_n2_div[n2_sel].val; // MCLK_SRC + clk_ctrl_val |= mclk_n1_div[n1_sel].val << NAU8325_CLK_MUL_SRC_SFT; // N1 if (mclk_mult_sel >= 0 && mclk_mult_sel <= 3) - clk_ctrl_val |= mclk_n3_mult[mclk_mult_sel].val << NAU8325_MCLK_SEL_SFT; // N3 + clk_ctrl_val |= mclk_n3_mult[mclk_mult_sel].val << NAU8325_MCLK_SEL_SFT; // N3 - clk_ctrl_val |= 0x1000; // DAC_CLK = MCLK/2 (required!) + clk_ctrl_val |= 0x1000; // DAC_CLK = MCLK/2 (required!) writeRegister(NAU8325_R03_CLK_CTRL, clk_ctrl_val); - writeRegister(NAU8325_R03_CLK_CTRL, clk_ctrl_val); // Now written only once + writeRegister(NAU8325_R03_CLK_CTRL, clk_ctrl_val); // Now written only once // Configure ANALOG_CONTROL_5 for 4x/8x clock enable - switch (mclk_mult_sel) { - case 2: - writeRegisterBits(NAU8325_R65_ANALOG_CONTROL_5, - NAU8325_MCLK4XEN_EN, - NAU8325_MCLK4XEN_EN); - break; - case 3: - writeRegisterBits(NAU8325_R65_ANALOG_CONTROL_5, - NAU8325_MCLK4XEN_EN | NAU8325_MCLK8XEN_EN, - NAU8325_MCLK4XEN_EN | NAU8325_MCLK8XEN_EN); - break; - default: - writeRegisterBits(NAU8325_R65_ANALOG_CONTROL_5, - NAU8325_MCLK4XEN_EN | NAU8325_MCLK8XEN_EN, - 0); - break; + switch (mclk_mult_sel) + { + case 2: + writeRegisterBits(NAU8325_R65_ANALOG_CONTROL_5, + NAU8325_MCLK4XEN_EN, + NAU8325_MCLK4XEN_EN); + break; + case 3: + writeRegisterBits(NAU8325_R65_ANALOG_CONTROL_5, + NAU8325_MCLK4XEN_EN | NAU8325_MCLK8XEN_EN, + NAU8325_MCLK4XEN_EN | NAU8325_MCLK8XEN_EN); + break; + default: + writeRegisterBits(NAU8325_R65_ANALOG_CONTROL_5, + NAU8325_MCLK4XEN_EN | NAU8325_MCLK8XEN_EN, + 0); + break; } return true; } -int PCBCUPID_NAU8325::getMclkRatioAndN2Index(const SRateAttr *srate, int mclk_hz, int &n2_sel_out) { - int ratio = NAU8325_MCLK_FS_RATIO_NUM; // Default: not found +int PCBCUPID_NAU8325::getMclkRatioAndN2Index(const SRateAttr *srate, int mclk_hz, int &n2_sel_out) +{ + int ratio = NAU8325_MCLK_FS_RATIO_NUM; // Default: not found - for (int i = 0; i < 5; i++) { // mclk_n2_div[] has 5 entries + for (int i = 0; i < 5; i++) + { // mclk_n2_div[] has 5 entries int div = mclk_n2_div[i].param; int mclk_src = mclk_hz >> div; - if (srate->mclk_src[NAU8325_MCLK_FS_RATIO_256] == mclk_src) { + if (srate->mclk_src[NAU8325_MCLK_FS_RATIO_256] == mclk_src) + { ratio = NAU8325_MCLK_FS_RATIO_256; n2_sel_out = i; break; } - if (srate->mclk_src[NAU8325_MCLK_FS_RATIO_400] == mclk_src) { + if (srate->mclk_src[NAU8325_MCLK_FS_RATIO_400] == mclk_src) + { ratio = NAU8325_MCLK_FS_RATIO_400; n2_sel_out = i; break; } - if (srate->mclk_src[NAU8325_MCLK_FS_RATIO_500] == mclk_src) { + if (srate->mclk_src[NAU8325_MCLK_FS_RATIO_500] == mclk_src) + { ratio = NAU8325_MCLK_FS_RATIO_500; n2_sel_out = i; break; @@ -234,8 +247,10 @@ int PCBCUPID_NAU8325::getMclkRatioAndN2Index(const SRateAttr *srate, int mclk_hz const SRateAttr *PCBCUPID_NAU8325::getSRateAttr(int fs) { - for (size_t i = 0; i < sizeof(target_srate_table) / sizeof(target_srate_table[0]); ++i) { - if (target_srate_table[i].fs == fs) { + for (size_t i = 0; i < sizeof(target_srate_table) / sizeof(target_srate_table[0]); ++i) + { + if (target_srate_table[i].fs == fs) + { return &target_srate_table[i]; } } @@ -244,25 +259,29 @@ const SRateAttr *PCBCUPID_NAU8325::getSRateAttr(int fs) bool PCBCUPID_NAU8325::chooseClockSource(int fs, int mclk, const SRateAttr *&srate, - int &n1_sel, int &mult_sel, int &n2_sel) { - if (fs <= 0 || mclk <= 0) { - Serial.println("[NAU8325] Invalid fs or mclk."); + int &n1_sel, int &mult_sel, int &n2_sel) +{ + if (fs <= 0 || mclk <= 0) + { + log_i("[NAU8325] Invalid fs or mclk."); return false; } srate = getSRateAttr(fs); - if (!srate) { - Serial.printf("[NAU8325] Unsupported fs: %d\n", fs); + if (!srate) + { + log_i("[NAU8325] Unsupported fs: %d\n", fs); return false; } // First try direct N2 mapping int ratio = getMclkRatioAndN2Index(srate, mclk, n2_sel); - if (ratio != NAU8325_MCLK_FS_RATIO_NUM) { + if (ratio != NAU8325_MCLK_FS_RATIO_NUM) + { n1_sel = 0; - mult_sel = -1; // Bypass - Serial.printf("[NAU8325] Direct match: fs=%d, mclk=%d → N2=%d (Ratio=%d)\n", - fs, mclk, n2_sel, ratio); + mult_sel = -1; // Bypass + log_i("[NAU8325] Direct match: fs=%d, mclk=%d → N2=%d (Ratio=%d)\n", + fs, mclk, n2_sel, ratio); return true; } @@ -270,14 +289,15 @@ bool PCBCUPID_NAU8325::chooseClockSource(int fs, int mclk, int mclk_max = 0; int best_n1 = -1, best_mult = -1, best_n2 = -1; - for (int i = 0; i < 3; ++i) // N1 options + for (int i = 0; i < 3; ++i) // N1 options { - for (int j = 0; j < 4; ++j) // N3 multiplier options + for (int j = 0; j < 4; ++j) // N3 multiplier options { int m = (mclk << mclk_n3_mult[j].param) / mclk_n1_div[i].param; int r = getMclkRatioAndN2Index(srate, m, n2_sel); - if (r != NAU8325_MCLK_FS_RATIO_NUM && (m > mclk_max || best_n1 < i)) { + if (r != NAU8325_MCLK_FS_RATIO_NUM && (m > mclk_max || best_n1 < i)) + { mclk_max = m; best_n1 = i; best_mult = j; @@ -286,61 +306,70 @@ bool PCBCUPID_NAU8325::chooseClockSource(int fs, int mclk, } } - if (mclk_max > 0) { + if (mclk_max > 0) + { n1_sel = best_n1; mult_sel = best_mult; n2_sel = best_n2; - Serial.printf("[NAU8325] Matched via N1/N3 combo: fs=%d, mclk=%d → N1=%d, N3(mult)=%d, N2=%d\n", - fs, mclk, n1_sel, mult_sel, n2_sel); - Serial.printf("[NAU8325] chooseClockSource(): fs=%d, mclk=%d\n", fs, mclk); + log_i("[NAU8325] Matched via N1/N3 combo: fs=%d, mclk=%d → N1=%d, N3(mult)=%d, N2=%d\n", + fs, mclk, n1_sel, mult_sel, n2_sel); + log_i("[NAU8325] chooseClockSource(): fs=%d, mclk=%d\n", fs, mclk); return true; } - Serial.printf("[NAU8325] Failed to match MCLK %d Hz with fs %d Hz\n", mclk, fs); + log_i("[NAU8325] Failed to match MCLK %d Hz with fs %d Hz\n", mclk, fs); return false; } -bool PCBCUPID_NAU8325::configureClocks(int fs, int mclk) { +bool PCBCUPID_NAU8325::configureClocks(int fs, int mclk) +{ const SRateAttr *srate = nullptr; int n1_sel = 0, mult_sel = -1, n2_sel = 0; - if (!chooseClockSource(fs, mclk, srate, n1_sel, mult_sel, n2_sel)) { - Serial.printf("[NAU8325] Failed to choose clock source for fs=%d, mclk=%d\n", fs, mclk); + if (!chooseClockSource(fs, mclk, srate, n1_sel, mult_sel, n2_sel)) + { + log_i("[NAU8325] Failed to choose clock source for fs=%d, mclk=%d\n", fs, mclk); return false; } - if (!applySampleRateClocks(srate, n1_sel, mult_sel, n2_sel)) { - Serial.println("[NAU8325] Failed to apply sample rate clocks."); + if (!applySampleRateClocks(srate, n1_sel, mult_sel, n2_sel)) + { + log_i("[NAU8325] Failed to apply sample rate clocks."); return false; } - Serial.printf("[NAU8325] configureClocks(): fs=%u, mclk=%u\n", fs, mclk); + log_i("[NAU8325] configureClocks(): fs=%u, mclk=%u\n", fs, mclk); return true; } -const OsrAttr *PCBCUPID_NAU8325::getCurrentOSR() { +const OsrAttr *PCBCUPID_NAU8325::getCurrentOSR() +{ uint16_t value; - if (!readRegister(NAU8325_R29_DAC_CTRL1, value)) { + if (!readRegister(NAU8325_R29_DAC_CTRL1, value)) + { return nullptr; } uint8_t osr_index = value & NAU8325_DAC_OVERSAMPLE_MASK; - if (osr_index >= sizeof(osr_dac_sel) / sizeof(osr_dac_sel[0])) { + if (osr_index >= sizeof(osr_dac_sel) / sizeof(osr_dac_sel[0])) + { return nullptr; } return &osr_dac_sel[osr_index]; } -bool PCBCUPID_NAU8325::configureAudio(uint32_t fs, uint32_t mclk, uint8_t bits_per_sample) { +bool PCBCUPID_NAU8325::configureAudio(uint32_t fs, uint32_t mclk, uint8_t bits_per_sample) +{ this->fs = fs; this->mclk = mclk; // Configure clock tree - if (!configureClocks(fs, mclk)) { + if (!configureClocks(fs, mclk)) + { return false; } @@ -348,8 +377,9 @@ bool PCBCUPID_NAU8325::configureAudio(uint32_t fs, uint32_t mclk, uint8_t bits_p writeRegisterBits(NAU8325_R29_DAC_CTRL1, NAU8325_DAC_OVERSAMPLE_MASK, NAU8325_DAC_OVERSAMPLE_128); const OsrAttr *osr = getCurrentOSR(); - if (!osr || osr->osr == 0 || (fs * osr->osr > CLK_DA_AD_MAX)) { - Serial.println("[NAU8325] Invalid OSR or fs × OSR exceeds max."); + if (!osr || osr->osr == 0 || (fs * osr->osr > CLK_DA_AD_MAX)) + { + log_i("[NAU8325] Invalid OSR or fs × OSR exceeds max."); return false; } @@ -360,59 +390,63 @@ bool PCBCUPID_NAU8325::configureAudio(uint32_t fs, uint32_t mclk, uint8_t bits_p // Configure I2S word length uint16_t val_len = 0; - switch (bits_per_sample) { - case 16: - val_len = NAU8325_I2S_DL_16; - break; - case 20: - val_len = NAU8325_I2S_DL_20; - break; - case 24: - val_len = NAU8325_I2S_DL_24; - break; - case 32: - val_len = NAU8325_I2S_DL_32; - break; - default: - Serial.printf("[NAU8325] Invalid bit width: %u\n", bits_per_sample); - return false; + switch (bits_per_sample) + { + case 16: + val_len = NAU8325_I2S_DL_16; + break; + case 20: + val_len = NAU8325_I2S_DL_20; + break; + case 24: + val_len = NAU8325_I2S_DL_24; + break; + case 32: + val_len = NAU8325_I2S_DL_32; + break; + default: + log_i("[NAU8325] Invalid bit width: %u\n", bits_per_sample); + return false; } writeRegisterBits(NAU8325_R0D_I2S_PCM_CTRL1, NAU8325_I2S_DL_MASK, val_len); - Serial.printf("[NAU8325] configureAudio(): fs=%u, mclk=%u, bits=%u\n", fs, mclk, bits_per_sample); + log_i("[NAU8325] configureAudio(): fs=%u, mclk=%u, bits=%u\n", fs, mclk, bits_per_sample); return true; } -bool PCBCUPID_NAU8325::setI2SFormat(NAU8325_I2SFormat format, NAU8325_ClockPolarity polarity) { +bool PCBCUPID_NAU8325::setI2SFormat(AudioI2SFormat format, ClockPolarity polarity) +{ uint16_t ctrl1_val = 0; // Set BCLK polarity - if (polarity == INVERTED_BCLK) { + if (polarity == INVERTED_BCLK) + { ctrl1_val |= NAU8325_I2S_BP_INV; } // Set data format - switch (format) { - case I2S_STD: - ctrl1_val |= NAU8325_I2S_DF_I2S; - break; - case LEFT_JUSTIFIED: - ctrl1_val |= NAU8325_I2S_DF_LEFT; - break; - case RIGHT_JUSTIFIED: - ctrl1_val |= NAU8325_I2S_DF_RIGTH; - break; - case DSP_A: - ctrl1_val |= NAU8325_I2S_DF_PCM_AB; - break; - case DSP_B: - ctrl1_val |= NAU8325_I2S_DF_PCM_AB | NAU8325_I2S_PCMB_EN; - break; - default: - return false; + switch (format) + { + case I2S_STD: + ctrl1_val |= NAU8325_I2S_DF_I2S; + break; + case LEFT_JUSTIFIED: + ctrl1_val |= NAU8325_I2S_DF_LEFT; + break; + case RIGHT_JUSTIFIED: + ctrl1_val |= NAU8325_I2S_DF_RIGTH; + break; + case DSP_A: + ctrl1_val |= NAU8325_I2S_DF_PCM_AB; + break; + case DSP_B: + ctrl1_val |= NAU8325_I2S_DF_PCM_AB | NAU8325_I2S_PCMB_EN; + break; + default: + return false; } // Apply to I2S_PCM_CTRL1 @@ -421,39 +455,45 @@ bool PCBCUPID_NAU8325::setI2SFormat(NAU8325_I2SFormat format, NAU8325_ClockPolar ctrl1_val); } -bool PCBCUPID_NAU8325::setSysClock(uint32_t freq) { - if (freq < MASTER_CLK_MIN || freq > MASTER_CLK_MAX) { - Serial.printf("[NAU8325] Invalid MCLK: %u Hz (allowed: %u - %u)\n", freq, MASTER_CLK_MIN, MASTER_CLK_MAX); +bool PCBCUPID_NAU8325::setSysClock(uint32_t freq) +{ + if (freq < MASTER_CLK_MIN || freq > MASTER_CLK_MAX) + { + log_i("[NAU8325] Invalid MCLK: %u Hz (allowed: %u - %u)\n", freq, MASTER_CLK_MIN, MASTER_CLK_MAX); return false; } this->mclk = freq; - Serial.printf("[NAU8325] MCLK set to %u Hz\n", mclk); + log_i("[NAU8325] MCLK set to %u Hz\n", mclk); return true; } -void PCBCUPID_NAU8325::setPowerUpDefault(bool enable) { +void PCBCUPID_NAU8325::setPowerUpDefault(bool enable) +{ writeRegisterBits(NAU8325_R40_CLK_DET_CTRL, NAU8325_PWRUP_DFT, enable ? NAU8325_PWRUP_DFT : 0); } -void PCBCUPID_NAU8325::setOversampling(OversamplingMode mode) { +void PCBCUPID_NAU8325::setOversampling(OversamplingMode mode) +{ writeRegisterBits(NAU8325_R29_DAC_CTRL1, NAU8325_DAC_OVERSAMPLE_MASK, mode); } -void PCBCUPID_NAU8325::setVolume(uint8_t left, uint8_t right) { +void PCBCUPID_NAU8325::setVolume(uint8_t left, uint8_t right) +{ uint16_t value = ((left & 0xFF) << 8) | (right & 0xFF); writeRegister(NAU8325_R13_DAC_VOLUME, value); } -void PCBCUPID_NAU8325::softMute(bool enable) // under testing +void PCBCUPID_NAU8325::softMute(bool enable) { writeRegisterBits(NAU8325_R12_MUTE_CTRL, NAU8325_SOFT_MUTE, enable ? NAU8325_SOFT_MUTE : 0); - delay(30); // Same as msleep(30) + delay(30); } -void PCBCUPID_NAU8325::initRegisters() { +void PCBCUPID_NAU8325::initRegisters() +{ // Set ALC parameters default timing and max gain writeRegisterBits(NAU8325_R2C_ALC_CTRL1, NAU8325_ALC_MAXGAIN_MASK, 0x7 << NAU8325_ALC_MAXGAIN_SFT); writeRegisterBits(NAU8325_R2D_ALC_CTRL2, @@ -461,7 +501,8 @@ void PCBCUPID_NAU8325::initRegisters() { (0x5 << NAU8325_ALC_DCY_SFT) | (0x3 << NAU8325_ALC_ATK_SFT) | (0x5 << NAU8325_ALC_HLD_SFT)); /* Enable ALC to avoid signal distortion when battery low. */ - if (alc_enable) { + if (alc_enable) + { writeRegisterBits(NAU8325_R2E_ALC_CTRL3, NAU8325_ALC_EN, NAU8325_ALC_EN); } @@ -489,10 +530,13 @@ void PCBCUPID_NAU8325::initRegisters() { else if (dac_vref_microvolt == 3060000) vref_val = 3 << NAU8325_DACVREFSEL_SFT; - if (vref_val != 0xFFFF) { + if (vref_val != 0xFFFF) + { writeRegisterBits(NAU8325_R73_RDAC, NAU8325_DACVREFSEL_MASK, vref_val); - } else { - Serial.printf("[NAU8325] Invalid DAC Vref: %lu uV\n", dac_vref_microvolt); + } + else + { + log_i("[NAU8325] Invalid DAC Vref: %lu uV\n", dac_vref_microvolt); } /* DAC Reference Voltage Decoupling Capacitors. */ @@ -511,10 +555,13 @@ void PCBCUPID_NAU8325::initRegisters() { else if (vref_impedance_ohms == 2500) vref_imp_val = 3 << NAU8325_BIAS_VMID_SEL_SFT; - if (vref_imp_val != 0xFFFF) { + if (vref_imp_val != 0xFFFF) + { writeRegisterBits(NAU8325_R60_BIAS_ADJ, NAU8325_BIAS_VMID_SEL_MASK, vref_imp_val); - } else { - Serial.printf("[NAU8325] Invalid VMID impedance: %lu ohms\n", vref_impedance_ohms); + } + else + { + log_i("[NAU8325] Invalid VMID impedance: %lu ohms\n", vref_impedance_ohms); } // Enable VMID, BIAS, DACs, etc., Voltage / current Amps @@ -523,7 +570,8 @@ void PCBCUPID_NAU8325::initRegisters() { (0x1 << NAU8325_DACEN_SFT) | (0x1 << NAU8325_DACCLKEN_SFT) | (0x1 << NAU8325_DACEN_R_SFT) | (0x1 << NAU8325_DACCLKEN_R_SFT) | (0x1 << NAU8325_CLASSDEN_SFT) | (0x1 << NAU8325_VMDFSTENB_SFT) | (0x1 << NAU8325_BIASEN_SFT) | (1 << NAU8325_VMIDEN_SFT)); /* Enable ALC to avoid signal distortion when battery low. */ - if (alc_enable) { + if (alc_enable) + { writeRegisterBits(NAU8325_R2E_ALC_CTRL3, NAU8325_ALC_EN, NAU8325_ALC_EN); } @@ -546,8 +594,41 @@ void PCBCUPID_NAU8325::initRegisters() { NAU8325_DAC_OVERSAMPLE_128); } -bool PCBCUPID_NAU8325::writeRegister(uint16_t reg, uint16_t val) { - Serial.printf("[WRITE] reg=0x%04X val=0x%04X\n", reg, val); +void PCBCUPID_NAU8325::powerOn() +{ + // Clear soft mute to allow audio output + softMute(false); + + // Enable DACs, clocks, bias, class-D + writeRegisterBits(NAU8325_R61_ANALOG_CONTROL_1, + NAU8325_DACEN_MASK | NAU8325_DACCLKEN_MASK | NAU8325_DACEN_R_MASK | NAU8325_DACCLKEN_R_MASK | NAU8325_CLASSDEN_MASK | NAU8325_VMDFSTENB_MASK | NAU8325_BIASEN_MASK | NAU8325_VMIDEN_MASK, + (3 << NAU8325_DACEN_SFT) | (3 << NAU8325_DACCLKEN_SFT) | (3 << NAU8325_DACEN_R_SFT) | (3 << NAU8325_DACCLKEN_R_SFT) | (3 << NAU8325_CLASSDEN_SFT) | (3 << NAU8325_VMDFSTENB_SFT) | (3 << NAU8325_BIASEN_SFT) | 0x03); // VMID_EN + + // If clock detection is disabled, enable PWRUP_DFT + setPowerUpDefault(true); + + delay(30); // Let analog circuitry stabilize +} + +void PCBCUPID_NAU8325::powerOff() +{ + // Soft mute first to prevent pop + softMute(true); + + // Disable analog and DAC power + writeRegisterBits(NAU8325_R61_ANALOG_CONTROL_1, + NAU8325_DACEN_MASK | NAU8325_DACCLKEN_MASK | NAU8325_DACEN_R_MASK | NAU8325_DACCLKEN_R_MASK | NAU8325_CLASSDEN_MASK | NAU8325_VMDFSTENB_MASK | NAU8325_BIASEN_MASK | NAU8325_VMIDEN_MASK, + 0x0000); + + // If clock detection is disabled, clear PWRUP_DFT + setPowerUpDefault(false); + + delay(30); // Let output fade out +} + +bool PCBCUPID_NAU8325::writeRegister(uint16_t reg, uint16_t val) +{ + log_i("[WRITE] reg=0x%04X val=0x%04X\n", reg, val); i2c.beginTransmission(i2c_addr); i2c.write((reg >> 8) & 0xFF); i2c.write(reg & 0xFF); @@ -556,7 +637,7 @@ bool PCBCUPID_NAU8325::writeRegister(uint16_t reg, uint16_t val) { return i2c.endTransmission() == 0; } -bool PCBCUPID_NAU8325::writeRegisterBits(uint16_t reg, uint16_t mask, uint16_t value) // mask ---> is to set the the particular bit renaining bit untouched. +bool PCBCUPID_NAU8325::writeRegisterBits(uint16_t reg, uint16_t mask, uint16_t value) // mask ---> is to set the the particular bit renaining bit untouched. { uint16_t current; if (!readRegister(reg, current)) @@ -566,15 +647,18 @@ bool PCBCUPID_NAU8325::writeRegisterBits(uint16_t reg, uint16_t mask, uint16_t v return writeRegister(reg, new_value); } -bool PCBCUPID_NAU8325::readRegister(uint16_t reg, uint16_t &value) { +bool PCBCUPID_NAU8325::readRegister(uint16_t reg, uint16_t &value) +{ i2c.beginTransmission(i2c_addr); i2c.write((reg >> 8) & 0xFF); i2c.write(reg & 0xFF); - if (i2c.endTransmission(false) != 0) { + if (i2c.endTransmission(false) != 0) + { return false; } - if (i2c.requestFrom(i2c_addr, (uint8_t)2) != 2) { + if (i2c.requestFrom(i2c_addr, (uint8_t)2) != 2) + { return false; } @@ -582,34 +666,4 @@ bool PCBCUPID_NAU8325::readRegister(uint16_t reg, uint16_t &value) { uint8_t low = i2c.read(); value = ((uint16_t)high << 8) | low; return true; -} - -void PCBCUPID_NAU8325::powerOn() { - // Clear soft mute to allow audio output - softMute(false); - - // Enable DACs, clocks, bias, class-D - writeRegisterBits(NAU8325_R61_ANALOG_CONTROL_1, - NAU8325_DACEN_MASK | NAU8325_DACCLKEN_MASK | NAU8325_DACEN_R_MASK | NAU8325_DACCLKEN_R_MASK | NAU8325_CLASSDEN_MASK | NAU8325_VMDFSTENB_MASK | NAU8325_BIASEN_MASK | NAU8325_VMIDEN_MASK, - (3 << NAU8325_DACEN_SFT) | (3 << NAU8325_DACCLKEN_SFT) | (3 << NAU8325_DACEN_R_SFT) | (3 << NAU8325_DACCLKEN_R_SFT) | (3 << NAU8325_CLASSDEN_SFT) | (3 << NAU8325_VMDFSTENB_SFT) | (3 << NAU8325_BIASEN_SFT) | 0x03); // VMID_EN - - // If clock detection is disabled, enable PWRUP_DFT - setPowerUpDefault(true); - - delay(30); // Let analog circuitry stabilize -} - -void PCBCUPID_NAU8325::powerOff() { - // Soft mute first to prevent pop - softMute(true); - - // Disable analog and DAC power - writeRegisterBits(NAU8325_R61_ANALOG_CONTROL_1, - NAU8325_DACEN_MASK | NAU8325_DACCLKEN_MASK | NAU8325_DACEN_R_MASK | NAU8325_DACCLKEN_R_MASK | NAU8325_CLASSDEN_MASK | NAU8325_VMDFSTENB_MASK | NAU8325_BIASEN_MASK | NAU8325_VMIDEN_MASK, - 0x0000); - - // If clock detection is disabled, clear PWRUP_DFT - setPowerUpDefault(false); - - delay(30); // Let output fade out -} +} \ No newline at end of file diff --git a/src/Driver/nau8325/PCBCUPID_NAU8325.h b/src/Driver/nau8325/PCBCUPID_NAU8325.h index 54f87f9..1182ecf 100644 --- a/src/Driver/nau8325/PCBCUPID_NAU8325.h +++ b/src/Driver/nau8325/PCBCUPID_NAU8325.h @@ -4,6 +4,8 @@ #include #include +#define NAU8325_I2C_ADDR 0x21 + #define NAU8325_R00_HARDWARE_RST 0x00 #define NAU8325_R01_SOFTWARE_RST 0x01 #define NAU8325_R02_DEVICE_ID 0x02 @@ -391,7 +393,7 @@ struct RegDefault uint16_t val; }; -enum NAU8325_I2SFormat +enum AudioI2SFormat { I2S_STD, LEFT_JUSTIFIED, @@ -399,7 +401,7 @@ enum NAU8325_I2SFormat DSP_A, DSP_B }; -enum NAU8325_ClockPolarity +enum ClockPolarity { NORMAL_BCLK, INVERTED_BCLK @@ -412,7 +414,7 @@ enum OversamplingMode OSR_32 = 4 }; -enum +enum ClockRatio { NAU8325_MCLK_FS_RATIO_256 = 0, NAU8325_MCLK_FS_RATIO_400 = 1, @@ -423,7 +425,7 @@ enum class PCBCUPID_NAU8325 { public: - PCBCUPID_NAU8325(TwoWire &wire, uint8_t i2c_addr); + PCBCUPID_NAU8325(TwoWire &wire); static const RegDefault reg_defaults[]; @@ -434,6 +436,34 @@ class PCBCUPID_NAU8325 static const OsrAttr osr_dac_sel[]; static const SRateAttr target_srate_table[]; + /* + @brief + fs- frame rate in Hz (e.g., 8000, 16000, 44100, 48000) + bits_per_sample- bits per sample (e.g., 16, 24) + ratio- MCLK to FS ratio (e.g., 256, 384, 512, 640) + @return + true if initialization is successful, false otherwise + @note + The begin() function initializes the NAU8325 codec with the specified + frame rate, bits per sample, and MCLK to FS ratio. It configures the codec + for audio playback and sets up the necessary registers. The function returns + true if the initialization is successful, or false if there is an error. + The default values for bits_per_sample and ratio are 16 and 256, respectively. + If only fs is provided, it uses the default values for bits_per_sample and ratio. + If no parameters are provided, it uses default values for fs, bits_per_sample, + and ratio. + Example: + PCBCUPID_NAU8325 codec; + codec.begin(44100, 16, 256); // Initialize with 44.1kHz, 16 bits, and MCLK/FS ratio of 256 + codec.begin(16000, 24); // Initialize with 16kHz, 24 bits, and default MCLK/FS ratio of 256 + codec.begin(); // Initialize with default values (e.g., 8000Hz, 16 bits, 256 ratio) + @note + The begin() function must be called before using the codec for audio playback. + It sets up the codec's internal registers and prepares it for audio processing. + If the initialization fails, the codec will not function correctly, and audio playback + may not work as expected. + */ + bool begin(uint32_t fs, uint8_t bits_per_sample, uint16_t ratio); bool begin(uint32_t fs, uint8_t bits_per_sample); bool begin(); @@ -449,12 +479,9 @@ class PCBCUPID_NAU8325 void setOversampling(OversamplingMode mode); void setVolume(uint8_t left, uint8_t right); // public - - private: - // Add private members (Wire, I2C address, etc.) here later TwoWire &i2c; - uint8_t i2c_addr; + uint8_t i2c_addr = NAU8325_I2C_ADDR; uint32_t fs; uint32_t mclk; @@ -462,7 +489,7 @@ class PCBCUPID_NAU8325 void initRegisters(); // Initialize codec - bool setSysClock(uint32_t freq); + bool setSysClock(uint32_t freq); // setup MCLK for I2S bool configureAudio(uint32_t fs, uint32_t mclk, uint8_t bits_per_sample); bool chooseClockSource(int fs, int mclk, const SRateAttr *&srate, int &n1_sel, int &mult_sel, int &n2_sel); bool configureClocks(int fs, int mclk); @@ -470,10 +497,11 @@ class PCBCUPID_NAU8325 int getMclkRatioAndN2Index(const SRateAttr *srate, int mclk_hz, int &n2_sel_out); const SRateAttr *getSRateAttr(int fs); const OsrAttr *getCurrentOSR(); - bool setI2SFormat(NAU8325_I2SFormat format, NAU8325_ClockPolarity polarity); + bool setI2SFormat(AudioI2SFormat format, ClockPolarity polarity); - void enableDAPMBlocks(); + void enableDAPMBlocks(); // analog path power enable + // I2C register controls bool readRegister(uint16_t reg, uint16_t &value); bool writeRegister(uint16_t reg, uint16_t value); bool writeRegisterBits(uint16_t reg, uint16_t mask, uint16_t value); diff --git a/src/DriverPins.h b/src/DriverPins.h index 6b37637..fb8632b 100644 --- a/src/DriverPins.h +++ b/src/DriverPins.h @@ -809,7 +809,7 @@ static PinsAudioKitEs8388v2Class PinsAudioKitEs8388v2; /// @ingroup audio_driver static PinsAudioKitAC101Class PinsAudioKitAC101; /// @ingroup audio_driver -static DriverPins PinsNAU8325; //changeTo PinsGmodNAU8325 +static DriverPins PinsGmodNAU8325; } // namespace audio_driver \ No newline at end of file From 7ebdd7dabe0db49dc27eb263919b6283d26acb6b Mon Sep 17 00:00:00 2001 From: codingkarthik94 Date: Fri, 1 Aug 2025 11:45:22 +0530 Subject: [PATCH 7/8] version 0.0.5 Changelogs -Modified: minor changes on custom audiodriver class as per the nau8325 library. -Tested: tested with simple sine wave generator with our custom driver class, it is generating the sound. --- .../sinewave-generator/sinewave-generator.ino | 67 --- src/AudioBoard.h | 3 +- src/Driver.h | 152 +++--- src/Driver/nau8325/PCBCUPID_NAU8325.cpp | 512 ++++++++++-------- src/Driver/nau8325/PCBCUPID_NAU8325.h | 50 +- src/DriverPins.h | 3 +- 6 files changed, 399 insertions(+), 388 deletions(-) delete mode 100644 examples/custom/custom-nau8325/sinewave-generator/sinewave-generator.ino diff --git a/examples/custom/custom-nau8325/sinewave-generator/sinewave-generator.ino b/examples/custom/custom-nau8325/sinewave-generator/sinewave-generator.ino deleted file mode 100644 index cc62edd..0000000 --- a/examples/custom/custom-nau8325/sinewave-generator/sinewave-generator.ino +++ /dev/null @@ -1,67 +0,0 @@ -#include -#include -#include -#include "Driver.h" - -using namespace audio_tools; -using namespace audio_driver; - -// === Pins === -#define SDAPIN 4 -#define SCLPIN 5 -#define I2CSPEED 100000 -#define NAU8325ADDR 0x21 -#define MCLKPIN 22 -#define BCLKPIN 25 -#define WSPIN 24 -#define DOPIN 23 -#define DIPIN -1 - -// === Audio Setup === -AudioInfo audio_info(44100, 2, 16); -SineWaveGenerator sine_wave(30000); -GeneratedSoundStream sound_stream(sine_wave); -StreamCopy copier; - -TwoWire myWire = TwoWire(0); -DriverPins pins; -AudioBoard board(AudioDriverNAU8325, pins); -I2SCodecStream i2s_out(board); - -void setup() { - Serial.begin(115200); - delay(1000); // Allow time for serial monitor to open - AudioLogger::instance().begin(Serial, AudioLogger::Info); - AudioDriverLogger.begin(Serial, AudioDriverLogLevel::Debug); - - // Setup I2C and I2S pins - pins.addI2C(PinFunction::CODEC, SCLPIN, SDAPIN, NAU8325ADDR, I2CSPEED, myWire); - pins.addI2S(PinFunction::CODEC, MCLKPIN, BCLKPIN, WSPIN, DOPIN, DIPIN); - pins.begin(); - - // Codec config - CodecConfig config; - config.i2s.rate = RATE_44K; - config.i2s.bits = BIT_LENGTH_16BITS; - config.i2s.channels = CHANNELS2; - - // Start codec board - if (!board.begin(config)) { - Serial.println("[NAU8325] board.begin() failed!"); - while (1); - } - - // Configure I2S output - auto cfg = i2s_out.defaultConfig(); - cfg.copyFrom(audio_info); - i2s_out.begin(cfg); - copier.begin(i2s_out, sound_stream); - - // Start sine wave - sine_wave.begin(audio_info, N_B4); - Serial.println("Sinewave output started."); -} - -void loop() { - copier.copy(); -} diff --git a/src/AudioBoard.h b/src/AudioBoard.h index d36d2dc..d8561a8 100644 --- a/src/AudioBoard.h +++ b/src/AudioBoard.h @@ -131,8 +131,7 @@ static AudioBoard NoBoard{NoDriver, NoPins}; static AudioBoard GenericWM8960{AudioDriverWM8960, NoPins}; /// @ingroup audio_driver static AudioBoard GenericCS43l22{AudioDriverCS43l22, NoPins}; -/// @ingroup audio_driver -static AudioBoard NAU8325Board{AudioDriverNAU8325, NoPins}; + #if defined(ARDUINO_GENERIC_F411VETX) /// @ingroup audio_driver diff --git a/src/Driver.h b/src/Driver.h index 98baff9..0183ff0 100644 --- a/src/Driver.h +++ b/src/Driver.h @@ -1,4 +1,3 @@ - #pragma once #include "Driver/ac101/ac101.h" #include "Driver/ad1938/ad1938.h" @@ -17,7 +16,7 @@ #include "Driver/wm8978/WM8978.h" #include "Driver/wm8994/wm8994.h" #include "Driver/nau8325/PCBCUPID_NAU8325.h" -#include "ConfigAudioDriver.h" +#include "ConfigAudioDriver.h" #include "DriverCommon.h" #include "DriverPins.h" @@ -517,7 +516,7 @@ namespace audio_driver { codec_cfg = codecCfg; uint32_t freq = getFrequency(codec_cfg.i2s.rate); - uint16_t outputDevice = getOutput(codec_cfg.output_device); + uint16_t outputDevice = getOutput(codecCfg.output_device); return cs43l22_Init(deviceAddr, outputDevice, this->volume, freq, getI2C()) == 0; } @@ -1671,93 +1670,92 @@ namespace audio_driver AudioDriverES7243Class adc; }; - /** - * @brief Driver API for NAU8325 CODEC WITH AMPLIFIER - * @author - * @copyright MIT Licence - */ - // *************** NAU8325 DRIVER WRAPPER ************ - -/** - * @brief Driver API for NAU8325 CODEC WITH AMPLIFIER - * @author You - * @copyright MIT License - */ -// -- NAU8325 Driver Class -class AudioDriverNAU8325Class : public AudioDriver { -public: - PCBCUPID_NAU8325 *nau = nullptr; // pointer to construct later - - ~AudioDriverNAU8325Class() { - if (nau) delete nau; - } - - bool begin(CodecConfig cfg, DriverPins &pins) override { - AD_LOGI("AudioDriverNAU8325Class::begin"); - - this->p_pins = &pins; - - // Get I2C config - auto i2c_opt = pins.getI2CPins(PinFunction::CODEC); - if (!i2c_opt) { - AD_LOGE("No I2C pins defined for codec"); - return false; + /* -- NAU8325 Driver Class--- */ + class AudioDriverNAU8325Class : public AudioDriver + { + public: + PCBCUPID_NAU8325 *nau8325 = nullptr; // pointer to construct later + + ~AudioDriverNAU8325Class() + { + if (nau8325) + delete nau; } - PinsI2C val = i2c_opt.value(); - // Create instance with required params - nau = new PCBCUPID_NAU8325(*((TwoWire *)val.p_wire), val.address); + bool begin(CodecConfig cfg, DriverPins &pins) override + { + AD_LOGI("AudioDriverNAU8325Class::begin"); - // Set MCLK if available - int mclk = pins.getPinID(PinFunction::MCLK_SOURCE); - if (mclk > 0) { - pinMode(mclk, OUTPUT); - digitalWrite(mclk, HIGH); - delay(10); // optional small delay to stabilize - } + this->p_pins = &pins; - // Begin your driver using known public methods - int fs = cfg.getRateNumeric(); - int bits = cfg.getBitsNumeric(); + // Get I2C config + auto i2c_opt = pins.getI2CPins(PinFunction::CODEC); + if (!i2c_opt) + { + AD_LOGE("No I2C pins defined for codec"); + return false; + } + PinsI2C val = i2c_opt.value(); - AD_LOGI("Calling nau->begin(fs=%d, bits=%d)", fs, bits); - if (!nau->begin(fs, bits)) { - AD_LOGE("NAU8325 beginDynamic failed"); - return false; - } + // Create instance with required params (only TwoWire&) + nau8325 = new PCBCUPID_NAU8325(*((TwoWire *)val.p_wire)); - return true; - } + // Set MCLK if available + int mclk = pins.getPinID(PinFunction::MCLK_SOURCE); + if (mclk > 0) + { + pinMode(mclk, OUTPUT); + digitalWrite(mclk, HIGH); + delay(10); // optional small delay to stabilize + } - bool end() override { - if (nau) { - nau->powerOff(); // if it's void, just call directly - } - return true; - } + // Begin your driver using known public methods + int fs = cfg.getRateNumeric(); + int bits = cfg.getBitsNumeric(); - bool setMute(bool enable) override { - if (!nau) return false; - nau->softMute(enable); // void return - return true; - } + AD_LOGI("Calling nau->begin(fs=%d, bits=%d)", fs, bits); + if (!nau->begin(fs, bits)) + { + AD_LOGE("NAU8325 begin failed"); + return false; + } - bool setVolume(int volume) override { - if (!nau) return false; - // Mapping volume (0–100) to 0–255 (NAU range) - uint8_t val = map(volume, 0, 100, 0, 255); - nau->setVolume(val, val); // assuming stereo - return true; - } + return true; + } - int getVolume() override { - // You can return dummy volume since NAU8325 doesn't have a getVolume - return 100; - } -}; + bool end() override + { + if (nau8325) + { + nau->powerOff(); + } + return true; + } + bool setMute(bool enable) override + { + if (!nau8325) + return false; + nau->softMute(enable); + return true; + } + bool setVolume(int volume) override + { + if (!nau8325) + return false; + // Mapping volume (0–100) to 0–255 (NAU range) + uint8_t val = mapVolume(volume, 0, 100, 0, 255); + nau->setVolume(val, val); // assuming stereo + return true; + } + int getVolume() override + { + // NAU8325 doesn't have a getVolume, so return last set or dummy + return 100; + } + }; #ifdef ARDUINO // currently only supported in Arduino because we do not have a platform diff --git a/src/Driver/nau8325/PCBCUPID_NAU8325.cpp b/src/Driver/nau8325/PCBCUPID_NAU8325.cpp index c643ea3..d7c01ad 100644 --- a/src/Driver/nau8325/PCBCUPID_NAU8325.cpp +++ b/src/Driver/nau8325/PCBCUPID_NAU8325.cpp @@ -1,9 +1,52 @@ #include "PCBCUPID_NAU8325.h" +#include "esp_log.h" #define MASTER_CLK_MIN 2048000 #define MASTER_CLK_MAX 49152000 -uint16_t set_ratio = 0; //declared as global +// N1 divider options +const SrcAttr PCBCUPID_NAU8325::mclk_n1_div[] = { + {1, 0x0}, + {2, 0x1}, + {3, 0x2}}; + +// N2 divider options +const SrcAttr PCBCUPID_NAU8325::mclk_n2_div[] = { + {0, 0x0}, + {1, 0x1}, + {2, 0x2}, + {3, 0x3}, + {4, 0x4}}; + +// N3 multiplier options +const SrcAttr PCBCUPID_NAU8325::mclk_n3_mult[] = { + {0, 0x1}, + {1, 0x2}, + {2, 0x3}, + {3, 0x4}}; + +// DAC oversampling options +const OsrAttr PCBCUPID_NAU8325::osr_dac_sel[] = { + {64, 2}, // OSR 64, SRC 1/4 + {256, 0}, // OSR 256, SRC 1 + {128, 1}, // OSR 128, SRC 1/2 + {0, 0}, + {32, 3} // OSR 32, SRC 1/8 +}; + +// accessing the table as expected based oon sample rate and mclk +const SRateAttr PCBCUPID_NAU8325::target_srate_table[] = { + {48000, 2, true, {12288000, 19200000, 24000000}}, + {16000, 1, false, {4096000, 6400000, 8000000}}, + {8000, 0, false, {2048000, 3200000, 4000000}}, + {44100, 2, true, {11289600, 17640000, 22050000}}, + {64000, 3, false, {16384000, 25600000, 32000000}}, + {96000, 3, true, {24576000, 38400000, 48000000}}, + {12000, 0, true, {3072000, 4800000, 6000000}}, + {24000, 1, true, {6144000, 9600000, 12000000}}, + {32000, 2, false, {8192000, 12800000, 16000000}}}; + +uint16_t set_ratio = 0; // Clock ratio for enabling the I2S device bool alc_enable = true; bool clock_detection = true; @@ -11,17 +54,21 @@ bool clock_det_data = true; uint32_t dac_vref_microvolt = 2880000; uint32_t vref_impedance_ohms = 125000; -PCBCUPID_NAU8325::PCBCUPID_NAU8325(TwoWire &wire, uint8_t i2c_addr) - : i2c(wire), i2c_addr(i2c_addr) {} +PCBCUPID_NAU8325::PCBCUPID_NAU8325(TwoWire &wire) + : i2c(wire), i2c_addr(NAU8325_I2C_ADDR) {} + +bool PCBCUPID_NAU8325::begin(uint32_t fs, uint8_t bits_per_sample, uint16_t ratio) +{ + + log_i("[NAU8325] beginDynamic() starting..."); + log_i(" fs = %lu Hz", fs); + log_i(" ratio = %u Hz", ratio); + log_i(" bits = %u", bits_per_sample); -bool PCBCUPID_NAU8325::begin(uint32_t fs, uint8_t bits_per_sample, uint16_t ratio) { - Serial.println("[NAU8325] beginDynamic() starting..."); - Serial.printf(" fs = %lu Hz\n", fs); - Serial.printf(" ratio = %u Hz\n", ratio); - Serial.printf(" bits = %u\n", bits_per_sample); + if (bits_per_sample != 16 && bits_per_sample != 24 && bits_per_sample != 32) + { - if (bits_per_sample != 16 && bits_per_sample != 24 && bits_per_sample != 32) { - Serial.printf("[NAU8325] Unsupported bit width: %u\n", bits_per_sample); + log_i("[NAU8325] Unsupported bit width: %u\n", bits_per_sample); return false; } @@ -56,25 +103,24 @@ bool PCBCUPID_NAU8325::begin(uint32_t fs, uint8_t bits_per_sample, uint16_t rati /*default powerOn*/ powerOn(); - Serial.println("[NAU8325] beginDynamic() complete - sound should play"); + log_i("[NAU8325] beginDynamic() complete - sound should play"); return true; } /*If the user didn't provide the ratio it will call this function with default ratio*/ -bool PCBCUPID_NAU8325::begin(uint32_t fs, uint8_t bits) { - uint16_t ratio = 256; - return begin(fs, bits, ratio); +bool PCBCUPID_NAU8325::begin(uint32_t fs, uint8_t bits) +{ + return begin(fs, bits, 256); // Passing 256 as the default ratio for clock division } /*if the user didn't pass any of the parameter like fs,bits,ratio*/ -bool PCBCUPID_NAU8325::begin() { - uint32_t fs = 44100; - uint16_t ratio = 256; - uint8_t bits = 16; - return begin(fs, bits, ratio); +bool PCBCUPID_NAU8325::begin() +{ + return begin(44100, 16, 256); // FS - 44100, BitRate - 16, Ratio 256 } -void PCBCUPID_NAU8325::enableDAPMBlocks() { +void PCBCUPID_NAU8325::enableDAPMBlocks() +{ // Enable DAC L/R in ENA_CTRL (R04) writeRegisterBits(NAU8325_R04_ENA_CTRL, NAU8325_ENA_CTRL_DAC_EN_MASK, @@ -91,79 +137,40 @@ void PCBCUPID_NAU8325::enableDAPMBlocks() { NAU8325_ANALOG_CTRL6_EN_MASK); } -void PCBCUPID_NAU8325::resetChip() { +void PCBCUPID_NAU8325::resetChip() +{ writeRegister(0x0000, 0x0001); delay(2); writeRegister(0x0000, 0x0000); delay(2); } -uint16_t PCBCUPID_NAU8325::readDeviceId() { +uint16_t PCBCUPID_NAU8325::readDeviceId() +{ uint16_t id; - if (readRegister(NAU8325_R02_DEVICE_ID, id)) { + if (readRegister(NAU8325_R02_DEVICE_ID, id)) + { return id; - } else { - return 0xFFFF; // Invalid or failed read + } + else + { + return 0xFFFF; // Invalid or failed read } } -// N1 divider options -const SrcAttr PCBCUPID_NAU8325::mclk_n1_div[] = { - { 1, 0x0 }, - { 2, 0x1 }, - { 3, 0x2 } -}; - -// N2 divider options -const SrcAttr PCBCUPID_NAU8325::mclk_n2_div[] = { - { 0, 0x0 }, - { 1, 0x1 }, - { 2, 0x2 }, - { 3, 0x3 }, - { 4, 0x4 } -}; - -// N3 multiplier options -const SrcAttr PCBCUPID_NAU8325::mclk_n3_mult[] = { - { 0, 0x1 }, - { 1, 0x2 }, - { 2, 0x3 }, - { 3, 0x4 } -}; - -// DAC oversampling options -const OsrAttr PCBCUPID_NAU8325::osr_dac_sel[] = { - { 64, 2 }, // OSR 64, SRC 1/4 - { 256, 0 }, // OSR 256, SRC 1 - { 128, 1 }, // OSR 128, SRC 1/2 - { 0, 0 }, - { 32, 3 } // OSR 32, SRC 1/8 -}; - -// accessing the table as expected based oon sample rate and mclk -const SRateAttr PCBCUPID_NAU8325::target_srate_table[] = { - { 48000, 2, true, { 12288000, 19200000, 24000000 } }, - { 16000, 1, false, { 4096000, 6400000, 8000000 } }, - { 8000, 0, false, { 2048000, 3200000, 4000000 } }, - { 44100, 2, true, { 11289600, 17640000, 22050000 } }, - { 64000, 3, false, { 16384000, 25600000, 32000000 } }, - { 96000, 3, true, { 24576000, 38400000, 48000000 } }, - { 12000, 0, true, { 3072000, 4800000, 6000000 } }, - { 24000, 1, true, { 6144000, 9600000, 12000000 } }, - { 32000, 2, false, { 8192000, 12800000, 16000000 } } -}; - -bool PCBCUPID_NAU8325::applySampleRateClocks(const SRateAttr *srate, int n1_sel, int mclk_mult_sel, int n2_sel) { - if (!srate || n1_sel < 0 || n1_sel >= 3 || n2_sel < 0 || n2_sel >= 5) { - Serial.println("[NAU8325] Invalid N1/N2 divider index."); +bool PCBCUPID_NAU8325::applySampleRateClocks(const SRateAttr *srate, int n1_sel, int mclk_mult_sel, int n2_sel) +{ + if (!srate || n1_sel < 0 || n1_sel >= 3 || n2_sel < 0 || n2_sel >= 5) + { + log_i("[NAU8325] Invalid N1/N2 divider index."); return false; } // --- Debug --- - Serial.println("--- Debugging clock dividers ---"); - Serial.printf("n1_sel=%d (val=%d)\n", n1_sel, mclk_n1_div[n1_sel].val); - Serial.printf("n2_sel=%d (val=%d)\n", n2_sel, mclk_n2_div[n2_sel].val); - Serial.printf("mult_sel=%d\n", mclk_mult_sel); + log_i("--- Debugging clock dividers ---"); + log_i("n1_sel=%d (val=%d)", n1_sel, mclk_n1_div[n1_sel].val); + log_i("n2_sel=%d (val=%d)", n2_sel, mclk_n2_div[n2_sel].val); + log_i("mult_sel=%d", mclk_mult_sel); // Set sample rate range and max mode writeRegisterBits(NAU8325_R40_CLK_DET_CTRL, @@ -171,57 +178,63 @@ bool PCBCUPID_NAU8325::applySampleRateClocks(const SRateAttr *srate, int n1_sel, (srate->range << NAU8325_REG_SRATE_SFT) | (srate->max ? NAU8325_REG_DIV_MAX : 0)); // Batch update CLK_CTRL (0x03) uint16_t clk_ctrl_val = 0; - clk_ctrl_val |= mclk_n2_div[n2_sel].val; // MCLK_SRC - clk_ctrl_val |= mclk_n1_div[n1_sel].val << NAU8325_CLK_MUL_SRC_SFT; // N1 + clk_ctrl_val |= mclk_n2_div[n2_sel].val; // MCLK_SRC + clk_ctrl_val |= mclk_n1_div[n1_sel].val << NAU8325_CLK_MUL_SRC_SFT; // N1 if (mclk_mult_sel >= 0 && mclk_mult_sel <= 3) - clk_ctrl_val |= mclk_n3_mult[mclk_mult_sel].val << NAU8325_MCLK_SEL_SFT; // N3 + clk_ctrl_val |= mclk_n3_mult[mclk_mult_sel].val << NAU8325_MCLK_SEL_SFT; // N3 - clk_ctrl_val |= 0x1000; // DAC_CLK = MCLK/2 (required!) + clk_ctrl_val |= 0x1000; // DAC_CLK = MCLK/2 (required!) writeRegister(NAU8325_R03_CLK_CTRL, clk_ctrl_val); - writeRegister(NAU8325_R03_CLK_CTRL, clk_ctrl_val); // Now written only once + writeRegister(NAU8325_R03_CLK_CTRL, clk_ctrl_val); // Now written only once // Configure ANALOG_CONTROL_5 for 4x/8x clock enable - switch (mclk_mult_sel) { - case 2: - writeRegisterBits(NAU8325_R65_ANALOG_CONTROL_5, - NAU8325_MCLK4XEN_EN, - NAU8325_MCLK4XEN_EN); - break; - case 3: - writeRegisterBits(NAU8325_R65_ANALOG_CONTROL_5, - NAU8325_MCLK4XEN_EN | NAU8325_MCLK8XEN_EN, - NAU8325_MCLK4XEN_EN | NAU8325_MCLK8XEN_EN); - break; - default: - writeRegisterBits(NAU8325_R65_ANALOG_CONTROL_5, - NAU8325_MCLK4XEN_EN | NAU8325_MCLK8XEN_EN, - 0); - break; + switch (mclk_mult_sel) + { + case 2: + writeRegisterBits(NAU8325_R65_ANALOG_CONTROL_5, + NAU8325_MCLK4XEN_EN, + NAU8325_MCLK4XEN_EN); + break; + case 3: + writeRegisterBits(NAU8325_R65_ANALOG_CONTROL_5, + NAU8325_MCLK4XEN_EN | NAU8325_MCLK8XEN_EN, + NAU8325_MCLK4XEN_EN | NAU8325_MCLK8XEN_EN); + break; + default: + writeRegisterBits(NAU8325_R65_ANALOG_CONTROL_5, + NAU8325_MCLK4XEN_EN | NAU8325_MCLK8XEN_EN, + 0); + break; } return true; } -int PCBCUPID_NAU8325::getMclkRatioAndN2Index(const SRateAttr *srate, int mclk_hz, int &n2_sel_out) { - int ratio = NAU8325_MCLK_FS_RATIO_NUM; // Default: not found +int PCBCUPID_NAU8325::getMclkRatioAndN2Index(const SRateAttr *srate, int mclk_hz, int &n2_sel_out) +{ + int ratio = NAU8325_MCLK_FS_RATIO_NUM; // Default: not found - for (int i = 0; i < 5; i++) { // mclk_n2_div[] has 5 entries + for (int i = 0; i < 5; i++) + { // mclk_n2_div[] has 5 entries int div = mclk_n2_div[i].param; int mclk_src = mclk_hz >> div; - if (srate->mclk_src[NAU8325_MCLK_FS_RATIO_256] == mclk_src) { + if (srate->mclk_src[NAU8325_MCLK_FS_RATIO_256] == mclk_src) + { ratio = NAU8325_MCLK_FS_RATIO_256; n2_sel_out = i; break; } - if (srate->mclk_src[NAU8325_MCLK_FS_RATIO_400] == mclk_src) { + if (srate->mclk_src[NAU8325_MCLK_FS_RATIO_400] == mclk_src) + { ratio = NAU8325_MCLK_FS_RATIO_400; n2_sel_out = i; break; } - if (srate->mclk_src[NAU8325_MCLK_FS_RATIO_500] == mclk_src) { + if (srate->mclk_src[NAU8325_MCLK_FS_RATIO_500] == mclk_src) + { ratio = NAU8325_MCLK_FS_RATIO_500; n2_sel_out = i; break; @@ -234,8 +247,10 @@ int PCBCUPID_NAU8325::getMclkRatioAndN2Index(const SRateAttr *srate, int mclk_hz const SRateAttr *PCBCUPID_NAU8325::getSRateAttr(int fs) { - for (size_t i = 0; i < sizeof(target_srate_table) / sizeof(target_srate_table[0]); ++i) { - if (target_srate_table[i].fs == fs) { + for (size_t i = 0; i < sizeof(target_srate_table) / sizeof(target_srate_table[0]); ++i) + { + if (target_srate_table[i].fs == fs) + { return &target_srate_table[i]; } } @@ -244,25 +259,29 @@ const SRateAttr *PCBCUPID_NAU8325::getSRateAttr(int fs) bool PCBCUPID_NAU8325::chooseClockSource(int fs, int mclk, const SRateAttr *&srate, - int &n1_sel, int &mult_sel, int &n2_sel) { - if (fs <= 0 || mclk <= 0) { - Serial.println("[NAU8325] Invalid fs or mclk."); + int &n1_sel, int &mult_sel, int &n2_sel) +{ + if (fs <= 0 || mclk <= 0) + { + log_i("[NAU8325] Invalid fs or mclk."); return false; } srate = getSRateAttr(fs); - if (!srate) { - Serial.printf("[NAU8325] Unsupported fs: %d\n", fs); + if (!srate) + { + log_i("[NAU8325] Unsupported fs: %d\n", fs); return false; } // First try direct N2 mapping int ratio = getMclkRatioAndN2Index(srate, mclk, n2_sel); - if (ratio != NAU8325_MCLK_FS_RATIO_NUM) { + if (ratio != NAU8325_MCLK_FS_RATIO_NUM) + { n1_sel = 0; - mult_sel = -1; // Bypass - Serial.printf("[NAU8325] Direct match: fs=%d, mclk=%d → N2=%d (Ratio=%d)\n", - fs, mclk, n2_sel, ratio); + mult_sel = -1; // Bypass + log_i("[NAU8325] Direct match: fs=%d, mclk=%d → N2=%d (Ratio=%d)\n", + fs, mclk, n2_sel, ratio); return true; } @@ -270,14 +289,15 @@ bool PCBCUPID_NAU8325::chooseClockSource(int fs, int mclk, int mclk_max = 0; int best_n1 = -1, best_mult = -1, best_n2 = -1; - for (int i = 0; i < 3; ++i) // N1 options + for (int i = 0; i < 3; ++i) // N1 options { - for (int j = 0; j < 4; ++j) // N3 multiplier options + for (int j = 0; j < 4; ++j) // N3 multiplier options { int m = (mclk << mclk_n3_mult[j].param) / mclk_n1_div[i].param; int r = getMclkRatioAndN2Index(srate, m, n2_sel); - if (r != NAU8325_MCLK_FS_RATIO_NUM && (m > mclk_max || best_n1 < i)) { + if (r != NAU8325_MCLK_FS_RATIO_NUM && (m > mclk_max || best_n1 < i)) + { mclk_max = m; best_n1 = i; best_mult = j; @@ -286,61 +306,70 @@ bool PCBCUPID_NAU8325::chooseClockSource(int fs, int mclk, } } - if (mclk_max > 0) { + if (mclk_max > 0) + { n1_sel = best_n1; mult_sel = best_mult; n2_sel = best_n2; - Serial.printf("[NAU8325] Matched via N1/N3 combo: fs=%d, mclk=%d → N1=%d, N3(mult)=%d, N2=%d\n", - fs, mclk, n1_sel, mult_sel, n2_sel); - Serial.printf("[NAU8325] chooseClockSource(): fs=%d, mclk=%d\n", fs, mclk); + log_i("[NAU8325] Matched via N1/N3 combo: fs=%d, mclk=%d → N1=%d, N3(mult)=%d, N2=%d\n", + fs, mclk, n1_sel, mult_sel, n2_sel); + log_i("[NAU8325] chooseClockSource(): fs=%d, mclk=%d\n", fs, mclk); return true; } - Serial.printf("[NAU8325] Failed to match MCLK %d Hz with fs %d Hz\n", mclk, fs); + log_i("[NAU8325] Failed to match MCLK %d Hz with fs %d Hz\n", mclk, fs); return false; } -bool PCBCUPID_NAU8325::configureClocks(int fs, int mclk) { +bool PCBCUPID_NAU8325::configureClocks(int fs, int mclk) +{ const SRateAttr *srate = nullptr; int n1_sel = 0, mult_sel = -1, n2_sel = 0; - if (!chooseClockSource(fs, mclk, srate, n1_sel, mult_sel, n2_sel)) { - Serial.printf("[NAU8325] Failed to choose clock source for fs=%d, mclk=%d\n", fs, mclk); + if (!chooseClockSource(fs, mclk, srate, n1_sel, mult_sel, n2_sel)) + { + log_i("[NAU8325] Failed to choose clock source for fs=%d, mclk=%d\n", fs, mclk); return false; } - if (!applySampleRateClocks(srate, n1_sel, mult_sel, n2_sel)) { - Serial.println("[NAU8325] Failed to apply sample rate clocks."); + if (!applySampleRateClocks(srate, n1_sel, mult_sel, n2_sel)) + { + log_i("[NAU8325] Failed to apply sample rate clocks."); return false; } - Serial.printf("[NAU8325] configureClocks(): fs=%u, mclk=%u\n", fs, mclk); + log_i("[NAU8325] configureClocks(): fs=%u, mclk=%u\n", fs, mclk); return true; } -const OsrAttr *PCBCUPID_NAU8325::getCurrentOSR() { +const OsrAttr *PCBCUPID_NAU8325::getCurrentOSR() +{ uint16_t value; - if (!readRegister(NAU8325_R29_DAC_CTRL1, value)) { + if (!readRegister(NAU8325_R29_DAC_CTRL1, value)) + { return nullptr; } uint8_t osr_index = value & NAU8325_DAC_OVERSAMPLE_MASK; - if (osr_index >= sizeof(osr_dac_sel) / sizeof(osr_dac_sel[0])) { + if (osr_index >= sizeof(osr_dac_sel) / sizeof(osr_dac_sel[0])) + { return nullptr; } return &osr_dac_sel[osr_index]; } -bool PCBCUPID_NAU8325::configureAudio(uint32_t fs, uint32_t mclk, uint8_t bits_per_sample) { +bool PCBCUPID_NAU8325::configureAudio(uint32_t fs, uint32_t mclk, uint8_t bits_per_sample) +{ this->fs = fs; this->mclk = mclk; // Configure clock tree - if (!configureClocks(fs, mclk)) { + if (!configureClocks(fs, mclk)) + { return false; } @@ -348,8 +377,9 @@ bool PCBCUPID_NAU8325::configureAudio(uint32_t fs, uint32_t mclk, uint8_t bits_p writeRegisterBits(NAU8325_R29_DAC_CTRL1, NAU8325_DAC_OVERSAMPLE_MASK, NAU8325_DAC_OVERSAMPLE_128); const OsrAttr *osr = getCurrentOSR(); - if (!osr || osr->osr == 0 || (fs * osr->osr > CLK_DA_AD_MAX)) { - Serial.println("[NAU8325] Invalid OSR or fs × OSR exceeds max."); + if (!osr || osr->osr == 0 || (fs * osr->osr > CLK_DA_AD_MAX)) + { + log_i("[NAU8325] Invalid OSR or fs × OSR exceeds max."); return false; } @@ -360,59 +390,63 @@ bool PCBCUPID_NAU8325::configureAudio(uint32_t fs, uint32_t mclk, uint8_t bits_p // Configure I2S word length uint16_t val_len = 0; - switch (bits_per_sample) { - case 16: - val_len = NAU8325_I2S_DL_16; - break; - case 20: - val_len = NAU8325_I2S_DL_20; - break; - case 24: - val_len = NAU8325_I2S_DL_24; - break; - case 32: - val_len = NAU8325_I2S_DL_32; - break; - default: - Serial.printf("[NAU8325] Invalid bit width: %u\n", bits_per_sample); - return false; + switch (bits_per_sample) + { + case 16: + val_len = NAU8325_I2S_DL_16; + break; + case 20: + val_len = NAU8325_I2S_DL_20; + break; + case 24: + val_len = NAU8325_I2S_DL_24; + break; + case 32: + val_len = NAU8325_I2S_DL_32; + break; + default: + log_i("[NAU8325] Invalid bit width: %u\n", bits_per_sample); + return false; } writeRegisterBits(NAU8325_R0D_I2S_PCM_CTRL1, NAU8325_I2S_DL_MASK, val_len); - Serial.printf("[NAU8325] configureAudio(): fs=%u, mclk=%u, bits=%u\n", fs, mclk, bits_per_sample); + log_i("[NAU8325] configureAudio(): fs=%u, mclk=%u, bits=%u\n", fs, mclk, bits_per_sample); return true; } -bool PCBCUPID_NAU8325::setI2SFormat(NAU8325_I2SFormat format, NAU8325_ClockPolarity polarity) { +bool PCBCUPID_NAU8325::setI2SFormat(AudioI2SFormat format, ClockPolarity polarity) +{ uint16_t ctrl1_val = 0; // Set BCLK polarity - if (polarity == INVERTED_BCLK) { + if (polarity == INVERTED_BCLK) + { ctrl1_val |= NAU8325_I2S_BP_INV; } // Set data format - switch (format) { - case I2S_STD: - ctrl1_val |= NAU8325_I2S_DF_I2S; - break; - case LEFT_JUSTIFIED: - ctrl1_val |= NAU8325_I2S_DF_LEFT; - break; - case RIGHT_JUSTIFIED: - ctrl1_val |= NAU8325_I2S_DF_RIGTH; - break; - case DSP_A: - ctrl1_val |= NAU8325_I2S_DF_PCM_AB; - break; - case DSP_B: - ctrl1_val |= NAU8325_I2S_DF_PCM_AB | NAU8325_I2S_PCMB_EN; - break; - default: - return false; + switch (format) + { + case I2S_STD: + ctrl1_val |= NAU8325_I2S_DF_I2S; + break; + case LEFT_JUSTIFIED: + ctrl1_val |= NAU8325_I2S_DF_LEFT; + break; + case RIGHT_JUSTIFIED: + ctrl1_val |= NAU8325_I2S_DF_RIGTH; + break; + case DSP_A: + ctrl1_val |= NAU8325_I2S_DF_PCM_AB; + break; + case DSP_B: + ctrl1_val |= NAU8325_I2S_DF_PCM_AB | NAU8325_I2S_PCMB_EN; + break; + default: + return false; } // Apply to I2S_PCM_CTRL1 @@ -421,39 +455,45 @@ bool PCBCUPID_NAU8325::setI2SFormat(NAU8325_I2SFormat format, NAU8325_ClockPolar ctrl1_val); } -bool PCBCUPID_NAU8325::setSysClock(uint32_t freq) { - if (freq < MASTER_CLK_MIN || freq > MASTER_CLK_MAX) { - Serial.printf("[NAU8325] Invalid MCLK: %u Hz (allowed: %u - %u)\n", freq, MASTER_CLK_MIN, MASTER_CLK_MAX); +bool PCBCUPID_NAU8325::setSysClock(uint32_t freq) +{ + if (freq < MASTER_CLK_MIN || freq > MASTER_CLK_MAX) + { + log_i("[NAU8325] Invalid MCLK: %u Hz (allowed: %u - %u)\n", freq, MASTER_CLK_MIN, MASTER_CLK_MAX); return false; } this->mclk = freq; - Serial.printf("[NAU8325] MCLK set to %u Hz\n", mclk); + log_i("[NAU8325] MCLK set to %u Hz\n", mclk); return true; } -void PCBCUPID_NAU8325::setPowerUpDefault(bool enable) { +void PCBCUPID_NAU8325::setPowerUpDefault(bool enable) +{ writeRegisterBits(NAU8325_R40_CLK_DET_CTRL, NAU8325_PWRUP_DFT, enable ? NAU8325_PWRUP_DFT : 0); } -void PCBCUPID_NAU8325::setOversampling(OversamplingMode mode) { +void PCBCUPID_NAU8325::setOversampling(OversamplingMode mode) +{ writeRegisterBits(NAU8325_R29_DAC_CTRL1, NAU8325_DAC_OVERSAMPLE_MASK, mode); } -void PCBCUPID_NAU8325::setVolume(uint8_t left, uint8_t right) { +void PCBCUPID_NAU8325::setVolume(uint8_t left, uint8_t right) +{ uint16_t value = ((left & 0xFF) << 8) | (right & 0xFF); writeRegister(NAU8325_R13_DAC_VOLUME, value); } -void PCBCUPID_NAU8325::softMute(bool enable) // under testing +void PCBCUPID_NAU8325::softMute(bool enable) { writeRegisterBits(NAU8325_R12_MUTE_CTRL, NAU8325_SOFT_MUTE, enable ? NAU8325_SOFT_MUTE : 0); - delay(30); // Same as msleep(30) + delay(30); } -void PCBCUPID_NAU8325::initRegisters() { +void PCBCUPID_NAU8325::initRegisters() +{ // Set ALC parameters default timing and max gain writeRegisterBits(NAU8325_R2C_ALC_CTRL1, NAU8325_ALC_MAXGAIN_MASK, 0x7 << NAU8325_ALC_MAXGAIN_SFT); writeRegisterBits(NAU8325_R2D_ALC_CTRL2, @@ -461,7 +501,8 @@ void PCBCUPID_NAU8325::initRegisters() { (0x5 << NAU8325_ALC_DCY_SFT) | (0x3 << NAU8325_ALC_ATK_SFT) | (0x5 << NAU8325_ALC_HLD_SFT)); /* Enable ALC to avoid signal distortion when battery low. */ - if (alc_enable) { + if (alc_enable) + { writeRegisterBits(NAU8325_R2E_ALC_CTRL3, NAU8325_ALC_EN, NAU8325_ALC_EN); } @@ -489,10 +530,13 @@ void PCBCUPID_NAU8325::initRegisters() { else if (dac_vref_microvolt == 3060000) vref_val = 3 << NAU8325_DACVREFSEL_SFT; - if (vref_val != 0xFFFF) { + if (vref_val != 0xFFFF) + { writeRegisterBits(NAU8325_R73_RDAC, NAU8325_DACVREFSEL_MASK, vref_val); - } else { - Serial.printf("[NAU8325] Invalid DAC Vref: %lu uV\n", dac_vref_microvolt); + } + else + { + log_i("[NAU8325] Invalid DAC Vref: %lu uV\n", dac_vref_microvolt); } /* DAC Reference Voltage Decoupling Capacitors. */ @@ -511,10 +555,13 @@ void PCBCUPID_NAU8325::initRegisters() { else if (vref_impedance_ohms == 2500) vref_imp_val = 3 << NAU8325_BIAS_VMID_SEL_SFT; - if (vref_imp_val != 0xFFFF) { + if (vref_imp_val != 0xFFFF) + { writeRegisterBits(NAU8325_R60_BIAS_ADJ, NAU8325_BIAS_VMID_SEL_MASK, vref_imp_val); - } else { - Serial.printf("[NAU8325] Invalid VMID impedance: %lu ohms\n", vref_impedance_ohms); + } + else + { + log_i("[NAU8325] Invalid VMID impedance: %lu ohms\n", vref_impedance_ohms); } // Enable VMID, BIAS, DACs, etc., Voltage / current Amps @@ -523,7 +570,8 @@ void PCBCUPID_NAU8325::initRegisters() { (0x1 << NAU8325_DACEN_SFT) | (0x1 << NAU8325_DACCLKEN_SFT) | (0x1 << NAU8325_DACEN_R_SFT) | (0x1 << NAU8325_DACCLKEN_R_SFT) | (0x1 << NAU8325_CLASSDEN_SFT) | (0x1 << NAU8325_VMDFSTENB_SFT) | (0x1 << NAU8325_BIASEN_SFT) | (1 << NAU8325_VMIDEN_SFT)); /* Enable ALC to avoid signal distortion when battery low. */ - if (alc_enable) { + if (alc_enable) + { writeRegisterBits(NAU8325_R2E_ALC_CTRL3, NAU8325_ALC_EN, NAU8325_ALC_EN); } @@ -546,8 +594,41 @@ void PCBCUPID_NAU8325::initRegisters() { NAU8325_DAC_OVERSAMPLE_128); } -bool PCBCUPID_NAU8325::writeRegister(uint16_t reg, uint16_t val) { - Serial.printf("[WRITE] reg=0x%04X val=0x%04X\n", reg, val); +void PCBCUPID_NAU8325::powerOn() +{ + // Clear soft mute to allow audio output + softMute(false); + + // Enable DACs, clocks, bias, class-D + writeRegisterBits(NAU8325_R61_ANALOG_CONTROL_1, + NAU8325_DACEN_MASK | NAU8325_DACCLKEN_MASK | NAU8325_DACEN_R_MASK | NAU8325_DACCLKEN_R_MASK | NAU8325_CLASSDEN_MASK | NAU8325_VMDFSTENB_MASK | NAU8325_BIASEN_MASK | NAU8325_VMIDEN_MASK, + (3 << NAU8325_DACEN_SFT) | (3 << NAU8325_DACCLKEN_SFT) | (3 << NAU8325_DACEN_R_SFT) | (3 << NAU8325_DACCLKEN_R_SFT) | (3 << NAU8325_CLASSDEN_SFT) | (3 << NAU8325_VMDFSTENB_SFT) | (3 << NAU8325_BIASEN_SFT) | 0x03); // VMID_EN + + // If clock detection is disabled, enable PWRUP_DFT + setPowerUpDefault(true); + + delay(30); // Let analog circuitry stabilize +} + +void PCBCUPID_NAU8325::powerOff() +{ + // Soft mute first to prevent pop + softMute(true); + + // Disable analog and DAC power + writeRegisterBits(NAU8325_R61_ANALOG_CONTROL_1, + NAU8325_DACEN_MASK | NAU8325_DACCLKEN_MASK | NAU8325_DACEN_R_MASK | NAU8325_DACCLKEN_R_MASK | NAU8325_CLASSDEN_MASK | NAU8325_VMDFSTENB_MASK | NAU8325_BIASEN_MASK | NAU8325_VMIDEN_MASK, + 0x0000); + + // If clock detection is disabled, clear PWRUP_DFT + setPowerUpDefault(false); + + delay(30); // Let output fade out +} + +bool PCBCUPID_NAU8325::writeRegister(uint16_t reg, uint16_t val) +{ + log_i("[WRITE] reg=0x%04X val=0x%04X\n", reg, val); i2c.beginTransmission(i2c_addr); i2c.write((reg >> 8) & 0xFF); i2c.write(reg & 0xFF); @@ -556,7 +637,7 @@ bool PCBCUPID_NAU8325::writeRegister(uint16_t reg, uint16_t val) { return i2c.endTransmission() == 0; } -bool PCBCUPID_NAU8325::writeRegisterBits(uint16_t reg, uint16_t mask, uint16_t value) // mask ---> is to set the the particular bit renaining bit untouched. +bool PCBCUPID_NAU8325::writeRegisterBits(uint16_t reg, uint16_t mask, uint16_t value) // mask ---> is to set the the particular bit renaining bit untouched. { uint16_t current; if (!readRegister(reg, current)) @@ -566,15 +647,18 @@ bool PCBCUPID_NAU8325::writeRegisterBits(uint16_t reg, uint16_t mask, uint16_t v return writeRegister(reg, new_value); } -bool PCBCUPID_NAU8325::readRegister(uint16_t reg, uint16_t &value) { +bool PCBCUPID_NAU8325::readRegister(uint16_t reg, uint16_t &value) +{ i2c.beginTransmission(i2c_addr); i2c.write((reg >> 8) & 0xFF); i2c.write(reg & 0xFF); - if (i2c.endTransmission(false) != 0) { + if (i2c.endTransmission(false) != 0) + { return false; } - if (i2c.requestFrom(i2c_addr, (uint8_t)2) != 2) { + if (i2c.requestFrom(i2c_addr, (uint8_t)2) != 2) + { return false; } @@ -582,34 +666,4 @@ bool PCBCUPID_NAU8325::readRegister(uint16_t reg, uint16_t &value) { uint8_t low = i2c.read(); value = ((uint16_t)high << 8) | low; return true; -} - -void PCBCUPID_NAU8325::powerOn() { - // Clear soft mute to allow audio output - softMute(false); - - // Enable DACs, clocks, bias, class-D - writeRegisterBits(NAU8325_R61_ANALOG_CONTROL_1, - NAU8325_DACEN_MASK | NAU8325_DACCLKEN_MASK | NAU8325_DACEN_R_MASK | NAU8325_DACCLKEN_R_MASK | NAU8325_CLASSDEN_MASK | NAU8325_VMDFSTENB_MASK | NAU8325_BIASEN_MASK | NAU8325_VMIDEN_MASK, - (3 << NAU8325_DACEN_SFT) | (3 << NAU8325_DACCLKEN_SFT) | (3 << NAU8325_DACEN_R_SFT) | (3 << NAU8325_DACCLKEN_R_SFT) | (3 << NAU8325_CLASSDEN_SFT) | (3 << NAU8325_VMDFSTENB_SFT) | (3 << NAU8325_BIASEN_SFT) | 0x03); // VMID_EN - - // If clock detection is disabled, enable PWRUP_DFT - setPowerUpDefault(true); - - delay(30); // Let analog circuitry stabilize -} - -void PCBCUPID_NAU8325::powerOff() { - // Soft mute first to prevent pop - softMute(true); - - // Disable analog and DAC power - writeRegisterBits(NAU8325_R61_ANALOG_CONTROL_1, - NAU8325_DACEN_MASK | NAU8325_DACCLKEN_MASK | NAU8325_DACEN_R_MASK | NAU8325_DACCLKEN_R_MASK | NAU8325_CLASSDEN_MASK | NAU8325_VMDFSTENB_MASK | NAU8325_BIASEN_MASK | NAU8325_VMIDEN_MASK, - 0x0000); - - // If clock detection is disabled, clear PWRUP_DFT - setPowerUpDefault(false); - - delay(30); // Let output fade out -} +} \ No newline at end of file diff --git a/src/Driver/nau8325/PCBCUPID_NAU8325.h b/src/Driver/nau8325/PCBCUPID_NAU8325.h index 54f87f9..1182ecf 100644 --- a/src/Driver/nau8325/PCBCUPID_NAU8325.h +++ b/src/Driver/nau8325/PCBCUPID_NAU8325.h @@ -4,6 +4,8 @@ #include #include +#define NAU8325_I2C_ADDR 0x21 + #define NAU8325_R00_HARDWARE_RST 0x00 #define NAU8325_R01_SOFTWARE_RST 0x01 #define NAU8325_R02_DEVICE_ID 0x02 @@ -391,7 +393,7 @@ struct RegDefault uint16_t val; }; -enum NAU8325_I2SFormat +enum AudioI2SFormat { I2S_STD, LEFT_JUSTIFIED, @@ -399,7 +401,7 @@ enum NAU8325_I2SFormat DSP_A, DSP_B }; -enum NAU8325_ClockPolarity +enum ClockPolarity { NORMAL_BCLK, INVERTED_BCLK @@ -412,7 +414,7 @@ enum OversamplingMode OSR_32 = 4 }; -enum +enum ClockRatio { NAU8325_MCLK_FS_RATIO_256 = 0, NAU8325_MCLK_FS_RATIO_400 = 1, @@ -423,7 +425,7 @@ enum class PCBCUPID_NAU8325 { public: - PCBCUPID_NAU8325(TwoWire &wire, uint8_t i2c_addr); + PCBCUPID_NAU8325(TwoWire &wire); static const RegDefault reg_defaults[]; @@ -434,6 +436,34 @@ class PCBCUPID_NAU8325 static const OsrAttr osr_dac_sel[]; static const SRateAttr target_srate_table[]; + /* + @brief + fs- frame rate in Hz (e.g., 8000, 16000, 44100, 48000) + bits_per_sample- bits per sample (e.g., 16, 24) + ratio- MCLK to FS ratio (e.g., 256, 384, 512, 640) + @return + true if initialization is successful, false otherwise + @note + The begin() function initializes the NAU8325 codec with the specified + frame rate, bits per sample, and MCLK to FS ratio. It configures the codec + for audio playback and sets up the necessary registers. The function returns + true if the initialization is successful, or false if there is an error. + The default values for bits_per_sample and ratio are 16 and 256, respectively. + If only fs is provided, it uses the default values for bits_per_sample and ratio. + If no parameters are provided, it uses default values for fs, bits_per_sample, + and ratio. + Example: + PCBCUPID_NAU8325 codec; + codec.begin(44100, 16, 256); // Initialize with 44.1kHz, 16 bits, and MCLK/FS ratio of 256 + codec.begin(16000, 24); // Initialize with 16kHz, 24 bits, and default MCLK/FS ratio of 256 + codec.begin(); // Initialize with default values (e.g., 8000Hz, 16 bits, 256 ratio) + @note + The begin() function must be called before using the codec for audio playback. + It sets up the codec's internal registers and prepares it for audio processing. + If the initialization fails, the codec will not function correctly, and audio playback + may not work as expected. + */ + bool begin(uint32_t fs, uint8_t bits_per_sample, uint16_t ratio); bool begin(uint32_t fs, uint8_t bits_per_sample); bool begin(); @@ -449,12 +479,9 @@ class PCBCUPID_NAU8325 void setOversampling(OversamplingMode mode); void setVolume(uint8_t left, uint8_t right); // public - - private: - // Add private members (Wire, I2C address, etc.) here later TwoWire &i2c; - uint8_t i2c_addr; + uint8_t i2c_addr = NAU8325_I2C_ADDR; uint32_t fs; uint32_t mclk; @@ -462,7 +489,7 @@ class PCBCUPID_NAU8325 void initRegisters(); // Initialize codec - bool setSysClock(uint32_t freq); + bool setSysClock(uint32_t freq); // setup MCLK for I2S bool configureAudio(uint32_t fs, uint32_t mclk, uint8_t bits_per_sample); bool chooseClockSource(int fs, int mclk, const SRateAttr *&srate, int &n1_sel, int &mult_sel, int &n2_sel); bool configureClocks(int fs, int mclk); @@ -470,10 +497,11 @@ class PCBCUPID_NAU8325 int getMclkRatioAndN2Index(const SRateAttr *srate, int mclk_hz, int &n2_sel_out); const SRateAttr *getSRateAttr(int fs); const OsrAttr *getCurrentOSR(); - bool setI2SFormat(NAU8325_I2SFormat format, NAU8325_ClockPolarity polarity); + bool setI2SFormat(AudioI2SFormat format, ClockPolarity polarity); - void enableDAPMBlocks(); + void enableDAPMBlocks(); // analog path power enable + // I2C register controls bool readRegister(uint16_t reg, uint16_t &value); bool writeRegister(uint16_t reg, uint16_t value); bool writeRegisterBits(uint16_t reg, uint16_t mask, uint16_t value); diff --git a/src/DriverPins.h b/src/DriverPins.h index b57cf92..29f1e37 100644 --- a/src/DriverPins.h +++ b/src/DriverPins.h @@ -808,8 +808,7 @@ static PinsAudioKitEs8388v1Class PinsAudioKitEs8388v1; static PinsAudioKitEs8388v2Class PinsAudioKitEs8388v2; /// @ingroup audio_driver static PinsAudioKitAC101Class PinsAudioKitAC101; -/// @ingroup audio_driver -static DriverPins PinsNAU8325; + } // namespace audio_driver \ No newline at end of file From c4011a978c0ae1142624eee3248368566165537a Mon Sep 17 00:00:00 2001 From: Neutrino Date: Fri, 1 Aug 2025 18:56:47 +0530 Subject: [PATCH 8/8] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1ccb4b7..6d736e7 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ This library privides the following drivers: - AudioDriverWM8960 - AudioDriverWM8978 - AudioDriverWM8994 -- +- AudioDriverNAU8325 And it supports the following boards: