|
| 1 | +// SPDX-FileCopyrightText: 2023 Ladyada for Adafruit Industries |
| 2 | +// |
| 3 | +// SPDX-License-Identifier: MIT |
| 4 | + |
| 5 | +/* |
| 6 | + This example plays a 'raw' PCM file from memory to I2S |
| 7 | +*/ |
| 8 | + |
| 9 | +#include <Adafruit_TLV320DAC3100.h> |
| 10 | +#include <I2S.h> |
| 11 | +#include <math.h> |
| 12 | + |
| 13 | +#include "startup.h" // audio file in flash |
| 14 | + |
| 15 | +Adafruit_TLV320DAC3100 codec; // Create codec object |
| 16 | + |
| 17 | +// Create the I2S port using a PIO state machine |
| 18 | +I2S i2s(OUTPUT); |
| 19 | + |
| 20 | +// GPIO pin numbers on Feather RP2040 |
| 21 | +#define pBCLK D9 // BITCLOCK |
| 22 | +#define pWS D10 // LRCLOCK |
| 23 | +#define pDOUT D11 // DATA |
| 24 | + |
| 25 | +void setup() { |
| 26 | + Serial.begin(115200); |
| 27 | + while (!Serial) delay(10); |
| 28 | + Serial.println("I2S playback demo"); |
| 29 | +} |
| 30 | + |
| 31 | +void loop() { |
| 32 | +} |
| 33 | + |
| 34 | +void setup1() { |
| 35 | + Serial.begin(115200); |
| 36 | + |
| 37 | + while (!Serial) delay(10); |
| 38 | + Serial.println("\n\nTLV320DAC3100 Sine Tone Test"); |
| 39 | + // Start I2C communication with codec |
| 40 | + Serial.println("Initializing codec..."); |
| 41 | + if (!codec.begin()) { |
| 42 | + Serial.println("Failed to initialize codec!"); |
| 43 | + } |
| 44 | + codec.reset(); |
| 45 | + // Step 1: Set codec interface to I2S with 16-bit data |
| 46 | + Serial.println("Configuring codec interface..."); |
| 47 | + if (!codec.setCodecInterface(TLV320DAC3100_FORMAT_I2S, TLV320DAC3100_DATA_LEN_16)) { |
| 48 | + Serial.println("Failed to configure codec interface!"); |
| 49 | + } |
| 50 | + |
| 51 | + // Step 2: Configure clock - using PLL with BCLK as input |
| 52 | + Serial.println("Configuring codec clocks..."); |
| 53 | + if (!codec.setCodecClockInput(TLV320DAC3100_CODEC_CLKIN_PLL) || |
| 54 | + !codec.setPLLClockInput(TLV320DAC3100_PLL_CLKIN_BCLK)) { |
| 55 | + Serial.println("Failed to configure codec clocks!"); |
| 56 | + } |
| 57 | + |
| 58 | + // Step 3: Set up PLL - these values work well for 44.1kHz sample rate |
| 59 | + if (!codec.setPLLValues(1, 1, 8, 0)) { |
| 60 | + Serial.println("Failed to configure PLL values!"); |
| 61 | + } |
| 62 | + |
| 63 | + // Step 4: Configure DAC dividers |
| 64 | + if (!codec.setNDAC(true, 8) || |
| 65 | + !codec.setMDAC(true, 2) || |
| 66 | + !codec.setDOSR(128)) { |
| 67 | + Serial.println("Failed to configure DAC dividers!"); |
| 68 | + } |
| 69 | + |
| 70 | + // Step 5: Power up PLL |
| 71 | + if (!codec.powerPLL(true)) { |
| 72 | + Serial.println("Failed to power up PLL!"); |
| 73 | + } |
| 74 | + |
| 75 | + // Step 6: Configure DAC path - power up both left and right DACs |
| 76 | + Serial.println("Configuring DAC path..."); |
| 77 | + if (!codec.setDACDataPath(true, true, |
| 78 | + TLV320_DAC_PATH_NORMAL, |
| 79 | + TLV320_DAC_PATH_NORMAL, |
| 80 | + TLV320_VOLUME_STEP_1SAMPLE)) { |
| 81 | + Serial.println("Failed to configure DAC data path!"); |
| 82 | + } |
| 83 | + |
| 84 | + // Step 7: Route DAC output to headphone |
| 85 | + if (!codec.configureAnalogInputs(TLV320_DAC_ROUTE_MIXER, // Left DAC to mixer |
| 86 | + TLV320_DAC_ROUTE_MIXER, // Right DAC to mixer |
| 87 | + false, false, false, // No AIN routing |
| 88 | + false)) { // No HPL->HPR |
| 89 | + Serial.println("Failed to configure DAC routing!"); |
| 90 | + } |
| 91 | + |
| 92 | + // Step 8: Unmute DAC and set volume (higher for testing) |
| 93 | + Serial.println("Setting DAC volume..."); |
| 94 | + if (!codec.setDACVolumeControl( |
| 95 | + false, false, TLV320_VOL_INDEPENDENT) || // Unmute both channels |
| 96 | + !codec.setChannelVolume(false, 18) || // Left DAC +0dB |
| 97 | + !codec.setChannelVolume(true, 18)) { // Right DAC +0dB |
| 98 | + Serial.println("Failed to configure DAC volume control!"); |
| 99 | + } |
| 100 | + |
| 101 | + |
| 102 | + if (!codec.setChannelVolume(false, 12.0) || |
| 103 | + !codec.setChannelVolume(true, 12.0)) { |
| 104 | + Serial.println("Failed to set DAC channel volumes!"); |
| 105 | + } |
| 106 | + |
| 107 | + if (!codec.enableSpeaker(true) || // Dis/Enable speaker amp |
| 108 | + !codec.configureSPK_PGA(TLV320_SPK_GAIN_6DB, // Set gain to 6dB |
| 109 | + true) || // Unmute |
| 110 | + !codec.setSPKVolume(true, 0)) { // Enable and set volume to 0dB |
| 111 | + Serial.println("Failed to configure speaker output!"); |
| 112 | + } |
| 113 | + |
| 114 | + // Initialize I2S peripheral |
| 115 | + Serial.println("Initializing I2S..."); |
| 116 | + i2s.setBCLK(pBCLK); |
| 117 | + i2s.setDATA(pDOUT); |
| 118 | + i2s.setBitsPerSample(16); |
| 119 | + |
| 120 | +} |
| 121 | + |
| 122 | +void loop1() { |
| 123 | + // the main loop will tell us when it wants us to play! |
| 124 | + play_i2s(startupAudioData, sizeof(startupAudioData), startupSampleRate); |
| 125 | + delay(1000); |
| 126 | +} |
| 127 | + |
| 128 | +void play_i2s(const uint8_t *data, uint32_t len, uint32_t rate) { |
| 129 | + // start I2S at the sample rate with 16-bits per sample |
| 130 | + if (!i2s.begin(rate)) { |
| 131 | + Serial.println("Failed to initialize I2S!"); |
| 132 | + delay(500); |
| 133 | + i2s.end(); |
| 134 | + return; |
| 135 | + } |
| 136 | + |
| 137 | + for(uint32_t i=0; i<len; i++) { |
| 138 | + uint16_t sample = (uint16_t)data[i] << 6; // our data is 10 bit but we want 16 bit so we add some gain |
| 139 | + // write the same sample twice, once for left and once for the right channel |
| 140 | + i2s.write(sample); |
| 141 | + i2s.write(sample); |
| 142 | + } |
| 143 | + i2s.end(); |
| 144 | +} |
0 commit comments