Keeping Time on ARM

Implementing support for high resolution timers on ARM platforms is an interesting thought exercise if you’re used to having your OS do the work for you. In this case, I’m using a Cortex-M3 as a part of a LPC1343 package. Timers can vary from vendor to vendor, but the Cortex-M3 spec at the very least defines a Systick timer. In the case of the LPC1343 there are also two 16 bit timers as well as two 32 bit timers. Systick can be configured to tick every cycle or every N cycles based on a prescale value, but for the purposes of most userspace applications it’s not a good fit. When an interrupt is triggered if excessive time is spent in the handler then the clock itself can be delayed or skewed. Obviously this isn’t a good fit for general userspace tasks, but fortunately there are better options for this chip.

The four timers mentioned earlier all have similar configuration registers and interfaces. The only difference between the two sets mentioned are the max value of the clock ticks. A tick itself isn’t that useful for the purposes of timing, but mapping to actual timings isn’t very difficult. A quick primer:

Imagine a 1 Mhz cpu:
1 Mhz = 1,000,000 Hertz per second
1 second = 1,000,000 microseconds
1 cycle = 1 microsecond

The main registers of interest for our uses are the Timer Control Register, Prescalar Register and Match register. The first controls enabling/disabling/resetting and not much else. However, the prescale and match registers are more interesting. A value in the prescale register will delay increasing the cycle counter until that many cycles have passed. This is crucial for accurate timings due to varying clock speeds. If a prescale value is set to the same value as the cpu’s clock rate in Mhz then it means every cycle counted is a microsecond. From there we can wire up interrupt handlers or simple wait loops to trigger upon a change.

More interestingly, you can also configure a match value to fire off an event when the timer counter matches. These can be interrupts, value resets or digital io shifts. For example, we could configure a line to toggle every time a certain amount of time has passed (this is in effect a PWM output on the line).

1ms pwm