Quad channel output on Teensy 3.6

Hi Paul and everyone. I'm designing a similar board to use Teensy with the CS42448 and was wondering if it'd be possible to use 3.3V for VA? I need to make it battery-powered (lithium 3.7-4.2V) so I was planning to just have a battery connected to VIN and use the regulated 3.3V to power all the rest. On the datasheet it says the CS42448 can operate from 3.3 to 5..so it should be possible, right?
In this case, could I just connect VA and VD with a ferrite bead to separate the 3.3V lines? Any suggestions? Thanks.
 
with the CS42448 and was wondering if it'd be possible to use 3.3V for VA?

The datasheet says it can run from 3.14 to 5.25V, so looks like it ought to be possible.

sc.png


Any suggestions?

I'd first just try connecting VA to the battery. Well, maybe a quick voltmeter check to verify it's under 5.25V.

You could also try just connecting it to 3.3V and see what happens. I'd watch the 3.3V line while doing so, and make a point to (carefully) touch the voltage regulator to make sure it's not getting burning hot. Teensy's little regulator might not be able to supply enough current. But the worst case should be it overheats and shuts down.
 
The datasheet says it can run from 3.14 to 5.25V, so looks like it ought to be possible.

View attachment 23615




I'd first just try connecting VA to the battery. Well, maybe a quick voltmeter check to verify it's under 5.25V.

You could also try just connecting it to 3.3V and see what happens. I'd watch the 3.3V line while doing so, and make a point to (carefully) touch the voltage regulator to make sure it's not getting burning hot. Teensy's little regulator might not be able to supply enough current. But the worst case should be it overheats and shuts down.

That's great Paul. I will try VA to the battery first, good idea! Then I will do another test with 3.3V and if the regulator can't cope with it I will use another one externally. Thanks!
 
since you are looking for battery power this might be of interest. Provides battery charging facilities as well. Very cheap - about $3 if I remember correctly.

EDIT:
You can get them here.
 
Hi,

I'm currently using these boards for a project, and I'm running into an issue where I hear the audio output when the board boots up via programming; however, when I unplug and replug power to the teensy it fails to output audio at all. I know that cs42448_1.enable() and cs42448_1.volume(1)is returning true in both cases, so I'm a bit stumped. It looks like there is some timing dependency on how the board boots up when the teensy boots from programming versus a straight power on. Additionally, the MCLK from the Teensy doesn't seem to be reached 0.2 VA when I measure on a scope (breaking the VIH spec of the CS42448)... Could this be a problem?
 
I was wondering if it would be possible to update the GUI audio tool to allow 4 and 8 channel .wav files from the SD card. That would be an amazing tool combined with the CS42448!
 
Hi,

I am looking for an alternative to replace CM6206-LX (USB TO SOUNDCARD 5.1), the ic is out of stock everywhere, and it seems that only cmedia has a all in solution. You can see my pcb below:
pcb.PNG


I started looking into alternatives and I came into this thread created by Paul, can you please let me know if using a Teensy + CS42448 TDM pcb, I would be able to see a 5.1 channel (or 7.1 channel) sound card in the windows control panel?
Like this?
speaker.PNG
 
I am having the same VQ startup problem referred to earlier in this thread and referenced in the control_cs42448.cpp Audio Library. When I download a simple sketch outputting to the CS42448, the VQ rises to 2.5v "almost" all the time. When I unplug the teensy 4.1 a plug it back in, VQ "always" goes to some random value around a 1 volt. I am using the OSHPARK board and have a CAT811 hooked up to the reset pin. BTW, all the TDM communication is working properly, even when VQ is not at 2.5 v. But the analog IO isn't working.

I tried to set the magic bit by modifying control_cs42488.cpp as follows:

bool AudioControlCS42448::enable(void)
{
Wire.begin();
// TODO: wait for reset signal high??
if (!write(CS42448_Power_Control, 0xFF)) return false; // power down
if (!write(CS42448_Functional_Mode, default_config, sizeof(default_config))) return false;
if (!write(CS42448_Power_Control, 0)) return false; // power up
write(CS42448_Functional_Mode, 0xF4 | 0x01 ); /////////////////////////////////////////////////////////////magic bit
return true;
}

