Counting pulses against time?

I'm working on a project to convert standard digital RPM output (4 pulses per rev) to CANBus. I'm very new to all of this (did some java programming 10 years ago but I'm rusty as can be). I'm using an Arduino Uno and a CAN-Shield for this project. With the ECU outputting 4 pulses/rev, I need to convert that to a digital RPM value, which I have to then convert to HEX to send out on the CANBus output.

Is there a clock function I can call against to compare the pulses in/time?

Thanks,
Chris

CLSegraves:
I'm working on a project to convert standard digital RPM output (4 pulses per rev) to CANBus. I'm very new to all of this (did some java programming 10 years ago but I'm rusty as can be). I'm using an Arduino Uno and a CAN-Shield for this project. With the ECU outputting 4 pulses/rev, I need to convert that to a digital RPM value, which I have to then convert to HEX to send out on the CANBus output.

Is there a clock function I can call against to compare the pulses in/time?

Thanks,
Chris

That is basically describing a frequency counter application. I would search for 'arduino frequency counter' and you should find many examples you could work (with possible modifications), for your needs.

Lefty

Thanks. Some times the hardest part is knowing what to search for.

Another question:
I found a function called pulseIn which people seem to use to count every time a pin goes either HIGH or LOW. What is the better way to count pulses:

  • pulses in a fixed time (say 10milliseconds)
    or
  • time to get 4 pulses

I'm inclined to think I'd be better having a fixed time and counting the number of pulses in that period. Yes/No?

CLSegraves:
Thanks. Some times the hardest part is knowing what to search for.

Another question:
I found a function called pulseIn which people seem to use to count every time a pin goes either HIGH or LOW. What is the better way to count pulses:

  • pulses in a fixed time (say 10milliseconds)
    or
  • time to get 4 pulses

I'm inclined to think I'd be better having a fixed time and counting the number of pulses in that period. Yes/No?

The question if it's best to measure time between pulses to determine nominal frequency or to count pulses for a fixed time period is best answered when you define the maximum possible frequency of your signal to be measured and what is the resolution precision you require in your measurement. It's a trade-off type decision and normally takes some analysis to determine the best method. However then again your specific signal range and resolution requirements might be met fine using either method. It's basically a math decision based on the max signal speed and the hardware/software speed counting capabilities of your measurement equipment (an arduino in this case). I'm afraid I can't recommend anything more specific.

Lefty

Well, for headroom sake lets say 10,000RPM MAX (actually only going to 7500RPM but lets go overkill here).

  • 10,000RPM / 60seconds = 167rev/sec
  • 167 * 4pulses/rev = 668pulses/sec

so that's less than 1 pulse per millisecond.

Are Arduino's fast enough to count a pulse/millisecond? Would seem like it with the data sheet saying "20 MIPS Throughput at 20 MHz".

CLSegraves:
Are Arduino's fast enough to count a pulse/millisecond?

It's plenty fast enough. However, humans can't process display data at 100 Hz. You may want to consider updating less frequently.

If your engine speed is slow, like maybe 700 RPM, you'll need more than 10 milliseconds to see even one pulse - more than 20, actually. Your measurement interval will have to be larger than 10 ms.

pulseIn() will give you a value in microseconds, which is plenty of resolution. Note that it will lock up the processor for as long as it takes to see the required transitions on the input - maybe as long as two cycles, or about 40 ms at 700 RPM. You'll have to decide if your processor can wait that long. If it can't, there are other ways to do it, but they take more programming effort, and more thinking.

Okay, so I'm using the following code:

int TachIn = 2;                                //Pin serving as external interrupt (pin 2) to count the pulses 

int TestPin=13;                                   //Pin which generates test pulses, this is not needed when actual pulse is available for measurement

static float siPulseCounter=0;                       //Variable keeping track of pulse counts
static float siSecondCount=0;                        //Variable keeping track no. of seconds in the timer1 block

float rpm;

long randNumber;                                      //Random number generator for RPM testing

int val = 0;

void SetupTimer1()                                //Setting up timer1 with appropriate register values
  {

      Serial.begin(9600);
      //Serial.begin(115200);
    
      TCCR1A = 0;                                 // No PWM is used.So set this to zero.
      TCCR1B = 1<<CS12 | 0<<CS11 | 0<<CS10;       // Input clock is set to F(cpu)/256
      TIMSK1 = 1<<TOIE1;                          // Enable timer interrupt to detect overflow
      /***
      Set the timer count to overflow to in 1 second.
      My board has a 16MHz oscillator.
      Reload Timer Value  = ((2 ^ Bits) - 1) - ((Input Frequency / Prescale) / Target Frequency)
                          = 65535 - ((16 x 10 ^6 Hz)/256)/1 Hz 
                          = 65535 - 62500
                          = 3035
      ***/ 
      TCNT1=0x0bdb;                               //3035 in hex
  }


ISR(TIMER1_OVF_vect)                             // Timer1 ISR when it overflows
  {
   
    TCNT1=0x0bdb;                                //Reset Timer1 to count another second      
    siSecondCount++;                             //Increase second count 
    if(siSecondCount>=1)                        //If 1 Seconds are attained Print the Pulse count to serial port.
      {
        rpm = siPulseCounter*30;             //Multiplier has to do with Reference Voltage this is at 5v ref.
        Serial.print("RPM: ");
        Serial.println(rpm);  	                //Print as decimal value
       
        Serial.println(siPulseCounter);
        
       val = digitalRead(TachIn);    
      Serial.print("TachIn: ");  	                //Print as decimal value
      Serial.println(val);  	                //Print as decimal value   
        
        siPulseCounter=0;                       //Reset counter values for next minute cycle
        siSecondCount=0;
       }
   }

void setup(void)
    {
  
  
      pinMode(TachIn, INPUT);                  // Set Pulsepin to accept inputs
      digitalWrite(TachIn, LOW);  
      pinMode(TestPin, OUTPUT);                  // Test signal generator pin as output. Can be ignored if the actual digital signal is available
      attachInterrupt(0, count, RISING);          // Caputres external interrupt 0  at iPulsepin at rising edge and calls funtion count defined below 
      Serial.begin(115200);                         // Begin Serial communication at baudrate 9600 bps
      SetupTimer1();                              // Call Setuptimer1 function
      Serial.println(" Initialising, Please wait...");
  
    }

void loop(void) 
  {
  
  
  /*** Following 5 lines just generate pulses for testing purposes.
    All the 5 lines Can be ignored if the actual digital signal is available ***/
  
      digitalWrite(TestPin, HIGH);             // sets the iTestPin ON
      randNumber = random(100, 300);
      delay(37.5);                              // waits 
      digitalWrite(TestPin, LOW);              // sets the iTestPin off
      delay(1);                              // waits 
   
  
  
  }


void count()                                    // Function called by AttachInterrupt at interrupt at int 0
    {
  
      siPulseCounter++;                        //increment the pulse count
     
    }

But for some reason when I connect it to my vehicle, I don't get any RPM. Output from PCM Tach Signal is 5V square wave form, 2 pulses per Rev, (4 cylinder mode).

Serial.begin(115200);

This statement is executed twice: once in SetupTimer1(), and once in setup(). I'd delete one of them. They name different bitrates, too. I'm not sure whether you'll get the first one or the last one.

static float siPulseCounter=0;

siPulseCounter takes on only integer values. It shouldn't be a float. The Timer1 overflow ISR bumps this variable, and then compares it to 1. Both of those operations take a lot longer with float, with no added benefit. As long as siPulseCounter stays below 2^16, unsigned int will work. It should stay below that - 65536 Hz corresponds to almost a million RPM at 4 pulses/rev, which seems to be an unlikely speed for an auto engine. The attached ISR bumps this variable, and the Timer1 ISR examines it. It should be declared volatile; otherwise, the compiler may optimize it our of existence. I don't see any benefit from declaring it static, since it's already global; others may be able to illuminate a reason for doing it, though.

static float siSecondCount=0;

siSecondCount is either 0 or 1. It shouldn't be a float, either. char or unsigned char will work. The ISR bumps this variable; it should be declared volatile. The Timer1 ISR bumps and examines this variable, so it needs to be volatile, too. I don't think calling it static helps anything.

rpm = siPulseCounter*30;     //Multiplier has to do with Reference Voltage this is at 5v ref.

I don't see any reference voltage in your description, and I particularly don't see any correlation between the RPM reading and any voltage. At 4 pulses/rev with a one-second counting window, the RPM is (60 sec/min)(1 sec)(1/4 rev/pulse) = 15 * pulse count. I don't know where 30 comes from. I don't see the point of declaring rpm as float, either, since it will only take on integer values.

Serial.println("RPM: ");
Serial.println(rpm);

You'll be better off to avoid using Serial.print() inside an ISR. I'll recommend that you set a flag in the ISR, and use it to trigger the main program to output the value.

CLSegraves:
... I'm trying to understand the timing of the timer ...

Here's a link to the ATmega328P datasheet: www.atmel.com/Images/doc8271.pdf. In the version dated 07/2012, the description of Timer1 operation is in chapter 16, starting on page 112. There's a lot to digest. Here's how I think the timer is used in your program:

  • PWM outputs are disabled
  • Waveform generation mode is "normal." That means that Timer1 counts up from its current value until it rolls over, and then starts over at zero.
  • The input clock is the system frequency clock by 256 - 16000000 Hz/256 = 62500 Hz.
  • The timer overflow interrupt is enabled. That happens when the timer rolls from 0xFFFF to 0.
  • The initial value of the timer is set at 62500 ticks away from overflow.

Having said all that, I ran your code with a couple of modifications: Deleted Serial.begin(9600); set pin 5 to output; wrote an analog to pin 5; and added a jumper from pin 5 to pin 2. That gives me pulses on pin 2 at the default PWM frequency of about 977 Hz. I get readings of around 29280. I expected to see readings that bounce between 14640 and 14655, so what I got was pretty close to twice what I'd expect, and that's exactly what I'd expect with a multiplier of 30. So, maybe all that stuff above doesn't matter as much as I think.

Yeah.... wow.... I tried to read through that big PDF document and it all went over my head..... :frowning:

I used your advice and finally got my program reading the tach signal, however it maxes out at around 900RPM. No matter how high I spin the engine, it still says Max of 900RPM. I have removed the test pulse section so that can't be it. I think it's something in my timer throwing everything off.

CLSegraves :
Did you get this working? I am working on a project that needs to calculate RPM from an ignition module also.
I am totally new to Arduino, so I dont understand all to code without referencing it all to the manuals.
Could you post the code you have so far?
I understand all the math, just learning the arduino data types as I go.
Thanks for you help
Cory

Have not completed the code yet. I got busy with work and keep having a hard time picking it up.