CS42448 Codec with SD Card Audio

Status
Not open for further replies.

grinch

Well-known member
Hi, I've been messing around with using the CS42448 codec with a teensy 3.6, based on the design given in this article (thanks Paul!): https://hackaday.io/project/2984-teensy-audio-library/log/57537-tdm-support-for-many-channel-audio-io

I think this would be a pretty cool format for various audio projects, I managed to clone the PCB and add some stuff I wanted like preamps and power input.

I'm running into an issue however where one of the pins for the built-in SD card on the teensy is given a different function in regards to the codec, and the two seem to be mutually exclusive (as soon as I start using the SD card for wav audio the codec stops working). Pin 11, which is MOSI for the built-in SD card is connected to something called MCLK on the codec.

Wondering what the best solution is to solve this? I could build an SD card socket into my PCB, and switch the SPI function to the alternate pins, but this seems pretty wasteful. Wondering if it's possible to switch this MCLK function to another pin? Or if there's something else here I might be missing? Given that the two are pretty likely to be used together sticking a codec signal on the same pin used by the SD card seems like a strange choice.

Here is the code I am using to test, I get output on the built-in DACs, but no output from the codec :/. Please advise!

Code:
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>

AudioPlaySdWav           playWav[10];
AudioOutputTDM           tdm1;
AudioOutputAnalogStereo  dacs;
AudioSynthWaveformSine   testSine;
AudioConnection          patchCord1(testSine, 0, tdm1, 0);
AudioConnection          patchCord2(playWav[1], 0, tdm1, 2);
AudioConnection          patchCord3(playWav[2], 0, tdm1, 4);
AudioConnection          patchCord4(playWav[3], 0, tdm1, 6);
AudioConnection          patchCord5(playWav[4], 0, tdm1, 8);
AudioConnection          patchCord6(playWav[5], 0, tdm1, 10);
AudioConnection          patchCord7(playWav[6], 0, tdm1, 12);
AudioConnection          patchCord8(playWav[7], 0, tdm1, 14);
AudioConnection          patchCord10(playWav[8], 0, dacs, 0);
AudioConnection          patchCord11(playWav[9], 0, dacs, 1);
AudioControlCS42448      cs42448_1;

// Use these with the Teensy 3.5 & 3.6 SD card
#define SDCARD_CS_PIN    BUILTIN_SDCARD
#define SDCARD_MOSI_PIN  11  // not actually used
#define SDCARD_SCK_PIN   13  // not actually used

const char *files[] = {"FIRE1.wav", "FIRE2.wav", "FIRE3.wav", "FIRE4.wav", "RAIN1.wav", "RAIN2.wav", "RAIN3.wav", "RAIN4.wav", 
                      "WIND1.wav", "WIND2.wav", "WIND3.wav", "WIND4.wav", "ANIMAL1.wav", "ANIMAL2.wav", "ANIMAL3.wav", "ANIMAL4.wav"};


void setup() {
  
  delay(100);
  Serial.begin(115200);
  delay(100);

  pinMode(0, OUTPUT);
  digitalWrite(0, HIGH);
  AudioMemory(256);
  dacs.analogReference(INTERNAL);
  testSine.amplitude(1);
  testSine.frequency(100);

  SPI.begin();

  SPI.setMOSI(SDCARD_MOSI_PIN);
  SPI.setSCK(SDCARD_SCK_PIN);
  while (!(SD.begin(SDCARD_CS_PIN))) {
    // stop here, but print a message repetitively, try again
    Serial.println("Unable to access the SD card");
//    delay(500);
  }

  cs42448_1.enable();
  cs42448_1.volume(1);

  Serial.println("Setup Done!");
  
}

int timer = 0;

void loop() {

  //loop wav file playback
  for(int i = 0; i < 10; ++i){
    if(!(playWav[i].isPlaying())){
      playFileFromPlayer(files[i], i);
    }
  }

  //blink LED
  if(millis() - timer > 250){
    digitalWrite(0, !digitalRead(0));
    timer = millis();
  }

}

void playFileFromPlayer(const char *filename, int player)
{
  Serial.print("Playing file: ");
  Serial.print(filename);

  // Start playing the file.  This sketch continues to
  // run while the file plays.
  playWav[player].play(filename);

  // A brief delay for the library read WAV info
  delay(5);

  Serial.println(" Check!");
}
 
The built in SD card on Teensy 3.6 does not share any pins. Refer to the schematic for details.

https://www.pjrc.com/teensy/schematic.html




