Counting pulses in the background while the main program is running

Hello there,

I would to know if is possible to set two independent pins as pulse counters for counting in the background while the main program is running, and retrieve the count every minute, clear the accumulators and start the count again.

The pulses will be at low freq, i'm expecting less than 200 hertz in one counter and maybe 2 or 3 KHz in the another one.

I'm using a Arduino Nano (AATmega328 based).

Thanks in advance.

I would use interrupts on pin 2 and 3.

The Arduino is a single core processor. with no operating system. There is NO "background" in which things can happen.

Besides counting "pulses" from two pins, what else is the Arduino going to be doing?

PaulS, Thanks for the correction.

The Arduino will be sensing a BMP180 Barometric module, an DS1307 RTC, one -or more- 256K EEPROM's, a temp/hum sensor (DHT11, DHT22 or maybe an SHT device), if things going well there will be an nRF24L01 RF module.

Nick Gammon has a very good Interrupt Tutorial

...R

INTx pins can be used for counting the pulses but mRF24L01 uses one INTx pin. You need two for pulse counting. So, you have to use Mega2560.

You could use pinchange interrupts for the pulse counting,
if one of your external interrupts is already in use.

I use that technique for IR receivers, 433MHz receivers, rotary encoders and buttons.
I did not make the NRF24 use pinchange interrupts yet, but its on the list.

I have not connected the interrupt pin on my nRF24s. I doubt if it is necessary unless you are streaming data such as music - an unlikely event on an Arduino.

...R

Timer 1 configured for External Clock is an excellent choice for the faster signal. The micros / millis code provides a good example for handling an overflow into a larger integer. I believe one of Paul Stoffregen's frequency libraries works exactly that way.

Because timer 0 is used for micros / millis and because of the location of the External Clock input for timer 2 you will not be able to do the same for the slower signal (unless you move micros / millis to timer 2).

Note that DHT11/22 has a handshake that is a sensitive for interrupts as that can break its timing. A handshale takes ~ 5 millisec so the 200Hz will just interrupt once, the 3KHz will interrupt ~15 times.
As an interrupt takes 1 uSec at least it will affect the timing.

At least my DHT lib has troubles with too many interrupts as it uses hard coded thresholds.

jayanthd:
INTx pins can be used for counting the pulses but mRF24L01 uses one INTx pin. You need two for pulse counting. So, you have to use Mega2560.

Sorry, anything but the Arduino Nano is very far from budget. :confused: (Putting aside the fact that is difficult to find in my country).

yv1hx:
Sorry, anything but the Arduino Nano is very far from budget. :confused: (Putting aside the fact that is difficult to find in my country).

Is there budget for an external counter chip? That would be another way to go.

Thanks you all for your recommendations.

Actually, an external counter chip has been always my first thought, however, I was exploring the Arduino-internal-counters option for keep low the component count and footprint.

Based upon your comments and inspired by this example, now I'm considering use a Picaxe X2 part as external counter...lucky me, I have a single unit of this in my components box... (In our screwed economy, Nowadays the Picaxe 28X2 part cost about the double that my Arduino Nano a month ago :o ).

There is an excellent frequency counter, released by Nick Gammon in 2012, counting pulses at Pin-5 (T1).
It was not too difficult to extend it to count also pulses at Pin-4 (T0) at the same time. It works up to frequencies of 5 MHz.
I am using it to compare crystals with the Serial Plotter.

/* Timer and Counter example
  Author: Nick Gammon
  Date: 17th January 2012
  Modified/extended: 3rd July 2018
  Timer-2 controls the gate time
  Input: Pin D4 for Timer-0 (T0)
  Input: Pin D5 for Timer-1 (T1)
*/
// these are checked for in the main program
volatile unsigned long timer0Counts;
volatile unsigned long timer1Counts;
volatile boolean counter2Ready;

// internal to counting routine
word overflow0Count;
byte overflow1Count;      // was: unsigned long. makes no difference at 4 MHz
unsigned int timer2Ticks;
unsigned int timer2Period;
boolean first = true;     // the first value mostly is bad
const float korr = 0.99999975; 

void setup () {
  Serial.begin(9600);
  Serial.println(__FILE__);
  /* as ISR (TIMER1_OVF_vect) is already used by Arduino
    we go for ISR (TIMER0_COMPA_vect)
    It will trigger an interrupt at 0xFF match, a bit too early. */
  OCR0A = 0xFF;
}

void loop () {
  // stop Timer 0 interrupts from throwing the count out
  byte oldTCCR0B = TCCR0B;

  startCounting (1000);   // was 500. how many ms to count for

  while (!counter2Ready); // loop until count over

  // adjust counts by counting interval to give frequency in Hz
  float frq0 = timer0Counts * 1000.0 / timer2Period * korr;
  float frq1 = timer1Counts * 1000.0 / timer2Period;

  if (!first) {
    Serial.print("Frequency:\t");
    Serial.print(frq0);
    Serial.print("\tHz\t");
    Serial.print(frq1);
    Serial.print("\tHz\t");
    Serial.print(frq1 - frq0);
    Serial.println("\tHz");
  }
  first = false;
  // restart timer 0
  TCCR0B = oldTCCR0B;
  // let serial stuff finish
  delay(200);
}

