This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

Proper way of synchronizing a timer to a GPIO edge?

Good day, gentlemen!

My project relies on very high time-keeping accuracy (<1µs). To do that I am using the PPS (Pulse Per Second) signal of a GPS (jitter ~10ns) that interrupts a GPIO pin. Furthermore I am using a general purpose timer to keep track of the unix time (I need very accurate timing (<1µs) WITHIN every full second). This timer runs at CPU speed and since the CPU speed can vary quite a bit and I absolutely cannot risk the drift that happens over time I need to use the PPS (as far as its available = GPS has a fix) signal to synchronize the timer to the real beginning of every second and set the load to the current CPU speed, so in case the GPS fails for a longer period of time my time extrapolation is still good.

So in a nutshell:

- The RTC timer (PGMU_RTC_TIMER) runs with a load of the CPU speed
- The CPU speed (= RTC timer load) is measured using the PPS signal, which is an actual 1 second
- On each PPS the RTC timer needs to be corrected so it starts counting the moment the PPS interrupt

In theory the RTC timer interrupt and the PPS trigger at the exact same time - so I never know which interrupt triggered first. They both got the same priority (so they do not preempt each other), however higher priority than all the other interrupts.
The things that can now happen every second:
Case A. the PPS comes before the RTC interrupt it needs to reset the RTC timer value to 0, since the RTC timer was too slow. The problem is now that this skips the RTC overflow/interrupt, thus skips incrementing the unix time. As a  solution to this I could just manually trigger the RTC timer interrupt, but...
Case B. the RTC interrupt comes before the PPS the PPS ISR has to know if the RTC has already been incremented for the current second, so it does not trigger it again resulting in +2s in 1 second.
Case C. the PPS does not trigger at all (GPS has no fix) - the RTC timer just runs without "interference".

To let the both ISRs know which one executed first I brought in a second timer (PGMU_RTC_HYSTERESIS_TIMER), which is a one-shot-up timer that runs for about 990ms and resets to 0 after overflow. So the PPS ISR can check if TimerValue > 0 - which is true while the timer is running and therefore knows that the RTC has already been incremented for the current second.

The problem with my code is that after a "random" time (could be 10 minutes, could be 15 hours) the RTC is 1 or more seconds behind, which means that the RTC timer interrupt did not trigger at least one time. I have absolutely no idea how this can happen, since I think that in all the three cases above, the RTC should be triggered exactly once every second, no matter what.

Here is my code:

#define PGMU_PPS_PIN_PORT_BASE				GPIO_PORTA_BASE
#define PGMU_PPS_PIN_NUM					GPIO_PIN_6

#define PGMU_RTC_TIMER_BASE					TIMER0_BASE
#define PGMU_RTC_TIMER_INT					INT_TIMER0A
#define PGMU_RTC_HYSTERESIS_TIMER_BASE		TIMER5_BASE

volatile time_t p_time_RTC = 0; // Stores unix timestamp
volatile bool p_bool_RTCSynced = false;

void PPSIntHandler(void) {
	// Get elapsed SysTicks during the past second.
	const uint32_t ui32_SysTicksSecond = MAP_TimerValueGet(PGMU_RTC_TIMER_BASE, TIMER_A);

	// Reset RTC Timer value.
	HWREG(PGMU_RTC_TIMER_BASE + TIMER_O_TAV) = 0;

	// Clear the GPIO interrupt flag.
	MAP_GPIOIntClear(PGMU_PPS_PIN_PORT_BASE, PGMU_PPS_PIN_NUM);

	// Check if RTC timer ISR already triggered.
	if (MAP_TimerValueGet(PGMU_RTC_HYSTERESIS_TIMER_BASE, TIMER_A) == 0) {
		// Trigger the RTC timer interrupt.
		MAP_IntTrigger(PGMU_RTC_TIMER_INT);
	} else {
		// This means the RTC ISR already executed.
		// Reset the RTC hysteresis timer.
		HWREG(PGMU_RTC_HYSTERESIS_TIMER_BASE + TIMER_O_TAV) = 0;
	}

	// Update CPU frequency.
	// CPU speed is either (ui32_SysTicksSecond) OR (CPUFrequencyGet() + ui32_SysTicksSecond)
	CPUFrequencyUpdate(&ui32_SysTicksSecond);

	// Set RTC timer to expected PPS tick load.
	MAP_TimerLoadSet(PGMU_RTC_TIMER_BASE, TIMER_A, CPUFrequencyGet());
}

