32bit counter - How to handle interrupts during ISR

Hi all,

I am implementing a 32bit counter by software and would be grateful if I could have some guidance.

/* 
  * InputCapture 
  * uses timer hardware to measure pulses on pin 8 on Mega328 
  */ 

 const int inputCapturePin = 8;     // input pin fixed to internal Timer 
 volatile unsigned int results; 
 volatile unsigned int previous = 0;
 volatile int i=0;    // Timer 1 overflow counter 
 volatile int a=0;

 void setup() 
 
 { 
   Serial.begin(9600);             // 9600bps 
   pinMode(inputCapturePin, INPUT); // ICP pin (digital pin 8 on Arduino) as input 
   TCCR1B = (1<<CS10);              // Set timer 1 Clock source = FOsc
   TCCR1A = 0 ;                     // Normal counting mode 
   TIMSK1 |= _BV(TOIE1);            // enable timer 1 overflow flag 
   TIMSK1 |= _BV(ICIE1);            // enable input capture interrupt for timer 1
 } 

 //Print timer 1 total counts since last pulse 
 void loop() 
 { 
   float output = (((results-previous)+(65536*a)));  //Calculate the total output value
   //Debug only! Data to be transfered via SPI to the modulator. Add routine here.
   Serial.println(output); // print the value to be sent to the modulator
 } 

 //**********Interrupts************************************************************ 

 //Overflow interrupt vector 
 ISR(TIMER1_OVF_vect) 
 {    
   cli();  
   i++;    //Increment timer 1 overflow counter 
 }

 //ICR interrupt vector 
 ISR(TIMER1_CAPT_vect) 
 { 
   cli();                             //Disable interrupts
   a=i;                               //Save the Value of i into a variable a, so that calculations can be made outside the ISR  
   i=0;   //Clear i                   //Clear the overflow variable
   previous = results;                //Save the previous ICR1 Value so that no delay is added by reseting TCNT1
   results = ICR1;                    //Assign the value of the ICR1 Register to the variable results
 }

The problem is that often the overflow variable is incremented, during the ISR, resulting in 65536 being added to the correct result .

2048.00
2048.00
67584.00
2048.00
2048.00
2048.00
2048.00
2048.00
2048.00
2048.00
2047.00
2048.00
2048.00
2048.00
2048.00
2048.00
2048.00
2048.00
2048.00
2048.00
2048.00
2048.00
2048.00
2048.00
2048.00
2048.00
2048.00
2047.00
2048.00
2048.00
2048.00
2048.00
2048.00
2048.00
2048.00
67584.00
2048.00
2048.00
2048.00
2048.00

How to bets deal with this issue?

  1. You do not need the cli call in your ISR - interrupts are off during ISR's - Thats done by the CPU.

The problem is that often the overflow variable is incremented, during the ISR,

No i is not being incremented in the ISR as interrupts are turned off. our problem is else where.

Mark

Well worth looking here at an excellent demo of what your trying to do.

holmes4:

The problem is that often the overflow variable is incremented, during the ISR,

No i is not being incremented in the ISR as interrupts are turned off. our problem is else where.
Mark

Im pretty sure it is overflowing at the same time, or just before the ISR, before the interrupts are disabled.
Any suggestions?

Read that link again second para

When an interrupt service routine (ISR) is called, interrupts are automatically disabled. Interrupts are automatically enabled when returning from an ISR. It is not necessary to explicitly enable nor disable interrupts inside of an ISR. The processor does these two things for you.

Mark

holmes4:
Read that link again second para

When an interrupt service routine (ISR) is called, interrupts are automatically disabled. Interrupts are automatically enabled when returning from an ISR. It is not necessary to explicitly enable nor disable interrupts inside of an ISR. The processor does these two things for you.

Mark

Right, i was clearly confusing with atomic variables!

float output = (((results-previous)+(65536*a)));

Access to "a" should be protected.

65536 this value looks wrong, as its a small -ve number (Try Serial.print(65536)) try using UL65536.

You could os course just use longs which are 32 bit on the Arduino.

Mark

AWOL:

float output = (((results-previous)+(65536*a)));

Access to "a" should be protected.

So should I make it atomic or disable interrupts while performing this calculation?]

Something else I might need clarification on:
int for a variable that can overflow at 64 is an overkill. I have used char, but I don't believe that the most correct term.

What should I be using there?

holmes4:
65536 this value looks wrong, as its a small -ve number (Try Serial.print(65536)) try using UL65536.

??

holmes4:
You could of course just use longs which are 32 bit on the Arduino.
Mark

I did, floats use a lots of memory, I just returned to floats to make sure the problem wasn't caused by some strange variable overflowing.

unsigned long int [variable]

Long's are not floats even through each takes 4 bytes.

you need to put cli()/sei eg

cli(); // turn off interrupts
float output = (((results-previous)+(65536*a)));
sei();// and turn them back on

as your TIMER1_CAPT_vect changes all 3 and could be called while your doing all that math.

Even better would be to copy the three vars to loacal vars and do the math on the loacal var to keep the time the interrupts are off to a minimum..

cli();
unsigned int r=results; 
unsigned int p=previous ;
int mya =a;
sli();
float output = (((r-p)+(65536*mya)));

Mark