Teensy 3.5 SPI Interrupt Routine on SPI Bus 1 or 2

Status
Not open for further replies.

JSON

Member
For a project I would like to use the SPI bus 1 or 2 as SPI slave and accept commands from the master using the interrupt routine and answer accordingly.
Since the SPI bus 0 of the Teensy is already occupied by an RFID reader (RC255) I have to switch to another SPI bus.

Here is my code, which works on an Arduino UNO without any problems:
Code:
#include <SPI.h>

volatile byte answerOne[] = {22,22,16,2,6,2,247,16,3}; //"L" answer
volatile byte answerTwo[] = {22,22,16,2,'i',7,0,0,0,0,1,142,16,3}; // "O" answer
//byte address[8] = {0x28, 0xFF, 0x15, 0xC6, 0x62, 0x15, 0x1, 0x71};
volatile int state_s1 = 0;
volatile int state_prev_s1 = 0;
volatile int answerType = 0;
volatile int counter = 0;

void setup (void)
{
  Serial.begin (1000000);   // debugging
  
  // turn on SPI in slave mode
  SPCR |= bit (SPE);

  // have to send on master in, *slave out*
  pinMode(MISO, OUTPUT);
  
  // now turn on interrupts
  SPI.attachInterrupt();

}  // end of setup


// SPI interrupt routine
ISR (SPI_STC_vect)
{
byte c = SPDR;  // grab byte from SPI Data Register
state_prev_s1 = state_s1;
switch (state_s1){
  case 0: //START
    if(c == 22){
      state_s1 = 1;
    }
    SPDR = 0;
  break;

  case 1: // 2nd byte
    if(c == 22){
      state_s1 = 2;
    }else{
      state_s1 = 0;
    }
    SPDR = 0;   
  break;

  case 2: // 3rd byte
    if(c == 16){
      state_s1 = 3;
    }else{
      state_s1 = 0;
    }
    SPDR = 0;
  break;

  case 3: // 4th byte
    if(c == 2){
      state_s1 = 4;
    }else{
      state_s1 = 0;
    }
    SPDR = 0;
  break;

  case 4: // 5th byte
    if(c == 'L'){
      answerType = 1;
      state_s1 = 5;
    }else if(c == 'O'){
      answerType = 2;
      state_s1 = 5;
    }else{
      state_s1 = 0;
    }
    SPDR = 0;
  break;

  case 5: //1th end byte
    if(c == 16){
      state_s1 = 6;
    }
    SPDR = 0;
  break;

  case 6: // 2nd end byte
    if(c == 3){
      state_s1 = 7;
    }else{
      state_s1 = 5;
    }
    SPDR = 0;
  break;

  case 7: // Send Answer
    if(answerType == 1){
      if(answerOne[counter] == 3){
        SPDR = answerOne[counter];
        state_s1 = 0;
        counter = 0;
        break;
      }
      SPDR = answerOne[counter++];
    }else if(answerType == 2){
      if(answerTwo[counter] == 3){
        SPDR = answerTwo[counter];
        state_s1 = 0;
        counter = 0;
        break;
      }
       SPDR = answerTwo[counter++];
    }
    testPrint(counter);
  break;

}     
}  // end of interrupt routine SPI_STC_vect




void testPrint(int c){
  Serial.print("Question complete! - Answer type=");
  Serial.print(answerType);
  Serial.print(" - Counter = ");
  Serial.println(c);
}

// main loop - wait for flag set in interrupt routine
void loop (void)
{
  if(state_prev_s1 != state_s1){
    Serial.println(state_s1);
  }
  

}  // end of loop

How can I get the interrupt routine to use the SPI bus 1 or 2 from the teensy?
 
Does anybody have an idea, or does anybody know?

Edit: I've been searching the internet a bit and I think I need the right interrupt number. Which is the correct interrupt number for SPI bus 1 and 2? Where is such a thing documented?
 
Last edited:
ISR(Vector) works only on AVR but not on ARM processors like the Teensy. The SPI library for the Teensy does AFAIK not support attachinterrupt().
 
The ISR page you mentioned is for the older Teensy 2 which is also AVR based.

Yes Teensy 3.x (and soon to be Teensy 4) also support Interrupts and have a set of interrupts...

