Piece of code - Need a smart person :)

Hello

I am just wanting help to understand a piece of code I have ‘borrowed’ from another arduino.cc member (http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1274834441)

Basically what the code does is count pulses coming into a digital pin using an interrupt, and adjusts the counts based on Timer0 to get 4us accuracy, and then outputs the number of pulses it ‘estimates’ would occur in 60 seconds. It basically looks at the time between interrupt pulses + timer0 @ 4us, and estimates the pulses in 60 seconds.

I have a few questions, which are listed after the code.

Code in question is here (very nicely done):

extern volatile unsigned long timer0_overflow_count;  // Record the most recent timer ticks
volatile boolean ticks_valid;                         // Indicates a valid reading
volatile unsigned long ticks_per_rev;                 // Number of ticks counted in the current revolution
volatile unsigned long ticks_now;
float lastRPM;                                        // Most recent RPM reading

const float TICKS_TO_RPMS = 15e6;                     // Convert clock ticks to RPM by dividng ticks into this number
                                                      // 16000000Mhz / 64 Prescaler = 4uS (Timer0 resolution)
                                                      // 1000*1000*60/4us = 15e6 

void setup()
{
  Serial.begin(115200);                               // Initialise serial comms.
  attachInterrupt(0, RPM_ISR, RISING);                // RPM sense will cause an interrupt on pin2                              
  lastRPM = 0;
}

void RPM_ISR()
{
  static unsigned long pre_count;                 // Last timer0_overflow_count value
  ticks_now = timer0_overflow_count;              // Read the timer over count (number of times timer0 has overflowed since startup)

  byte t = TCNT0;                                 // Copy the current value of Timer0
  if ((TIFR0 & _BV(TOV0)) && (t<255))             // If the Timer has overflowed
    ticks_now++;                                  // Increment the number of Ticks (Timer0 counting to 255 takes 1024uS, therefore this is 1024uS resoluation)
  ticks_now = (ticks_now << 8) + t;               // Bit shift Ticks by 8 bits and add Timer0 current value, to make ticks_now 4uS resolution now

  if (pre_count == 0)                             // If this was the first time around the loop
  {                         
    pre_count = ticks_now;                        // Set the precount to the current count, don't use this number.
  } 
  else 
  {
    ticks_per_rev = ticks_now - pre_count;        // Calculate the difference to get the actual number of ticks...
    pre_count = ticks_now;                        // reset the counter with the current count value
    ticks_valid = true;                           // and flag the change up
  }
}

void loop()
{
  unsigned long thisMillis = millis();          // Read the time once

  // Calculate RPM
  if (ticks_valid)                              // Only come here if we have a valid RPM reading
  {                            
    unsigned long thisTicks;
    
    noInterrupts();                             // Disable interrupts so the next calculation isnt changed half way through
    thisTicks = ticks_per_rev;
    ticks_valid = false;
    interrupts();                               // Enable interrupts again
    
    lastRPM = TICKS_TO_RPMS / thisTicks;        // Convert ticks to RPMs
    ticks_valid = false;                        // Reset the flag.
  } 
}

My question is around this piece of code specifically:

byte t = TCNT0;                                 // Copy the current value of Timer0
  if ((TIFR0 & _BV(TOV0)) && (t<255))             // If the Timer has overflowed
    ticks_now++;                                  // Increment the number of Ticks (1024uS resolution)
  ticks_now = (ticks_now << 8) + t;               // Bit shift Ticks by 8 bits and add Timer0 current value, to make ticks_now 4uS resolution now

