4.2.1 Timer Configuration

Given a clock frequency of 16MHz and a desired period of 20ms, we can compute that we want the timer to overflow every $16000000\mathrm{Hz}\times 20\mathrm{ms} = 320000$ main clock cycles. Since the overflow value is a 16-bit number, we know that we need a divider to reduce the counting frequency.

$320000 \div 65536$ is more than 4 but less than 5. As a result, we know a prescalar of more than 4 will work. According to page 134 of the ATMega128 data sheet, the closest prescaler is a divide-by-8 prescaler. This means, from the table, that the clock select bit pattherns to be $010_2$. There three bits are bit 2 to bit 0 in Timer/Counter1 Control Register B (TCCR1B).

We have so far determined that TCCR1B has a bit pattern of $?????010_2$. Because we are not using the device as a counter, bits related to ``input capture'' are useless, so we can further determine that the bit pattern is $00???010_2$. Bit 5 is reserved and must be written as a 0, so our pattern is now $000??010_2$.

Based on the divide-by-8 prescaler, the counter counts at a frequency of 2MHz. At this frequency, we need an overflow value of $2000000 \times 20ms = 40000$. We can use the single slope ``fast PWM'' mode because we do not change the period. We can choose to use either OCR1A (Output Compare Register 1 for Channel A) or ICR1 (Input Capture Register 1) for this overflow value. Since we want to to reserve as many PWM output channels as possible, we use ICR1.

Each I/O location is only 8-bit, but ICR1 is a 16-bit quantity. As a result, ICR1 is split into two 8-bit I/O locations: ICR1L and ICR1H. AVRs use an internal buffer to synchronize the update of a 16-bit number. It requires that the high byte be written first, then the write of the low byte updates all 16 bits at once. The following code does just this:

ICR1H = 40000U >> 8;
ICR1L = 40000U & 0xff;

As per table 61 on page 132 of the data sheet, to use ICR1 as the overflow value and use fast PWM, we need to use mode 14. The WGM (Wave Generation Mode) bit pattern should be $1110_2$. According to page 129, bit 1 and bit 0 of WGM are bits 1 and 0 of Timer/counter1 Control Register A (TCCR1A). We know that TCCR1A should have a bit pattern of $??????10$. Bit 3 and bit 2 of WGM are bit 4 and bit 3 of TCCR1B. Since we already know all the other bits of TCCR1B, we now know the following:

TCCR1B = 0x1a; // 00011010

Because hardware PWM is such a useful feature, we want to use all channel A, B and C for that purpose. Each channel has a 2 bit COM pattern to configure its characteristics. According to table 59 on page 130 of the datasheet, bit 1 and bit 0 of COM has the following meanings when the timer is set up for fast PWM:

It is clear that we want to use either 10 or 11. Mode 10 is more intuitive because a larger OCR (Output Compare Register) value means more time without the corresponding pin at a high voltage. However, some electronic devices are ``active-low'', which means the device is on when the control signal is low, and off when the control signal is high. For these ``active-low'' devices, mode 11 makes more sense.

Assuming we want mode 10 for all three channels, we can now determine the value of TCCR1A (page 129):

TCCR1A = 0xfe; // 11 11 11 10

We are almost done. We still need to set up the timer to interrupt when the counter overflows. This is done by setting bits in TIMSK (Timer/counter Interrupt Mask Register). According to page 137, we want bit 2 set. Because TIMSK is also used to control timer0, we want to make sure bits unrelated to timer1 remains unchanged. The following code can be used:

TIMSK = (TIMSK & ~(0x3c)) | 0x04;

It is also a good idea to reset the counter TCNT1. Again, the counter is a 16-bit number, which means we need to write to the high byte first, then the low byte:

TCNT1H = 0;
TCNT1L = 0;

That's it! We are done with the initialization of timer1.

Let's look at the code so far:

TCCR1B = 0x1a; // 00011010
ICR1H = 40000U >> 8;
ICR1L = 40000U & 0xff;
TCCR1A = 0xfe; // 11 11 11 10
TIMSK = (TIMSK & ~(0x3c)) | 0x04;
TCNT1H = 0;
TCNT1L = 0;

Although this code makes sense from the perspective of working out the values, it is not the best sequence. With this sequence, the timer is ``ticking'' after the first statement. The following code is safer, because

_CLI(); // disable all interrupts
TCCR1B = 0; // disable ticking
TIMSK = (TIMSK & ~(0x3c)) | 0x04; // enable overflow interrupt
TCNT1H = 0; // reset counter
TCNT1L = 0;
ICR1H = 40000U >> 8; // set overflow value
ICR1L = 40000U & 0xff;
TCCR1A = 0xfe; // 11 11 11 10, set channel config
TCCR1B = 0x1a; // 00011010 start ticking 
_SEI(); // reenable interrupts

Copyright © 2006-02-15 by Tak Auyeung