How to measure execution time of timer interrupt?

Hello,

I have a timer interrupt function set to fire every 5 ms. I am using an Arduino UNO R3 and MsTimer2.h. The ISR has a bunch of functions (i know, it should be as short as possible, but in this case, i have no choice) and data from sensors are collected and then processed through some calculations and finally, the PWM values outputted to the motors.

My main question: How do i measure the execution time of the ISR? I want to display the number of milliseconds or microseconds for the whole ISR to be executed each time.
On a side note, i also want to prove that 5ms is enough for the entire ISR to be executed and additionally justify my choice of the Arduino UNO R3 which has only 16 MHz clock speed.

My attempt: (it's not a complete code ready to compile, as what matters is essentially the ISR timing issue)

unsigned long time_now = 0;
float time_ISR = 0;

void setup() 
{
  Serial.begin(9600);
  
  pinMode(right_R1,OUTPUT); 
  pinMode(right_R2,OUTPUT);
  pinMode(PWM_R,OUTPUT);
  pinMode(left_L1,OUTPUT);
  pinMode(left_L2,OUTPUT);
  pinMode(PWM_L,OUTPUT);

  pinMode(PinA_left,INPUT); 
  pinMode(PinA_right,INPUT);
  
  MsTimer2::set(5, inter);
  MsTimer2::start(); 
}

void loop() 
{
    Serial.print("The time taken to run the ISR: "); //display execution time to 2 d.p.
    Serial.print(time_ISR);
    Serial.println(" ms.");
}

//interrupt function
void inter()    
{
    time_now = millis();
    sei(); 
    
    sensordata();
    motor_encoder();
    calc();
    pwm();
    time_ISR = millis() - time_now;
}

I am using millis() in the first and last statements inside of the ISR to record the start and end time of execution of the whole ISR, but i'm not sure about using millis() inside an ISR. Isn't the millis function itself an interrupt? What are the possible ways of resolving this ISR timing problem?

Why do you think you need to do all of that on a timer interrupt? There’s an awful lot of stuff there that doesn’t belong in an interrupt. Usually you fire the interrupt and get the timing critical measurement and then put all the calculations and what-not in the loop function.

It looks like you’re trying to solve a different problem and going about it the wrong way. Looks like it will work now. But this could get you in real a jam later. This looks like poor design. Maybe we could help you do this in a smarter way.

As far as getting the time of your interrupt, use micros. It will give you a good time. If you need milliseconds to run an ISR then it is WAY too long and it will start having other consequences.

I updated my code to use micros():

unsigned long time_now = 0, time_ISR = 0;

void setup()
{
  Serial.begin(9600);
 
  pinMode(right_R1,OUTPUT);
  pinMode(right_R2,OUTPUT);
  pinMode(PWM_R,OUTPUT);
  pinMode(left_L1,OUTPUT);
  pinMode(left_L2,OUTPUT);
  pinMode(PWM_L,OUTPUT);

  pinMode(PinA_left,INPUT);
  pinMode(PinA_right,INPUT);
 
  MsTimer2::set(5, inter);
  MsTimer2::start();
}

void loop()
{
    Serial.print("The time taken to run the ISR: ");
    Serial.print(time_ISR);
    Serial.println(" us.");
}

//interrupt function
void inter()   
{
    time_now = micros();
    sei();
   
    sensordata(); //from IMU
    motor_encoder(); //from both DC motors
    calc(); //calculate angle
    pwm(); //send calculated PWM values to both DC motors
    time_ISR = micros() - time_now;
}

The basic problem to run my robot correctly (it’s a two-wheels DC motors self-balancing robot) is that i need to fire all these functions at a specific time interval (5 ms) and there is no way of doing that without using a timer interrupt. I could use micros or millis inside the loop function, but then i also have a bunch of Serial.print functions there and i’m also using Serial.available to check if any commands are being sent via the Arduino IDE Serial Monitor to control the robot. This all means that i have (maybe?) only one option; use a timer interrupt.

and there is no way of doing that without using a timer interrupt. I could use micros or millis inside the loop function, but then i also have a bunch of Serial.print functions there and i'

Baloney

I could use micros or millis inside the loop function, but then i also have a bunch of Serial.print functions there and i'm also using Serial.available to check if any commands are being sent via the Arduino IDE Serial Monitor to control the robot. This all means that i have (maybe?) only one option; use a timer interrupt.

None of those are reasons you need an interrupt for this. That sounds like a few microseconds of code. You certainly don't have to do all that stuff in the ISR. If you have a measurement that is critical that it be taken at 5ms to the nanosecond then take that reading and set a flag and let the loop handle the calculations.

I'm only telling you this because you're setting yourself up for failure. Take it from someone who's been down this road before. Or don't. You'll have fun until it bites you and then you'll have fun rewriting the whole thing.

Delta_G:
None of those are reasons you need an interrupt for this. That sounds like a few microseconds of code. You certainly don't have to do all that stuff in the ISR. If you have a measurement that is critical that it be taken at 5ms to the nanosecond then take that reading and set a flag and let the loop handle the calculations.

OK, i will try to fix the code based on your advice. I am setting a flag from within the ISR and if that flag is true, then the loop function will execute the corresponding functions.

Here is the skeleton of my code:

bool IMU_flag = false;

void setup()
{
  Serial.begin(9600);
 
  pinMode(right_R1,OUTPUT);
  pinMode(right_R2,OUTPUT);
  pinMode(PWM_R,OUTPUT);
  pinMode(left_L1,OUTPUT);
  pinMode(left_L2,OUTPUT);
  pinMode(PWM_L,OUTPUT);

  pinMode(PinA_left,INPUT);
  pinMode(PinA_right,INPUT);
 
  MsTimer2::set(5, inter);
  MsTimer2::start();
}

void loop()
{

// Bunch of Serial.print functions and Serial.available check.
    
if (IMU_flag == true) {
    calc(); //calculate angle
    pwm(); //send calculated PWM values to both DC motors
    IMU_flag = false;
}

}

//interrupt function
void inter()   
{
    sei();
    IMU_data(); //read data from IMU
    motor_encoder(); // read encoders values from both DC motors
    IMU_flag = true;
}

I need to read encoder values from two DC motors and the data (gyro and accelerometer) from the IMU every 5 ms. I hope it's not too much for the ISR? Are there any fixes and improvements to be made?

That flag variable needs to be volatile. So does any other variable that might be changed from the ISR.

Is this correct? I googled but i couldn't find a volatile bool type?

volatile bool IMU_flag = false;

And i store the IMU values in these global variable declarations:

int16_t ax, ay, az, gx, gy, gz;     //Define three-axis acceleration, three-axis gyroscope variables

So, this should become?

volatile int16_t ax, ay, az, gx, gy, gz;

Just google “C++ volatile”.

It’s a modifier, not a type. It lets the compiler know that it might be modified in some place other than where the compiler happens to be looking at that moment. So it doesn’t try to optimize it away because it is only modified in a function that is never called. Compiler doesn’t know that function might run as an interrupt.

I did a search based on your recommendation and i found this post with an excellent explanation by the great Nick Gammon. I'll post the link here for others who also encounter the same issue: c++ - Why the need to use the volatile keyword on global variables when handling interrupts in Arduino? - Arduino Stack Exchange

I was also wondering what if i didn't declare these variables from the ISR as volatile. Answer from that post, under "Summary":

Code may "appear to work" even if you don't take the above precautions. That is because the chances of the interrupt occurring at the exact wrong moment is fairly low (maybe 1 in 100, depending on the code size). But sooner or later it will happen at the wrong moment, and your code will either crash, or occasionally return the wrong results.

So for reliable code, pay attention to protecting access to shared variables.

That explanation is about protecting access with critical sections. That’s not about volatile.

What happens if you don’t use volatile is that the compiler sees that the variable is never modified anywhere in any function that is actually called so it optimized the variable away and just replaces it with a true or a false depending on what initial value it had.

The compiler doesn’t know that the ISR happens without being directly called. So it doesn’t count what happens in there.

The result is that despite the interrupt it seems the value of your variable never changes.