IntervalTimer and SPISlave_T4 library conflict?

ggmd

New member
Hi, i am using IntervalTimer and SPIslave libraries to make a system.

The system's schematic diagram is
1721811275322.png

What i'm trying to do is read Encoder value from Motor Encoder and IMU signal from rp2040 nano (SPI) and save these data into SD.
It works fine without using interrupt (put code in loop), but if i put code into interval timer, Encoder values are shifted.

1721811631305.png
1721811647222.png


Left chart is logged encoder data when i put code into loop, right one is encoder data when i use interval timer.
Starting point (0 degree) is fixed so it does not change. I rotated the motor few times and returned to starting point (0 degree)
While i'm using interval timer, it seems encoder values are wrong and shifted(not back to 0degree).

I'm not and expert in programming, maybe I miss something. But i assumed there is conflict between intevaltimer and SPISlave_T4 libraries.
It would be really appreciated if any helps.

Code:
#include <Encoder.h>
#include <SD.h>
#include <SPI.h>
#include <IntervalTimer.h>
#include <TimeLib.h>
#include "SPISlave_T4.h"
SPISlave_T4<&SPI, SPI_16_BITS> mySPI;
Encoder myEnc(0, 1);
IntervalTimer dataTimer;
#define LED_1 7
#define LED_2 5
#define SIGNAL_PIN 9
#define uint16 32678.0
#define gear_ratio 6.0
#define pulse 7040.0

float x_a,y_a,z_a;
const int chipSelect = BUILTIN_SDCARD;
double newPosition;

void setup()
{
  Serial.begin(115200);
  Serial.print("Initializing SD card...");

  // see if the card is present and can be initialized:
  if (!SD.begin(chipSelect)) {
    Serial.println("Card failed, or not present");
    while (1) {      analogWrite(LED_2,255);
      // No SD card, so don't do anything more - stay stuck here
    }
  }
  Serial.println("card initialized.");
  delay(100);
  mySPI.onReceive(myFunc);
  mySPI.begin();
  dataTimer.begin(Controller, 5000);
}

void loop() {
  /*
  newPosition = (myEnc.read()/(4.0*pulse*gear_ratio/360));
  File dataFile = SD.open("loop_test.csv", FILE_WRITE);
  if (dataFile) {
    dataFile.print(newPosition);
    dataFile.print(",");
    dataFile.print(x_a);
    dataFile.print(",");
    dataFile.print(y_a);
    dataFile.print(",");
    dataFile.println(z_a);
    //dataFile.println(newENC/(4.0*7040.0*6.0/360));
    dataFile.close();
    // print to the serial port too:
    Serial.println(newPosition);
    //Serial.print("          ");
    //Serial.println(newENC/(4.0*7040.0*6.0/360));
    
  }
  else {
    // if the file isn't open, pop up an error:
    analogWrite(LED_1,255);
         // wait for a second
  }
*/
  Serial.println(newPosition);
}
 
 
void Controller() {
  newPosition = (myEnc.read()/(4.0*pulse*gear_ratio/360));

  File dataFile = SD.open("timer_test.csv", FILE_WRITE);

  // if the file is available, write to it:
  if (dataFile) {
    dataFile.print(newPosition);
    dataFile.print(",");
    dataFile.print(x_a);
    dataFile.print(",");
    dataFile.print(y_a);
    dataFile.print(",");
    dataFile.println(z_a);

    dataFile.close();
    // print to the serial port too:

  }
  else {
    // if the file isn't open, pop up an error:
                // wait for a second
  }
}

void myFunc() {
  while (mySPI.active()) {
    if (mySPI.available()) {
      mySPI.pushr(2);
      x_a = (mySPI.popr());
      mySPI.pushr(0);
      y_a = (mySPI.popr());
      mySPI.pushr(0);
      z_a = (mySPI.popr());
    }
  }
}
 
Your IntervalTimer is set for a period of 5000 us, or 5 ms. It's fine to read your encoder from the ISR, but you should not write to the SD from the ISR. The reason is that it can take much longer than 5 ms to open and write to a file. Even if you keep the file open, it can take much longer than 5 ms to write to an SD file. The classic solution for a datalogger that writes to SD is to sample (read the encoder) from the ISR, and write the data to SD from loop(). Between the ISR and loop() is a buffer, so it looks like this:

Code:
     [ISR]--->[ring buffer]--->[loop]---->[SD]

There are a lot of posts on the forum about dataloggers that work this way, and if you look at the SdFat library example TeensySDIOLogger, that will help.

Since you are new to programming, though, you might want to try an intermediate step. The T4 has a lot of RAM, so a simple way for you to do things would be to create an array for your samples. In the ISR, read your encoder, write the value to the array, and increment a counter that will serve as an index to where to write the next value. When the counter reaches the size of the array, set a flag to indicate the array is full. In loop(), wait for the "array is full" flag to be set and then write all of the values from the array to your SD file. Keep in mind that when the array is full, you should stop writing to it from the ISR.
 
Back
Top