Introduction: How to Get an Arduino Micros() Function With 0.5us Precision
I love Arduino microcontroller programming, and I regularly use it in aerospace research, as well as in home projects. As I work on my many home projects, however, I frequently find myself needing a very precise timer. This is because my home projects focus around Radio Control (RC) aircraft, and RC hobby communication signals are timing-based signals. These RC communication signals are called PWM (Pulse Width Modulation) and PPM (Pulse Position Modulation). In the signals, a full stick deflection in one direction, on a hand-held radio transmitter, usually corresponds to ~1000us (microseconds), and a full stick deflection in the opposite direction corresponds to ~2000us.
To measure the timing in these signals, I first used the Arduino micros() function, in conjunction with external interrupts. The built-in Arduino micros() function is good, but not good enough for my needs. It has a precision of 4us (see here). This means that if I'm reading in a PWM signal that I know is exactly 1500us (center-stick position on a hand-held transmitter), I would see readings of 1496, 1504, 1500, and even sometimes 1492 or 1508us. This is too much deviation.
So...I looked and I looked and I looked for a solution. The only solution I could find used the Atmega328 microcontroller's Timer1, which is unacceptable for my needs, because then I lose use of the Arduino servo library. Therefore, I decided to do a ton of reading and get down into the guts of the microcontroller, accessing the Atmel's timers directly, and writing my own timer function.
From this endeavor is born my Timer2_Counter "library," which provides functions which can be used in place of the micros() function, and which provide a precision of 0.5us, which is 8x better than the micros() function!
For more detailed information on my code, as well as the latest version of my code, including a full list of its functions, please refer to my article here:
Arduino micros() function with 0.5us precision - using my Timer2_Counter "Library".
Step 1: Make Sure You Have an Arduino
This code is based on the Arduinos that use the Atmel ATmega328 microcontroller, such as the Arduino Uno and Nano. I highly recommend the Elegoo Uno kits from Amazon, as they are the best value I've seen, so check them out here. Their Uno kit prices range from ~$17 to ~$60, but they include tons of parts and will keep you busy for many months (if not years :)).
Step 2: Download & Extract the Timer2_Counter Code!
Go here: http://electricrcaircraftguy.blogspot.com/2014/02/Timer2Counter-more-precise-Arduino-micros-function.html
-This is where the most up-to-date article on this code, and link to download the code, will always be kept. Refer to this main page for any future changes to the code.
Click the download link near the top, following any instructions written there to download and save the zip file. Extract the files.
Feel free to check out my other articles by looking at the menus down the right side of the screen. Check out my most recent article by going to "Blog Home," and be sure to subscribe and share using the icons at the top right!
-Links to additional articles you may be interested in reading are at the very bottom of this instructable.
Step 3: Open the Example File in Your Arduino IDE
Click the "Timer2_Counter_Basic_Example.ino" file to open it in your Arduino IDE. When the IDE opens, notice that it automatically opens the "Timer2_Counter.ino" file with it, as a second tab. Click the tab to view its contents, including detailed descriptions of the available functions.
To get access to my Timer2_Counter 0.5us-precision timing functions, you must first call "setup_T2();" in order to properly set up the Atmega Timer2 prescalers and such. In my example code, I called this function within the Arduino setup() function.
This timer is 8x more precise than what the built-in Arduino micros() function gives you!
You can call "get_T2_micros();" to get the microsecond value of the timer, down to the nearest 0.5 microsecond value, or you can call "get_T2_count();" to get the count value on the microcontroller's Timer2. Since the count value increments one count every 0.5us, dividing this value by 2 gives you the microsecond value on the timer.
This Timer2_Counter code automatically takes care of the 8-bit timer overflows via an interrupt, and will therefore return count values from 0 to 4,294,967,296, since it returns count values as an unsigned long data type. This corresponds to microsecond values of 0us to 2,147,483,648us. This means that the timer can run for up to 35.79 minutes before overflowing back to 0. If you'd like to manually reset the timer back to zero you may call the "reset_T2();" function.
Again, for a full description of the code, refer to my main article here, as well as to the documentation within the Timer2_Counter tab, which automatically opened up in the Arduino IDE when you opened up the "Timer2_Counter_Basic_Example.ino" file just a minute ago.
Step 4: Run the Example and View the Output
Upload the code to your Arduino and open up the Serial Monitor to view the output. Be sure your Serial Monitor baud rate is set to 115200. This is what you will see. As you study my example code and compare it to this output you will see how I show various methods to measure a time interval, and how I compare them to the precision of the time interval as measured by micros().
Note that this example code does *not* show clearly the true benefit of using my Timer2_Counter functions. Rather, it simply shows how to use them. If you ever use my Timer2_Counter to measure a timed signal, or the time span of any recurring event, however, you will see that it returns much more precise and consistent values than using the micros() function!
Thanks for reading.
Don't forget to go back to Step 1 and subscribe to my blog if you haven't done so yet.
Next step: references and additional articles to check out.
Step 5: References
My sincere thanks to Massimo Banzi and all of the developers who went into making Arduino possible!
Here are my primary references that helped me learn how to write this code:
1) http://www.gammon.com.au/forum/?id=11504 - Nick Gammon's article on "Timers and counters"
2) http://www.gammon.com.au/forum/?id=11488 - Nick Gammon's article on "Interrupts"
3) http://www.atmel.com/Images/Atmel-8271-8-bit-AVR-Microcontroller-ATmega48A-48PA-88A-88PA-168A-168PA-328-328P_datasheet.pdf - 660 pg. ATmega328 datasheet
Other Articles I've Written That You May Be Interested in Reading:
1) The Power of Arduino
2) The Goal of a Lifetime
3) A Few Tips & Tricks: Arduinos, PCB Tricopter Frames, Home-made Acid Etchant for Copper
4) Propeller Static & Dynamic Thrust Calculation
5) Arduino Power, Current, and Voltage Limitations
6) Quick Tip: 4 Ways to Power an Arduino