Measure precise time in microseconds

Hello All,

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.

int PPS = 2;
volatile unsigned long t0, t1;
volatile bool newpulse = false;



void setup() {
  Serial.begin(9600);
  pinMode(PPS, INPUT);
  attachInterrupt(digitalPinToInterrupt(PPS), PPSrising, RISING);
}  

void PPSrising(){
newpulse = true;
t0 = micros();
}



void loop() {
  if (newpulse) {
    newpulse = false;
    Serial.println(t0 - t1);
    t1 = t0;
  }
}

Hello, welcome to the forum.

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:

They have a link to Github for the source code.
What is the range of the frequency ?

1 Like

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.

1 Like

The method linked in post #3 is perfectly suitable for this task, as it uses a 32 bit counter to time events up to 268 seconds with 62.5 ns precision.

An interesting exersise for sure.

But is there any prupose in attmpting to (accuratly) measure the periods between the PPS pulses from a single GPS ?

3 Likes

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.

Hello Koepel,

Thanks for the quick response. I appreciate it.

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.

Thanks again

Hello jremington,

Thanks for that.

I will have a look the tutorial. I am not expert in coding for that reason my basic coding.

I understood that using the time1 I can have precision of 62.5 ns with the ATmega328p.

I will test this today and I get back to you.

Thanks

Hello srnet,

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.

Thanks

Why time or measure the PPS pulse at all though ?

If you sync the Arduinos on the same edge of the PPS pulse, then the Arduinos will be synchronised to within a few tens of nS.

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.

As far as I know, this kind of syncing is done with hardware PLL.

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.

Nope, just asking the OP to clarify what they were trying to achieve.

And the question revealed the the answer, there is a need to time (accuratly presumably) an "event" that occurs between the PPS pulses.

Yes,

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

for example
999198
1000175
1000165
1000185
999184
1000198
1000243
1000220
999224
1000184
1000222
1000223
1000206
999173
1000196
1000240
999199
1000181
999204
1000215
1000213
1000212
1000198
999244
1000222
1000211
1001220
998162

You will always have inconsistent results on any microprocessor when interrupts of any sort happen to occur during your timing period.

1 Like

But I do not have that inconsistency in the ATmega328P, for example:

994322
994322
994322
994322
994322
994322
994322
994322
994322
994322

Constant microseconds within pulses.

You forgot to accurately and completely describe how you did this other experiment.

Go back and read post #2 again, very carefully.