void startCounting (word ms) {
  // the gate:
  counter2Ready  = false;         // time not up yet
  timer2Period   = ms;            // how many 1 ms counts to do
  timer2Ticks    = 0;             // reset interrupt counter
  // the counters:
  overflow0Count = 0;             // no overflows yet
  overflow1Count = 0;

  // reset Timer 0
  TCCR0A = 0;
  TCCR0B = 0;
  // reset Timer 1
  TCCR1A = 0;
  TCCR1B = 0;
  // reset Timer 2
  TCCR2A = 0;
  TCCR2B = 0;

  // Timer 0 - counts events on pin D4
  TIMSK0 = 2;   // interrupt on Timer 0 compare match

  // Timer 1 - counts events on pin D5
  TIMSK1 = 1;   // interrupt on Timer 1 overflow

  // ----------- the gate -----------
  // Timer 2 - gives us our 1 ms counting interval
  // 16 MHz clock (62.5 ns per tick) - prescaled by 128
  //  counter increments every 8 µs.
  // So we count 125 of them, giving exactly 1000 µs (1 ms)
  TCCR2A = 2;   // CTC mode
  OCR2A  = 124; // count up to 125  (zero relative!!!!)
  // Timer 2 - interrupt on match (ie. every 1 ms)
  TIMSK2 = 2;   // enable Timer2 Interrupt
  // Reset prescalers
  GTCCR = 2;    // reset prescaler now
  // start Timer 2
  TCCR2B = 5 ;  // prescaler of 128
  // set counter-2 to zero
  TCNT2 = 0;
  // ----------- the gate -----------

  // set counter-0 to zero:
  TCNT0 = 0;
  // start Timer 0
  // External clock source on T0 pin (D4). Clock on rising edge.
  TCCR0B = 7;
  // set counter-1 to zero:
  TCNT1 = 0;
  // start Timer 1
  // External clock source on T1 pin (D5). Clock on rising edge.
  TCCR1B = 7;
}

ISR (TIMER0_COMPA_vect) {
  ++overflow0Count;       // count number of Counter0 compare matches
}

ISR (TIMER1_OVF_vect) {
  ++overflow1Count;       // count number of Counter1 overflows
}


//****** gate time *******************************************
/*  Timer2 Interrupt Service is invoked by
    hardware Timer-2 every 1 ms = 1000 Hz
    16Mhz / 128 / 125 = 1000 Hz            */

ISR (TIMER2_COMPA_vect) {
  // quickly grab counter values before they change any more
  byte timer0CounterValue = TCNT0;
  unsigned long overflow0Copy = overflow0Count;
  word timer1CounterValue = TCNT1;  // see datasheet 16.3, page 117 (Accessing 16-bit Registers)
  unsigned long overflow1Copy = overflow1Count;

  // see if we have reached timing period
  if (++timer2Ticks < timer2Period) return;  // not yet
  // --------------------------------------------------

  // if just missed an overflow
  if ((TIFR1 & 1) && timer1CounterValue < 256) overflow1Copy++;

  // end of gate time, measurement ready
  TCCR0A = 0;    // stop timer 0
  TCCR0B = 0;

  TCCR1A = 0;    // stop timer 1
  TCCR1B = 0;

  TCCR2A = 0;    // stop timer 2
  TCCR2B = 0;

  TIMSK1 = 0;    // disable Timer1 Interrupt
  TIMSK2 = 0;    // disable Timer2 Interrupt

  // calculate total count
  timer0Counts = (overflow0Copy <<  8) + timer0CounterValue; // each overflow is   256 more
  timer1Counts = (overflow1Copy << 16) + timer1CounterValue; // each overflow is 65536 more
  counter2Ready = true;                             // set global flag for end count period
}

is it possible to count pulses and calculate pulse per second ?

mortenx:
is it possible to count pulses and calculate pulse per second ?

Yes.

If you know how many pulses have been counted over a period then it is easy. What have you tried ?

The MCP23017 sports two configurable interrupts to go with its sixteen digital I/O. They can be OR'ed together for one IRQ to a processor.

Just sayin'.

PaulS:
The Arduino is a single core processor. with no operating system. There is NO "background" in which things can happen.

While (for example) the ATmega328P is busy exchanging data with another MCU over the I2C Bus, the counters (TC0, TC1) can count external pulses without the intervention of the MCU. Can we say that the counting events are happening in the background?

An Operating System makes available a number of APIs which a user can hook up in his application program through Programming Language. In the Arduino IDE Environment, we have similar facilities (millis(), delay(), ...) by virtue of the init() function being loaded in the flash along with the sketch. Would it be too incorrect to say that there is a silent Operating System in the Arduino IDE Platform?

is this the same problem when wanted count pulses and take time between pulses ?
so that one program counts pulses and other calculates time between them ?

There are 2 basic ways to do it

1 - measure the time interval between pulses and calculate the PPS. It is more accurate if you average the intervals over a period

2 - count the number of pulses in a fixed period and calculate the PPS from that. The longer the period, the more accurate the figure will be (for that period). Counting over a period of 1 second, if that is convenient, means that no calculation is required.