/* ES8311 - An ES8311 Codec driver library for Arduino This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . examples: //one I2C bus: (default behaviour) ES8311 es; es.begin(sda, scl); //two I2C busses: TwoWire i2cBusOne = TwoWire(0); TwoWire i2cBusTwo = TwoWire(1); ES8311 es(&i2cBusOne); i2cBusOne.begin(sda, scl, 400000); */ #include "es8311.h" /* codec hifi mclk clock divider coefficients */ static const struct _coeff_div coeff_div[] = { /*!end(); } } /* * look for the coefficient in coeff_div[] table */ int ES8311::get_coeff(uint32_t mclk, uint32_t rate){ for (int i = 0; i < (sizeof(coeff_div) / sizeof(coeff_div[0])); i++) { if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk) { return i; } } return -1; } bool ES8311::begin(int32_t sda, int32_t scl, uint32_t frequency) { bool ok = true; uint8_t reg = 0; if((sda >= 0) && (scl >= 0)){ ok = _TwoWireInstance->begin(sda, scl, frequency); _TwoWireInstance->beginTransmission(ES8311_ADDR); ok = (Wire.endTransmission() == 0); if(!ok) { _TwoWireInstance->end(); log_e("ES8311 not found"); return false; } } else { log_e("Invalid SDA/SCL pins"); return false; } ok |= WriteReg(0x00, 0x1F); // Reset vTaskDelay(20 / portTICK_PERIOD_MS); ok |= WriteReg(0x00, 0x00); // Release reset ok |= WriteReg(0x00, 0x80); // Power on ok |= WriteReg(0x01, 0x3F); // Enable all clocks reg = ReadReg(0x06); reg &= ~BIT(5); // SCLK (BCLK) pin not inverted ok |= WriteReg(0x06, reg); // ok |= setSampleRate(ES8311_SAMPLE_RATE48); // default ok |= setBitsPerSample(ES8311_BITS_PER_SAMPLE16); // default ok |= WriteReg(0x0D, 0x01); // Power up analog circuitry ok |= WriteReg(0x0E, 0x02); // Enable analog PGA, enable ADC modulator ok |= WriteReg(0x12, 0x00); // Power-up DAC ok |= WriteReg(0x13, 0x10); // Enable output to HP drive ok |= WriteReg(0x1C, 0x6A); // ADC Equalizer bypass, cancel DC offset in digital domain ok |= WriteReg(0x37, 0x08); // Bypass DAC equalizer return ok; } bool ES8311::setVolume(uint8_t volume){ // 0...100 if (volume > 100) {volume = 100;} int reg32; if (volume == 0) {reg32 = 0;} else { reg32 = ((volume) * 256 / 100) - 1;} return WriteReg(0x32, reg32); } uint8_t ES8311::getVolume(){ uint8_t reg32 = ReadReg(0x32); uint8_t volume; if (reg32 == 0) { volume = 0; } else { volume = ((reg32 * 100) / 256) + 1; } return volume; } bool ES8311::setSampleRate(uint32_t sample_rate){ uint8_t reg = 0; bool ok = true; _mclk_hz = sample_rate * 256; // default MCLK frequency if(sample_rate > 64000) _mclk_hz /= 2; int coeff = get_coeff(_mclk_hz, sample_rate); if (coeff < 0) {log_e("Invalid sample rate %i", sample_rate); return false;} const struct _coeff_div *const selected_coeff = &coeff_div[coeff]; reg = ReadReg(0x02); reg |= (selected_coeff->pre_div - 1) << 5; reg |= selected_coeff->pre_multi << 3; ok |= WriteReg(0x02, reg); // Set pre_div and pre_multi const uint8_t reg03 = (selected_coeff->fs_mode << 6) | selected_coeff->adc_osr; ok |= WriteReg(0x03, reg03); // Set fs_mode and adc_osr ok |= WriteReg(0x04, selected_coeff->dac_osr); // Set dac_osr const uint8_t reg05 = ((selected_coeff->adc_div - 1) << 4) | (selected_coeff->dac_div - 1); ok |= WriteReg(0x05, reg05); // Set adc_div and dac_div reg = ReadReg(0x06); reg &= 0xE0; if (selected_coeff->bclk_div < 19) {reg |= (selected_coeff->bclk_div - 1) << 0;} else { reg |= (selected_coeff->bclk_div) << 0;} ok |= WriteReg(0x06, reg); // Set bclk_div reg = ReadReg(0x07); reg &= 0xC0; reg |= selected_coeff->lrck_h << 0; ok |= WriteReg(0x07, reg); // Set lrck_h ok |= WriteReg(0x08, selected_coeff->lrck_l); // Set lrck_l return ok; } bool ES8311::setBitsPerSample(uint8_t bps){ uint8_t reg09 = ReadReg(0x09); uint8_t reg0A = ReadReg(0x0A); switch (bps) { case 16: reg09 |= (3 << 2); reg0A |= (3 << 2); break; case 18: reg09 |= (2 << 2); reg0A |= (2 << 2); break; case 20: reg09 |= (1 << 2); reg0A |= (1 << 2); break; case 24: reg09 |= (0 << 2); reg0A |= (0 << 2); break; case 32: reg09 |= (4 << 2); reg0A |= (4 << 2); break; default: return false; // Invalid bits per sample } bool ok = WriteReg(0x09, reg09); ok |= WriteReg(0x0A, reg0A); return ok; } bool ES8311::enableMicrophone(bool enable){ uint8_t reg = 0x1A; // enable analog MIC and max PGA gain if (enable) { reg |= BIT(6); } bool ok = WriteReg(0x17, 0xC8); // ADC_VOLUME ok |= WriteReg(0x14, reg); // Enable MIC return ok; } bool ES8311::setMicrophoneGain(uint8_t gain){ // 0...7 uint8_t reg = ReadReg(0x16); reg &= 0xF8; // Clear gain bits if (gain > 7) {gain = 7;} reg |= gain; // Set gain bits bool ok = WriteReg(0x16, gain); // ADC_VOLUME return ok; } uint8_t ES8311::getMicrophoneGain(){ uint8_t reg = ReadReg(0x16); return (reg & 0x07); // Get gain bits } bool ES8311::WriteReg(uint8_t reg, uint8_t val){ _TwoWireInstance->beginTransmission(ES8311_ADDR); _TwoWireInstance->write(reg); _TwoWireInstance->write(val); return _TwoWireInstance->endTransmission() == 0; } uint8_t ES8311::ReadReg(uint8_t reg){ _TwoWireInstance->beginTransmission(ES8311_ADDR); _TwoWireInstance->write(reg); _TwoWireInstance->endTransmission(false); uint8_t val = 0u; _TwoWireInstance->requestFrom(uint16_t(ES8311_ADDR), (uint8_t)1, true); if(_TwoWireInstance->available() >= 1){ val = _TwoWireInstance->read(); } _TwoWireInstance->endTransmission(); return val; } void ES8311::read_all(){ for (uint8_t i = 0; i < 0x4A; i++) { Serial.printf("0x%02X: 0x%02X\n", i, ReadReg(i)); } }