From what I have pieced together, it takes a copy of the Timer0 count, and then checks to see if Timer0 has overflowed using (TIFR0 & _BV(TOV0). What I am wondering is, how does it know if the timer hasnt elapsed twice since this was run, and how does the TIFR0 etc get reset to say it hasnt overflowed anymore.
Basically what I am getting at is if TIFR0 & _BV(TOV0) is saying that Timer0 has overflowed, if its never reset then the next time this code is executed it will be true again.

Does that make sense? Or am I talking total rubbish…

I know TIFR0 is a register, and TOV0 is a bit in that register… from the reading I have done… but I dont know anything further about them.

Some of the comments above are my own, to remember what I found - so hopefully they are correct and not misleading anyone.

If someone can help me understand I would very much appriciate it.

I am raising this as the code does work very well, however the number of pulses it estimates does fluctuate a little. Not by much, but it does fluctuate. I have a 555 timer going into it for testing. I am just wondering if the piece of code mentioned above may be contributing to this?

It may be perfect, but I thought I would ask

Hats of to the creators, it has been very valuable to me and saved me alot of time and head scratching. Now I just need to get 100% comfortable with it so I am not just using another persons code without fully understanding it.

Thanks
J

Thinking about this a little more...

If this is using Timer0 at 64 prescaler to get a 4uS resoluation... would a better result be achieved if it used Timer2 at say 32 prescaler, which would then count to 65536... meaning the overflow of Timer2 would happen at say 131ms now, but each count of Timer2 would be 2uS...

Am I correct in saying that? Or am I again talking out of my bum. I am just guessing here, but to me that makes a bit of sense, but whether its true or not I await to hear.

I am assuming you can change the prescaler of Timers in Arduino ok? Does it affect anything else, if I changed Timer2 for example?

Appriciate you reading this, and hopefully someone will have an answer.

According to the datasheet on TIFR0:

TOV0 is cleared by hardware when executing the corresponding interrupt handling vector.

Similar code can be found in the implementation of micros(). I think it looks strange. Both micros and your pulse counter depends in timer0_overflow_count, which requires timer0 interrupts to be enabled. To me it looks like the code in micros() could never notice that TOV0 is set because that would immediately cause an interrupt that would clear it.

In the pulse counter, though, the code that checks TOV0 is also in an interrupt. So if RMP_ISR was triggered just before the timer0 interrupt was about to fire, it may be possible to catch TOV0 before it gets cleared.

Hmm. Not much of that made sense to be honest...

I was just reading through the references and found the pulsein function, so decided to try it just to see what sort of results it would give me.

I know for a fact that my 555 timer isnt outputting a pure square wave, as the on and off periods are different. The pulsein function seems to time in uS the On period, or the Off period. I needed both to get an accurate represention of the total period of 1 pulse, so I wrote the following:

float time_uS_ON = 0.0;
float time_uS_OFF = 0.0;
float seconds = 0.0;
float minutes = 0.0;
void setup()
{
  Serial.begin(115200);  

}

void loop()
{
  time_uS_ON = pulseIn(2, HIGH);
  time_uS_OFF = pulseIn(2, LOW);
  
  seconds = 1000000 / (time_uS_ON + time_uS_OFF); //number of pulses in 1 second
  minutes = seconds * 60;        //number of pulses in 1 minute

  Serial.print(seconds);  
  Serial.print(", "); 
  Serial.println(minutes);
}

I was plesantly surprised to find that the numbers were near identical to the code posted in the first post above.

I am not sure if by timing from the rising edge to the falling edge and then from the falling edge to the rising edge, happen on the same pulse cycle, or if it will have to time over two pulses. What I mean is rising edge to falling edge counting, as per pulseIn(2, HIGH), will count the 'high' period, however I am not sure if the pulseIn(2,LOW) will count the immediate 'low' period, or if it will have to wait until the next pulse to count that.

Does anyone know which of the sets of code will be more accurate? the one in the first post, or this pulsein code?

I dont have an oscilloscope and only have a frequency setting on my multimeter, which I dont know how accurate that is. It says 502.6

The first piece of code ouput the following (pulses per second, pulses per minute): 505.05, 30303.03 506.07, 30364.37 505.05, 30303.03 505.05, 30303.03 504.03, 30241.94 505.05, 30303.03 504.03, 30241.94 505.05, 30303.03

The pulsein code output the following, without changing the timer: 506.84, 30410.54 508.13, 30487.81 507.87, 30472.32 507.87, 30472.32 507.87, 30472.32 508.39, 30503.31 508.13, 30487.81

So they are both within ballpark - however they are not the same.

Thoughts?

Thanks J

The following post:

http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1247079375

offers more detail on why the code is written as is. The basic issue is getting an accurate timer reading from within an ISR.

Ah the penny drops.

Thanks for the link, that explained it well.

Thank you