ISR Questions

Hi, I’m relatively new to programming and have decided to take on a project where I’d have my Arduino Duemilanova output the Rate of Fire of a paintball marker using two optical sensors. I have the sensors working properly and I am able to check when they are triggered however I am having trouble figuring out how to have the program check the shotcount every second. Right now, I have my program increment the shotcount(“ShotCount++;”) every time a ball triggers both sensors. I simply need to check the value of ShotCount every second. Ive read some tutorials on Timer Interrupts but am not 100% sure how to go about coding one.

I need to be able to determine(With an accuracy of 1 decimal place) the ROF in Balls/Second. Could anyone with knowledge on interrupts show me how to program one to check my shotcount every second without disrupting the time sensitive code that’s already within my loop()? Thanks

Right now my code looks something like this:

#include <LiquidCrystal.h> 
#define pin6 (1 << PORTD6)
#define pin7 (1 << PORTD7)        
        
        int BPS;                 
        int FPS;                 
        int Flag;
        long Inches;             
        unsigned long StartTime; 
        unsigned long StopTime;  
        unsigned long Duration; 
        volatile unsigned long ShotCount;
     
        LiquidCrystal lcd(8, 9, 10, 11, 12, 13);
      
        void setup() 
        {
         DDRD &= ~ ((1 << PORTD6) | (1 << PORTD7));  //Sets Pins 6 and 7 as INPUTS
         lcd.begin(16, 2);              
         Duration=0;                    
         FPS=0;                         
         BPS=0;                      
         Flag=0;                     
        }

        void loop() 
        {
            
            if (PIND & (1 <<PORTD6) && Flag==0)     //Checks if Sensor 1 has been triggered(HIGH) 
            {
              StartTime=micros();                   
              Flag=1;                        
            }  

            if (PIND & (1 <<PORTD7) && Flag==1)     
            {
              StopTime=micros();                    
              ShotCount++;                            //Increments the ShotCount by 1
              Flag=0;                                   //Triggers the ChronoFlag to equal 1
              Duration=StopTime-StartTime;   //Calculates the time interval for the projectile to trigger both sensors
              
            } 

      }

That code will not compile, it is missing a closing brace. Also, please post code in a code box, using the # icon on the toolbar.

If you don't have an ISR, then the volatile qualifier isn't needed, and may reduce the performance of your sketch.

You could try using the timer1 library.

#include <TimerOne.h>

volatile unsigned int shotCount = 0;
volatile unsigned int shotsPerSecond = 0;

void setup() {
  Timer1.initialize(1000000UL); // 1000000 microseconds (unsigned long) is 1 second
  Timer1.attachInterrupt(oneSecInterrupt);
}

void loop() {
  if (ready) shotCount ++;
}

void oneSecInterrupt() {
  shotsPerSecond = shotCount;
  shotCount = 0;
}

I fixed the code by adding the closing brace. As for the volatile, I know I don't need it unless Im using an ISR. I put it there because I had read it was the proper way to declare a variable that is going to be shared with your main code and your ISR. If this is incorrect, ill change it..

thanks for the sample code WizenedEE, that looks like it will work...I just have one more question. I want my program to print the shotspersecond value only if that value is greater than 3. Is there a way to have the interrupt output that value or should i add some "if" functions within the interrupt?

Is there a way to have the interrupt output that value

An ISR has to be as short as possible so it is not a good idea having a print in an ISR, do the outputting in the main code.

I added the sample code given to me by WizenedEE and it seems to work, however, I'm not getting any decimal precision which is standard on your average paintball Chrono...looking at my code, I'm not sure it's been written to do so and it might require a re-write of certain sections. Anyone know how I could go about having the Shots/Second displayed with an accuracy of 1 decimal place?

         DDRD &= ~ ((1 << PORTD6) | (1 << PORTD7));  //Sets Pins 6 and 7 as INPUTS

Aw, is there a problem with using the more readable:

pinMode (6, INPUT);
pinMode (7, INPUT);

?

Maybe play with the ports in time-critical code. The setup function is not such a place.

Anyway, all pins default as inputs after a reset.

Moving on from that, if you are timing two pins, why not put the pins onto the interrupts? Rather than using timers. Timers are just a way of polling. Interrupt react pretty quickly.

I didn't put the pins in the interrupts because I want them to continuously take measurements, not stop and take measurements every second...perhaps I'm not understanding what you mean exactly. Im still having problems with my shot counter,

DTM22: I added the sample code given to me by WizenedEE and it seems to work, however, I'm not getting any decimal precision which is standard on your average paintball Chrono...looking at my code, I'm not sure it's been written to do so and it might require a re-write of certain sections. Anyone know how I could go about having the Shots/Second displayed with an accuracy of 1 decimal place?

I didn't put the pins in the interrupts because I want them to continuously take measurements, not stop and take measurements every second...

In an interrupt, you're "measuring" the stae of the pin every instruction cycle. At 16MHz, this means every 62.5 nanoseconds. Is that continuous enough?

If you're not using interrupts, then the title of the thread is misleading.

I am using an ISR in my code, just not for the input pins...my questions now still remains, how to get an accuracy of 1 decimal place with my BPS value?

Perhaps if we could see your code, we could help

how to get an accuracy of 1 decimal place with my BPS value?

You need to count the number of shots in one tenth of a second and then multiply that figure by ten.

You should use interrupts to increment the shot counter. Then you should use the 1/10 th of a second ISR to get the shot counter and stores it in another variable and set a flag that indicates that the new value is ready for output or logging or averaging or what ever. Finally it resets the shots counter.

