Introduction: 6.25 Nanosecond Resolution Timer for Any Microcontroller!
While designing a solid state replacement for a vacuum-tubes-and-CRT radar display subsystem, I was stuck at getting a suitable system to time the period between the synch pulse and the echo pulse. The synch pulse and echo pulse were being captured on two pins of an Arduino Due. The Arduino Due has a MCLK/2 clock of 42 MHz, which would give me a resolution of 23.81 nanoseconds (ns) which, for the speed of light, would translate to a distance of 6.86 meters. I wanted a better resolution.
After looking into solutions using high speed oscillators and discrete logic ICs (counters, shift registers), FPGAs and off the shelf solutions costing in the range of USD 2,000/-, I decided to examine the ESP8266 for some solution to my timing problem.
To my delight, I discovered two functions in the Arduino ESP8266 API, namely ESP.getCycleCount() and ESP.reset()
ESP.getCycleCount() counts the instruction cycles since start, in an internal unsigned 32-bit variable, liable to overflow every 28 seconds or so. A one microsecond period will give 160 instruction cycles. Now to measure the software overhead required by your software, you need to only count the instruction cycles your routine counts for a 1 microsecond delay and subtract 160 from that count, to give you the software overhead in acquiring the count. For 1 microsecond delay, I got a count of 213. Which worked out to 213-160 = 53 counts (53 x 6.25 = 331.25 nanoseconds) software overhead to acquire the count. Subtracting 53 from every count gives me a count accurate to within a few tens of picoseconds, for periods from 30 microseconds to about 500 microseconds.
Step 1: Setup the ESP8266
Open the Arduino IDE.
Select the appropriate ESP8266
Select the speed, 160 MHz.
Step 2: Write a Program to Trap a START and a STOP Interrupt
Setup two pins to respond to RISING or FALLING interrupts. Within the START ISR, capture the instruction count. Within the STOP interrupt capture the instruction count and raise a transmit flag. Reset the CPU to obviate a counter overflow. For interrupts that may be far in between, an automatic periodic reset or an overflow handler could be written.
Within the loop() function, transmit the count over the I2C bus. The slave should be setup to receive from the I2C slave.
Check out the program. It is quite self-explanatory.
I am now able to time the period between two events, to a resolution of 6.25 ns, which with the speed of light, resolves to a resolution of about 1.8 meters.
Profiling the count acquisition instructions for the software overhead, which will be a constant, is very important.