void RTCTimerIntHandler(void) {
	// Clear the timer interrupt flag.
	MAP_TimerIntClear(PGMU_RTC_TIMER_BASE, TIMER_TIMA_TIMEOUT);

	// Enable RTC hysteresis timer.
	MAP_TimerEnable(PGMU_RTC_HYSTERESIS_TIMER_BASE, TIMER_A);

	// Advance the RTC by 1 second.
	p_time_RTC++;
}

This is probably not the most elegant solution for what I am trying to accomplish but its the best one I can currently think of.

I would be so happy if someone had a solution to this problem or better yet a more elegant (bullet proof) way of synchronizing a timer to an external pulse.

  • Hello Cedric,

    The issue is probably the accuracy of the timer running of the system clock. The Hibernate Module in the TM4C device has a 32KHz crystal oscillator that can be used to set a 1sec RTC Interrupt. The accuracy of Hibernate RTC is determined only by the accuracy of the 32KHz crystal (which works excellent in the vendor's honest opinion). We have run it for an application with sub second drift on one unit over months, that also due to the temp cycling the crystal underwent as well.

    Regards

    Amit

  • Thank you Amit for the response. I have read about the 32KHz OSC in the data sheet somewhere. I am afraid I don't think that I can make any use of it because its maximum accuracy is 1/32768 = 30.5µs and I am taking timestamped measurements over a period of months or even years and I cannot afford the timestamps being off >20µs at any given time. That's the reason why I have to use a GPS to constantly sync the RTC so I get the most accurate time possible.

    Someone pointed out to me that I could use a timer in time capture mode, driven by the PPS pin to determine how many ticks are generated during each second (=> capture positive edge of the PPS) and then also use DMA to reset the timer and/or get the timer value. Something like that. Unfortunally I have not worked with neither timers in capture mode nor with DMA, so I am looking into all of that now since it seems like a better way of determining a frequency or rather period length. But I do not think that that would help me with the problem that I gain or lose a second during some weird interrupt (service handler) constellation - which is my main issue at the moment...

  • Hello Cedric,

    AFAIK, the Hibernate RTC is the most accurate as it is on the a separate clock and can be made resilient to power failure by running it of a coin cell. Also when using the Timers, the system clock using a PL would introduce errors as the PLL jitter over time may affect the resolution. Still if you wish to use the Timer the ideal method would be the Time Capture where the PPS signal causes the timer to snapshot the value and generate a DMA request to transfer the value from the snapshot register to memory for analysis. This way you can get the approximate drift over time between two captures.

    Which device TM4C123 or TM4C129 are you targeting the application for?

    Regards

    Amit

  • I'm using the TM4C1294NCPDT - I tagged it not obviously enough in the first post ;-)

    I am aware of the PLL drift that happens over time (I've measured it using the code above) - that's why I use the PPS signal from a GPS to constantly measure the exact CPU frequency and can then generate a very precise internal RTC with a timer running at that measured load.

    Even if I did use an external RTC - which drifts way too much than I can allow over my runtime - I would still need some form of a timer (even it its SysTicks) that enables me to interpolate the µs between two full seconds, so I can get an accurate timestamp like 1409510926 . 123456 - and on top of that I would now have THREE things that need to be synchronized :-\ And I cannot even manage the two at this point :-(

  • Hi,

    May i ask you if you need only unix time to use or some other processes/signals to manage?

    Look also for this: http://www.conwin.com/time-frequency_references-gps_disciplined-gps_references.html

    Petrei

  • Hello Petrei,

    the project measures/processes external analog and digital signals that need this very accurate timestamping - if that's what you're asking. Other than that there is nothing else involved in the time-keeping process except the GPS module.

    At first glance the WI125 module from the website you've posted looks like exactly what I need... However, I'm afraid I might not be able to implement a different device before the deadline of the prototype I'm currently building. It's a hot candidate for v2 though! Thank you for this awesome tip!