Teensy 4.0 DMA SPI Speed issues

Status
Not open for further replies.

Paula

Active member
I'm attempting to move from block SPI transfers to DMA based, and I'm having some success, but seem to be struggling with the DMA SPI speed

I'm trying to run two separate SPI busses SPI0 and SPI2, I've started moving the SPI0 transfers as per the DMASPI read me and examples. The problem is that no matter what I do with SPISettings, the DMA always seems to be using a 4Mhz clock for the SPI Clock, which is, well, kind of useless for me.

The first SPI0 transfer works at 25Mhz (Yes I know it says 26Mhz, but for some reason if I set it to 25 in the SPI settings I get 20) anyway here's my code;

Code:
#include <Arduino.h>
#include <SPI.h>
#include <DmaSpi.h>


#define DMASIZE 100
uint8_t src[DMASIZE];
volatile uint8_t dest[DMASIZE];
volatile uint8_t dest1[DMASIZE];

void setSrc()
{
  for (size_t i = 0; i < DMASIZE; i++)
  {
    src[i] = i;
  }
}

void clrDest(uint8_t* dest_)
{
  memset((void*)dest_, 0x00, DMASIZE);
}

// Initialise stuff
void initSPI(void)
{

    Serial.print("init SPI0:");

    SPISettings(26000000, MSBFIRST, SPI_MODE0);
    SPI.begin();
    SPI.beginTransaction(SPISettings(26000000, MSBFIRST, SPI_MODE0));   // for some reason, I get 20Mhz when the rate is set to 25000000
                                                                        // I do get 25Mhz, when the rate is set to 26000000... huh?

    SPI2.begin();
    SPI2.beginTransaction(SPISettings(26000000, MSBFIRST, SPI_MODE0)); 


  //************************ TESTING
  delay(3000);
  Serial.println("regular SPI transer test");
  setSrc();
  elapsedMicros us;
  digitalWrite (ChipSelect, LOW);
  for (size_t i = 0; i < DMASIZE; i++)
  {
    dest[i] = SPI.transfer(src[i]);
  }
  digitalWrite (ChipSelect, HIGH);
  uint32_t t = us;
  Serial.print("Time for non-DMA transfer: ");Serial.print(t);Serial.println("us");
  SPI.endTransaction();

  
  delay(500);

  SPISettings(26000000, MSBFIRST, SPI_MODE0);             // Seems to have no affect

  DMASPI0.begin();
  DMASPI0.start();
  DmaSpi::Transfer trx(nullptr, 0, nullptr);


  Serial.println("Testing src -> dest, single transfer");
  Serial.println("--------------------------------------------------");
  trx = DmaSpi::Transfer(src, DMASIZE, dest);
  clrDest((uint8_t*)dest);
  DMASPI0.registerTransfer(trx);                                                        // only seems to transfer at 4Mhz :(
  while(trx.busy())
  {
    digitalWrite (ChipSelect, LOW);
  }
  digitalWrite (ChipSelect, HIGH);
  Serial.println("Finished DMA transfer");
  Serial.println("==================================================\n\n");

  //************************ END OF TESTING

    Serial.println(" done");

    // need to add in Second SPI (SPI1) for osc DAC

}

Could someone point me at how to set the SPI Clock speed for the DMA SPI transfer please?

It would also be nice to know how to get the default Chip Select pin (10) to be driven by the DMA as well.

many thanks in advance
Paula
 
Last edited:
When you do something like: SPI.beginTransaction(SPISettings(26000000, MSBFIRST, SPI_MODE0))
The 26mhz is telling the system that the maximum speed your device can handle is 26mhz.

At which point the SPI system will convert that value into the highest speed it can that does not exceed your requested speed.
Note: a constructor to SPITransaction that has no parameters will default to request for 4mhz.
We then find the best divisor from a system clock to get as close as we can.

Note a statement like: SPISettings(26000000, MSBFIRST, SPI_MODE0);
does nothing, it simply creates an object that goes away without doing anything.
Only place it does anything is when you pass the SPISettings into a beginTransaction call.

Note: You can create static one like:
SPISettings mysettings = new SPISettings(26000000, MSBFIRST, SPI_MODE0);

Or the like.

As for why you are only going at 4mhz? Not sure, I don't use this library but setup my own SPI structures.

Again is this your whole code?
 
Thanks for the quick response. The only other code is setting a few variable names and of course the setup and loop, I'm using platformio (as the arduino editor was driving me nuts).

as you say, the SPISetting line was doing nothing, I was just 'playing' to see if I could get it work, but to no avail.

As for the 26000000 giving me 25Mhz, I tried setting the speed to 25000000 and I get 20Mhz

