Audio board with SD card and concurrent SPI usage

Status
Not open for further replies.

cike

Member
Hi!

As far as i've seen, the audio library accesses the SD-card in an "update"-function, which is called inside an interrupt routine (I guess).
Accessing the SD-Card therefore utilizes the SPI Hardware.

Is it possible to Play wav-files from SD Card with the Audio Adapter Board on teensy 3.2 while using hardware SPI concurrently? If so, how do you do it?

I was thinking about 3 ways how to achieve this:

  • Disabling audiolibraries Interrupt while running (will probably wreak havok?)
  • Attaching my SPI-code to the audio library Interrupt (where do I register my function for that?)
  • Using software SPI (at this point i am struggling with the 8MHz clock timing because the arduino delay functions are too rough)

Thanks
 
Hi!

As far as i've seen, the audio library accesses the SD-card in an "update"-function, which is called inside an interrupt routine (I guess).
Accessing the SD-Card therefore utilizes the SPI Hardware.

Is it possible to Play wav-files from SD Card with the Audio Adapter Board on teensy 3.2 while using hardware SPI concurrently? If so, how do you do it?

I was thinking about 3 ways how to achieve this:

  • Disabling audiolibraries Interrupt while running (will probably wreak havok?)
  • Attaching my SPI-code to the audio library Interrupt (where do I register my function for that?)
  • Using software SPI (at this point i am struggling with the 8MHz clock timing because the arduino delay functions are too rough)

Thanks
I cannot give you an example, but "begin_transaction / end_transaction" combo handles for you the switch between different spi hardware devices.
this can be done in an interleaved mode, but not concurrently, as spi ony handle one device at a time. e.g while reading a block from sd card, you cannot access, say spi-based sram, but between reads you can.
 
beginTransaction and endTransactions are a good point, but I can't find any confirmation this won't mess up the audio libraries interrupts if called directly.
Calling beginTransaction inside the loop function should disable the audio libraries software interrupt completely, but as far as i see there are also dma-interrupts registered (which i don't know how they correlate with audio lib's SOFTWARE_IRQ).


I found this interessting hint in the audio library design tool:

While playing, the audio library accesses the SD card automatically. If card access is required, you must use AudioNoInterrupts() to prevent the library from accessing the SD card while you use it. Disabling the audio library interrupt for too long may cause audible dropouts or glitches.
Disabling the SD-card for the library should ultimately free the SPI bus.

I think calling AudioNoInterrupts(); SPI.beginTransaction(); ... SPI.endTranscation(); AudioInterrupts(); in this order inside the loop function should work, but I have to test this first.
If this works my plan is to move that function inside a IntervalTimer callback function, but this gives me headaches since the timer callback is also running as an ISR. Any thoughts on that?
 
beginTransaction and endTransactions are a good point, but I can't find any confirmation this won't mess up the audio libraries interrupts if called directly.
Calling beginTransaction inside the loop function should disable the audio libraries software interrupt completely, but as far as i see there are also dma-interrupts registered (which i don't know how they correlate with audio lib's SOFTWARE_IRQ).


I found this interessting hint in the audio library design tool:


Disabling the SD-card for the library should ultimately free the SPI bus.

I think calling AudioNoInterrupts(); SPI.beginTransaction(); ... SPI.endTranscation(); AudioInterrupts(); in this order inside the loop function should work, but I have to test this first.
If this works my plan is to move that function inside a IntervalTimer callback function, but this gives me headaches since the timer callback is also running as an ISR. Any thoughts on that?

This is most likely Paul's domain of expertise, but he is knocked down at the moment, so I try to comment.
First, simply try it.
I understand, you wanted to use audio card playing wav files from sd card and in parallel accessing some other spi device. Would you mind to tell us what other device you are connecting to?

