Hi everyone,
TL;DR: Built a modular Teensy-based audio dev board using the TAC5212 codec. Getting sound, but clocks aren't locking. Looking for help debugging TDM slave clock setup.
I'm excited to share the first prototype of my open modular digital audio platform: T-DSP. The project is aimed at musicians, tinkerers, and audio professionals who want powerful, hackable audio tools. The T-DSP TAC5212 Pro Audio Module will be available commercially and is not released as open hardware. Reference hardware designs, such as the backplanes, are released under Creative Commons BY-NC-SA 4.0. Software will be open-source and released under the MIT License. The project will be available as both DIY modules and ready-to-use gear through t-dsp.com (coming soon!).
This post is specifically about the T-DSP TAC5212 Pro Audio Module, which is at the heart of my modular design strategy. The module can be used by itself or in multiples, and it can be daisy-chained through a custom backplane or 20-pin IDE cable. This modular strategy makes designing audio backplanes easy—enabling all sorts of opportunities for custom audio systems.
The first backplane, which I’m calling my "desktop soundcard" backplane, features a Teensy 4.1, ESP32 DevKitC, and a T-DSP TAC5212 Pro Audio Module, LEDs, a rotary encoder, a headphone output, a headphone input, onboard microphones, and other features I’ll talk more about later.
For now, I’m reaching out for help getting the clocks to sync.
I’m able to get audio out of the headphone jack using a Teensy 4.1 and a minimal sketch (shared below). That gives me confidence that the hardware and signal path are mostly correct.
The sound has some crackling, and the codec clock doesn't seem to be locking properly. I’m still tweaking the BCLK, MCLK, and frame sync settings and would greatly appreciate help verifying the correct clock configuration for the TAC5212 when it's set up as a TDM slave.
I’ve read through the TAC5212 datasheet, but the clocking diagrams are a bit difficult to decipher at first glance, and I’m not sure how they compare to how the Teensy generally handles things. I don’t know if I’m missing something obvious in my Teensy setup or if the codec needs a different initialization sequence, clock polarity, RX Offset, or some other configuration.
Here’s the minimal Teensy code I’m using right now. This setup allows two sine waves to play through the headphones, but the output is sloppy and crackly. Also, I wind up getting Clock Status (0x13): 0x0 | Clocks not locked errors. I’d love feedback if you notice anything off or missing.
Here is the result.
If you’ve worked with TDM audio on Teensy or similar codecs, I’d really value your insight on what to try next—especially around clock settings and initialization. While I’ve been working on audio and embedded projects for years, I’m still early in this particular journey and have big goals for where I’d like to take it. If you're into Teensy, TDM, modular audio, or just love to tinker—let’s connect.
I’ll be sharing more details soon, including the full backplane schematic, expansion plans, and some of the other modules I’m working on.
Thanks in advance for your help!
Best,
Jay Shoemaker
t-dsp.com
TL;DR: Built a modular Teensy-based audio dev board using the TAC5212 codec. Getting sound, but clocks aren't locking. Looking for help debugging TDM slave clock setup.
I'm excited to share the first prototype of my open modular digital audio platform: T-DSP. The project is aimed at musicians, tinkerers, and audio professionals who want powerful, hackable audio tools. The T-DSP TAC5212 Pro Audio Module will be available commercially and is not released as open hardware. Reference hardware designs, such as the backplanes, are released under Creative Commons BY-NC-SA 4.0. Software will be open-source and released under the MIT License. The project will be available as both DIY modules and ready-to-use gear through t-dsp.com (coming soon!).
This post is specifically about the T-DSP TAC5212 Pro Audio Module, which is at the heart of my modular design strategy. The module can be used by itself or in multiples, and it can be daisy-chained through a custom backplane or 20-pin IDE cable. This modular strategy makes designing audio backplanes easy—enabling all sorts of opportunities for custom audio systems.
The first backplane, which I’m calling my "desktop soundcard" backplane, features a Teensy 4.1, ESP32 DevKitC, and a T-DSP TAC5212 Pro Audio Module, LEDs, a rotary encoder, a headphone output, a headphone input, onboard microphones, and other features I’ll talk more about later.
For now, I’m reaching out for help getting the clocks to sync.
What Works:
I’m able to get audio out of the headphone jack using a Teensy 4.1 and a minimal sketch (shared below). That gives me confidence that the hardware and signal path are mostly correct.
What’s Not Working:
The sound has some crackling, and the codec clock doesn't seem to be locking properly. I’m still tweaking the BCLK, MCLK, and frame sync settings and would greatly appreciate help verifying the correct clock configuration for the TAC5212 when it's set up as a TDM slave.I’ve read through the TAC5212 datasheet, but the clocking diagrams are a bit difficult to decipher at first glance, and I’m not sure how they compare to how the Teensy generally handles things. I don’t know if I’m missing something obvious in my Teensy setup or if the codec needs a different initialization sequence, clock polarity, RX Offset, or some other configuration.
Photo of the setup:
Code
Here’s the minimal Teensy code I’m using right now. This setup allows two sine waves to play through the headphones, but the output is sloppy and crackly. Also, I wind up getting Clock Status (0x13): 0x0 | Clocks not locked errors. I’d love feedback if you notice anything off or missing.
C++:
#include <Arduino.h>
#include <Wire.h>
#include <Audio.h>
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>
#define TAC5212_ADDR 0x50
AudioSynthWaveformSine sine1;
AudioSynthWaveformSine sine2;
AudioOutputTDM tdmOut;
AudioInputUSB usb1; //xy=109.33333969116211,45.000003814697266
AudioConnection patchCord1(sine1, 0, tdmOut, 0);
//AudioConnection patchCord2(sine1, 0, tdmOut, 1);
AudioConnection patchCord3(sine2, 0, tdmOut, 2);
//AudioConnection patchCord4(sine1, 0, tdmOut, 3);
// AudioConnection patchCord5(sine2, 0, tdmOut, 4);
//AudioConnection patchCord6(sine1, 0, tdmOut, 5);
// AudioConnection patchCord7(sine2, 0, tdmOut, 6);
//AudioConnection patchCord8(sine1, 0, tdmOut, 7);
// AudioConnection patchCord9(sine2, 0, tdmOut, 8);
//AudioConnection patchCord2(sine2, 0, tdmOut, 0);
AudioControlSGTL5000 dummyControl;
void writeReg(uint8_t reg, uint8_t val) {
Wire.beginTransmission(TAC5212_ADDR);
Wire.write(reg);
Wire.write(val);
Wire.endTransmission();
}
uint8_t readReg(uint8_t reg) {
Wire.beginTransmission(TAC5212_ADDR);
Wire.write(reg);
Wire.endTransmission(false);
Wire.requestFrom(TAC5212_ADDR, 1);
return Wire.available() ? Wire.read() : 0xFF;
}
void dumpRegisters(uint8_t count = 64) {
Serial.println("🧾 Dumping first codec registers:");
for (uint8_t r = 0; r < count; r++) {
uint8_t val = readReg(r);
Serial.print("0x"); if (r < 0x10) Serial.print("0");
Serial.print(r, HEX);
Serial.print(": 0x"); if (val < 0x10) Serial.print("0");
Serial.println(val, HEX);
delay(5);
}
}
void setupCodec(bool usePLL, uint8_t pasi_cfg0) {
writeReg(0x01, 0x01); delay(100); // Reset
writeReg(0x02, 0x09); delay(100); // Wakeup
if (usePLL) {
Serial.println("🔧 Enabling PLL Mode");
writeReg(0x10, 0x01); // PLL Enable
writeReg(0x11, 0x30); // PLL Config
delay(50);
} else {
Serial.println("🎧 Using External Clock Mode");
}
//writeReg(0x19, 0x10); // ASI_CFG1: TDM Mode
// writeReg(0x1A, 0x00); // PASI_CFG0: frame size = 16 slots
writeReg(0x19, 0x13); // 0x10 | 0x03: TDM mode + 32-bit words
writeReg(0x26, 0x00); // Set PASI_RX_OFFSET to 1
//writeReg(0x1A, pasi_cfg0);// PASI_CFG0
//writeReg(0x26, 0x00); // Slot 0
// DAC Time Slot Assignments
writeReg(0x41, 0); // DAC L1 -> Slot 0
writeReg(0x42, 0); // DAC R1 -> Slot 1
writeReg(0x43, 0); // DAC L2 -> Slot 0
writeReg(0x44, 0); // DAC R2 -> Slot 1
// writeReg(0x12, 0x01); // Accept external clock
// Unmute and set gain
// writeReg(0x40, 128); // Unmute (if supported)
writeReg(0x67, 128); // DAC L1 Vol
writeReg(0x69, 128); // DAC R1 Vol
writeReg(0x6E, 128); // DAC L2 Vol
writeReg(0x70, 128); // DAC R2 Vol
writeReg(0x76, 0x0F); // Enable DACs 0x0f is both
writeReg(0x78, 0x40); // Power Up
Serial.println(readReg(0x76), HEX); // Expect: 0F
}
void configureTDM() {
I2S1_TCR4 = I2S_TCR4_FRSZ(15); // 16 slots
I2S1_TCR5 = I2S_TCR5_WNW(15) | I2S_TCR5_W0W(15) | I2S_TCR5_FBT(15);
I2S1_TCR2 = I2S_TCR2_SYNC(1);
}
void setup() {
delay(10000);
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, LOW);
Serial.begin(115200);
Wire.begin();
delay(1000);
Serial.println(" Start Audio Library");
AudioMemory(320);
delay(1000);
sine1.frequency(440);
sine1.amplitude(0.8);
sine2.frequency(240);
sine2.amplitude(0.8);
Serial.println(" Dump Registers");
dumpRegisters(64);
}
void loop() {
//static const uint8_t cfgs[] = {0x00, 0x04, 0x10, 0x14};
static const uint8_t cfgs[] = {0x00};
static bool pllMode = false;
static uint8_t idx = 0;
// Serial.println();
// Serial.print(pllMode ? "🧪 PLL Mode — " : "🔬 External Clk — ");
// Serial.print("Trying PASI_CFG0 = 0x"); Serial.println(cfgs[idx], HEX);
uint8_t clk2 = readReg(0x13);
if ((clk2 & 0x07) == 0x07) {
Serial.println("✅ Clocks locked");
} else {
Serial.println("❌ Clocks not locked");
}
setupCodec(pllMode, cfgs[idx]);
//configureTDM();
//delay(300);
uint8_t clk = readReg(0x13);
Serial.print("⏱ Clock Status (0x13): 0x"); Serial.println(clk, HEX);
if (clk & 0x07) {
Serial.println("✅ CLOCKS LOCKED!");
digitalWrite(LED_BUILTIN, HIGH);
while (true) {
digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
delay(500);
}
}
// idx++;
// if (idx >= sizeof(cfgs)) {
// idx = 0;
// pllMode = !pllMode;
// Serial.println("🔁 Switching clock mode...");
// }
delay(2000);
}
Here is the result.
Code:
Start Audio Library
Dump Registers
🧾 Dumping first codec registers:
0x00: 0x00
0x01: 0x00
0x02: 0x09
0x03: 0x00
0x04: 0x00
0x05: 0x15
0x06: 0x35
0x07: 0x00
0x08: 0x00
0x09: 0x00
0x0A: 0x32
0x0B: 0x00
0x0C: 0x00
0x0D: 0x00
0x0E: 0x00
0x0F: 0x00
0x10: 0x52
0x11: 0x80
0x12: 0x00
0x13: 0x00
0x14: 0x00
0x15: 0x00
0x16: 0x00
0x17: 0x00
0x18: 0x40
0x19: 0x12
0x1A: 0x30
0x1B: 0x00
0x1C: 0x00
0x1D: 0x00
0x1E: 0x20
0x1F: 0x21
0x20: 0x02
0x21: 0x03
0x22: 0x04
0x23: 0x05
0x24: 0x06
0x25: 0x07
0x26: 0x00
0x27: 0x00
0x28: 0x20
0x29: 0x21
0x2A: 0x02
0x2B: 0x03
0x2C: 0x04
0x2D: 0x05
0x2E: 0x06
0x2F: 0x07
0x30: 0x00
0x31: 0x00
0x32: 0x00
0x33: 0x00
0x34: 0x40
0x35: 0x00
0x36: 0x00
0x37: 0x20
0x38: 0x00
0x39: 0x00
0x3A: 0x00
0x3B: 0x00
0x3C: 0x00
0x3D: 0x10
0x3E: 0x50
0x3F: 0x00
❌ Clocks not locked
🎧 Using External Clock Mode
F
⏱ Clock Status (0x13): 0x0
❌ Clocks not locked
🎧 Using External Clock Mode
F
⏱ Clock Status (0x13): 0x0
❌ Clocks not locked
🎧 Using External Clock Mode
F
If you’ve worked with TDM audio on Teensy or similar codecs, I’d really value your insight on what to try next—especially around clock settings and initialization. While I’ve been working on audio and embedded projects for years, I’m still early in this particular journey and have big goals for where I’d like to take it. If you're into Teensy, TDM, modular audio, or just love to tinker—let’s connect.
I’ll be sharing more details soon, including the full backplane schematic, expansion plans, and some of the other modules I’m working on.
Thanks in advance for your help!
Best,
Jay Shoemaker
t-dsp.com
Last edited: