Can you use DueTimer and SPI libraries together?

Hi all, I’m trying to move to my Due from my Mega.
the code below runs a RGB LED cube.

I can get the interrupts working, checking with my oscilloscope. The interrupts code is from the DueTimer library, appears to work well, the time is set then doubled each pass of the cube until we reach the max resolution then sets. (Works when SPI.transfer(myData) is remarked out)

The SPI transfer uses the SPI library, however when I get to send SPI.transfer(myData), we seem to get lost in some loop. The interrupt only executes once, I can see this with my scope.

I have read though the DueTimer thread and see MarkEMarkEMark asked the same question some time ago. http://forum.arduino.cc/index.php?topic=130423.45 but don’t see how it was resolved.

To me it appears that I have a conflict with the two libraries, but since both libraries have been around for some time, I guess that this is not the case.

My question is can you use the DueTime library and the SPI library together?
If yes, what am I missing?
I’m new to the Due, but ok with Mega.

I have included the Setup and the Interrupt parts of my code below.

#include <SPI.h>

#include <DueTimer.h>

#define BAM_RESOLUTION 4    // EG 4 bit colour = 15 variation of R, G & B (4096 colours)
#define latch_pin (1<<23)   // Defines actual BIT or PortC "PIOC" for latch (LE)- Arduino Due Digital Pin 7
#define blank_pin (1<<24)   // Defines actual BIT or PortC "PIOC" for LEDS on/off (OE) -  Arduino Due Digital Pin 6

