Audio functionality and SPI communication - again! (Teensy 3.2)

Status
Not open for further replies.

kat

Member
Hi all,

I have seen several threads discussing how to use Teensy+Audio board and also use SPI at the same time. And nonetheless, I cannot seem to get this to work in practice. I am hoping that I am making a stupid mistake that a generous forum user can easily correct.

I am using Teensy 3.2 with the audio shield and using MCP4922 DAC chips for analog outputs as part of a larger Teensy based system. An example sketch that replicates the issue is shown below. To make the example sketch I merged together part of an audio tutorial and some of the code I am using to test the DAC outputs (in other words, this is an example just for the purposes of showcasing the problem - it is not the code I am actually trying to use, which is part of a larger library I am writing. But it shows the same problem.).

Here is a statement of the problem in words:
1) when I run this sketch as written, SPI communication with the DACs works fine (I get a nice output ramp), but there is no audio output.
2) if I comment out (a) the SPI.begin() function, and (b) calls to the MCP4922_write() function in my loop, I (obviously) lose the DAC output but now I do have audio output and I can hear the kick sample play repeatedly with headphones connected to the audio shield.

So, clearly, I have not figured out the right way to use the SPI library to enable both of these at once. What am I missing?

Code:
//awkward merge of a DAC hardware test and part of an 
//audio library example to illustrate a
//problem when using SPI.begin() with the audio library

#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>
#include "AudioSampleKick.h"


int chip_select = 1;


AudioPlayMemory          playMem1;  
AudioMixer4              mixer1;  
AudioOutputI2S           i2s1;    
AudioConnection          patchCord4(playMem1, 0, mixer1, 0); 
AudioConnection          patchCord5(mixer1, 0, i2s1, 0);
AudioConnection          patchCord6(mixer1, 0, i2s1, 1);
AudioControlSGTL5000     sgtl5000_1;


void setup() {

  AudioMemory(8);
  sgtl5000_1.enable();
  sgtl5000_1.volume(0.5);
  mixer1.gain(0, 0.4);

  
  pinMode(chip_select, OUTPUT);
  digitalWrite(chip_select, HIGH); // HIGH);
 
  SPI.begin();
  SPI.setMOSI(7);
  SPI.setSCK(14);


}


void loop() {

  playMem1.play(AudioSampleKick);

  for (int i = 0; i < 4096; i ++) {
      MCP4922_write(chip_select, 0, i);  //OUTPUT 2
      MCP4922_write(chip_select, 1, i);  //OUTPUT 4
      delay(1);
  }
  
}


void MCP4922_write(int cs_pin, byte dac, int value) {
  byte low = value & 0xff;
  byte high = (value >> 8) & 0x0f;
  dac = (dac & 1) << 7;
  SPI.beginTransaction(SPISettings(4000000,MSBFIRST,SPI_MODE0));
  digitalWrite(cs_pin, LOW);
  SPI.transfer(dac | 0x30 | high);
  SPI.transfer(low);
  digitalWrite(cs_pin, HIGH);
  SPI.endTransaction();
}
 
Hey i'm not expert on audio library but maybe if you declare the SPI MCP4922 DAC chip with it's unique name on top of the sketch you will avoid the conflict ?
Code:
#include <SPI.h>
// set up the speed, mode and endianness of each device
SPISettings MCP4922settings(4000000, MSBFIRST, SPI_MODE0);
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
int cs_pin = 1; // <-------

void setup() {
  // put your setup code here, to run once:
  SPI.begin();
}

void loop() {
  // put your main code here, to run repeatedly:
}

void MCP4922_write(int cs_pin, byte dac, int value) {
  byte low = value & 0xff;
  byte high = (value >> 8) & 0x0f;
  dac = (dac & 1) << 7;
  //SPI.beginTransaction(SPISettings(4000000,MSBFIRST,SPI_MODE0));
  SPI.beginTransaction(MCP4922settings);
  // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  digitalWrite(cs_pin, LOW);
  SPI.transfer(dac | 0x30 | high);
  SPI.transfer(low);
  digitalWrite(cs_pin, HIGH);
  SPI.endTransaction();
}
 
Thanks for your reply, Chris. I didn't really think that a moving around the settings info would make any difference but I did try it - just in case. And I continue to see the same problem.

I think the issue here is more about the way that the audio shield uses SPI and the right way of working with the SPI library to ensure that it can still be used with other devices at the same time. I know that beginTransaction and endTransaction function to allow this kind of thing... but for me, invoking SPI.begin() seems to already create a problem. I don't know if there is a different way of handling the hardware initialization that SPI.begin() performs in order to make this possible, or just something else that I am missing.

Still hoping someone might have some insights...
 
This part is likely the problem.

SPI.begin();
SPI.setMOSI(7);
SPI.setSCK(14);

You need to use SPI.setMOSI() before SPI.begin().

If you do change the pins after SPI.begin(), then begin uses the default pins 11, 12, 13. But pin 11 is the audio clock. So SPI.begin() reconfigures pin 11 to work as SPI. Audio is immediately disrupted, because pin 11 isn't sending the clock anymore. Then when you later use SPI.setMOSI(7), pin 7 becomes the SPI MOSI signal.

SPI.setMOSI(7) does not know how to put pin 11 back to working as the audio clock. The SPI library doesn't store info about how the pin was configured before SPI started using it. When you use SPI.setMOSI(7) with the SPI library already running, it return pin 11 (which SPI had been using) to an unused state, as if you (or the audio lib) had never used it at all. Audio completely stops working, because no clock.

The audio lib has many examples that use the SD card over SPI. There too, SPI.setMOSI needs to be called before SD.begin, which calls SPI.begin internally.
 
Last edited:
Ah, fabulous. Thank you Paul! I am very grateful for this quick and pointed reply. That completely makes sense, and I am sure eventually I would have figured it out by digging into the audio library and/or the SPI code itself, but I might have chased a lot of incorrect theories before noticing the way that the order matters when changing pins and invoking the SPI.begin() function. I now see why the order of the SPI.begin() and setMOSI calls was just fine previously but became an issue once we were trying to use the audio library.

We have confirmed that this fixes the problem with our system. Hopefully this forum thread can help others fix similar issues as they arise, too! Thanks again.
 
Status
Not open for further replies.
Back
Top