-
Notifications
You must be signed in to change notification settings - Fork 5
Implementing a high resolution Teensy clock
- Extending the cycle counter to 64bit
- The periodic timer of the real time clock
- Implementing the Teensy clock
- Usage examples
As shown here it
is easy to set up the chrono::system_clock for using any convenient time base.
Instead of the millis() based example we could as well use the cycle counter
as underlying time base. Here we
already discussed how to use it to define a custom duration type which we could
use for the high resolution clock. However, we need to take the following into account:
- The 32bit cycle counter of a T4.x @600MHz quickly rolls over (2^32 / 600MHz = 7.6s).
- The
chrono::system_clockclass uses nanosecond based uint64_t time ticks. the cycle counter runs at 1/F_BUS = 1.667ns (T4.x, 600MHz). Mapping this to nanoseconds is possible of course but would introduce a significant rounding error for small times. - To be able to track absolute times a synchronization to the Teensy RTC would useful.
To achieve useful rollover times we first need to extend the 32bit cycle counter to 64bit. This will give us a roll over time of 2^64 / 600MHz = 975 years which seems to be sufficient. Extending is not difficult:
uint32_t oldLow = ARM_DWT_CYCCNT;
uint32_t curHigh = 0;
uint64_t getCnt()
{
uint32_t curLow = ARM_DWT_CYCCNT;
if (curLow < oldLow) // we had a roll over
{
curHigh++;
}
oldLow = curLow;
uint64_t curVal = ((uint64_t)curHigh << 32) | curLow;
return curVal;
}The code above checks if the current value of the cycle counter is smaller than the last one we have seen. This is only possible if the counter rolled over in between and we have to increment the high word its high word by 1 before we return the combined 64bit value.
Obviously, this pattern only works if we can ensure that getCnt() is called
at least once per ARM_DWT_CYCCNT overflow period (7.6s @600MHz). We could use
one of the Interval timers to call it periodically but, using one of the 4 timers
would be too expensive.
Fortunately there is a seldom/never used periodic timer in the real time clock
module of the T4.x processors which is perfectly suited to call getCnt() say once
per second. Since the RTC registers are already set up by Teensyduino enabling
the perioding interrupt is straight forward:
// disable periodic interrupt
SNVS_HPCR &= ~SNVS_HPCR_PI_EN;
while ((SNVS_HPCR & SNVS_HPCR_PI_EN)){} // spin until PI_EN is reset...
// set interrupt frequency to 1Hz
SNVS_HPCR = SNVS_HPCR_PI_FREQ(0b1111);
// enable periodic interrupt
SNVS_HPCR |= SNVS_HPCR_PI_EN;
while (!(SNVS_HPCR & SNVS_HPCR_PI_EN)){} // spin until PI_EN is set...
// attach a callback
attachInterruptVector(IRQ_SNVS_IRQ, SNVS_isr);
NVIC_SET_PRIORITY(IRQ_SNVS_IRQ, 255); // lowest priority
NVIC_ENABLE_IRQ(IRQ_SNVS_IRQ);And here the simple callback we attached to the interrupt:
void SNVS_isr(void)
{
SNVS_HPSR |= 0b11; // reset interrupt flag
getCnt() // dummy call to check for overflow of the cycle counter
asm("dsb"); // wait until flag is synced on busses to prevent double calls of the isr
}TBD
TBD
Teensy is a PJRC trademark. Notes here are for reference and will typically refer to the ARM variants unless noted.