The SPCR, SPSR, SPDR objects that are referenced in your code are AVR specific registers. Which are for the one and only SPI hardware on things like the Arduino UNO...

There is code in place on the Teensy to emulate some of the old AVR SPI code, although I am not sure if ever any of the interrupt processing was emulated specifically for this particular case.

You should instead use the SPI library to access the different hardware busses, like SPI, SPI1, SPI2.

Note I have done very little to use SPI in client mode (almost always in master mode). So not sure how much help I would be able to give. Again even if I could help some here, would never spend my time trying to horseshoe the functionality by trying to emulate an AVR board, but instead would either use existing libraries and/or look at the SPI section of the Teensy 3.x manual I was using.

Good luck
Kurt
 
The ISR page you mentioned is for the older Teensy 2 which is also AVR based.

Yes Teensy 3.x (and soon to be Teensy 4) also support Interrupts and have a set of interrupts...

The SPCR, SPSR, SPDR objects that are referenced in your code are AVR specific registers. Which are for the one and only SPI hardware on things like the Arduino UNO...

There is code in place on the Teensy to emulate some of the old AVR SPI code, although I am not sure if ever any of the interrupt processing was emulated specifically for this particular case.

You should instead use the SPI library to access the different hardware busses, like SPI, SPI1, SPI2.

Note I have done very little to use SPI in client mode (almost always in master mode). So not sure how much help I would be able to give. Again even if I could help some here, would never spend my time trying to horseshoe the functionality by trying to emulate an AVR board, but instead would either use existing libraries and/or look at the SPI section of the Teensy 3.x manual I was using.

Good luck
Kurt

Thank you for the detailed answer. I'd like to try the library of tonton81, but here I don't have the possibility to answer the master as I understand it...do I?!

As described in my first post I have to send a 0 (zero) back to the master after every byte sent by the master and after the message from the master has arrived completely I have to send the answer. Either answerOne[] or answerTwo[].

@tonton81 Does this work with your library? If so, do you have a short example or approach for me? If not, can someone recommend a suitable library?
 
Last edited:
As mentioned the SPI library only supports master mode. But the chips do support slave mode.

If you do a search on google like: teensy spi slave
You will see several thread that hit, plus a few libraries:
Like: https://forum.pjrc.com/threads/49315-Teensy-3-6-SPI-Slave-Mode-in-Teensyduino

Here @tonton81 does give a link to another thread with zip file...

As for slave responding, I know it can. There are always interesting things to look at like how many bytes that are received by the slave, before it can respond and the master get the response.

A lot of this depends on things like how the Fifo queues are configured and the like. I did play around once with it, but did not take it very far... Some day maybe.
 
As mentioned the SPI library only supports master mode. But the chips do support slave mode.

If you do a search on google like: teensy spi slave
You will see several thread that hit, plus a few libraries:
Like: https://forum.pjrc.com/threads/49315-Teensy-3-6-SPI-Slave-Mode-in-Teensyduino

Here @tonton81 does give a link to another thread with zip file...

Thanks again KurtE, that has helped a lot.

During my search I found the following library of tonton81 and defragster. It looks very promising and I will give it a try.

https://github.com/Defragster/SPI_MSTransfer
 
tonton81 p#5 linked another above - not sure if that is the newer better version for your purpose.

I think this is the library I need!!! I took a closer look at the example and try it out on Monday.
“popr and pushr are used to read and write respectively the data once the available() is triggered” -> that’s exactly what I need.
As soon as I have a result I will get back to you.
 
I tried to set up a SPI slave with a Teensy 3.5 using the library of tonton81 (tspi_slave). But it is not working!!!
I use the SPI Bus 1 of the Teensy 3.5 and 8 bit word length for the slave contrary to the example from the library.

I made a Teensy 3.6 the SPI master with the following code:

Code:
// Written by Nick Gammon
// February 2011


#include <SPI.h>

void setup (void)
{

  digitalWrite(SS, HIGH);  // ensure SS stays high for now

  // Put SCK, MOSI, SS pins into output mode
  // also put SCK, MOSI into LOW state, and SS into HIGH state.
  // Then put SPI hardware into Master mode and turn SPI on
  SPI.begin ();

  // Slow down the master a bit
  SPI.setClockDivider(SPI_CLOCK_DIV8);
  
}  // end of setup


