@@ -651,6 +651,106 @@ class ES8388Source : public I2SSource {
651651
652652};
653653
654+ /* ES8311 Sound Module
655+ This is an I2S sound processing unit that requires initialization over
656+ I2C before I2S data can be received.
657+ */
658+ class ES8311Source : public I2SSource {
659+ private:
660+ // I2C initialization functions for es8311
661+ void _es8311I2cBegin () {
662+ Wire.setClock (100000 );
663+ }
664+
665+ void _es8311I2cWrite (uint8_t reg, uint8_t val) {
666+ #ifndef ES8311_ADDR
667+ #define ES8311_ADDR 0x18 // default address is... foggy
668+ #endif
669+ Wire.beginTransmission (ES8311_ADDR);
670+ Wire.write ((uint8_t )reg);
671+ Wire.write ((uint8_t )val);
672+ uint8_t i2cErr = Wire.endTransmission (); // i2cErr == 0 means OK
673+ if (i2cErr != 0 ) {
674+ DEBUGSR_PRINTF (" AR: ES8311 I2C write failed with error=%d (addr=0x%X, reg 0x%X, val 0x%X).\n " , i2cErr, ES8311_ADDR, reg, val);
675+ }
676+ }
677+
678+ void _es8311InitAdc () {
679+ //
680+ // Currently only tested with the ESP32-P4 EV board with the onboard mic.
681+ // Datasheet with I2C commands: https://dl.xkwy2018.com/downloads/RK3588/01_Official%20Release/04_Product%20Line%20Branch_NVR/02_Key%20Device%20Specifications/ES8311%20DS.pdf
682+ //
683+ _es8311I2cBegin ();
684+ _es8311I2cWrite (0x00 , 0b00011111 ); // RESET, default value
685+ _es8311I2cWrite (0x45 , 0b00000000 ); // GP, default value
686+ _es8311I2cWrite (0x01 , 0b00111010 ); // CLOCK MANAGER was 0b00110000 trying 0b00111010 (MCLK enable?)
687+
688+ _es8311I2cWrite (0x02 , 0b00000000 ); // 22050hz calculated
689+ _es8311I2cWrite (0x05 , 0b00000000 ); // 22050hz calculated
690+ _es8311I2cWrite (0x03 , 0b00010000 ); // 22050hz calculated
691+ _es8311I2cWrite (0x04 , 0b00010000 ); // 22050hz calculated
692+ _es8311I2cWrite (0x07 , 0b00000000 ); // 22050hz calculated
693+ _es8311I2cWrite (0x08 , 0b11111111 ); // 22050hz calculated
694+ _es8311I2cWrite (0x06 , 0b11100011 ); // 22050hz calculated
695+
696+ _es8311I2cWrite (0x16 , 0b00100000 ); // ADC was 0b00000011 trying 0b00100100 now
697+ _es8311I2cWrite (0x0B , 0b00000000 ); // SYSTEM at default
698+ _es8311I2cWrite (0x0C , 0b00100000 ); // SYSTEM was 0b00001111 trying 0b00100000
699+ _es8311I2cWrite (0x10 , 0b00010011 ); // SYSTEM was 0b00011111 trying 0b00010011
700+ _es8311I2cWrite (0x11 , 0b01111100 ); // SYSTEM was 0b01111111 trying 0b01111100
701+ _es8311I2cWrite (0x00 , 0b11000000 ); // *** RESET (again - seems important?)
702+ _es8311I2cWrite (0x01 , 0b00111010 ); // CLOCK MANAGER was 0b00111111 trying 0b00111010 (again??)
703+ _es8311I2cWrite (0x14 , 0b00010000 ); // *** SYSTEM was 0b00011010 trying 0b00010000 (or 0b01111010) (PGA gain)
704+ _es8311I2cWrite (0x12 , 0b00000000 ); // SYSTEM - DAC, likely don't care
705+ _es8311I2cWrite (0x13 , 0b00010000 ); // SYSTEM - output, likely don't cate
706+ _es8311I2cWrite (0x09 , 0b00001000 ); // SDP IN (likely don't care) was 0b00001100 (16-bit) - changed to 0b00001000 (I2S 32-bit)
707+ _es8311I2cWrite (0x0A , 0b00001000 ); // *** SDP OUT, was 0b00001100 trying 0b00001000 (I2S 32-bit)
708+ _es8311I2cWrite (0x0E , 0b00000010 ); // *** SYSTEM was 0b00000010 trying 0b00011010 (seems best so far!) (or 0b00000010)
709+ _es8311I2cWrite (0x0F , 0b01000100 ); // SYSTEM was 0b01000100
710+ _es8311I2cWrite (0x15 , 0b00000000 ); // ADC soft ramp (disabled)
711+ _es8311I2cWrite (0x1B , 0b00000101 ); // ADC soft-mute was 0b00000101
712+ _es8311I2cWrite (0x1C , 0b01100101 ); // ADC EQ and offset freeze was 0b01100101 (bad at 0b00101100)
713+ _es8311I2cWrite (0x17 , 0b10111111 ); // ADC volume was 0b11111111 trying ADC volume 0b10111111 = 0db (maxgain) 0x16
714+ _es8311I2cWrite (0x44 , 0b00000000 ); // 0b10000000 - loopback test. on: 0x88; off: 0x00; mic--" speak
715+
716+ }
717+
718+ public:
719+ ES8311Source (SRate_t sampleRate, int blockSize, float sampleScale = 1 .0f , bool i2sMaster=true ) :
720+ I2SSource (sampleRate, blockSize, sampleScale, i2sMaster) {
721+ _config.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT;
722+ };
723+
724+ void initialize (int8_t i2swsPin, int8_t i2ssdPin, int8_t i2sckPin, int8_t mclkPin) {
725+ DEBUGSR_PRINTLN (" es8311Source:: initialize();" );
726+
727+ // if ((i2sckPin < 0) || (mclkPin < 0)) { // WLEDMM not sure if this check is needed here, too
728+ // ERRORSR_PRINTF("\nAR: invalid I2S es8311 pin: SCK=%d, MCLK=%d\n", i2sckPin, mclkPin);
729+ // return;
730+ // }
731+ // BUG: "use global I2C pins" are valid as -1, and -1 is seen as invalid here.
732+ // Workaround: Set I2C pins here, which will also set them globally.
733+ // Bug also exists in ES7243.
734+ if ((i2c_sda < 0 ) || (i2c_scl < 0 )) { // check that global I2C pins are not "undefined"
735+ ERRORSR_PRINTF (" \n AR: invalid es8311 global I2C pins: SDA=%d, SCL=%d\n " , i2c_sda, i2c_scl);
736+ return ;
737+ }
738+ if (!pinManager.joinWire (i2c_sda, i2c_scl)) { // WLEDMM specific: start I2C with globally defined pins
739+ ERRORSR_PRINTF (" \n AR: failed to join I2C bus with SDA=%d, SCL=%d\n " , i2c_sda, i2c_scl);
740+ return ;
741+ }
742+
743+ // First route mclk, then configure ADC over I2C, then configure I2S
744+ _es8311InitAdc ();
745+ I2SSource::initialize (i2swsPin, i2ssdPin, i2sckPin, mclkPin);
746+ }
747+
748+ void deinitialize () {
749+ I2SSource::deinitialize ();
750+ }
751+
752+ };
753+
654754class WM8978Source : public I2SSource {
655755 private:
656756 // I2C initialization functions for WM8978
0 commit comments