Now, from the examples and the way the audio library is written, the library is completely interrupt driven (e.g. it works without other user code).
I recall, that the software interrupt is set to very low priority. Audio updates happen every 2.9 ms (or so) (128 samples/44100 kHz), so in order to maintain ongoing audio processing integrity, no other ISR, which typically runs at higher priority, should last longer than 2.9 ms. Well, it should be shorter than that, as audio processing and SD card access needs their time. So, every 2.9 s you can access the SPI bus using begin/end_transaction to access devices with other CS than the one of the SD card. Outside the begin/end_transaction bracket, the SPI bus is free for other operations.
 
That's exactly what i want to do.
I'd just like to read and write a bunch of input and output shift registers simultaneously at a reliable 8MHz speed (i am also using a voltage level shifter that adds a small delay to all signals) using MISO and MOSI pins about every millisecond.

I have tested the procedure in the main loop and it seems to work pretty good - the audio library is coming along without any glitches, awesome!
Even adding a delay of 500ms between AudioNoInterrupt() and AudioInterrupt() doesn't break the library, just a short playback pause occurs.

Code:
void loop() {
  if (playSdWav1.isPlaying() == false) {
    Serial.println("Start playing");
    playSdWav1.play("SDTEST1.WAV");
    delay(10); // wait for library to parse WAV info
  }

  // Print alive on serial if and toggle spi output every second to see if cpu is still alive
  if(timerstart < millis())
  {
    timerstart=millis()+1000;
    Serial.println("alive");
    high=!high;
  }

  // Do this every millisecond
  delay(1);
  AudioNoInterrupts();                // Disable audio library interrupts to prevent sd card access and free spi bus
  SPI.beginTransaction(settings8Mhz); // Prepare SPI transcation
  digitalWriteFast(latchpin, LOW);    // Shift begin (like SPI's chip select - low on transmission)
  for(int i=0;i<100;i++)              // Worst case: 100 8bit shift registers
  {
    SPI.transfer(high?0x01:0x02);     // Just send stupid data every cycle
  }
  digitalWriteFast(latchpin, HIGH);   // Shift end (release CS)
  SPI.endTransaction();               // Free SPI
  AudioInterrupts();                  // Resume audio library
}

The transfer using spi is pretty fast (consumes 144µs in the example), so it shouldn't be a problem to run it every millisecond.

I'm not sure if i should run the spi-functions in a timer isr yet, but I'd like to know how to do it properly.
You mentioned that the audio library uses interrupt with a low priority. I found this in the audio stream class - is this the part where the interrupt is defined?
Code:
NVIC_SET_PRIORITY(IRQ_SOFTWARE, 208); // 255 = lowest priority

If i understand correctly, this means that my timer interrupt should have a priority of >=209 at least to prevent my code interrupting the audio library, right?
 
If i understand correctly, this means that my timer interrupt should have a priority of >=209 at least to prevent my code interrupting the audio library, right?
I would try it, there are only two outcomes, it works or doesn't work
 
You don't need to use AudioNoInterrupts, unless your SPI device is sensitive to extra delays between the bytes (I've never seen any which are).

Simply using SPI.beginTransaction() should be enough.
 
Thank you for the hint. SPI is (still ;)) working as expected and the output looks very good (via oscilloscope).
Amazingly, switching the MISO pin inside a transaction and reverting it back afterwards is no problem for teensy. I'm impressed.

Finally I have one last question:
I've seen that the audio library also uses I2C for the audio board communication, but I'd like to connect another I2C device to the same bus.
Is it safe to do so and call wire functions from the main loop or do I have to solder additional pins on the back to use the second I2C interface? The device has addresses starting from 0xA0 and higher.

I'm sorry for asking so many questsions, unfortunately I dont look through the libraries architecture yet.
 
I've seen that the audio library also uses I2C for the audio board communication, but I'd like to connect another I2C device to the same bus.
Is it safe to do so and call wire functions from the main loop

Yes, works fine, as long as every I2C device has a unique I2C address.
 
Status
Not open for further replies.
Back
Top