Here's my code now;

Code:
// Initialise stuff
void initSPI(void)
{
 
    Serial.print("init SPI:");

    SPI.begin();
    SPI.beginTransaction(SPISettings(26000000, MSBFIRST, SPI_MODE0));   // for some reason, I get 20Mhz when the rate is set to 25000000
                                                                        // I do get 25Mhz, when the rate is set to 26000000... huh?

    SPI2.begin();
    SPI2.beginTransaction(SPISettings(26000000, MSBFIRST, SPI_MODE0)); 


  delay(3000);
  Serial.println("regular SPI transer test");
  setSrc();
  elapsedMicros us;
  digitalWrite (ChipSelect, LOW);
  for (size_t i = 0; i < DMASIZE; i++)
  {
    SPI.transfer(src[i]);
  }
  digitalWrite (ChipSelect, HIGH);

  uint32_t t = us;
  Serial.print("Time for non-DMA transfer: ");Serial.print(t);Serial.println("us");
  SPI.endTransaction();


  delay(500);
  DMASPI0.begin();
  DMASPI0.start();
  DmaSpi::Transfer trx(nullptr, 0, nullptr);


  Serial.println("Testing src -> dest, single transfer");
  Serial.println("--------------------------------------------------");
  trx = DmaSpi::Transfer(src, DMASIZE, nullptr);

  DMASPI0.registerTransfer(trx);        // THIS dumps the buffer and lets the code resume, PERFECT
                                        // However it does NOT toggle the CS line :(
                                        // also had to edit speed in this file - C:\Users\Paula\.platformio\packages\framework-arduinoteensy\libraries\SPI (line 1043)
  
  // Need to figure out how remove this stuff below

  while(trx.busy())
  {
    digitalWriteFast(ChipSelect, LOW);
  }
  digitalWriteFast(ChipSelect, HIGH);

  // Need to figure out how to remove the above and have the Chip Select toggling as part of the DMA transfer.
 

  Serial.println("Finished DMA transfer");
  Serial.println("==================================================\n\n");

 
    Serial.println(" done");

   // need to add in Second SPI (SPI1) for osc DAC

}

I found I had to edit the line in the spi.h driver to change the clock setting, there doesn't seem to be a way to set the speed and mode for the DMASPI0

I'm ok with C, but C++ isn't something I'm at all familiar with, I'm still very much finding my feet.
 
ok, I eventually figured it out, at least for SPI0 so I thought I'd share.

There's an extra line needed "ActiveChipLowSelect"
And there's some extra paramters that need passing to the transfer.

of note, I'm only interested in sending data, so I don't bother with a receive buffer.

Code:
#include <Arduino.h>
#include <SPI.h>
#include <DmaSpi.h>


#define DMASIZE 100
uint8_t src[DMASIZE];
volatile uint8_t dest[DMASIZE];
volatile uint8_t dest1[DMASIZE];

void setSrc()
{
  for (size_t i = 0; i < DMASIZE; i++)
  {
    src[i] = i;
  }
}

void initSPI(void)
{
 
    Serial.print("init SPI:");

    SPI.begin();

    // SPI0 (DMA based)

    delay(3000);
    setSrc();         // for testing


    DMASPI0.begin();
    DMASPI0.start();
    DmaSpi::Transfer trx(nullptr, 0, nullptr);

    ActiveLowChipSelect cs(10, SPISettings(26000000, MSBFIRST, SPI_MODE0));          /// gives me 25Mhz with digital pin 10 as Chip Select
    DmaSpi::Transfer(nullptr, DMASIZE0, nullptr, 0, &cs);
    DMASPI0.start();
    trx = DmaSpi::Transfer(src0, DMASIZE0, nullptr, 0, &cs);
    DMASPI0.registerTransfer(trx);

    // SPI 2


    SPI2.begin();
    SPI2.beginTransaction(SPISettings(26000000, MSBFIRST, SPI_MODE0)); 


    Serial.println(" done");


}
 
Your code doesn't compile for me. It gives the errors

Code:
'DMASPI0' was not declared in this scope

Code:
'DMASIZE0' was not declared in this scope

and

Code:
'src0' was not declared in this scope

Any idea why?
 
Your code doesn't compile for me. It gives the errors

Code:
'DMASPI0' was not declared in this scope

Code:
'DMASIZE0' was not declared in this scope

and

Code:
'src0' was not declared in this scope

Any idea why?
Looks sort of like a quick and dirty cut and paste of fragments that don't compile...

I can get to mostly compile if I change all references of DMASIZE0 -> DMASIZE
and src0->src

But again it is not a complete thing, that is there is no setup() no loop()...
 
Status
Not open for further replies.
Back
Top