But that didn't work. Has anyone been successful in getting a CS42448 and VQ to start correctly? Am I missing something?
 
I had the same issue as mmalex with the CS42448 not working properly, VQ at 1.25V instead of 2.5V and muted outputs.

my fix for it is to set bit 0 (a reserved bit) in the CS42448 FunctionalMode register to 1:

Code:
        // set the magic bit!
        __write(CS42448_Functional_Mode, 0xF4 | 0x01 );

av500, this just completely saved my project --- I'd almost given up after several late nights of trying to debug the hardware.
Paul --- you are a saint for including the link to this thread in the .cpp file!

strangely enough, this issue has happened with every one of the three CS42448s I've ordered and used on my custom boards --- very odd.
also strangely enough, I found that briefly connecting SCLK and MCLK to 0V after startup would kick the outputs into life. this "magic bit" fix now works like a charm :)

thank you all! very ecstatic right now!
 
I am having the same VQ startup problem referred to earlier in this thread and referenced in the control_cs42448.cpp Audio Library. When I download a simple sketch outputting to the CS42448, the VQ rises to 2.5v "almost" all the time. When I unplug the teensy 4.1 a plug it back in, VQ "always" goes to some random value around a 1 volt. I am using the OSHPARK board and have a CAT811 hooked up to the reset pin. BTW, all the TDM communication is working properly, even when VQ is not at 2.5 v. But the analog IO isn't working.

I tried to set the magic bit by modifying control_cs42488.cpp as follows:

bool AudioControlCS42448::enable(void)
{
Wire.begin();
// TODO: wait for reset signal high??
if (!write(CS42448_Power_Control, 0xFF)) return false; // power down
if (!write(CS42448_Functional_Mode, default_config, sizeof(default_config))) return false;
if (!write(CS42448_Power_Control, 0)) return false; // power up
write(CS42448_Functional_Mode, 0xF4 | 0x01 ); /////////////////////////////////////////////////////////////magic bit
return true;
}

But that didn't work. Has anyone been successful in getting a CS42448 and VQ to start correctly? Am I missing something?

for me it worked like this:
Code:
bool AudioControlCS42448::enable(void)
{
	Wire.begin();
	// TODO: wait for reset signal high??
	if (!write(CS42448_Power_Control, 0xFF)) return false; // power down
	if (!write(CS42448_Functional_Mode, default_config, sizeof(default_config))) return false;

    // set the magic bit!
    write(CS42448_Functional_Mode, 0xF4 | 0x01 );
	
	if (!write(CS42448_Power_Control, 0)) return false; // power up
	return true;
}
 
CS42448 magic bit

