Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 4 of 4

Thread: Teensy 4.0 DMA SPI Speed issues

  1. #1

    Teensy 4.0 DMA SPI Speed issues

    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 by Paula; Yesterday at 11:42 PM. Reason: additional question

  2. #2
    Senior Member+ KurtE's Avatar
    Join Date
    Jan 2014
    Posts
    5,934
    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?

  3. #3
    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.

  4. #4
    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");
    
    
    }

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •