Skip to content

Commit 4538d24

Browse files
authored
Merge pull request #144 from troyhacks/AC101-Support
AC101 audio support
2 parents 60fbf07 + 1c33a4b commit 4538d24

2 files changed

Lines changed: 121 additions & 0 deletions

File tree

usermods/audioreactive/audio_reactive.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2010,6 +2010,19 @@ class AudioReactive : public Usermod {
20102010
if (i2c_sda >= 0) sdaPin = -1; // -1 = use global
20112011
if (i2c_scl >= 0) sclPin = -1;
20122012

2013+
if (audioSource) audioSource->initialize(i2swsPin, i2ssdPin, i2sckPin, mclkPin);
2014+
break;
2015+
case 8:
2016+
DEBUGSR_PRINTLN(F("AR: AC101 Source (Line-In)"));
2017+
audioSource = new AC101Source(SAMPLE_RATE, BLOCK_SIZE, 1.0f);
2018+
//useInputFilter = 0; // to disable low-cut software filtering and restore previous behaviour
2019+
delay(100);
2020+
// WLEDMM align global pins
2021+
if ((sdaPin >= 0) && (i2c_sda < 0)) i2c_sda = sdaPin; // copy usermod prefs into globals (if globals not defined)
2022+
if ((sclPin >= 0) && (i2c_scl < 0)) i2c_scl = sclPin;
2023+
if (i2c_sda >= 0) sdaPin = -1; // -1 = use global
2024+
if (i2c_scl >= 0) sclPin = -1;
2025+
20132026
if (audioSource) audioSource->initialize(i2swsPin, i2ssdPin, i2sckPin, mclkPin);
20142027
break;
20152028

@@ -2911,6 +2924,11 @@ class AudioReactive : public Usermod {
29112924
#else
29122925
oappend(SET_F("addOption(dd,'WM8978 ☾',7);"));
29132926
#endif
2927+
#if SR_DMTYPE==8
2928+
oappend(SET_F("addOption(dd,'AC101 ☾ (⎌)',8);"));
2929+
#else
2930+
oappend(SET_F("addOption(dd,'AC101 ☾',8);"));
2931+
#endif
29142932
#ifdef SR_SQUELCH
29152933
oappend(SET_F("addInfo(ux+':config:squelch',1,'<i>&#9100; ")); oappendi(SR_SQUELCH); oappend("</i>');"); // 0 is field type, 1 is actual field
29162934
#endif

usermods/audioreactive/audio_source.h

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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("\nAR: 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("\nAR: 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

Comments
 (0)