Don't know what's causing this problem, but I can tell you with certainty it's not a pin conflict.

Hmmmm... Interesting... Perhaps it's these section of the code:
Code:
// Use these with the Teensy 3.5 & 3.6 SD card
#define SDCARD_CS_PIN    BUILTIN_SDCARD
#define SDCARD_MOSI_PIN  11  // not actually used
#define SDCARD_SCK_PIN   13  // not actually used
and
Code:
  SPI.begin();

  SPI.setMOSI(SDCARD_MOSI_PIN);
  SPI.setSCK(SDCARD_SCK_PIN);
  while (!(SD.begin(SDCARD_CS_PIN))) {
    // stop here, but print a message repetitively, try again
    Serial.println("Unable to access the SD card");
//    delay(500);
  }

This is taken from the audio library example for reading Wavs from an SD card, wherein SPI is activated using a set of default pins. This brings up several questions:

1.What is the purpose of setting SPI to these particular pins in the example? Or of activating SPI at all if none of the SPI pins are connected apparently?
2.Would it be possible just to set SPI to use some of the alternative MISO / MOSI pins to avoid the conflict?
3.What protocol is used to read from the built-in SD card if not SPI?
4.Is this something that came up at all when you were testing the codec? What is the function of MCLK and how is that set up within the library?

Thanks for getting back to me so quickly!
 
Update:

When I try changing the SPI pin assignment the codec output becomes extremely noisy, sounds like a mismatched data / clock signal kind of thing:

Code:
// Use these with the Teensy 3.5 & 3.6 SD card
#define SDCARD_CS_PIN    BUILTIN_SDCARD
#define SDCARD_MOSI_PIN  7     //previously 11  // not actually used
#define SDCARD_SCK_PIN   14   //previously 13  // not actually used

Any idea what might be causing this?
 
Updated update:

But then as soon as I comment out the lines activating SPI, everything starts working fine:

Code:
//  SPI.begin();
//
//  SPI.setMOSI(SDCARD_MOSI_PIN);
//  SPI.setSCK(SDCARD_SCK_PIN);
  while (!(SD.begin(SDCARD_CS_PIN))) {
    // stop here, but print a message repetitively, try again
    Serial.println("Unable to access the SD card");
//    delay(500);
  }

Is using SPI mutually exclusive with the codec? That'd be pretty disappointing :/
 
3.What protocol is used to read from the built-in SD card if not SPI?

SDIO protocol, 1-bit mode for commands, 4 bit mode for data.

https://electronics.stackexchange.c...t-is-the-difference-between-1-bit-sdio-vs-spi

You can find lots of info online about these protocols. The short version: all SD cards support 2 protocols, a high performance SDIO protocol using 6 signals, and a low performance SPI protocol with 4 signals. Newer versions of the SDIO protocol are not openly published, so you'll find *tons* of complaining online about how secret SDIO is... but the truth is version 2 is widely known, not much of a secret.


Is using SPI mutually exclusive with the codec? That'd be pretty disappointing :/

No, no and yes.

No, SPI should not be used at all here. The protocol is SDIO which has 6 dedicated pins.

No, SPI is not mutually exclusive if you configure SPI for the alternate pins.

But yes, SPI is mutually exclusive if you use the default pins, because 11 and 13 are needed for I2S or TDM protocol.


There is almost certainly some other problem at play here. As a blind guess, try turning on verbose output while compiling in File > Preferences. Then click Verify and look for the info with the full pathnames of all the libraries Arduino is using. Maybe you have a non-Teensy version of SD laying around somewhere, which does not understand "BUILTIN_SDCARD" and may be erroneously trying to use SPI protocol?
 
There is almost certainly some other problem at play here. As a blind guess, try turning on verbose output while compiling in File > Preferences. Then click Verify and look for the info with the full pathnames of all the libraries Arduino is using. Maybe you have a non-Teensy version of SD laying around somewhere, which does not understand "BUILTIN_SDCARD" and may be erroneously trying to use SPI protocol?

Indeed there was! I think I just needed to call SPI.begin() after setting the MOSI and SCLK to alternate pins, rather than before, was just taking this from the example, so I didn't realize the begin function would setup SPI on the default pins. Now that I changed the order of the lines SPI seems to be working (at least the setup function works), though I haven't tested it's actual interaction with anything else yet. Here's the code:

Code:
SPI.setMOSI(ALT_MOSI_PIN);
SPI.setSCK(ALT_SCK_PIN);
SPI.begin();
 
Status
Not open for further replies.
Back
Top