How to count how many times a timer runs in a loop?

Hello all,

I’m trying to come up with a piece of code that can determine how many times a timer cycles during one run of a loop.

For example: Lets say a timer counts up from 0 - 50. It starts at 24, the timer runs 4 times and lands on 34. AKA, counts from (24 to 50) + (0 to 50) + (0 to 50) + (0 to 50) + (0 to 34). I need a piece of code that determines the amount of timer cycles to accurately determine the amount of elapsed counts it took to run the loop, and therefore accurately determine the elapsed time of the loop.

I would need this code in a specific part of my quadcopter project so here is all the code that is related:

//Hardware Timer
volatile bool system_calculate = false, repeat_counter = false;
volatile int repeat = 0;
int start_count, end_count, elapsed_count, type;
int greatest_elapsed_count;
double elapsed_time;
const int max_count = 59998;
double average = 0;
bool allow_count = false;
volatile int loop_count = 0;

void setup()
{
  // put your setup code here, to run once:
  setup_system_timer();
}

void loop()
{
  //REG_TC5_COUNT16_COUNT = Current count in the timer
  // put your main code here, to run repeatedly:
  //if (system_calculate) Serial.println("\nSystem Calculate True");
  //else Serial.println("\nSystem Calculate False");
  //if (system_calculate)
  //{
  start_count = REG_TC5_COUNT16_COUNT;
  repeat_counter = true;
  //Serial.println("start");

  //constantly check status
  check_status();

  data_refresh();   //Refresh data
  PID(); //make calculations for yaw,pitch,roll
  motor_output(); //send values to the ESC's

  //Serial.println("end");
  if (system_calculate) run_count++;
  end_count = REG_TC5_COUNT16_COUNT;
  //}

  
  /*INSERT CODE THAT DETERMINE AMOUNTS OF REPEATS HERE*/

  //Type 1
  if ((end_count >=  start_count) && (end_count <= max_count) && (repeat == 0))
  {
    elapsed_count = end_count - start_count;
    type = 1;
  }
  //Type 2
  if ((end_count >=  start_count) && (end_count <= max_count) && (repeat > 0))
  {
    elapsed_count = (max_count * repeat) + (end_count - start_count);
    type = 2;
  }
  //Type 3
  if ((end_count <  start_count) && (repeat == 0))
  {
    elapsed_count = (max_count - start_count) + end_count;
    type = 3;
  }
  //Type 4
  if ((end_count < start_count) && (repeat > 0))
  {
    elapsed_count = (max_count * repeat) + (max_count - start_count) + end_count;
    type = 4;
  }

  elapsed_time = ((elapsed_count * 8) / 48000000) * (1000); //elapsed time calculation

  Serial.print("Type: "); Serial.println(type);
  Serial.print("Start Count: "); Serial.println(start_count);
  Serial.print("End Count: "); Serial.println(end_count);
  Serial.print("Repeat Times Count: "); Serial.println(repeat);
  Serial.print("Elasped Count: "); Serial.println(elapsed_count);
  Serial.print("Elasped Time: "); Serial.print(elapsed_time, 6);  Serial.println("ms");
  Serial.print("Run Count: "); Serial.println(run_count);

  repeat = 0;

  exit(1);
}


void setup_system_timer()
{
  // Set up the generic clock (GCLK4) used to clock timers
  REG_GCLK_GENDIV = GCLK_GENDIV_DIV(1) |          // Divide the 48MHz clock source by divisor 1: 48MHz/1=48MHz
                    GCLK_GENDIV_ID(5);            // Select Generic Clock (GCLK) 5
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization

  REG_GCLK_GENCTRL = GCLK_GENCTRL_IDC |           // Set the duty cycle to 50/50 HIGH/LOW
                     GCLK_GENCTRL_GENEN |         // Enable GCLK5
                     GCLK_GENCTRL_SRC_DFLL48M |   // Set the 48MHz clock source
                     GCLK_GENCTRL_ID(5);          // Select GCLK5
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization

  // Feed GCLK4 to TC4 and TC5
  REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN |         // Enable GCLK4 to TC4 and TC5
                     GCLK_CLKCTRL_GEN_GCLK5 |     // Select GCLK4
                     GCLK_CLKCTRL_ID_TC4_TC5;     // Feed the GCLK4 to TC4 and TC5
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization

  REG_TC5_COUNT16_CC0 = 0xEA5E;                   // Set the TC5 CC0 register as the TOP value in match frequency mode
  // 59,998 (0xEA5E) counts (10 ms) - 8 prescalar

  //REG_TC5_COUNT16_CC0 = 0xB71A;                   // 46,874 (0xB71A) counts (1s) - 1024 prescalar
  while (TC5->COUNT16.STATUS.bit.SYNCBUSY);       // Wait for synchronization

  //NVIC_DisableIRQ(TC4_IRQn);
  //NVIC_ClearPendingIRQ(TC4_IRQn);
  NVIC_SetPriority(TC5_IRQn, 0);    // Set the Nested Vector Interrupt Controller (NVIC) priority for TC5 to 0 (highest)
  NVIC_EnableIRQ(TC5_IRQn);         // Connect TC5 to Nested Vector Interrupt Controller (NVIC)

  REG_TC5_INTFLAG |= TC_INTFLAG_OVF;              // Clear the interrupt flags
  REG_TC5_INTENSET = TC_INTENSET_OVF;             // Enable TC5 interrupts
  // REG_TC4_INTENCLR = TC_INTENCLR_OVF;          // Disable TC5 interrupts

  REG_TC5_CTRLA |= TC_CTRLA_PRESCALER_DIV8 |   // Set prescaler to 8, 48MHz/8 = 6MHz (for 10ms calculations)
                   //REG_TC5_CTRLA |= TC_CTRLA_PRESCALER_DIV1024 |   // Set prescaler to 1024, 48MHz/1024 = 46.875kHz (for 1s calculations)
                   TC_CTRLA_WAVEGEN_MFRQ |        // Put the timer TC5 into match frequency (MFRQ) mode
                   TC_CTRLA_ENABLE;               // Enable TC5
  while (TC5->COUNT16.STATUS.bit.SYNCBUSY);       // Wait for synchronization
}