//****setup****
void setup() {

  //finally set up the Outputs
  pinMode (6, OUTPUT); // Sets PIOC bit 24 as output, blank_pin.
  pinMode (7, OUTPUT); // Sets PIOC bit 23 as output, latch_pin.
  pinMode(data_pin, OUTPUT);//MOSI DATA
  pinMode(clock_pin, OUTPUT);//SPI Clock


  //*** Here layer pins are set as outputs

  pinMode(23, OUTPUT);// Layer 1
  pinMode(25, OUTPUT);
  pinMode(27, OUTPUT);
  pinMode(29, OUTPUT);
  pinMode(31, OUTPUT);
  pinMode(33, OUTPUT);
  pinMode(35, OUTPUT);
  pinMode(37, OUTPUT);
  pinMode(39, OUTPUT);
  pinMode(41, OUTPUT);
  pinMode(43, OUTPUT);
  pinMode(45, OUTPUT);

  SPI.beginTransaction(SPISettings(25000000, MSBFIRST, SPI_MODE0));
 
 
  Timer3.attachInterrupt(UpdateCube);
  Timer3.start(1000);
 

}//***end setup***
//***MultiPlex BAM BEGIN***
void UpdateCube() { //Routine that occurs for each interrupt
  //**************  Tranfer  *****************************

  /********************************************************************************
    This method of transfer is using progressive extension of time.
    We double the interrupt clock until we reach our bam resolution then start again.
*/

  //***************************  LEDs off  **********************************************************
  //Turn all of the LEDs OFF, by writing a 1 to the blank pin (OE)
  PIOC  -> PIO_SODR = blank_pin ; // set output High (PC24)

  // *** This routine selects levels ***.

  switch (CubeLevel) {
    case 0://Level 1
      PIOC  -> PIO_CODR = layer12 ;//Turn off level 12 (PC18)
      PIOA  -> PIO_SODR = layer1; //Turn on level 1 (PA14)
      break;

    case 1: //Level 2
      PIOA  -> PIO_CODR = layer1; //Turn off level 1 (PA14)
      PIOD  -> PIO_SODR = layer2; //Turn on level 2 (PD0)
      break;

    case 2: //Level 3
      PIOD  -> PIO_CODR = layer2; //Turn off level 2 (PD0)
      PIOD  -> PIO_SODR = layer3; //Turn on level 3 (PD2)
      break;

    case 3: //Level 4
      PIOD  -> PIO_CODR = layer3; //Turn off level 3 (PD2)
      PIOD  -> PIO_SODR = layer4; //Turn on level 4 (PD6)
      break;

    case 4: //Level 5
      PIOD  -> PIO_CODR = layer4; //Turn off level 4 (PD6)
      PIOA -> PIO_SODR = layer5; //Turn on level 5 (PA7)
      break;

    case 5: //Level 6
      PIOA  -> PIO_CODR = layer5; //Turn off level 5 (PA7)
      PIOC  -> PIO_SODR = layer6; //Turn on level 6 (PC1)
      break;

    case 6: //Level 7
      PIOC  -> PIO_CODR = layer6; //Turn off level 6 (PC1)
      PIOC  -> PIO_SODR = layer7; //Turn on level 7 (PC3)
      break;

    case 7: //Level 8
      PIOC  -> PIO_CODR = layer7; //Turn off level 7 (PC3)
      PIOC  -> PIO_SODR = layer8; //Turn on level 8 (PC5)
      break;

    case 8: //Level 9
      PIOC  -> PIO_CODR = layer8; //Turn off level 8 (PC5)
      PIOC  -> PIO_SODR = layer9; //Turn on level 9 (PC7)
      break;

    case 9: //Level 10
      PIOC  -> PIO_CODR = layer9; //Turn off level 9 (PC7)
      PIOC  -> PIO_SODR = layer10; //Turn on level 10 (PC9)
      break;

    case 10: //Level 11
      PIOC  -> PIO_CODR = layer10; //Turn off level 10 (PC9)
      PIOA  -> PIO_SODR = layer11; //Turn on level 11 (PA20)
      break;

    case 11: //Level 12
      PIOA  -> PIO_CODR = layer11; //Turn off level 10 (PA20)
      PIOC  -> PIO_SODR = layer12; //Turn on level 11 (PC18)
      break;
  }  //switch (CubeLevel)


  //**** Send out the Layer data - 8 bytes (64 bits) per colour - Green, Red then Blue. ****
  // Note- No good using a loop as the loop slows down the shift time. Changing EG green[0] to green [Bam_bit] would give you the same result but doubles the transfer time.
  switch (BAM_Bit) { //The BAM bit will be a value from 0- to BAM_RESOLUTION, and only shift out the arrays corresponding to that bit.

    case 0: //One bit colour resolution
      //Green
      SPI.transfer(green[0][DataTransfer]);
      SPI.transfer(green[0][DataTransfer + 1]);
      SPI.transfer(green[0][DataTransfer + 2]);
      SPI.transfer(green[0][DataTransfer + 3]);
      SPI.transfer(green[0][DataTransfer + 4]);
      SPI.transfer(green[0][DataTransfer + 5]);
      SPI.transfer(green[0][DataTransfer + 6]);
      SPI.transfer(green[0][DataTransfer + 7]);

      //Red
      SPI.transfer(red[0][DataTransfer]);
      SPI.transfer(red[0][DataTransfer + 1]);
      SPI.transfer(red[0][DataTransfer + 2]);
      SPI.transfer(red[0][DataTransfer + 3]);
      SPI.transfer(red[0][DataTransfer + 4]);
      SPI.transfer(red[0][DataTransfer + 5]);
      SPI.transfer(red[0][DataTransfer + 6]);
      SPI.transfer(red[0][DataTransfer + 7]);

      //Blue
      SPI.transfer(blue[0][DataTransfer]);
      SPI.transfer(blue[0][DataTransfer + 1]);
      SPI.transfer(blue[0][DataTransfer + 2]);
      SPI.transfer(blue[0][DataTransfer + 3]);
      SPI.transfer(blue[0][DataTransfer + 4]);
      SPI.transfer(blue[0][DataTransfer + 5]);
      SPI.transfer(blue[0][DataTransfer + 6]);
      SPI.transfer(blue[0][DataTransfer + 7]);
      break;

    case 1: //Two bit colour resolution
     
      SPI.transfer(green[1][DataTransfer]);
      //ECT ECT -Same as Case 0 this also occurs for 3 bit, 4 bit colour and so on but choose not to put that code here as limited post space.
     
  }//switch_case
 
  PIOC  -> PIO_SODR = latch_pin ; // //Latch pin (LE) HIGH
  asm volatile("nop"); asm volatile("nop"); //short delay (May not bee needed)
  PIOC  -> PIO_CODR = latch_pin ; //Latch pin (LE) LOW
  PIOC  -> PIO_CODR = blank_pin ; // //Blank pin (OE) LOW to turn on the LEDs with the new data

  //********************************** LEDs ON  *****************************************************
  DataTransfer = DataTransfer + 8; //increment the DataTransfer variable by 8, which is used to load the shiftout buffer, next level is the next 8 bytes in the arrays
  //Reset to zero if max is reached, all layers of the cube have been on.
  if (++CubeLevel >= Size_Y) {
    CubeLevel = 0;
    DataTransfer = 0;

    if (++BAM_Bit >= BAM_RESOLUTION) {

      switch (BAM_RESOLUTION) {
        case 8:
          OnTime = 6;       //µs
          break;
        case 7:
          OnTime = 12;      //µs
          break;
        case 6:
          OnTime = 24;      //µs
          break;
        case 5:
          OnTime = 48;      //µs
          break;
        case 4:
          OnTime = 96;      //µs
          break;
        case 3:
          OnTime = 192;     //µs
          break;
        case 2:
          OnTime = 384;     //µs
          break;
        default:
          OnTime = 768;     //µs
          break;
      }//switch (BAM_RESOLUTION)
      BAM_Bit = 0;

    } else {
      OnTime <<= 1; //This doubles the last interrupt interval.

    }//if (++CubeLevel >= Size_Y)
 //   Serial.println(OnTime);
    Timer3.start(OnTime);   //Updates the interrupt clock with new time interval.

  }//************** END Tranfer ************************/


}//***MultiPlex BAM END***

