Clarification on using SPI within interrupts on the teensy 3.X series

Status
Not open for further replies.

Tomek

Well-known member
I have been trying to get to the bottom of using SPI during an interrupt with the teensy 3.X series. Documentation here suggests I need to configure the SPI library to be compatible with being used within an interrupt service routine. Ideally I have the isr service do a quick SPI transaction to read and update a volatile global variable, instead of simply checking a flag and requiring my code to always be polling to check if the spi transaction needs to be called for. The code would just need to call the global variable to get the most up to date version of it.

https://www.pjrc.com/teensy/td_libs_SPI.html is where I found some information,
and it suggests I need
Code:
SPI.usingInterrupt(interrupt)
If your program will perform SPI transactions within an interrupt, call this function to register the interrupt number or name with the SPI library. This allows beginTransaction() to prevent usage conflicts."

Now, I'm trying to figure out where in my sketch I indicate I'm using interrupts, and what the "interrupt" argument is asking for. Currently, the SPI library is not explicitly "included" in the main .ino, because the adafruit MAX3156 library does the SPI transaction and calls for SPI. Does that mean I need to add the interrupt awareness to the MAX3156 library's SPI configuration sequence instead of my startup code?
Secondly, what does the information mean by taking as input the "interrupt" that will be triggering SPI communication? Am I naming a Pin? A timer? An isr function? Not sure what I put within SPI.usingInterrupts(?HERE?);

Right now I am trying to modify the adafruit MAX3856 thermocouple chip library to work in "continuous mode" in which the chip automatically reads the thermocouple temperature and pulls down the DRDY line on the breakout board to trigger an interrupt that reads the current available temperature reading. I am trying to do this on four instances of the MAX3156 library, representing the 4 MAX3156 breakout boards I'm working with. They each have a DRDY pin that toggles a different digital interrupt capable pin on a teensy 3.5.

The thing with Adafruit's default library is that it uses a "one shot" reading mode of the chip, and has a blocking 250mS requirement to the one shot reading mode. So with 4 sensors all my cpu time would be spend in a delay function.

I am open to learning why my desire to make the spi transaction in the isr routines is a bad idea. I could also write it so that the isr routine just flips a flag, for which my main program is responsible to check and then make the spi updates to the temperature variable. Also if this is an OK practice, I would be great to figure out what I'm missing to configuring SPI.usingInterrupts(interrupt) :)!
 
Hopefully others can give you more information on using with interrupts... I have not done so myself.

But if it were me, I would probably setup, to have your interrupt pins just set some state variable and then have your main program check for these states and issue the SPI requests. This can give you more control on when each of these things happen...

Which library are you using? MAX31856?

Code:
void Adafruit_MAX31856::oneShotTemperature(void) {

  writeRegister8(MAX31856_CJTO_REG, 0x0);

  uint8_t t = readRegister8(MAX31856_CR0_REG);

  t &= ~MAX31856_CR0_AUTOCONVERT; // turn off autoconvert!
  t |= MAX31856_CR0_1SHOT;

  writeRegister8(MAX31856_CR0_REG, t);

  delay(250); // MEME FIX autocalculate based on oversampling
}


I assume this is the issue you ran into with the delay 250. Again if it were me, I would try by simply removing this delay.

This is used internal to a couple of main read functions. I would probably break up their calls like:
Code:
float Adafruit_MAX31856::readThermocoupleTemperature(void) {
  oneShotTemperature();

  int32_t temp24 = readRegister24(MAX31856_LTCBH_REG);
  if (temp24 & 0x800000) {
    temp24 |= 0xFF000000;  // fix sign
  }

  temp24 >>= 5;  // bottom 5 bits are unused

  float tempfloat = temp24;
  tempfloat *= 0.0078125;

  return tempfloat;
}
Into two parts. The simple call to oneshot... And then a second part that you would call after the needed time elapsed.

Then maybe have your code use something like an elapsedMillis object for each of these... Or maybe just one if you want to read all of them right after each other...

But then the code might look like:
Code:
elapsedMillis em_sensor1 = 0;
...

float read_my_sensor1() {
    int32_t temp24 = sensor1.readRegister24(MAX31856_LTCBH_REG);
     if (temp24 & 0x800000) {
        temp24 |= 0xFF000000;  // fix sign
    }

     temp24 >>= 5;  // bottom 5 bits are unused

      float tempfloat = temp24;
      tempfloat *= 0.0078125;

    return tempfloat;
}

void loop() {
    if (em_sensor1 >= 250) {
        temp_1 = read_my_sensor1();
        sensor1.oneShotTemperature(); // start off next read
        em_sensor1 = 0;
    }
 ...
}
Again many ways to do the above, I would probably add the read/calculate method as part of the Adafruit library, versus separate calls for my 4 sensors... Again as mentioned could have four different elapsed millis timers, or maybe you can initiate and read all 4 right after each other... You can also use the interrupt you mentioned, to set state variable and check that in loop, doing the same I showed, except launching the next read...

Hope that helps
 
Hi Kurt!

Ultimately I think you are correct, I should do things that way. One reason I changed to automatic-mode however is because it is actually a faster method internally, the one-shot reading can draw less power (by only reading whenever you actually care), but takes more time per the datasheet (the approximate time before being ready for reading is ~20-30% greater). The other thing is, I pretty much just need to comment out the oneshottemperature() function when I'm in auto-conversion mode!:) Because the library is "chunked" in a way as you describe.

But I am really presenting my two changes, (1) automatic conversion [which I learned about from adafruit forums and not trying to claim is "my" change, along with (2) interrupt use of the DRDY as if they were a single change. If we separate the two things then I think I would be OK with having the automatic conversion mode on, and the DRDY set a flag that another tempUpdate program checks before updating. This means that if the temperature updates on the chip internally twice between me calling tempUpdate, I lose the 1st update, but that would be the case even if the update happened within the interrupt.

-Tomek
 
Status
Not open for further replies.
Back
Top