I was looking at this, and scanned the CS42448 data sheet to see if there might be a clue in there ... and maybe there is (DS648F5 page 28, Figure 12):
2022-09-27 13_08_30-CS42448_F5.pdf (SECURED) - Adobe Acrobat Reader DC (32-bit).png
I read this as saying that the internal registers should be set up prior to the TDM clocks (and in particular MCLK) being enabled, otherwise the hardware will be in an "unknown state" (and maybe it can't recover from that). However, as normally implemented with the static Audio library, all the objects will be constructed (and thus the clocks will be running) before setup() is called and the AudioControl object used to configure the internal registers.

If anyone out there has a system which needs the magic bit to work, they might like to try to make a test sketch which:
  • leaves the AudioOutputTDM out of the static objects, so the clocks aren't started at boot
  • first does the AudioControlCS42448::enable() call in setup()
  • and only then creates the AudioOutputTDM object and the AudioConnection objects needed to "wire" it to the source
Try this with and without the magic bit, from a cold power up or just after re-programming, etc., to prove one way or another whether it makes a difference and eliminates the need for the magic bit.

I'm not sure at this point how to achieve correct sequencing of clock and register settings for all cases, as I think some parts (e.g. the SGTL5000) actually need MCLK running before their control interface will work. But I'm sure we can think of something, if needs be... :cool:
 
For what it's worth, I just want to send kudos to the folks on this thread for clearing away the obstacles in my path.
So far, I've successfully developed a 6 channel T4 Laser Synth, using the SGTL5000 + PT8211 DACs, plus an optimized ILDA standard op amp, with differential outputs for a laser projector.
My next step is to replace the SGTL5000 and PT8211 ICs with the CS42448, as Paul had suggested at the beginning of my journey. The CS42448 ICs are due to arrive this week, so receiving the notification of the most recent post on this thread is a great heads up regarding my expectations.
The folks on this forum are great!
Thank you for your contributions and guidance that benefits those of us who are naively unaware of the challenges ahead.
🙏🙏🙏
😎
 
I was looking at this, and scanned the CS42448 data sheet to see if there might be a clue in there ... and maybe there is (DS648F5 page 28, Figure 12):
View attachment 29462
I read this as saying that the internal registers should be set up prior to the TDM clocks (and in particular MCLK) being enabled, otherwise the hardware will be in an "unknown state" (and maybe it can't recover from that). However, as normally implemented with the static Audio library, all the objects will be constructed (and thus the clocks will be running) before setup() is called and the AudioControl object used to configure the internal registers.

If anyone out there has a system which needs the magic bit to work, they might like to try to make a test sketch which:
  • leaves the AudioOutputTDM out of the static objects, so the clocks aren't started at boot
  • first does the AudioControlCS42448::enable() call in setup()
  • and only then creates the AudioOutputTDM object and the AudioConnection objects needed to "wire" it to the source
Try this with and without the magic bit, from a cold power up or just after re-programming, etc., to prove one way or another whether it makes a difference and eliminates the need for the magic bit.

I'm not sure at this point how to achieve correct sequencing of clock and register settings for all cases, as I think some parts (e.g. the SGTL5000) actually need MCLK running before their control interface will work. But I'm sure we can think of something, if needs be... :cool:

Ok. I have also made a CS42448 board that shows this problem (no output, VQ is low). Setting the magic bit seems to make it work. I tried leaving AudioOutputTDM and AudioConnection out of the static objects. I created AudioControlCS42448 and AudioSynthWaveformSine as static objects and then I used 'new' to create AudioOutputTDM and AudioConnection in my setup code.

Interestingly enough, when I download this code, the outputs work every time, but if I cycle power, the outputs don't work unless the magic bit is set. I'm not sure what to make of that. I haven't yet looked at the sequencing with a scope to see if everything is happening in the right order, but I hope to get around to that soon. Or maybe someone will have something else to try?
 
OSH T3.x CS42448 PCB AOUT7+ & AOUT7- reversed

Hi folks,
Just wanted to inform others, who may be using the OSH Park PCB for the original Teensy 3.6 CS42448 IC that, after 3 days searching for a code error to explain why my 7th AOUT channel was exhibiting inverted polarity behavior, I found that the signal is indeed upside down.
After finding no differences in code, compared with the other 7 output channels, today, I checked the physical tracks on the PCB and discovered that the 7th AOUT channel is actually connected to AOUT7- (CS42448 pin #38), instead of AOUT7+ (pin #39).
Of course most audiophiles wouldn't notice one channel of 8 being reverse polarized. But, for my laser system, it meant that the blue controls for intensity were functioning backwards.
But, I thought the info is worth sharing.
Don't know whether the 2nd version of the OSH Park PCB has the same track layout, but could see how it could be easily overlooked. Other than that my prototype ADC/T4 Laser Synth/DAC is performing well.

BR
:cool:
 
Last edited:
CS42448 magic bit

If anyone out there has a system which needs the magic bit to work, they might like to try to make a test sketch which:
  • leaves the AudioOutputTDM out of the static objects, so the clocks aren't started at boot
  • first does the AudioControlCS42448::enable() call in setup()
  • and only then creates the AudioOutputTDM object and the AudioConnection objects needed to "wire" it to the source
Try this with and without the magic bit, from a cold power up or just after re-programming, etc., to prove one way or another whether it makes a difference and eliminates the need for the magic bit.
Thank you for posting this.
I've basically copied the OSHpark CS42448 PCB (which was working ok) into my project's PCB, with 6 output channels + op amps.

The CS42448 never works @ power up and only produces static negative offsets in the outputs;
It intermittently may or not start after compiling the sketch.

The following shows the pertinent code and order of AudioOutputTDM & AudioControlCS42448, without understanding the previous post regarding creating objects with 'new':

...
AudioEffectMultiply Y_Multiply;
AudioOutputTDM tdm1;
...
AudioConnection patchCord14(X_Mix, 0, X_Multiply, 1);
AudioConnection patchCord15(Y_Multiply, 0, tdm1, 10);
AudioConnection patchCord16(X_Multiply, 0, tdm1, 8);
AudioControlCS42448 cs42448_1;


void setup() {

AudioMemory(30);

cs42448_1.enable();
cs42448_1.volume(0.8);
cs42448_1.inputLevel(0.8);
I'm also unsure about the previous instructions. But, I'm interpreting it as the load order of AudioOutputTDM & AudioControlCS42448 needs to be reversed. Since AudioOutputTDM must be declared, prior to being patched to other objects, the only option is to move AudioControlCS42448 to the beginning of the declarations. Don't know whether it helps, but I've also moved AudioOutputTDM just above it being patched to other objects, as follows:

AudioControlCS42448 cs42448_1;
...
AudioConnection patchCord14(X_Mix, 0, X_Multiply, 1);
AudioOutputTDM tdm1;
AudioConnection patchCord15(Y_Multiply, 0, tdm1, 10);
AudioConnection patchCord16(X_Multiply, 0, tdm1, 8);



void setup() {

AudioMemory(30);

cs42448_1.enable();
cs42448_1.volume(0.8);
cs42448_1.inputLevel(0.8);
After making the above change, I copied and imported it into the ADT. The connections were correct, but the TDM object was moved. So I moved it back, Exported and pasted it back into my sketch. Surprisingly, the AudioOutputTDM was pasted in at one line above the patchCords. AudioControlCS42448 was pasted back in as the bottom line, beneath the patchCords, as shown in the original code
Upon power on, the CS42448 still doesn't produce any output, except for static negative offsets.
But, after compile, it appears to work reliably again.
However, after a power down and not working, sometimes it took a couple of compiles to get it working reliably again.
So the order of succession is important to getting the CS42448 to work reliably after being compiled. But the mod doesn't survive being Exported from the ADT then + pasted back into the sketch.
Hope this helps. I'll try the magic bit mod tomorrow, then provide an update with the results.
 
Last edited:
The CS42448 never works @ power up and only produces static negative offsets in the outputs;
It intermittently may or not start after compiling the sketch.

I recall at least a couple people saying CS42448 is quite sensitive to the reset circuit.

I only tested with the CAT811T chip. When I made the PCB, it seemed kinda overkill, but I had plenty leftover from the WIZ820 adaptor boards (the W5200 chip *really* needs a good reset). Now in hindsight, seems a part like CAT811T is probably needed.
 
Here's a slightly long-winded demo of the use of new to create the TDM objects after the CS42448 has been initialised:
C++:
#include <Audio.h>

// define this macro to use the "normal"
// statically-created objects
//#define USE_STATIC

// GUItool: begin automatically generated code
#if defined(USE_STATIC)
AudioInputTDM            tdmI;           //xy=86,212
#endif // defined(USE_STATIC)
AudioSynthWaveform       wav2; //xy=261,100
AudioSynthWaveform       wav1;      //xy=262,45
AudioMixer4              mixInL;       //xy=262,173
AudioMixer4              mixInR; //xy=262,252
AudioSynthWaveform       wav5; //xy=478,352
AudioSynthWaveform       wav7; //xy=479,437
AudioSynthWaveform       wav3; //xy=480,265
AudioSynthWaveform       wav4; //xy=480,309
AudioSynthWaveform       wav6; //xy=481,393
AudioMixer4              mixL; //xy=483,116
AudioMixer4              mixR; //xy=483,194
AudioSynthWaveform       wav8; //xy=483,484
#if defined(USE_STATIC)
AudioOutputTDM           tdmO;           //xy=708,308
AudioConnection          patchCord1(tdmI, 0, mixInL, 0);
AudioConnection          patchCord2(tdmI, 2, mixInR, 0);
AudioConnection          patchCord3(tdmI, 4, mixInL, 1);
AudioConnection          patchCord4(tdmI, 6, mixInR, 1);
AudioConnection          patchCord5(tdmI, 8, mixInL, 2);
AudioConnection          patchCord6(tdmI, 10, mixInR, 2);
AudioConnection          patchCord7(tdmI, 12, mixInL, 3);
AudioConnection          patchCord8(tdmI, 14, mixInR, 3);
#endif // defined(USE_STATIC)
AudioConnection          patchCord9(wav2, 0, mixR, 0);
AudioConnection          patchCord10(wav1, 0, mixL, 0);
AudioConnection          patchCord11(mixInL, 0, mixL, 1);
AudioConnection          patchCord12(mixInR, 0, mixR, 1);
#if defined(USE_STATIC)
AudioConnection          patchCord13(wav5, 0, tdmO, 8);
AudioConnection          patchCord14(wav7, 0, tdmO, 12);
AudioConnection          patchCord15(wav3, 0, tdmO, 4);
AudioConnection          patchCord16(wav4, 0, tdmO, 6);
AudioConnection          patchCord17(wav6, 0, tdmO, 10);
AudioConnection          patchCord18(mixL, 0, tdmO, 0);
AudioConnection          patchCord19(mixR, 0, tdmO, 2);
AudioConnection          patchCord20(wav8, 0, tdmO, 14);
#endif // defined(USE_STATIC)
AudioControlCS42448      cs42448_1;      //xy=699,458
// GUItool: end automatically generated code

//==========================================================
// Placeholders for audio objects to be created
// during setup()
#if !defined(USE_STATIC)
AudioInputTDM*            tdmI;           //xy=86,212
AudioOutputTDM*           tdmO;           //xy=708,308
AudioConnection* cords[16]; // 8 in, 8 out
#endif // !defined(USE_STATIC)


void createObjects(void)
{
#if !defined(USE_STATIC)
  // create the TDM input and output objects
 
  tdmI = new AudioInputTDM;
  tdmO = new AudioOutputTDM;

  // now create their connections
  // note the use of *tdmI and *tdmO, as we have pointers to
  // the TDM objects, not (references to) the objects themselves
 
  int cordNum = 0;
  cords[cordNum++] = new AudioConnection {*tdmI, 0, mixInL, 0 };
  cords[cordNum++] = new AudioConnection {*tdmI, 2, mixInR, 0 };
  cords[cordNum++] = new AudioConnection {*tdmI, 4, mixInL, 1 };
  cords[cordNum++] = new AudioConnection {*tdmI, 6, mixInR, 1 };
  cords[cordNum++] = new AudioConnection {*tdmI, 8, mixInL, 2 };
  cords[cordNum++] = new AudioConnection {*tdmI, 10, mixInR, 2 };
  cords[cordNum++] = new AudioConnection {*tdmI, 12, mixInL, 3 };
  cords[cordNum++] = new AudioConnection {*tdmI, 14, mixInR, 3 };
  cords[cordNum++] = new AudioConnection {wav5, 0, *tdmO, 8 };
  cords[cordNum++] = new AudioConnection {wav7, 0, *tdmO, 12 };
  cords[cordNum++] = new AudioConnection {wav3, 0, *tdmO, 4 };
  cords[cordNum++] = new AudioConnection {wav4, 0, *tdmO, 6 };
  cords[cordNum++] = new AudioConnection {wav6, 0, *tdmO, 10 };
  cords[cordNum++] = new AudioConnection {mixL, 0, *tdmO, 0 };
  cords[cordNum++] = new AudioConnection {mixR, 0, *tdmO, 2 };
  cords[cordNum++] = new AudioConnection {wav8, 0, *tdmO, 14 };
#endif // !defined(USE_STATIC)
}
//==========================================================

#define NP 8

AudioSynthWaveform* w[]={&wav1, &wav2, &wav3, &wav4, &wav5, &wav6, &wav7, &wav8};
int p[] =
{
  0b1000000000000, // I 
  0b1010000000000, // II 
  0b1010100000000, // III 
  0b1011100000000, // IV 
  0b1110000000000, // V 
  0b1110100000000, // VI 
  0b1110101000000, // VII 
  0b1110101010000, // VIII 
};
bool on[NP];
int pmask;
int state;

void setup()
{
  AudioMemory(32); // TDM uses many blocks!

  // set up CS42448, before MCLK is started
  delay(10); // not needed, useful for 'scope trace though
  cs42448_1.enable();
  cs42448_1.volume(0.5f);
  cs42448_1.inputLevel(0.5f);
  delay(10); // not needed, useful for 'scope trace though

  pinMode(LED_BUILTIN,OUTPUT);
  mixL.gain(0,0.5);
  mixL.gain(1,0.5);
  mixR.gain(0,0.5);
  mixR.gain(1,0.5);

  // create TDM objects, which will start MCLK
  // does nothing if objects are statically-defined
  createObjects();
}


void loop() {
  pmask >>= 1;
  if (0 == pmask)
  {
    pmask = p[0];
    //Serial.println("beep!");
    digitalToggle(LED_BUILTIN);
  }

  for (int i=0;i<NP;i++)
  {
    bool beep = (pmask & p[i]) != 0; // sounding this time?
    bool chg = on[i] != beep;
    if (chg) // need to change sound on this channel
    {
      if (beep)
        w[i]->begin(0.9f,1000.0f,0);
      else
        w[i]->amplitude(0.0);
    }
    on[i] = beep;
  }

  if (Serial.available())
  {
    while (Serial.available())
      Serial.read();

    state++;
    switch (state)
    {
      default:
        state = 0;
        break;

      case 0:
        Serial.println("mix");
        mixL.gain(0,0.5);
        mixL.gain(1,0.5);
        mixR.gain(0,0.5);
        mixR.gain(1,0.5);
        break;

      case 1:
        Serial.println("waves");
        mixL.gain(0,1.0);
        mixL.gain(1,0.0);
        mixR.gain(0,1.0);
        mixR.gain(1,0.0);
        break;

      case 2:
        Serial.println("inputs");
        mixL.gain(0,0.0);
        mixL.gain(1,1.0);
        mixR.gain(0,0.0);
        mixR.gain(1,1.0);
        state = -1;
        break;
        
    }
  }
  delay(100);
}
I modified a general "is it working?" sketch, which outputs the channel number in Roman numerals on each output - well, who knows Morse code? Long is V, short is I ... but that's not important right now.

As shown, with #define USE_STATIC commented out, the TDM objects and their connections aren't created until after the CS42448 has been initialised. I put some delays in to make this easier to see; scope traces are yellow = audio VII, magenta = audio VIII, cyan = MCLK, green = I²C SCL:
SDS00007.png


With #define USE_STATIC active (not commented), MCLK is active before the initialisation takes place, as you'd expect.

With regard to the CAT811, I did check the data sheet and it could hold reset low for up to 400ms, which would undoubtedly cause problems. The one I have looks to be about 200ms (cyan = CAT811-/RESET, all other traces as before; timebase is different):
SDS00006.png


I don't have power-up issues with my board, and I don't have a library that sets the magic bit, either.
 
I recall at least a couple people saying CS42448 is quite sensitive to the reset circuit.

I only tested with the CAT811T chip. When I made the PCB, it seemed kinda overkill, but I had plenty leftover from the WIZ820 adaptor boards (the W5200 chip *really* needs a good reset). Now in hindsight, seems a part like CAT811T is probably needed.
Thank you, Paul.
The only change I've made to your OSHpark PCB is the removal of the header to stack 2 boards.
Prior to finalizing my PCB design, I had previously asked about the purpose of the CAT811T and it was explained that it was required to stall initializing the CS42448's outputs. So, I'm using the CAT811T, as well. Here's my schematic:
1705940920927.png
 
for me it worked like this:
Code:
bool AudioControlCS42448::enable(void)
{
    Wire.begin();
    // TODO: wait for reset signal high??
    if (!write(CS42448_Power_Control, 0xFF)) return false; // power down
    if (!write(CS42448_Functional_Mode, default_config, sizeof(default_config))) return false;

    // set the magic bit!
    write(CS42448_Functional_Mode, 0xF4 | 0x01 );
  
    if (!write(CS42448_Power_Control, 0)) return false; // power up
    return true;
}
Thank you for sharing this easy solution that instantly solved my problems with starting up the CS42448 outputs, both at power on and after compiling the sketch.
"What monkey sees, monkey can do."
😎
 
Back
Top