First, the SPI subsystem is totally independent of the timers. There's nothing that you can do which will make those interfere with each other.

Second, don't post parts of your code. Invariably you post the pieces you understand and don't post the pieces you don't understand. Guess where the problem is? In the code you didn't post.

I don't see anywhere in your code where you set the CS (chip select) pin for your device. On the AVR Arduinos this is critical: if you leave pin 10 as an input, then SPI is in slave mode and random noise on that pin can disable your program. I forget if it is that critical on the Due, but I suspect that it is.

Thanks for replying. Here all of my code. I have removed all but one SPI.transfer. If I remark this transfer out the interrupts works, with it it does not. It appears to get to the SPI.transfer then stop, get lost.
I have also add pin ten as output (SPi- CS0), no change… Had to remove lots of comments to remain under 9000 characters

#include <SPI.h>
#include <DueTimer.h>
#define BAM_RESOLUTION 4    // EG 4 bit colour #define latch_pin (1<<23)   // Defines actual BIT or PortC "PIOC" for latch (LE)- Arduino Due Digital Pin 7#define blank_pin (1<<24)   // Defines actual BIT or PortC "PIOC" for LEDS on/off (OE) -  Arduino Due Digital Pin 6
#define data_pin 75         // used by SPI,  MOSI 
#define clock_pin 74        // used by SPI,  SCK 
#define layer1 (1<<14)      //(PA14) PortA Arduino Due Digital Pin 23 = Bottom layer 
#define layer2 (1<<0)       //(PD0) PortD Arduino Due Digital Pin 25
#define layer3 (1<<2)       //(PD2) PortD Arduino Due Digital Pin 27
#define layer4 (1<<6)       //(PD6) PortD Arduino Due Digital Pin 29
#define layer5 (1<<7)       //(PA7) PortA Arduino Due Digital Pin 31
#define layer6 (1<<1)       //(PC1) PortC Arduino Due Digital Pin 33
#define layer7 (1<<3)       //(PC3) PortC Arduino Due Digital Pin 35
#define layer8 (1<<5)       //(PC5) PortC Arduino Due Digital Pin 37
#define layer9 (1<<7)       //(PC7) PortC Arduino Due Digital Pin 39
#define layer10 (1<<9)      //(PC9) PortC Arduino Due Digital Pin 41
#define layer11 (1<<20)     //(PA20) PortA Arduino Due Digital Pin 43
#define layer12 (1<<18)     //(PC18) PortC Arduino Due Digital Pin 45 = Top layer 