void loop (void)
{

  char c;

  // enable Slave Select
  digitalWrite(SS, LOW);    // SS is pin 10

  // send test string
  for (const char * p = "Hello, world!\n" ; c = *p; p++)
    SPI.transfer (c);

  // disable Slave Select
  digitalWrite(SS, HIGH);

  delay (5000);  // 1 seconds delay 
}  // end of loop

That this works without problems I could proof with a Saleae Logic Analyser.


The slave has the following code and does not react to the commands of the master:
Code:
#include "TSPISlave.h"
//0,1,31,32 SPI1
uint8_t miso = 1;
uint8_t mosi = 0;
uint8_t sck = 32;
uint8_t cs = 31;
uint8_t spimode = 8;

TSPISlave mySPI = TSPISlave(SPI1, miso, mosi, sck, cs, spimode);

void setup() {
  Serial.begin(115200);
  mySPI.onReceive(myFunc);
}

void loop() {
  delay(1000);
  Serial.println(millis());
}

void myFunc() {
  Serial.println("START: ");
  uint8_t arr[2] = { 0x11, 0x22 };
  uint8_t i = 0;
  while (mySPI.active()) {
    if (mySPI.available()) {
      mySPI.pushr(arr[i++]);
      Serial.print("VALUE: 0x");
      Serial.println(mySPI.popr(), HEX);
    }
  }
  Serial.println("END");
}

Can someone tell me what is wrong here? Best tonton81 :)
 
But it is not working!!!

How do you expect getting help if you do not give more precise information about WHAT is not working... is it that myFunc() is not called, or is it called but no reply sent? What about the wiring? Crossed MISO and MOSI? Common GND? Did you get the unmodified example working on the default SPI, at first? etc. etc. etc.

Nobody here, even not tonton81 can do telepathy. You really should learn on how to break down and describe a problem scientifically.
 
yes, just be careful the example responds 2 bytes in an array, so either have master send 2 bytes or have the array pointer reset. Teensy to teensy lines are different between 3.x and LC, on one one can be miso to miso, other can be mosi to miso. I’m not exactly sure since it’s been awhile I played with SPI, be sure you have common ground between the 2 boards.

I would also suggest using SPI transactions instead for your master.
 
Thank you, you're right. Here comes a more detailed error description. The slave code is compiled without problems only the function myfunction() is not called. I have crosswired MISO and MOSI as in the example and tried it parallel, unfortunately without success.
 
the isr will only fire if the CS is fired with a common ground, i dont see how it wont work the only issues we faced in the past were bad jumper cables or connections

since the callback wont fire unless the hardware is triggered, i’d check the wiring. usually a common ground isn’t enough during tests, we had to go from GND pin next to pin0 from teensy to teensy directly
 
Alright I have a connected the ground of the teensys directly. Pin 0 -> Pin 0
so everything should be fine. I will check my cables again...

the isr will only fire if the CS is fired
What if I do have a master without CS? Can I just keep CS permanently HIGH and it will work?
 
no, well, yes but the array will continuously run in response, at least if you toggle you can fill new data, the function callback cuts out the moment the CS is deasserted

make sure you are using MSBFIRST mode 0 on master
 
Ok I have tried all the tips, but without success. Here is my setup:
I connected a Teensy 3.5 via USB, this is the SPI Slave. I am using SPI Bus 1.

The Teensy 3.6 gets its power supply from the Teensy 3.5 (PIN 3.3V to VIN and GND to GND). Here I use the standard ports for SPI. The master works according to Saleae Logic Analyser without problems.

The code from the upper posts has not changed.

I have measured the voltage at the CS pin and come to about 3.1 volts. Is that enough to fire the isr?

@tonton81 Can I use the library without CS? What would the construction look like then?
Code:
TSPISlave mySPI = TSPISlave(SPI1, miso, mosi, sck, cs <--!!!, spimode);
 
Without looking at the library’s source code, I’d guess that the interrupt is fired not on the absolute level on the cs pin, but on a transition from high to low, i.e. when the master de-asserts the cs pin after having send it’s stuff. But to be shure, look how it’s configured.
 
Status
Not open for further replies.
Back
Top