@@ -736,6 +736,109 @@ class WM8978Source : public I2SSource {
736736
737737};
738738
739+ class AC101Source : public I2SSource {
740+ private:
741+ // I2C initialization functions for WM8978
742+ void _ac101I2cBegin () {
743+ Wire.setClock (400000 );
744+ }
745+
746+ void _ac101I2cWrite (uint8_t reg_addr, uint16_t val) {
747+ #ifndef AC101_ADDR
748+ #define AC101_ADDR 0x1A
749+ #endif
750+ char send_buff[3 ];
751+ send_buff[0 ] = reg_addr;
752+ send_buff[1 ] = uint8_t ((val >> 8 ) & 0xff );
753+ send_buff[2 ] = uint8_t (val & 0xff );
754+ Wire.beginTransmission (AC101_ADDR);
755+ Wire.write ((const uint8_t *)send_buff, 3 );
756+ uint8_t i2cErr = Wire.endTransmission (); // i2cErr == 0 means OK
757+ if (i2cErr != 0 ) {
758+ DEBUGSR_PRINTF (" AR: AC101 I2C write failed with error=%d (addr=0x%X, reg 0x%X, val 0x%X).\n " , i2cErr, AC101_ADDR, reg_addr, val);
759+ }
760+ }
761+
762+ void _ac101InitAdc () {
763+ // https://files.seeedstudio.com/wiki/ReSpeaker_6-Mics_Circular_Array_kit_for_Raspberry_Pi/reg/AC101_User_Manual_v1.1.pdf
764+ // This supports mostly the older AI Thinkier AudioKit A1S that has an AC101 chip
765+ // Newer versions use the ES3833 chip - which we also support.
766+
767+ _ac101I2cBegin ();
768+
769+ #define CHIP_AUDIO_RS 0x00
770+ #define SYSCLK_CTRL 0x03
771+ #define MOD_CLK_ENA 0x04
772+ #define MOD_RST_CTRL 0x05
773+ #define I2S_SR_CTRL 0x06
774+ #define I2S1LCK_CTRL 0x10
775+ #define I2S1_SDOUT_CTRL 0x11
776+ #define I2S1_MXR_SRC 0x13
777+ #define ADC_DIG_CTRL 0x40
778+ #define ADC_APC_CTRL 0x50
779+ #define ADC_SRC 0x51
780+ #define ADC_SRCBST_CTRL 0x52
781+ #define OMIXER_DACA_CTRL 0x53
782+ #define OMIXER_SR 0x54
783+ #define HPOUT_CTRL 0x56
784+
785+ _ac101I2cWrite (CHIP_AUDIO_RS, 0x123 ); // I think anything written here is a reset as 0x123 is kinda suss.
786+
787+ delay (100 );
788+
789+ _ac101I2cWrite (SYSCLK_CTRL, 0b0000100000001000 ); // System Clock is I2S MCLK
790+ _ac101I2cWrite (MOD_CLK_ENA, 0b1000000000001000 ); // I2S and ADC Clock Enable
791+ _ac101I2cWrite (MOD_RST_CTRL, 0b1000000000001000 ); // I2S and ADC Clock Enable
792+ _ac101I2cWrite (I2S_SR_CTRL, 0b0100000000000000 ); // set to 22050hz just in case
793+ _ac101I2cWrite (I2S1LCK_CTRL, 0b1000000000110000 ); // set I2S slave mode, 24-bit word size
794+ _ac101I2cWrite (I2S1_SDOUT_CTRL, 0b1100000000000000 ); // I2S enable Left/Right channels
795+ _ac101I2cWrite (I2S1_MXR_SRC, 0b0010001000000000 ); // I2S digital Mixer, ADC L/R data
796+ _ac101I2cWrite (ADC_SRCBST_CTRL, 0b0000000000000100 ); // mute all boosts. last 3 bits are reserved/default
797+ _ac101I2cWrite (OMIXER_SR, 0b0000010000001000 ); // Line L/R to output mixer
798+ _ac101I2cWrite (ADC_SRC, 0b0000010000001000 ); // Line L/R to ADC
799+ _ac101I2cWrite (ADC_DIG_CTRL, 0b1000000000000000 ); // Enable ADC
800+ _ac101I2cWrite (ADC_APC_CTRL, 0b1011100100000000 ); // ADC L/R enabled, 0dB gain
801+ _ac101I2cWrite (OMIXER_DACA_CTRL, 0b0011111110000000 ); // L/R Analog Output Mixer enabled, headphone DC offset default
802+ _ac101I2cWrite (HPOUT_CTRL, 0b1111101111110001 ); // Headphone out from Analog Mixer stage, no reduction in volume
803+
804+ }
805+
806+ public:
807+ AC101Source (SRate_t sampleRate, int blockSize, float sampleScale = 1 .0f , bool i2sMaster=true ) :
808+ I2SSource (sampleRate, blockSize, sampleScale, i2sMaster) {
809+ _config.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT;
810+ };
811+
812+ void initialize (int8_t i2swsPin, int8_t i2ssdPin, int8_t i2sckPin, int8_t mclkPin) {
813+ DEBUGSR_PRINTLN (" AC101Source:: initialize();" );
814+
815+ // if ((i2sckPin < 0) || (mclkPin < 0)) { // WLEDMM not sure if this check is needed here, too
816+ // ERRORSR_PRINTF("\nAR: invalid I2S WM8978 pin: SCK=%d, MCLK=%d\n", i2sckPin, mclkPin);
817+ // return;
818+ // }
819+ // BUG: "use global I2C pins" are valid as -1, and -1 is seen as invalid here.
820+ // Workaround: Set I2C pins here, which will also set them globally.
821+ // Bug also exists in ES7243.
822+ if ((i2c_sda < 0 ) || (i2c_scl < 0 )) { // check that global I2C pins are not "undefined"
823+ ERRORSR_PRINTF (" \n AR: invalid AC101 global I2C pins: SDA=%d, SCL=%d\n " , i2c_sda, i2c_scl);
824+ return ;
825+ }
826+ if (!pinManager.joinWire (i2c_sda, i2c_scl)) { // WLEDMM specific: start I2C with globally defined pins
827+ ERRORSR_PRINTF (" \n AR: failed to join I2C bus with SDA=%d, SCL=%d\n " , i2c_sda, i2c_scl);
828+ return ;
829+ }
830+
831+ // First route mclk, then configure ADC over I2C, then configure I2S
832+ _ac101InitAdc ();
833+ I2SSource::initialize (i2swsPin, i2ssdPin, i2sckPin, mclkPin);
834+ }
835+
836+ void deinitialize () {
837+ I2SSource::deinitialize ();
838+ }
839+
840+ };
841+
739842#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0)
740843#if !defined(SOC_I2S_SUPPORTS_ADC) && !defined(SOC_I2S_SUPPORTS_ADC_DAC)
741844 #warning this MCU does not support analog sound input
0 commit comments