So I understand that when setting a timer interrupt you don't use nanoseconds or milliseconds, but the actual number of clock cycles. However, I don't understand how you would be able to setup a specific time this way. If I wanted my interrupt to go off every 20 milliseconds would I have to divide that by the duration of each clock cycle?
The timer takes its time from the system clock. That may be, typically, 16MHz. We call this F_clk_io for the Frequency of the IO CLocK.
Every time that clock ticks the timer counts. Or, depending on settings, every time that clock ticks the prescaler counts, and eventually the output of the prescaler ticks and then the timer counts.
So first you need to divide the F_clk_io by the prescaler. That may be 1:1, 1:2, 1:4, 1:8, etc, depending on settings. No prescaler is the same as a prescaler of 1:1.
So say you have selected a 1:4 prescaler. The timer's frequency is then F_clk_io / 4, or if it's 16MHz you're running at, then the timer's frequency is 16/4 = 4MHz.
So at 4MHz each tick lasts for 1/4000000 = 250ns.
If you want 20ms you need to know how many ticks that would be.
20ms in nanoseconds would be 20000000. 20000000 / 250 = 80000 ticks.
Now, there's a problem there. 80000 won't fit into the 16-bit registers of a 16-bit timer. The maximum you can have is 65535. So we're going to need to slow things down a bit.
Set the prescaler to 1:8 and see what happens...
16/8 = 2MHz. 1/2000000 = 500ns. 20000000 / 500 = 40000 ticks.
That fits. See how doubling the prescaler has simply halved the number of ticks you get in the same time?
So setting it to a prescaler of 1:8 and letting it tick 40000 times should give you 20ms.
Except it doesn't.
It gives you 20.0005ms.
Why? Because the interrupt triggers and the counter resets when it reaches the target count, not when it passes the target count. And since it starts counting at 0, there's actually 40001 ticks there. So you have to subtract 1 from the number of ticks.
So the finished formula is:
ticks = ((F_clk_io ÷ Prescale) × time) - 1
Things get more tricky when you are working with a smaller chip though and only have an 8-bit timer to use. With that the maximum ticks you can have is a mere 255. So you will have to use a much bigger prescale to get it to fit in. If that's even possible. If it's not, then there are other tricks you can use. My favourite is a manual pre-scale:
- Run the timer at a multiple of your target frequency (10x is easy)
- Count the number of times the interrupt triggers
- When the interrupt has triggered 10 times (if using a 10x faster timer) then reset the count to 0 and trigger your real ISR code.
10x is simple to program and visualize, but it's not very programatically efficient. Better is to use a "power of two" multiple since you can examine a single bit in your counter to see if it's reached the target count. Only minimal differences though, and not really worth worrying about unless you are having a very fast timer interrupt.