Your main loop should look at the flag and when it sees it set it should grab the shot counter store, and reset the flag. Then it can do the multiply by 10 or whatever, and display the result.

So if I understand correctly, I need to change the interrupt to check the shot count ever 1/10th of a second instead of every second. Do I need to add other interrupts or keeping the one will be sufficient? What other changes do I need to make?

Right now my code looks like this:

#include <LiquidCrystal.h> //Liquid Crystal Library
#include <TimerOne.h>      //Timer 1 Library
#define pin6 (1 << PORTD6)
#define pin7 (1 << PORTD7)        
              
        int Flag;                          
        unsigned long StartTime;          
        unsigned long StopTime;            
        unsigned long Duration;           
        unsigned long FPS;                 
        volatile unsigned long ShotCount;  
        volatile unsigned long BPS;        
   
        LiquidCrystal lcd(8, 9, 10, 11, 12, 13);

        void setup() 
        {
         DDRD &= ~ ((1 << PORTD6) | (1 << PORTD7));  // Sets Pins 6 and 7 as INPUTS
         Timer1.initialize(1000000UL);               // 1000000 microseconds (unsigned long) is 1 second
         Timer1.attachInterrupt(OneSecInterrupt);   
         lcd.begin(16, 2);              
         Duration=0;                   
         FPS=0;                          
         BPS=0;                         
         Flag=0;                         
        }

        void loop() 
        {
            if (PIND & (1 <<PORTD6) && Flag==0)    
            {
              StartTime=micros();                   
              Flag=1;                               
            }  
            
            if (PIND & (1 <<PORTD7) && Flag==1) 
            {
              StopTime=micros();                   
              ++ShotCount;                          
              Flag=0;                             
              Duration=StopTime-StartTime;          
              FPS=(0.5*(1000000))/Duration;         
            } 
            
           if(BPS<=3)
           {
            PrintVelocity(); 
           }  
           if(BPS>3)
           {
            PrintROF();
           }      
        }
        
        
        void OneSecInterrupt() 
        {
        BPS = ShotCount;
        ShotCount = 0;
        }
        
        void PrintVelocity()
        {
        lcd.clear();                    
        lcd.print("  Ball Velocity");    
        lcd.setCursor(0, 1);      
        lcd.print(FPS, DEC);             
        lcd.print("  Feet/Second");     
        }

        void PrintROF()
        {
        lcd.clear();                    
        lcd.print("    Fire Rate");     
        lcd.setCursor(0, 1);            
        lcd.print(BPS, DEC);            
        lcd.print("   Balls/Second");    
        }

DTM22:
I am using an ISR in my code, just not for the input pins.

Yes I see that.

DTM22:
…my questions now still remains, how to get an accuracy of 1 decimal place with my BPS value?

Divide it by 10? Say you get 42 BPS, then divide it by 10 and you get one decimal place, that is 4.2. :wink:


You don’t need a timer interrupt at all, that is not the right way. For example:

#include <Wire.h>
#include <LiquidCrystal.h> //Liquid Crystal Library

unsigned long StartTime;          
unsigned long StopTime;            
unsigned long Duration;
unsigned long FPS;                 
unsigned long ShotCount; 
unsigned long Now;

float BPS;  
float ROV;

LiquidCrystal lcd(8, 9, 10, 11, 12, 13);

void setup() 
{
  lcd.begin(16, 2);              
  FPS=0;                          
  BPS=0;                         

  StartTime=micros();    
}

void loop() 
{
  // another ball dropped
  if (digitalRead (7) == HIGH) 
    ++ShotCount;                          

  // time now
  Now = micros ();            

  // how long have they been dropping?
  Duration = Now - StartTime;
  
  if (Duration == 0)
    return;  // no divide by zero

  // convert duration to seconds and calculate balls per second
  BPS = (float) ShotCount / (Duration / 1000000.0);  
  ROV = 1.0 / BPS;
  
  PrintVelocity(); 
  PrintROF();
}


void PrintVelocity()
{
  lcd.clear();                    
  lcd.print("  Ball Velocity");    
  lcd.setCursor(0, 1);      
  lcd.print(FPS);             
  lcd.print("  Feet/Second");     
}

void PrintROF()
{
  lcd.clear();                    
  lcd.print("    Fire Rate");     
  lcd.setCursor(0, 1);            
  lcd.print(BPS);            
  lcd.print("   Balls/Second");    
}

That code simply divides the number of drops by the numbers of seconds. Hence, drops per second. Not tested.

Duration is the time measured for 1 single ball to trigger both sensors and shotcount is the number of shots counted in 1 second. Are you sure that relating shotcount to the duration of only one ball to travel past both sensors is the proper way of measuring a rate of fire in BPS?

Not in my code it isn’t. It is the cumulative duration. So if 20 balls drop that is the time it took 20 to fall. Normalized for seconds and you get balls per second.

[quote author=Nick Gammon link=topic=79670.msg602910#msg602910 date=1321905941]

void setup() 
{
  lcd.begin(16, 2);              
  FPS=0;                          
  BPS=0;                         

  StartTime=micros();    
}

[/quote]

Doesn't that assume you begin firing as soon as the device is turned on? what if I wait ten seconds before firing, that would affect the results

what if I wait ten seconds before firing, that would affect the results

Yes of course, because it is also part of the time. Do you only want to measure the firing rate from the time of the first shot? If so that is not what you said originally, you have changed the specification. Are you a boss? They are always doing that. :~

Sorry, I may have left that out, In my code I have it so that it starts timing as of the first shot