//******* Defining the Cube *******
const  byte Size_Y = 12;//Number of Layers Y axis
const  byte Size_X = 8; //Number of LEDs X axis
const  byte Size_Z = 8; //Number of LEDs Z axis
const  int Size_Layer = Size_X * Size_Z; // Number of LEDs per Layer
const  int Size_Cube = Size_Layer * Size_Y; //Number of LEDs whole Cube.
//const  int Size_CW = ((1 << BAM_RESOLUTION) - 1) * 6; //Number of steps in colour wheel
//byte Colour[Size_CW][3]; //Used to store colour for the cube in R, G and B = [3]
//******* Cube Array *******
byte red[BAM_RESOLUTION][Size_Cube / 8];
byte green[BAM_RESOLUTION][Size_Cube / 8];
byte blue[BAM_RESOLUTION][Size_Cube / 8];
// Interrupt variables
int DataTransfer = 0; //keeps track of which byte we are shifting data to
int CubeLevel = 0; //this increments through the anode levels
int BAM_Bit, BAM_Counter = 0; // Bit Angle Modulation variables to keep track of things
static int OnTime;

void setup() {
  //noInterrupts();// kill interrupts until everybody is set up

  //finally set up the Outputs
  pinMode (6, OUTPUT); // Sets PIOC bit 24 as output, blank_pin.
  pinMode (7, OUTPUT); // Sets PIOC bit 23 as output, latch_pin.
  pinMode(data_pin, OUTPUT);//MOSI DATA
  pinMode(clock_pin, OUTPUT);//SPI Clock
   pinMode (10, OUTPUT); // Sets 


  //*** Here layer pins are set as outputs
  pinMode(23, OUTPUT);// Layer 1
  pinMode(25, OUTPUT);
  pinMode(27, OUTPUT);
  pinMode(29, OUTPUT);
  pinMode(31, OUTPUT);
  pinMode(33, OUTPUT);
  pinMode(35, OUTPUT);
  pinMode(37, OUTPUT);
  pinMode(39, OUTPUT);
  pinMode(41, OUTPUT);
  pinMode(43, OUTPUT);
  pinMode(45, OUTPUT);
  SPI.beginTransaction(SPISettings(25000000, MSBFIRST, SPI_MODE0));
 
  pinMode(myLed, OUTPUT);
  
  Timer3.attachInterrupt(UpdateCube);
  Timer3.start(1000);
  Serial.begin(115200);

}//***end

void loop() {
 LED(1,1,1,15,0,0);
}
void LED(int CY, int CX, int CZ, int CR, int CG, int CB) { //****LED Routine
  // First, check and make sure nothing went beyond the limits, 
  CY = constrain(CY, 0, Size_Y - 1);//Cube Y axis
  CX = constrain(CX, 0, Size_X - 1);//Cube X axis
  CZ = constrain(CZ, 0, Size_Z - 1);//Cube Y axis
  CR = constrain(CR, 0, (1 << BAM_RESOLUTION) - 1); //Cube Red
  CG = constrain(CG, 0, (1 << BAM_RESOLUTION) - 1); //Cube Green
  CB = constrain(CB, 0, (1 << BAM_RESOLUTION) - 1); //Cube Blue
 int WhichByte = (CY * Size_Layer + CX * Size_X + CZ) / 8;
WhichByte) = 4
  int WhichBit = (CY * Size_Layer + CX * Size_X + CZ) - (8 * WhichByte) ;
 for (byte I = 0; I < BAM_RESOLUTION; I++) {
    //*** RED ***
    bitWrite(red[I][WhichByte], WhichBit, bitRead(CR, I));
    //*** GREEN ***
    bitWrite(green[I][WhichByte], WhichBit, bitRead(CG, I));
    //*** BLUE ***
    bitWrite(blue[I][WhichByte], WhichBit, bitRead(CB, I));
  }
}//****LED ROUTINE END****


