My name is Sebastian, I am new in the forum. I have been reading through to see if I can find the issue I have but nothing yet. For that reason I am opening this topic with the hope that you could help me.
introduction of the problem
I am want to measure time in Arduino (nano or Uno ATmega328p) in microseconds between two rising pulse of the PPS(pulse per second) of the GPS and I am using the function Micros(). In theory the timing of the PPS have to be precise in nanoseconds but I know about the resolution of 4 microseconds of the board itself. The test is working ok getting values close to the 1.000.000 microseconds ( 998.977 +/- 4 microseconds) I can work with it, because is constant for every pulse.
The problem
The problem is, when I try to use a different chip different than the 328p. For example the Arduino Every with the ATmega4809 as well another board with the ASR6502 (48 MHz ARM® Cortex® M0+ MCU) The values that I get are similar but no constant for every pulse ( difference more that 1000 microseconds of every pulse).
My question is: Why do I get this no constant measurements if I am using faster boards. Or do I am doing something wrong.
Thanks a Milllon, Hopefully I explain properly.
This is the code I am using. The PPS from the GPS is in the pin 2 where I am using the interrupt.
Can we go back one step please ?
You want to measure a frequency from a GPS. What is that signal ? Can you get the same information via I2C ?
The ATmega328P is only a 8-bit microcontroller at 16MHz, but it is optimized for fast input and output and has versatile timers to create pulses or to measure pulses.
The code that is running on the ATmega328P is simple and limited. That is its strength, together with the timers.
Other processors are more complex, and sometimes the Arduino code has to do more to make the Arduino layer compatible for every board. New processors might have hardware for floating point and 32-bit instructions, but not the simple code of a ATmega328P dedicated for fast input and output.
If you want to see optimized code that uses the timers of the ATmega328P, then have a look at these:
Use the Input Capture mode of Timer1, and you can measure the time between PPS pulses in increments of 62.5 nanoseconds. See this tutorial (scroll down to Input Capture).
Code to do that, averaging over 10 samples (divide result by 16 to get us):
// Frequency timer using input capture unit
// Author: Nick Gammon
// Date: 31 August 2013
// added averaging JR 2015
// Input: GPS 1PPS capture signal on Pin D8
volatile boolean first;
volatile boolean triggered;
volatile unsigned int overflowCount;
volatile unsigned long startTime;
volatile unsigned long finishTime;
// timer overflows (every 65536 counts)
ISR (TIMER1_OVF_vect)
{
overflowCount++;
} // end of TIMER1_OVF_vect
ISR (TIMER1_CAPT_vect)
{
// grab counter value before it changes any more
unsigned int timer1CounterValue;
timer1CounterValue = ICR1; // see datasheet, page 117 (accessing 16-bit registers)
unsigned long overflowCopy = overflowCount;
// if just missed an overflow
if ((TIFR1 & bit (TOV1)) && timer1CounterValue < 0x7FFF)
overflowCopy++;
// wait until we noticed last one
if (triggered)
return;
if (first)
{
startTime = (overflowCopy << 16) + timer1CounterValue;
first = false;
return;
}
finishTime = (overflowCopy << 16) + timer1CounterValue;
triggered = true;
TIMSK1 = 0; // no more interrupts for now
} // end of TIMER1_CAPT_vect
void prepareForInterrupts ()
{
noInterrupts (); // protected code
first = true;
triggered = false; // re-arm for next time
// reset Timer 1
TCCR1A = 0;
TCCR1B = 0;
TIFR1 = bit (ICF1) | bit (TOV1); // clear flags so we don't get a bogus interrupt
TCNT1 = 0; // Counter to zero
overflowCount = 0; // Therefore no overflows yet
// Timer 1 - counts clock pulses
TIMSK1 = bit (TOIE1) | bit (ICIE1); // interrupt on Timer 1 overflow and input capture
// start Timer 1, no prescaler
TCCR1B = bit (CS10) | bit (ICES1); // plus Input Capture Edge Select (rising on D8)
interrupts ();
} // end of prepareForInterrupts
void setup ()
{
Serial.begin(115200);
Serial.println("Frequency Counter");
pinMode(8, INPUT_PULLUP);
pinMode(7, OUTPUT);
digitalWrite(7, LOW);
pinMode(13, OUTPUT);
digitalWrite(13, HIGH);
// set up for interrupts
prepareForInterrupts ();
} // end of setup
void loop ()
{
static unsigned long average = 0;
static int n = 0;
// wait till we have a reading
if (!triggered)
return;
PINB |= (1 << 5); //blink LED
// period is clock cycles in one second
unsigned long elapsedTime = finishTime - startTime;
Serial.println (elapsedTime);
average += elapsedTime;
n++;
if (n == 10) {
Serial.print("System clock count, average of ten: ");
Serial.println(average / 10);
n = 0;
average = 0;
}
// so we can read it
delay (500);
prepareForInterrupts ();
} // end of loop
+1 for using the input capture - great for short timing, however maybe not so suitable for this specific application. OP is talking about 1 mln µs, so 1 second intervals.
For starters, your current code you can make far more accurate. You do not capture the micros() value in the ISR, instead you wait until that line in loop() is run, therefore there's an unpredictable delay - it depends on where in the code it was when the interrupt happened. Outside of loop() the Arduino is doing other things, like updating micors() and millis().
So store the value of micros() in the ISR, don't just set a flag. The flag is for the rest of your code to know that a new value has been captured and it can do something about it.
You can improve on this by using timer1, letting it run without prescaler, and record the current value of timer1 in your ISR. Then you have a 62.5 ns resolution on 16 MHz, and 20.8 ns resolution on 48 mHz. Of course it takes time to get into the ISR, but this is the same every time (bar one clock tick) so that cancels out.
The challenge is that you're looking for pulses of exactly 1 second. That can not be done directly with Timer1 as it is only a 16-bit timer, overflowing every 65536 ticks, every 4096 µs. You'd need a second counter, counting the times TImer1 overflows until the actual event happens. From that you can calculate the total clock ticks that elapsed between two events. The easiest to count the overflows is through a timer overflow interrupt.
Of note: a typical crystal has a tolerance of 25 or 50 ppm, so that 16 MHz is not exact either.
Yes! You can very accurately calibrate the Arduino oscillator using the code in post #3, and then use that correction to make other very accurate frequency or period measurements with the Arduino.
you want to measure a frequency from a GPS. What is that signal ? //
No, I want to measure the time within this pulse that I get from the GPS. The signal is a square wave at 1HZ.(900ms 0v and and 100 ms 5v.) The idea is measure this event when it rise to get the 1million microseconds (Could it be no exactly 1million but I need it to be stable / constant ) and I am not getting that with the other boards.
The purpose to measure the PPS pulse accurately is that after knowing the value, I can have another Arduino receiving the same PPS signal (That is in nanoseconds precision) and I can synchronize both with a radio to after trigger another event accurate. I am using it for triggering seismic surveys.
For the task I am doing I have an another event that I have to know at what time happened within the pulses. But my main question was why I have constant/stable measuring time within edges in the ATmega328p an not in the fasters boards. In the example code I posted is just to test the boards.
I will add some images of the results I am getting with the different boards.
You're thinking about this backwards. The objective is not to measure the time of the PPS, it's to measure the frequency offset/drift of the microcontroller timebase (the crystal or resonator).
The GPS 1 PPS has some jitter due to (mostly) clock on clock effects, but the long term time stability is very very good, so this can be used to measure the drift of other systems' clocks. This is commonly done, for instance, to synchronize time and frequency between cellular towers and stratum 0 NTP servers. GPS disciplined clocks are, if not ubiquitous, pretty common.
And that is done because the OP's square wave is generated in the receiver, not the GPS satellite. The satellite signal initiates the pulse and nothing more. Any two identical receivers will not have identical square wave outputs.
I want to know the time that passed since the last pulse (PPS) when I triggered an event. I am using the GPS PPS to trigger at the same time measured but a second later. for that reason I need to be stable/constant.
The ATmego328p is getting constant microseconds, (that I can work with it because it is a constant time within pulses. (does not matter if is not 1million microseconds). but I am using another board for example this one (CubeCell GPS-6502 (HTCC-AB02S) – Heltec Automation) that use the ASR6502 (48 MHz ARM® Cortex® M0+ MCU) and I have not constant values when I measured the microseconds within the pulses