void TC5_Handler()                              // Interrupt Service Routine (ISR) for timer TC5
{
  // Check for overflow (OVF) interrupt
  if (TC5->COUNT16.INTFLAG.bit.OVF && TC5->COUNT16.INTENSET.bit.OVF)
  {
    system_calculate = !system_calculate; //toggle

    // Put your timer overflow (OVF) code here:
    //if (repeat_counter && ) repeat++;
    run_count = 0;
    //else system_calculate = !system_calculate; //toggle

    REG_TC5_INTFLAG = TC_INTFLAG_OVF;         // Clear the OVF interrupt flag
  }
}

I’m really overthinking this and think the answer is right in front of me, yet I can’t figure it out. I would like a fresh perspective so any help is appreciated.

Why not use millis()?
Stamp Start_time = millis() at beginning of loop() and then stamp End_time = millis() at the end.

End_time - Start_time gives the time used in loop().

Why not use millis()?

Using a counter will provide a very accurate elapsed time. The way my code runs on the Arduino Zero, I don't run into repeats. It runs in about 1.764ms. But, I want this system that times the loop to be modular and potentially work on another loop that might take a while to read.

from 0 - 50. It starts at 24,

On my Arduino, 24 is not the same as 0.

I think the answer is right in front of you even though I don't fully understand the question yet. In the overflow handler, you should increment the count. That will count the number of times it reset from 50 to 0.

On my Arduino, 24 is not the same as 0.

When I start my loop, the timer has already been setup and begun, so the count will not be at 0. Therefore, I gave an example at starting at 24 when the loop starts.

So, I can't use the overflow handler to increment because if the loop starts while the counter is at 24 and counts up to 50, it won't exactly be one full cycle of counting.

You need 2 counters. One to count the timer ticks and one to count the overflows.

Usually the timer itself is used to count ticks.

For more education, open up the millis() function and see how it works. It adds a count of overflows to the current timer value to give total milliseconds.

sukalo98:
For example: Lets say a timer counts up from 0 - 50. It starts at 24, the timer runs 4 times and lands on 34. AKA, counts from (24 to 50) + (0 to 50) + (0 to 50) + (0 to 50) + (0 to 34). I need a piece of code that determines the amount of timer cycles to accurately determine the amount of elapsed counts it took to run the loop, and therefore accurately determine the elapsed time of the loop.

Counting up from 0 to 50 and then jumping back to 0: is that supposed to be 50 cycles or 51? What exactly happens when the count reaches 50 ?
(Think of how the seconds of a watch count up from 0 to 59 and then jump back to 0: that takes 60 seconds, not 59.)

const int max_count = 59998;

Nope. An int can only go up to 32767. Perhaps you want an unsigned int or a long?

  //Type 1

if ((end_count >=  start_count) && (end_count <= max_count) && (repeat == 0))
 {
   elapsed_count = end_count - start_count;
   type = 1;
 }
 //Type 2
 if ((end_count >=  start_count) && (end_count <= max_count) && (repeat > 0))
 {
   elapsed_count = (max_count * repeat) + (end_count - start_count);
   type = 2;

You are, of course, aware that anything times zero is zero. So, what does your “Type 1” handle that “Type 2” doesn’t? Why not just use “Type 2” for both?

I’m really overthinking this and think the answer is right in front of me, yet I can’t figure it out. I would like a fresh perspective so any help is appreciated.

I believe the expression is “not seeing the forest for the trees”.

How high do you expect to need to count up to? Will your numbers fit in an int? In an unsigned int? Or will you need a long?

Also, I noticed trouble with your use of elapsed_time in your code. I’d rather not get into a detailed explanation of what kind of trouble. Suffice it to say that you should read this:

Counting up from 0 to 50 and then jumping back to 0: is that supposed to be 50 cycles or 51? What exactly happens when the count reaches 50 ?
(Think of how the seconds of a watch count up from 0 to 59 and then jump back to 0: that takes 60 seconds, not 59.)

I don't think I explained that example very well, I understand that 60 counts (0-59) is one cycle.

You need 2 counters.

I figure out my issue, thank you for the explanation. I guess I needed to 'hear' the "2 counters" part.

Nope. An int can only go up to 32767. Perhaps you want an unsigned int or a long?

You're right, silly mistake. I changed it to an unsigned int. I wonder if that has messed up my calculations...

You are, of course, aware that anything times zero is zero. So, what does your "Type 1" handle that "Type 2" doesn't? Why not just use "Type 2" for both?

Yes, I am aware. I wanted to see exactly what situation unfolded. In the final version, Type 2 and Type 4 will be the only equations.

Also, I noticed trouble with your use of elapsed_time in your code. I'd rather not get into a detailed explanation of what kind of trouble. Suffice it to say that you should read this:
c++ - Why does dividing two int not yield the right value when assigned to double? - Stack Overflow

Thank you for this, another silly mistake on my part.