//***MultiPlex BAM BEGIN***
void UpdateCube() { //Routine that occurs for each interrupt
 //Turn all of the LEDs OFF, by writing a 1 to the blank pin (OE)
  PIOC  -> PIO_SODR = blank_pin ; // set output High (PC24)

  // *** This routine selects levels ***.

  switch (CubeLevel) {
    case 0://Level 1
      PIOC  -> PIO_CODR = layer12 ;//Turn off level 12 (PC18)
      PIOA  -> PIO_SODR = layer1; //Turn on level 1 (PA14)
      break;

    case 1: //Level 2
      PIOA  -> PIO_CODR = layer1; //Turn off level 1 (PA14)
      PIOD  -> PIO_SODR = layer2; //Turn on level 2 (PD0)
      break;

    case 2: //Level 3
      PIOD  -> PIO_CODR = layer2; //Turn off level 2 (PD0)
      PIOD  -> PIO_SODR = layer3; //Turn on level 3 (PD2)
      break;
    case 3: //Level 4
      PIOD  -> PIO_CODR = layer3; //Turn off level 3 (PD2)
      PIOD  -> PIO_SODR = layer4; //Turn on level 4 (PD6)
      break;
    case 4: //Level 5
      PIOD  -> PIO_CODR = layer4; //Turn off level 4 (PD6)
      PIOA -> PIO_SODR = layer5; //Turn on level 5 (PA7)
      break;
    case 5: //Level 6
      PIOA  -> PIO_CODR = layer5; //Turn off level 5 (PA7)
      PIOC  -> PIO_SODR = layer6; //Turn on level 6 (PC1)
      break;
    case 6: //Level 7
      PIOC  -> PIO_CODR = layer6; //Turn off level 6 (PC1)
      PIOC  -> PIO_SODR = layer7; //Turn on level 7 (PC3)
      break;
   case 7: //Level 8
      PIOC  -> PIO_CODR = layer7; //Turn off level 7 (PC3)
      PIOC  -> PIO_SODR = layer8; //Turn on level 8 (PC5)
      break;
    case 8: //Level 9
      PIOC  -> PIO_CODR = layer8; //Turn off level 8 (PC5)
      PIOC  -> PIO_SODR = layer9; //Turn on level 9 (PC7)
      break;
   case 9: //Level 10
      PIOC  -> PIO_CODR = layer9; //Turn off level 9 (PC7)
      PIOC  -> PIO_SODR = layer10; //Turn on level 10 (PC9)
      break;
    case 10: //Level 11
      PIOC  -> PIO_CODR = layer10; //Turn off level 10 (PC9)
      PIOA  -> PIO_SODR = layer11; //Turn on level 11 (PA20)
      break;
    case 11: //Level 12
      PIOA  -> PIO_CODR = layer11; //Turn off level 10 (PA20)
      PIOC  -> PIO_SODR = layer12; //Turn on level 11 (PC18)
      break;
  }  //switch (CubeLevel)

  SPI.transfer(green[0][DataTransfer]);
  PIOC  -> PIO_SODR = latch_pin ; // //Latch pin (LE) HIGH
  asm volatile("nop"); asm volatile("nop"); //short delay (May not bee needed)
  PIOC  -> PIO_CODR = latch_pin ; //Latch pin (LE) LOW
  PIOC  -> PIO_CODR = blank_pin ; // //Blank pin (OE) LOW to turn on the LEDs with the new data
 DataTransfer = DataTransfer + 8; 
  if (++CubeLevel >= Size_Y) {
    CubeLevel = 0;
    DataTransfer = 0;
    if (++BAM_Bit >= BAM_RESOLUTION) {
      switch (BAM_RESOLUTION) {
        case 8:
          OnTime = 6;       //µs
          break;
        case 7:
          OnTime = 12;      //µs
          break;
        case 6:
          OnTime = 24;      //µs
          break;
        case 5:
          OnTime = 48;      //µs
          break;
        case 4:
          OnTime = 96;      //µs
          break;
        case 3:
          OnTime = 192;     //µs
          break;
        case 2:
          OnTime = 384;     //µs
          break;
        default:
          OnTime = 768;     //µs
          break;
      }//switch (BAM_RESOLUTION)
      BAM_Bit = 0;

    } else {
      OnTime <<= 1; //This doubles the last interrupt interval.

    }//if (++CubeLevel >= Size_Y)
 //   Serial.println(OnTime);
    Timer3.start(OnTime);   //Updates the interrupt clock with new time interval.

  }//************** END Tranfer ************************/
}//***MultiPlex BAM END***

