Engine RPM (Part 2)
The previous post covered the hardware part of the engine RPM sensor, it is time to care about the software component.
Nothing complex ahead, and we have a variety of options for taking care about pulse counting. Globally, all we need is a pin which will read the state of the signal from the adapter circuit. Two options are available: The pin repeatedly reads the signal level or an interruption is generated when a signal level is sensed on the pin. Considering the maximum signal frequency (say 58 pulses per round time 6000 rpm / 60 = approx 6kHz), the first option can be retained and it is a very to program. However, in accordance to the principle “Why should we simple things while it is possible to make complex things”, I decided to use the second option. In fact , it is no big deal, as we can use the pins to which an interruption can be attached, using the standard attachInterrupt() function. It is very convenient as this function can trigger an event which is based on four different situations:
- LOW to trigger the interrupt whenever the pin is low,
- CHANGE to trigger the interrupt whenever the pin changes value
- RISING to trigger when the pin goes from low to high,
- FALLING for when the pin goes from high to low
Just fine. However, this is not enough for us. Say that my application already uses the default pins on which it is possible to attach an interrupt… Well, the micro controller offers a fancy option which allows the programmer to attach an interrupt to any pin fro any port!
Setting the parameters is pretty easy though. Setting PINB0 as an interruptible pin requires the following two lines of code
PCICR |= (1 << PCIE0); PCMSK0 = (1 << PINB0);
Next is the programming of the service executed when any type of change is sensed by PINB0
ISR(PCINT0_vect) { static uint8_t previousState = 0; uint8_t state = (PINB >> PINB0) & 0x01; if (state != previousState) { counts += 1; previousState = state; } }
In this case a counter is incremented on any change on PINB0. Remember that the counts variable must be declared as a global volatile variable.
So far so good. Except that the code is not very versatile, as I would like to be able to set any pin from any port. Well, this is still no big deal.
setting the pins looks like
if (_inputPort == &PORTB) { PCICR |= (1 << PCIE0); PCMSK0 = _inputPinMask; } else if (_inputPort == &PORTC) { PCICR |= (1 << PCIE1); PCMSK1 = _inputPinMask; } else if (_inputPort == &PORTD) { PCICR |= (1 << PCIE2); PCMSK2 = _inputPinMask; }
And setting the appropriate service must must be programmed as
ISR(PCINT0_vect) { static uint8_t previousState = 0; uint8_t state = (PINB >> PINB0) & 0x01; if (state != previousState) { counts += 1; previousState = state; } } ISR(PCINT1_vect, ISR_ALIASOF(PCINT0_vect)); ISR(PCINT2_vect, ISR_ALIASOF(PCINT0_vect));
This all about the minimal requirements for counting pulses sensed on any pin of the Arduino board. These lines may be encapsulated in a refined library which will offer a great flexibility without the least programming complexity. This is the the aim of PlainCNTR library that I will be happly share with those we may need it. Next is a print of the usermanual which comes with the code and which give a good idea about the features of the library
Copyright (C) 2012-2013 Didier Longueville This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. PlainCNTR library contains a set of functions which are dedicated to counting pulses There are two ways of using the counting functions: interrupted mode or non interrupted mode In the non interrupted mode, the pulse counts or frequency measurement is made during a prefixed period of time (dwell time) during which nothing else is executed. In the interrupted mode, the pulse counts or frequency measurement is performed after starting the couting mode using the StartCounter() function, until the counter is stopped using the StopCounter() function. The pulse counts can be read at any time. Any pin form any port can be used for sensing pulses. Functions and commands --------------------------------------------------------------------------------------------------- InitializeCounter(inputPin, port, biasPin, [interrupted]) --------------------------------------------------------------------------------------------------- arguments: - inputPin: sensing pin - port: port to which the sensing pin is attached - biasPin: bias sensing pin or not - interrupted: set the interrupt mode or not returns: - None --------------------------------------------------------------------------------------------------- ReleaseCounter() --------------------------------------------------------------------------------------------------- arguments: - None returns: - None --------------------------------------------------------------------------------------------------- StartCounter([initPulses]) --------------------------------------------------------------------------------------------------- arguments: - initPulses: initial pulse counts returns: - None --------------------------------------------------------------------------------------------------- StopCounter() --------------------------------------------------------------------------------------------------- arguments: - None returns: - None --------------------------------------------------------------------------------------------------- GetPulses([dwellTime]) --------------------------------------------------------------------------------------------------- arguments: - dwellTime: time during which the pulses are sampled, required in the non interrupted mode only returns: - Number of pulses comments: - In non interrupted mode, dwellTime is not to be set; pulses can be read while counter is still on, or on completion of counting (after execution of StopCounter) --------------------------------------------------------------------------------------------------- GetFrequency(dwellTime) --------------------------------------------------------------------------------------------------- arguments: - dwellTime: time during which the pulses are sampled, non interrupted mode only returns: - frequency in Hertz comments: - None --------------------------------------------------------------------------------------------------- Constants --------------------------------------------------------------------------------------------------- Global public constants: - CNTR_BIA_DISABLED - CNTR_BIA_ENABLED - CNTR_NON_INTERRUPTED - CNTR_INTERRUPTED
The rest is just a matter of imagination and programming skills. Here is an example of use of the engine rpm counting functions …
Hi,
Could you send me the PlainCNTR library?
Thanks
Would you please have a look at the “code request” menu ? 😉