Menu

rc::Timer1

dvdouden

As you probably know, the Atmega328 (as used in the Arduino Uno and Nano) has three timers: timer0,1 and 2. Timers 0 and 2 are both 8 bit timers, meaning they'll count to 255 and will then overflow to 0. Timer 1 is the only 16 bit timer available, it'll count to 65535 before overflowing, which is way more useful when you're trying to time things. By the way, timer 0 is used by the standard wiring/arduino code for getting your millis() and micros(). Timer 2 is used by the Tone functions (and by rc::Buzzer and rc::Speaker)

Whenever a timer hits an overflow it'll generate an interrupt and kick off the interrupt handler. Another way to trigger an overflow is by setting a compare value. For timer 1 these are the 16 bit OCR1A and OCR1B registers (Output Compare Register 1 A/B). Whenever the value of timer equals that of the OCR it'll trigger an interrupt, combine that with the 16 bit range and you've got a pretty accurate way to trigger interrupts at certain intervals.

Timers also have a so called prescaler. This will tell the timers how fast to run. For prescaler N, the timer will increase its value by one once every N CPU cycles. N may be either 1, 8, 64, 256 or 1024. Since the Arduino is running at 16 MHz, or 16,000,000 clock cycles per second, timer 1 would normally overflow about 244 times per second (16M / 65536), that's about once every 0.004 seconds. By applying a prescaler of 8 we bring that down to about 30 times per second or once every 0.033 seconds. The timer is effectively running at 2 MHz, which means the timer will increase its value every 0.0000005 seconds, that's every half microsecond. That's something that's easy to work with! That means that if you want to generate an interrupt after 1500 microseconds you simply need to set the OCR to 3000. It also gives you plenty of room to play with for RC purposes, since there are virtually no occasions where you'll want to wait more than 30 milliseconds.

As you may have guessed, this is how Timer1 is used by the signal generating classes ([rc::PPMOut] and [rc::ServoOut]). They simply set the OCR to a specific value (for example the length of a pulse) and when the interrupt hits they toggle one of the output pins and they set a new OCR value for the next action.

There's one other neat feature of Timer1, and that's the ability to toggle a pin when OCR1A matches without having to do that manually. There are two pins that can be used; OC1A and OC1B which map to pins 9 and 10 respectively. PPMOut has the ability to use this feature if you specify one of these two pins as its output pin. In fact, PPMOut used to be limited to just these two pins. These pins are guaranteed to toggle even if the interrupt handler itself has been delayed (by other interrupts for example). Another handy feature is the ability to reset the timer on a compare match, this increases precision even further as there's no need to reset the timer in the interrupt handler (and there's always a delay between the actual interrupt and the time the interrupt handler is called). This is not used by the output classes, instead they increase the OCR by the amount of time until the next interrupt, this gives the same result but still allows the timer to overflow, which is needed by the input classes.

The signal input classes ([rc::PPMIn] and [rc::ServoIn]) use timer 1 simply as a high precision clock to know at what time input pins have changed. From the pin change interrupt handler they read the value of timer 1 and compare it to the last time a pin has changed. All they need to do is divide the difference by 2 to get the time in microseconds (remember 2 clock ticks per microsecond).

It's also possible to use the input classes together with the output classes, but I have to emphasize that timings may be off when there are too many interrupts taking place. If you have a pin change interrupt, for example, and while its interrupt handler is being called you also get a compare match interrupt, then the compare match interrupt handler will have to wait until the pin change interrupt handler has finished; only one interrupt handler can be running at a time. This also means that if you need to toggle a pin manually in the compare match interrupt handler, that pin will be toggled later than it would have been if there had not been a pin change interrupt. More interrupts, more problems. Oh, and remember timer 0? That one is kicking interrupts once every 1024 microseconds as well...


Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.