├── .gitignore ├── LICENSE ├── PCM51xx.cpp ├── PCM51xx.h ├── README.md ├── examples └── ESP32_3_Wire_I2S │ └── ESP32_3_Wire_I2S.ino ├── library.json └── library.properties /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Tom Magnier 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /PCM51xx.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2018 Tom Magnier 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | #include "PCM51xx.h" 26 | 27 | PCM51xx::PCM51xx(TwoWire& wire, uint8_t i2cAddr): 28 | _wire(&wire), 29 | _i2cAddr(i2cAddr) 30 | { 31 | 32 | } 33 | 34 | 35 | bool PCM51xx::begin(BitDepth bps) 36 | { 37 | //Force a page counter sync between the local variable and the IC 38 | _currentPage = 0xFF; 39 | selectPage(RESET); 40 | 41 | // Set correct I2S config 42 | uint8_t config = 0; 43 | switch (bps) { 44 | case BITS_PER_SAMPLE_16: config = 0x00; break; 45 | case BITS_PER_SAMPLE_24: config = 0x02; break; 46 | case BITS_PER_SAMPLE_32: config = 0x03; break; 47 | }; 48 | writeRegister(I2S_FORMAT, config); 49 | 50 | delay(10); //Wait for calibration, startup, etc 51 | 52 | return getPowerState() == POWER_STATE_RUN; 53 | } 54 | 55 | bool PCM51xx::begin(SamplingRate rate, BitDepth bps) 56 | { 57 | //See here : https://e2e.ti.com/support/data_converters/audio_converters/f/64/t/428281 58 | // for a config example 59 | 60 | // Check that the bit clock (PLL input) is between 1MHz and 50MHz 61 | uint32_t bckFreq = rate * bps * 2; 62 | if (bckFreq < 1000000 || bckFreq > 50000000) 63 | return false; 64 | 65 | // 24 bits is not supported for 44.1kHz and 48kHz. 66 | if ((rate == SAMPLE_RATE_44_1K || rate == SAMPLE_RATE_48K) && bps == BITS_PER_SAMPLE_24) 67 | return false; 68 | 69 | //Force a page counter sync between the local variable and the IC 70 | _currentPage = 0xFF; 71 | 72 | //Initialize system clock from the I2S BCK input 73 | writeRegister(IGNORE_ERRORS, 0x1A); // Disable clock autoset and ignore SCK detection 74 | writeRegister(PLL_CLOCK_SOURCE, 0x10); // Set PLL clock source to BCK 75 | writeRegister(DAC_CLOCK_SOURCE, 0x10); // Set DAC clock source to PLL output 76 | 77 | //PLL configuration 78 | int p, j, d, r; 79 | 80 | //Clock dividers 81 | int nmac, ndac, ncp, dosr, idac; 82 | 83 | if (rate == SAMPLE_RATE_11_025K || rate == SAMPLE_RATE_22_05K || rate == SAMPLE_RATE_44_1K) 84 | { 85 | //44.1kHz and derivatives. 86 | //P = 1, R = 2, D = 0 for all supported combinations. 87 | //Set J to have PLL clk = 90.3168 MHz 88 | p = 1; 89 | r = 2; 90 | j = 90316800 / bckFreq / r; 91 | d = 0; 92 | 93 | //Derive clocks from the 90.3168MHz PLL 94 | nmac = 2; 95 | ndac = 16; 96 | ncp = 4; 97 | dosr = 8; 98 | idac = 1024; // DSP clock / sample rate 99 | } 100 | else 101 | { 102 | //8kHz and multiples. 103 | //PLL config for a 98.304 MHz PLL clk 104 | if (bps == BITS_PER_SAMPLE_24 && bckFreq > 1536000) 105 | p = 3; 106 | else if (bckFreq > 12288000) 107 | p = 2; 108 | else 109 | p = 1; 110 | 111 | r = 2; 112 | j = 98304000 / (bckFreq / p) / r; 113 | d = 0; 114 | 115 | //Derive clocks from the 98.304MHz PLL 116 | switch (rate) { 117 | case SAMPLE_RATE_16K: nmac = 6; break; 118 | case SAMPLE_RATE_32K: nmac = 3; break; 119 | default: nmac = 2; break; 120 | }; 121 | 122 | ndac = 16; 123 | ncp = 4; 124 | dosr = 384000 / rate; 125 | idac = 98304000 / nmac / rate; // DSP clock / sample rate 126 | } 127 | 128 | 129 | // Configure PLL 130 | writeRegister(PLL_P, p - 1); 131 | writeRegister(PLL_J, j); 132 | writeRegister(PLL_D_MSB, (d >> 8) & 0x3F); 133 | writeRegister(PLL_D_LSB, d & 0xFF); 134 | writeRegister(PLL_R, r - 1); 135 | 136 | // Clock dividers 137 | writeRegister(DSP_CLOCK_DIV, nmac - 1); 138 | writeRegister(DAC_CLOCK_DIV, ndac - 1); 139 | writeRegister(NCP_CLOCK_DIV, ncp - 1); 140 | writeRegister(OSR_CLOCK_DIV, dosr - 1); 141 | 142 | // IDAC (nb of DSP clock cycles per sample) 143 | writeRegister(IDAC_MSB, (idac >> 8) & 0xFF); 144 | writeRegister(IDAC_LSB, idac & 0xFF); 145 | 146 | // FS speed mode 147 | int speedMode; 148 | if (rate <= SAMPLE_RATE_48K) 149 | speedMode = 0; 150 | else if (rate <= SAMPLE_RATE_96K) 151 | speedMode = 1; 152 | else if (rate <= SAMPLE_RATE_192K) 153 | speedMode = 2; 154 | else 155 | speedMode = 3; 156 | writeRegister(FS_SPEED_MODE, speedMode); 157 | 158 | return begin(bps); 159 | } 160 | 161 | void PCM51xx::setPowerMode(PowerMode mode) 162 | { 163 | writeRegister(STANDBY_POWERDOWN, mode); 164 | } 165 | 166 | void PCM51xx::reset() 167 | { 168 | setPowerMode(POWER_MODE_STANDBY); 169 | writeRegister(RESET, 0x11); 170 | setPowerMode(POWER_MODE_ACTIVE); 171 | } 172 | 173 | PCM51xx::PowerState PCM51xx::getPowerState() 174 | { 175 | uint8_t regValue = readRegister(DSP_BOOT_POWER_STATE); 176 | 177 | return (PowerState)(regValue & 0x0F); 178 | } 179 | 180 | void PCM51xx::setVolume(uint8_t vol) 181 | { 182 | writeRegister(DIGITAL_VOLUME_L, vol); 183 | writeRegister(DIGITAL_VOLUME_R, vol); 184 | } 185 | 186 | void PCM51xx::writeRegister(Register address, uint8_t data) 187 | { 188 | selectPage(address); 189 | 190 | _wire->beginTransmission(_i2cAddr); 191 | _wire->write(address); 192 | _wire->write(data); 193 | _wire->endTransmission(); 194 | } 195 | 196 | void PCM51xx::writeRegisters(Register startAddress, uint8_t *data, uint8_t len) 197 | { 198 | selectPage(startAddress); 199 | 200 | _wire->beginTransmission(_i2cAddr); 201 | _wire->write((uint8_t)startAddress | REG_AUTO_INCREMENT_EN); 202 | for (int i = 0; i < len; i++) 203 | _wire->write(data[i]); 204 | _wire->endTransmission(); 205 | } 206 | 207 | uint8_t PCM51xx::readRegister(Register address) 208 | { 209 | selectPage(address); 210 | 211 | _wire->beginTransmission(_i2cAddr); 212 | _wire->write(address); 213 | _wire->endTransmission(); 214 | 215 | _wire->requestFrom(_i2cAddr, 1); 216 | return _wire->read(); 217 | } 218 | 219 | uint8_t PCM51xx::readRegisters(Register startAddress, uint8_t *buffer, uint8_t len) 220 | { 221 | selectPage(startAddress); 222 | 223 | _wire->beginTransmission(_i2cAddr); 224 | _wire->write((uint8_t)startAddress | REG_AUTO_INCREMENT_EN); 225 | _wire->endTransmission(); 226 | 227 | _wire->requestFrom(_i2cAddr, len); 228 | return _wire->readBytes(buffer, len); 229 | } 230 | 231 | void PCM51xx::selectPage(Register address) 232 | { 233 | uint8_t page = (address >> 8) & 0xFF; 234 | 235 | if (page != _currentPage) 236 | { 237 | _wire->beginTransmission(_i2cAddr); 238 | _wire->write(0); 239 | _wire->write(page); 240 | _wire->endTransmission(); 241 | 242 | _currentPage = page; 243 | } 244 | } 245 | -------------------------------------------------------------------------------- /PCM51xx.h: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2018 Tom Magnier 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | #ifndef PCM51XX_H 26 | #define PCM51XX_H 27 | 28 | #include 29 | #include 30 | 31 | class PCM51xx { 32 | public: 33 | /* Supported sampling rates */ 34 | enum SamplingRate { 35 | SAMPLE_RATE_8K = 8000, 36 | SAMPLE_RATE_11_025K = 11025, 37 | SAMPLE_RATE_16K = 16000, 38 | SAMPLE_RATE_22_05K = 22050, 39 | SAMPLE_RATE_32K = 32000, 40 | SAMPLE_RATE_44_1K = 44100, 41 | SAMPLE_RATE_48K = 48000, 42 | SAMPLE_RATE_96K = 96000, 43 | SAMPLE_RATE_192K = 192000, 44 | SAMPLE_RATE_384K = 384000 45 | }; 46 | 47 | /* Supported bit depth (bits per sample) */ 48 | enum BitDepth { 49 | BITS_PER_SAMPLE_16 = 16, 50 | BITS_PER_SAMPLE_24 = 24, 51 | BITS_PER_SAMPLE_32 = 32, 52 | }; 53 | 54 | /* Power modes */ 55 | enum PowerMode { 56 | POWER_MODE_ACTIVE = 0x00, 57 | POWER_MODE_STANDBY = 0x10, 58 | POWER_MODE_POWERDOWN = 0x01 59 | }; 60 | 61 | /* Power states */ 62 | enum PowerState { 63 | POWER_STATE_POWERDOWN = 0x00, 64 | POWER_STATE_WAIT_FOR_CP = 0x01, 65 | POWER_STATE_CALIB_1 = 0x02, 66 | POWER_STATE_CALIB_2 = 0x03, 67 | POWER_STATE_VOL_RAMP_UP = 0x04, 68 | POWER_STATE_RUN = 0x05, 69 | POWER_STATE_LINE_SHORT = 0x06, 70 | POWER_STATE_VOL_RAMP_DN = 0x07, 71 | POWER_STATE_STANDBY = 0x08, 72 | POWER_STATE_I2C_FAILURE = 0x0F 73 | }; 74 | 75 | /* Register addresses. Set as 16-bit values (the high byte sets the page number) */ 76 | enum Register { 77 | /* Page 0 */ 78 | RESET = 0x0000 + 1, //Reset 79 | STANDBY_POWERDOWN = 0x0000 + 2, //Standby, Powerdown requests 80 | MUTE = 0x0000 + 3, //Mute 81 | PLL_LOCK_PLL_ENABLE = 0x0000 + 4, //PLL Lock Flag, PLL enable 82 | SPI_MISO_FUNCTION = 0x0000 + 6, //SPI MISO function select 83 | DEEMP_EN_SDOUT_SEL = 0x0000 + 7, //De-emphasis enable, SDOUT select 84 | GPIO_EN = 0x0000 + 8, //GPIO enables 85 | BCK_LRCK_CONFIG = 0x0000 + 9, //BCK, LRCLK configuration 86 | DSG_GPIO_INPUT = 0x0000 + 10, //DSP GPIO Input 87 | MASTER_BCK_LRCK_RESET = 0x0000 + 12, //Master mode BCK, LRCLK reset 88 | PLL_CLOCK_SOURCE = 0x0000 + 13, //PLL Clock source selection 89 | DAC_CLOCK_SOURCE = 0x0000 + 14, //DAC clock source selection 90 | GPIO_SRC_FOR_PLL = 0x0000 + 18, //GPIO source for PLL reference 91 | CLOCK_SYNC_REQUEST = 0x0000 + 19, //C 92 | PLL_P = 0x0000 + 20, //PLL divider P factor 93 | PLL_J = 0x0000 + 21, //PLL J part of the overall PLL multiplication factor J.D * R 94 | PLL_D_MSB = 0x0000 + 22, //PLL D part (MSB) of the overall PLL multiplication factor J.D * R 95 | PLL_D_LSB = 0x0000 + 23, //PLL D part (LSB) of the overall PLL multiplication factor J.D * R 96 | PLL_R = 0x0000 + 24, //PLL R part of the overall PLL multiplication factor J.D * R 97 | DSP_CLOCK_DIV = 0x0000 + 27, //DSP clock divider 98 | DAC_CLOCK_DIV = 0x0000 + 28, //DAC clock divider 99 | NCP_CLOCK_DIV = 0x0000 + 29, //CP clock divider 100 | OSR_CLOCK_DIV = 0x0000 + 30, //OSR clock divider 101 | MASTER_BCK_DIV = 0x0000 + 32, //Master mode BCK divider 102 | MASTER_LRCK_DIV = 0x0000 + 33, //Master mode LRCK divider 103 | FS_SPEED_MODE = 0x0000 + 34, //16x interpolation, FS speed mode 104 | IDAC_MSB = 0x0000 + 35, //IDAC MSB (number of DSP clock cycles available in one audio frame) 105 | IDAC_LSB = 0x0000 + 36, //IDAC LSB (number of DSP clock cycles available in one audio frame) 106 | IGNORE_ERRORS = 0x0000 + 37, //Ignore various errors 107 | I2S_FORMAT = 0x0000 + 40, //I2S data format, word length 108 | I2S_SHIFT = 0x0000 + 41, //I2S shift 109 | DAC_DATA_PATH = 0x0000 + 42, //DAC data path 110 | DSP_PROGRAM_SEL = 0x0000 + 43, //DSP program selection 111 | CLK_MISSING_DETECTION = 0x0000 + 44, //Clock missing detection period 112 | AUTO_MUTE_TIME = 0x0000 + 59, //Auto mute time 113 | DIGITAL_VOLUME_CTRL = 0x0000 + 60, //Digital volume control 114 | DIGITAL_VOLUME_L = 0x0000 + 61, //Digital volume for left channel 115 | DIGITAL_VOLUME_R = 0x0000 + 62, //Digital volume for right channel 116 | DIGITAL_VOLUME_RAMP = 0x0000 + 63, //Digital volume ramp up/down behavior 117 | DIGITAL_VOLUME_EMERG = 0x0000 + 64, //Digital volume emergency ramp down behavior 118 | AUTO_MUTE = 0x0000 + 65, //Auto mute 119 | GPIOn_OUTPUT_SEL = 0x0000 + 80, //GPIOn output selection. GPIO1 - GPIO6 are available (80 - 85) 120 | GPIO_OUTPUT_CTRL = 0x0000 + 86, //GPIO output value 121 | GPIO_OUTPUT_INVERT = 0x0000 + 87, //GPIO output inversion 122 | DSP_OVERFLOW = 0x0000 + 90, //DSP overflow status 123 | DET_FS_SCK_RATIO = 0x0000 + 91, //Detected FS and SCK ratio 124 | DET_BCK_RATIO_MSB = 0x0000 + 92, //Detected BCK ratio (MSB) 125 | DET_BCK_RATIO_LSB = 0x0000 + 93, //Detected BCK ratio (LSB) 126 | CLOCK_STATUS = 0x0000 + 94, //Various clock and sample rate status 127 | CLOCK_ERROR = 0x0000 + 95, //Critical clock failures reporting 128 | ANALOG_MUTE_MON = 0x0000 + 108, //Analog mute monitor 129 | SHORT_DETECT_MON = 0x0000 + 109, //Line output short monitor 130 | XSMUTE_MON = 0x0000 + 114, //XSMUTE pin level monitor 131 | FS_SPEED_MODE_MON = 0x0000 + 115, //FS speed mode monitor 132 | DSP_BOOT_POWER_STATE = 0x0000 + 118, //DSP boot done flag & power state 133 | GPIO_INPUT = 0x0000 + 119, //GPIO input 134 | AUTO_MUTE_FLAGS = 0x0000 + 120, //Auto Mute flags 135 | 136 | /* Page 1 */ 137 | OUTPUT_AMPL_TYPE = 0x0100 + 1, //Output amplitude type 138 | ANALOG_GAIN_CTRL = 0x0100 + 2, //Analog gain control 139 | UV_PROTECTION = 0x0100 + 5, //Undervoltage protection 140 | ANALOG_MUTE_CTRL = 0x0100 + 6, //Analog mute control 141 | ANALOG_GAIN_BOOST = 0x0100 + 7, //Analog gain boost 142 | VCOM_RAMP_SPEED = 0x0100 + 8, //VCOM ref ramp up speed 143 | VCOM_POWERDOWN = 0x0100 + 9, //VCOM powerdown control 144 | 145 | /* Page 44 */ 146 | CRAM_CONTROL = 0x2C00 + 1, //Coefficient memory (CRAM) control 147 | 148 | /* Page 253 */ 149 | CLOCK_FLEX_1 = 0xFD00 + 63, //Clock flex register #1 150 | CLOCK_FLEX_2 = 0xFD00 + 64, //Clock flex register #2 151 | }; 152 | 153 | /* Constructor 154 | * Parameters : 155 | * * The TwoWire class to use which must be initialized externally 156 | * (i.e. call Wire.begin() before begin()) 157 | * * the I2C address to use (0x4C to 0x4F depending on the ADR1, ADR2 pins) 158 | */ 159 | PCM51xx(TwoWire& wire = Wire, uint8_t i2cAddr = 0x4C); 160 | 161 | /* Start operation in 4-Wire I2S mode (SCK supplied to the IC) 162 | * Parameters : 163 | * * Bits per sample (BITS_PER_SAMPLE_16 to BITS_PER_SAMPLE_32) 164 | * 165 | * Return : true if startup succeeded, false otherwise (I2C communication problem, 166 | * clock configuration not supported, etc) 167 | */ 168 | bool begin(BitDepth bps); 169 | 170 | /* Start operation in 3-Wire PCM mode (SCK derived from BCK clock) 171 | * Parameters : 172 | * * Sampling rate (SAMPLE_RATE_8K to SAMPLE_RATE_384K) 173 | * * Bits per sample (BITS_PER_SAMPLE_16 to BITS_PER_SAMPLE_32) 174 | * 175 | * 2 channels are assumed. 176 | * Not all combinations are supported : for proper PLL operation, the BCK clock 177 | * (sampling rate * bits per sample * nb of channels) must be at least 1MHz and 178 | * must not exceed 50MHz. 179 | * At 44.1kHz and 48kHz only 16 and 32 bits are supported. 180 | * 181 | * Recommended minimum parameters : 32kHz, 16bit or 16kHz, 32bit 182 | * 183 | * See PM5141 datasheet table 132 for the full list of supported combinations 184 | * (fs = sampling rate, RSCK = 2*bit depth) 185 | * 186 | * Return : true if startup succeeded, false otherwise (I2C communication problem, 187 | * clock configuration not supported, etc) 188 | */ 189 | bool begin(SamplingRate rate, BitDepth bps); 190 | 191 | /* Set power mode : active, standby, powerdown */ 192 | void setPowerMode(PowerMode mode); 193 | 194 | /* Reset internal modules and registers */ 195 | void reset(); 196 | 197 | /* Get current power state */ 198 | PowerState getPowerState(); 199 | 200 | /* Set volume for left and right channels. 201 | * 0 : loudest (+24dB) 202 | * 1 : +23.5dB 203 | * ... 204 | * 254 : most quiet (-103dB) 205 | * 255 : mute 206 | */ 207 | void setVolume(uint8_t vol); 208 | 209 | //TODO mute control 210 | //TODO DSP control (program selection, set coefficients) 211 | 212 | /* Write a single register */ 213 | void writeRegister(Register address, uint8_t data); 214 | 215 | /* Write multiple contiguous registers */ 216 | void writeRegisters(Register startAddress, uint8_t *data, uint8_t len); 217 | 218 | /* Read a single register */ 219 | uint8_t readRegister(Register address); 220 | 221 | /* Read multiple contiguous registers and return the number of bytes read */ 222 | uint8_t readRegisters(Register startAddress, uint8_t *buffer, uint8_t len); 223 | 224 | /* Get configured I2C address */ 225 | uint8_t getI2CAddr() { return _i2cAddr; } 226 | 227 | private: 228 | static constexpr uint8_t REG_AUTO_INCREMENT_EN = 0x80; //Auto-increment mode for multiple writes / reads 229 | 230 | TwoWire *_wire; 231 | uint8_t _i2cAddr; 232 | uint8_t _currentPage = 0; 233 | 234 | /* Called when reading/writing a register to select the correct page */ 235 | void selectPage(Register address); 236 | }; 237 | 238 | #endif //PCM51XX_H 239 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PCM51xx_Arduino 2 | Arduino library for TI PCM51xx DAC ICs software configuration via I2C. 3 | 4 | The following ICs are compatible and supported : 5 | * PCM5121 / PCM5122 6 | * PCM5141 / PCM5142 7 | 8 | The PCM51x2 devices have a better SNR than the PCM51x1. 9 | 10 | The PCM514x have a full featured DSP whereas the PCM512x only supports a fixed processing flow. Otherwise they are identical. 11 | 12 | The PCM51xx can be used in hardware or software configuration mode. To set software configuration mode, set the pins MODE1 and MODE2 to select I2C / SPI mode. This library only supports I2C mode at the moment. 13 | 14 | Four different I2C addresses can be selected using the ADR1 and ADR2 pins (0x4C to 0x4F). The library defaults to 0x4C (ADR1 and ADR2 low). 15 | 16 | ## Usage 17 | See the examples. 18 | 19 | The Wire library must be initialized before calling `PCM51xx::begin()`. 20 | 21 | If 3-wire I2S is used (i.e. the DAC system clock is derived from the I2S bit clock), the I2S source should be started before configuring the clock. 22 | -------------------------------------------------------------------------------- /examples/ESP32_3_Wire_I2S/ESP32_3_Wire_I2S.ino: -------------------------------------------------------------------------------- 1 | /* PCM51xx library example, using the ESP8266Audio "PlayRTTTLToI2SDAC" example 2 | * 3 | * Tested on an ESP32 board. 4 | * 5 | * Don't forget to set the correct pinout for the I2S and I2C peripherals for 6 | * your hardware. 7 | * 8 | * You have to change the rate to 44100 in AudioGeneratorRTTTL.cpp to output 9 | * the correct bit clock. 10 | */ 11 | 12 | //ESP8266Audio includes 13 | #include "AudioFileSourcePROGMEM.h" 14 | #include "AudioGeneratorRTTTL.h" 15 | #include "AudioOutputI2S.h" 16 | 17 | #include "PCM51xx.h" 18 | #include "Wire.h" 19 | 20 | const char rudolph[] PROGMEM = 21 | "Rudolph the Red Nosed Raindeer:d=8,o=5,b=250:g,4a,g,4e,4c6,4a,2g.,g,a,g,a,4g,4c6,2b.,4p,f,4g,f,4d,4b,4a,2g.,g,a,g,a,4g,4a,2e.,4p,g,4a,a,4e,4c6,4a,2g.,g,a,g,a,4g,4c6,2b.,4p,f,4g,f,4d,4b,4a,2g.,g,a,g,a,4g,4d6,2c.6,4p,4a,4a,4c6,4a,4g,4e,2g,4d,4e,4g,4a,4b,4b,2b,4c6,4c6,4b,4a,4g,4f,2d,g,4a,g,4e,4c6,4a,2g.,g,a,g,a,4g,4c6,2b.,4p,f,4g,f,4d,4b,4a,2g.,4g,4a,4g,4a,2g,2d6,1c.6."; 22 | // Plenty more at: http://mines.lumpylumpy.com/Electronics/Computers/Software/Cpp/MFC/RingTones.RTTTL 23 | 24 | AudioGeneratorRTTTL *rtttl; 25 | AudioFileSourcePROGMEM *file; 26 | AudioOutputI2S *out; 27 | 28 | PCM51xx pcm(Wire); //Using the default I2C address 0x74 29 | 30 | void setup() 31 | { 32 | Serial.begin(115200); 33 | delay(1000); 34 | 35 | out = new AudioOutputI2S(); 36 | out->SetPinout(25, 27, 26); //HW dependent ! BCK, LRCK, DATA 37 | out->SetGain(0.1); 38 | 39 | Wire.begin(2, 4); //HW dependent ! SDA, SCL 40 | 41 | //!\ the AudioGeneratorRTTTL must be set to 44.1kHz, 16 bits. 42 | // You have to change the rate to 44100 in AudioGeneratorRTTTL.cpp 43 | if (pcm.begin(PCM51xx::SAMPLE_RATE_44_1K, PCM51xx::BITS_PER_SAMPLE_16)) 44 | Serial.println("PCM51xx initialized successfully."); 45 | else 46 | { 47 | Serial.println("Failed to initialize PCM51xx."); 48 | uint8_t powerState = pcm.getPowerState(); 49 | if (powerState == PCM51xx::POWER_STATE_I2C_FAILURE) 50 | { 51 | Serial.print("No answer on I2C bus at address "); 52 | Serial.println(pcm.getI2CAddr()); 53 | } 54 | else 55 | { 56 | Serial.print("Power state : "); 57 | Serial.println(pcm.getPowerState()); 58 | Serial.println("Check that the sample rate / bit depth combination is supported."); 59 | } 60 | } 61 | 62 | //Set volume 63 | pcm.setVolume(127); 64 | 65 | Serial.println("RTTTL start"); 66 | file = new AudioFileSourcePROGMEM( rudolph, strlen_P(rudolph) ); 67 | 68 | rtttl = new AudioGeneratorRTTTL(); 69 | rtttl->begin(file, out); 70 | } 71 | 72 | void loop() 73 | { 74 | if (rtttl->isRunning()) { 75 | if (!rtttl->loop()) rtttl->stop(); 76 | } else { 77 | Serial.println("RTTTL done"); 78 | pcm.setPowerMode(PCM51xx::POWER_MODE_STANDBY); 79 | delay(1000); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "PCM51xx", 3 | "keywords": "dac, pcm5121, pcm5122, pcm5141, pcm5142, audio, i2s", 4 | "description": "Arduino library for TI PCM51xx DAC ICs software configuration via I2C.", 5 | "repository": 6 | { 7 | "type": "git", 8 | "url": "https://github.com/tommag/PCM51xx_Arduino" 9 | }, 10 | "authors": 11 | [ 12 | { 13 | "name": "Tom Magnier", 14 | "email": "tom@tmagnier.fr", 15 | "maintainer": true 16 | } 17 | ], 18 | "dependencies": 19 | { 20 | "name": "Wire", 21 | "frameworks": "arduino" 22 | }, 23 | "version": "1.1", 24 | "frameworks": "arduino", 25 | "platforms": "*" 26 | } -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=PCM51xx DAC 2 | version=1.1 3 | author=Tom Magnier 4 | maintainer=Tom Magnier 5 | sentence=Arduino library for TI PCM51xx DAC ICs software configuration via I2C. 6 | paragraph= 7 | category=Uncategorized 8 | url=https://github.com/tommag/PCM51xx_Arduino 9 | architectures=* 10 | --------------------------------------------------------------------------------