If I remark out this line, code interrupt works.

SPI.transfer(green[0][DataTransfer]);

I can see that i am getting past this line here PIOC -> PIO_SODR = blank_pin ; It goes and remains high, I expect because we don't progress any more after we reach SPI.transfer(green[0][DataTransfer]);

Ok, now we have access to your full code it beginning to make sense to an extent why it is not working.

How many devices do you have on SPI? Presumably the only purpose for SPI is shifting bit patterns to some sort of latch? What is the CS pin for that latch?

Regards,

Graham

24 Shift Registers chained together.So one, Graham.

But the good news, I have it working.

Fixed, sort of...

Everything works if I replace

“SPI.beginTransaction(SPISettings(25000000, MSBFIRST, SPI_MODE0));”

With this

SPI.begin(); SPI.setBitOrder(MSBFIRST); SPI.setDataMode(SPI_MODE0); SPI.setClockDivider(4);//Run the data in at 21MHz

Ummm………………… Bug?

Thanks MarkEMarkEMark - I looked at some of your old "light code", you had the two libraries working.

But now I can move on.

Fair enough, that's basically what I was going to tell you 2 hours ago, but it is also nice when people figure it out for themselves ;)

Regards,

Graham

To finish this thread, everything is now working.

To answer my original question, YES you can run both libraries together.

I’m also going to add, I’m a little disappointed at how slow the SPI library runs. :’(

The data is running out at 20.83 MHz/bit but there is 1.33µs between packets making the total upload time 43 µs. That’s 3 µs slower than I was achieving on the mega for the same data transfer @ 8MHz.

I dropped the SPI library on the mega and managed SPI in my code to pick the speed up.
I found that waiting for the Data Sent Flag to set (SPIF) was taking >20% of the total time required for the 24 packet upload, so we dropped the wait. This speed things up from 55µs to 40µs for 24 packets to send. I guess this is also an issue with the DUE SPI library. That can wait.

Next task is to make a shield to manage the 3.3 volts verse 5 volt issue, tomorrow.

Thanks to all that helped resolve my issues in this and my previous thread.

And Graham, I would have been OK with you jumping in two hours earlier… :smiling_imp:

If anyone is interested, my 768 RGB LED, 12 layer LED cube is now fully operational using my Due. :D

As for the 3.3 v Verse 5v issue, I just changed the voltage for the registers, now 3.3v.

Next task is to slow down my existing animations, LOL. :fearful: The first animation I tried was a simple sine wave, had to add 100 millisecond delays between frames to make it run like it was on the Mega, which was doing a great job by the way.

So why change to a Due? Two reasons, one speed of code, obviously. But the main reason is a cube can be memory hungry. EG 768 LEDs * 3 colours * 4 bit colour resolution = 9216 Bits/ 8 = 768 bytes.

Then times that by two for animations that require a buffer. Arrays for colour wheels and tables for angle in radians and distances from centre ECT ECT. Before long you start to get warnings that 90% of the 8kBytes for variables is used and programs maybe unstable. Time to update….. :smiling_imp:

Thanks to everyone that has put time in to my threads over the last two years, without YOU guys I would still be trying to understand “Blink”…. What do they mean “pinMode(13,output)” :roll_eyes: