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.
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.
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.
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.
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
}
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 ?
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.