Timers and Counters

A counter is a n-bit register that counts in binary at a fixed rate. Counters that are incremented by a clock signal can be used as timers. Most timer/counters are constructed with a:

The comparator can be used to reset the counter when counter matches the value of the comparison register.

The overflow signal and the output of the comparator can be used to interrupt the microcontroller.

The output of the comparator is often connected to an external output pin. This output is used as frequency or clock signal source.

AVR 8-bit Timer0

The 8-bit timer0 supports the following features:

In the AVR timer, the maximum value is called top and the minimum value is called botton.

Controlling Timer 0

Timer/Counter Control Register A

TCCR0A7

COM0A1

rw
6

COM0A0

rw
5

COM0B1

r
4

COM0B0

r
3

-

r
2

-

r
1

WGM01

rw
0

WGM00

rw
COM0A1 (bit7) Compare Match Output A Mode - Timer 0

COM0A1 and COM0A0 control the OC0A output pin.

In non-PWM mode: (0,0) disconnects the pin from OC0A, (0,1) toggles OC0A on compare match, (1,0) clears (becomes 0) OC0A on compare match, and (1,1) sets (becomes 1) OC0A on compare match.

In fast PWM mode: (0,0) disconnects the pin from OC0A, (0,1) toggles OC0A on compare match if WGM02 = 1, (1,0) clears OC0A on compare match, sets at bottom, (1,1) sets OC0A on compare match, clears at bottom,

In Phase Correct PWM mode: (0,0) disconnects the pin from OC0A, (0,1) toggles OC0A on compare match if WGM02 = 1, (1,0) clears OC0A on compare match when counting up, sets on compare match when counting down, (1,1) sets OC0A on compare match when counting up, clears on compare match when counting down,

COM0A0 (bit6) Compare Match Output A Mode - Timer 0
See COM0A1.
COM0B1 (bit5) Compare Match Output B Mode - Timer 0

COM0B1 and COM0B0 control the OC0B output pin.

In non-PWM mode: (0,0) disconnects the pin from OC0B, (0,1) toggles OC0B on compare match, (1,0) clears (becomes 0) OC0B on compare match, and (1,1) sets (becomes 1) OC0B on compare match.

In fast PWM mode: (0,0) disconnects the pin from OC0B, (0,1) toggles OC0B on compare match if WGM02 = 1, (1,0) clears OC0B on compare match, sets at bottom, (1,1) sets OC0B on compare match, clears at bottom,

In Phase Correct PWM mode: (0,0) disconnects the pin from OC0B, (0,1) toggles OC0B on compare match if WGM02 = 1, (1,0) clears OC0B on compare match when counting up, sets on compare match when counting down, (1,1) sets OC0B on compare match when counting up, clears on compare match when counting down,

COM0B0 (bit4) Compare Match Output B Mode - Timer 0
See COM0B1.
- (bit3) unused
- (bit2) unused
WGM01 (bit1) Waveform Generation Mode (bit 1)

WGM02, WGM01, and WGM00 determine the operating mode of timer 0. The modes are:

  • 0 - (0,0,0) normal
  • 1 - (0,0,1) PWM, change at maximum
  • 2 - (0,1,0) CTC
  • 3 - (0,1,1) fast PWM, change at maximum
  • 4 - (1,0,0) reserved
  • 5 - (1,0,1) PWM, change at OCR0A
  • 6 - (1,1,0) reserved
  • 7 - (1,1,1) fast PWM, change at OCR0A
WGM00 (bit0) Waveform Generation Mode (bit 0)
See WGM01.

Controlling Timer 0

TCCR0B – Timer/Counter Control Register B

TCCR0B7

FOC0A

w
6

FOC0B

w
5

-

r
4

-

r
3

WGM02

rw
2

CS02

rw
1

CS01

rw
0

CS00

rw
FOC0A (bit7) FOC0A: Force Output Compare A
In now-PWM mode, force a compare A match. The setting of COM0A1:0 determine the effect.
FOC0B (bit6) FOC0B: Force Output Compare B
In now-PWM mode, force a compare B match. The setting of COM0B1:0 determine the effect.
- (bit5) unused
- (bit4)
WGM02 (bit3) Waveform Generation Mode (bit 2)
See WGM01.
CS02 (bit2) Clock Select (bit 2)

The settings of (CS02,CS01,CS00) select the counters clock source. The settings are:

  • (0,0,0) No clock source
  • (0,0,1) System clock
  • (0,1,0) System clock/8
  • (0,1,1) System clock/64
  • (1,0,0) System clock/256
  • (1,0,1) System clock/1024
  • (1,1,0) External clock source on T0 pin. Clock on falling edge.
  • (1,1,1) External clock source on T0 pin. Clock on rising edge.
CS01 (bit1) Clock Select (bit 1)
See CS02.
CS00 (bit0) Clock Select (bit 0)
See CS02.

Counter and Comparison Registers

The counter and comparsion registers can be read and written as 8-bit values. Care needs to be taking when updating the comparison register when the counter is counting.

TCNT0 - Timer/Counter Register

TCNT07

rw
6

rw
5

rw
4

rw
3

rw
2

rw
1

rw
0

rw

Output Compare Register A

OCR0A7

rw
6

rw
5

rw
4

rw
3

rw
2

rw
1

rw
0

rw

Output Compare Register B

OCR0B7

rw
6

rw
5

rw
4

rw
3

rw
2

rw
1

rw
0

rw

Interrupts and Flag Bits

Timer 0 supports the following interrupts:

These interrupts are monitored and controlled with the flag and interrupt enable registers.

Timer/Counter 0 Interrupt Flag Register

TIFR07

-

rw
6

-

rw
5

-

rw
4

-

rw
3

-

rw
2

OCF0B

rw
1

OCF0A

rw
0

TOV0

rw
OCF0B (bit2) Timer/Counter 0 Output Compare B Match Flag
The flag is set when OCR0B matches TCNT0.
OCF0A (bit1) Timer/Counter 0 Output Compare A Match Flag
The flag is set when OCR0A matches TCNT0.
TOV0 (bit0) Timer/Counter0 Overflow Flag
The flag is set counter overflows.

Timer/Counter Interrupt Mask Register

TIMSK07

-

rw
6

-

rw
5

-

rw
4

-

rw
3

-

rw
2

OCIE0B

rw
1

OCIE0A

rw
0

TOIE0

rw
OCIE0B (bit2) Timer/Counter Output Compare Match B Interrupt Enable
Timer/Counter Compare Match B interrupt is enabled when the OCIE0B is set and the matching flag bit is set.
OCIE0A (bit1) Timer/Counter Output Compare Match A Interrupt Enable
Timer/Counter Compare Match A interrupt is enabled when the OCIE0B is set and the matching flag bit is set.
TOIE0 (bit0) Timer/Counter0 Overflow Interrupt Enable
An Timer/Counter0 Overflow interrupt occurs when TOIE0 and the TOV0 flag are set.

Fixed Timer Interrupt Rate

Assuming a 8MHz system clock, program timer 0 to generate an interrupt every 10 milliseconds.

The CTC mode is best since the interrupt rate can be easily configured. The timer prescaler can be choosen with the aid of the following table:

Scale Precision Duration
1 1/8Mhz (0.125 uS) 256/8Mhz (32 us)
8 8/8Mhz (1 uS) 256/1Mhz (256 uS)
64 64/8Mhz (8 uS) 256/(1/8)Mhz (2048 uS)
256 256/8Mhz (32 uS) 256/(1/32)Mhz (8192 uS)
1024 1024/8Mhz (128 uS) 256/(1/128)Mhz (32768 uS)

A prescaler of 1024 provides sufficient range. All other prescalers are too short. The OCR0A compare value is calculated with:

10000uS / 128uS = 78.125

Unfortunately OCR0A must be an integer. Choosing 78 gives a period of:

78 * 128uS = 9984 uS

The timing error is 16 uS. Is this a concern? It depends!

10 ms Fixed Timer Interrupt Rate Configuration

TCCR0A sets the output pins and timer mode. The output should be disabled (ie., (COM0A1:COM0A0) and (COM0B1:COM0B0) are set to zero). The mode is set to CTC.

TCCR0A = _BV(WGM01); // disable outputs set mode to ctc 

The prescaler is set to 1024, and WGM02 is set to the CTC mode.

TCCR0B = _BV(CS02) | _BV(CS00); // CTC mode and prescaler to 1024 

The compare register is set with:

OCR0A = 78; // 9984uS interval 

Compare A interrupts are enabled with:

TIMSK0 |= _BV(OCIE0A); // enable compare A interrupts 

Losing Time

After 1000000 interrupts the above code has lost 16uS * 1000000 = 16 seconds. The elapsed time should have been 10000 seconds, but is 9984 seconds. 10000 seconds is 2.7 hours. Thus 16 seconds are lost every 2.7 hours.

The interrupt period can be adjusted to eliminate this error with:

#define TEN_MS_UNDER (78)
#define TEN_MS_OVER (79)
#define ADJUST_VALUE (8)
#define ADJUST_VALUE (8)
uint8_t timer0_adjust;
ISR(TIMER0_COMPA_vect)
{
    if ( timer0_adjust < ADJUST_VALUE ) { 
        timer0_adjust++;
        OCR0A = TEN_MS_UNDER;
    } 
    else {
       OCR0A = TEN_MS_OVER 
       timer0_adjust = 0;
    } 
    // rest of interrupt 
} 

Setting A 2mS Interval

A prescaler should be chosen such that the error is as small as possible (precision is smallest) and the duration is sufficently long. A prescaler value of 8 yeilds:

2000 uS / 8 uS = 250

A comparison value of 250 gives an exact interval of 2 mS. The initialize code is:

TCCR0A = _BV(WGM01); // disable outputs set mode to ctc 
TCCR0B = _BV(CS01); // CTC mode and prescaler to 8 
OCR0A = 250; // 2 mS interval 
TIMSK0 |= _BV(OCIE0A); // enable compare A interrupts 

What about an interval of 1 mS? What about 100 mS with the fewest interrupts?

Generating Frequencies with CTC

The CTC mode is often used to generate an 50% square wave output at a given frequency. Interrupts are not required for this application of timer 0. The range of frequencies is:

Scale Precision Lowest Frequency
1 1/8Mhz 31250 Hz
8 8/8Mhz (1 uS) 3906.25 Hz
64 64/8Mhz (8 uS) 488.28 Hz
256 256/8Mhz (32 uS) 122.07 Hz
1024 1024/8Mhz (128 uS) 30.51 Hz

The prescaler with the acceptable lowest frequency must be choosen.

Generate a frequence of 523.25 Hz (5th of C)

A 50% square wave changes at a rate of 523.25*2 = 1046.50 Hz. A frequency of 1046.50 is in the range of a prescaler of 64. The period of 1046.50 Hz is (1/1046.50) which equals 955.56 uS. The closest compare value is given by:

955.56 uS / 8 uS = 119.445

The closest frequency is 1 / (119 * 8uS) = 1050.42 Hz. This gives a frequence of 1050.42 / 2 = 525.2 Hz (or 2hz error).

The pin connected to compare A is configured to toggle when the match occurs. The bits (COM0A1,COM0A0) should be set to (1,0).

TCCR0A = _BV(COM0A0) | _BV(WGM01); // toggle output set mode to ctc 

The rest of the configuration is:

TCCR0B = _BV(CS01) | _BV(CS00); // CTC mode and prescaler to 64 
// a count of 1, takes 2 clock cycles 
OCR0A = 119 - 1; // 955.6 uS interval 
Notice that interrupts are not used. The 16-bit timer can be used to reduce the error.

Generate a frequence of 659.26 Hz (5th of E)

This musical note still requires a prescaler of 64. The compare value is given by:

(1/(2*659.26)) / 0.000008 = 94.8

The closest setting give a value of 1 / (2 * (95 * 0.000008 )) = 657.89 Hz. The timer configuration is:

TCCR0A = _BV(COM0A0) | _BV(WGM01); // toggle output set mode to ctc 
TCCR0B = _BV(CS01) | _BV(CS00); // CTC mode and prescaler to 64 
OCR0A = 95 - 1; 

The 4th octave of C is 261.63. How would this be handled?

Testing Timer0 Frequency Generator

Experimenting with the timer 0 can be done with the reg_lookup.c application on the microcontroller and the following Python program:

elif cmd == "freq" :
    freq = 118
    range = 3
    if len(sys.argv) > 2 :
        freq = int( sys.argv[2] )
        if len(sys.argv) > 3 :
            range = int( sys.argv[3] )
    write_reg( "ddrb", 0x08 )
    write_reg( "tccr0a", 0x42 )
    write_reg( "tccr0b", range & 0x7 )
    write_reg( "ocr0a", freq )

The program accepts a frequency argument and a range argument. The frequency argument sets the OCR0A register, and the counter's clock is selected with the range argument.

Limitation On Frequency Generation

A counter that can be cleared a preset count acts like a divider. A counter is clear when it reaches 1 divides the clock by 2. If the input is 1Mhz, the output changes only at 0.5MHz. Consider the following table of a 4-bit counter with a 1Mhz input clock.

Reset Value Output Clock Rate
0 1000000
1 500000
2 250000
3 125000
4 62500
5 31250
6 15625
7 7812.5
8 3906.2
9 1953.1
10 976.6
11 488.3
11 244.14
12 122.1
13 61.0
14 30.5
15 15.2

Notice that only fractions of the base frequence can be generated. Thus 17.0 can not be generated since in falls in between 30.5 and 15.2.

What would be involved in writting a program to output all possible generated frequencies for timer 0 of the atmega644?

Frequency Table

table.py
prescalar = [1, 8, 64, 256, 1024]
freq = 8000000.0
print "     ",
for pre in prescalar :
    print "%12d" % (pre),
print
for x in xrange(0,256) :
    print "%-4d " % x,
    for pre in prescalar :
        print "%12.2f" %  (freq / (2*pre*(1+x)) ),
    print


Write a script that given a target frequency, the script reports the closest prescaler and compare values.

Find Closest

find_min.py
import sys
target = float(sys.argv[1])
freq = 8000000.0

scaler = 1
compare = 0
v = freq / (2*scaler*(1+compare))
min = abs(freq-target)
prescalar = [1, 8, 64, 256, 1024]

for x in xrange(0,256) :
    for pre in prescalar :
        v = (freq / (2*pre*(1+x)) )
        m = abs(target-v)
        if  m < min  :
            min = m
            scaler = pre
            compare = x

print 'best: ', scaler, compare, (freq / (2*scaler*(compare+1)) )
# why is this not the best?

Efficient Version

efficient_min.py
import sys
target = float(sys.argv[1])
freq = 8000000.0

prescalar = [1, 8, 64, 256, 1024]

print "Prescaler  Compare   Actual   Target"
for scaler in prescalar :
    compare = (freq / (2*scaler)) / target
    icompare = round(compare)
    act = (freq / (2*scaler)) / icompare
    print "%9d %8d %8.2f %8.2f" % (scaler, icompare-1, act, target)

The output for 440 is:

440.sample
Prescaler  Compare   Actual   Target
        1     9090   440.00   440.00
        8     1135   440.14   440.00
       64      141   440.14   440.00
      256       35   434.03   440.00
     1024        8   434.03   440.00

PWM (Pulse Width Modulation)

PWM is a common control technique for setting the intensity of some device (e.g., amount of light, volume of sound, power in a motor). Some examples are:

1/8 th100000001
1/4 th110000001
1/2 th111100001
3/4 th111111001
7/8 th111111101
full111111111

A PWM is specified as a period and a duty cycle. The period is the interval of time the signal repeats. The duty cycle is the percentage of time the signal is high (on).

PWM with 2ms period and 10% duty

Configure timer 0 to generate a 2ms period with a 10% duty cycle.

The fast PWM mode where the period is controlled by OCRA has (WGM2,WGM1,WGM0) = (1,1,1). The compare match B output must be used and it should turn on at TOP and reset at match. This happens when (COM0B1,COM0B0) = (1,0). The prescaler is set to 64.

TCCR0A = _BV(COM0B1) | _BV(WGM01) | _BV(WGB00); 
TCCR0B = _BV(WGB02) | _BV(CS01) | _BV(CS00);
OCR0A = 250; 

The duty cycle (period the PWM is on) is controlled by OCR0B. The range of OCR0B is from 0 to OCR0A (or 250 in this case). If OCR0B exceeds OCR0A, then the PWM output will be alway on. A duty cycle of 10% is set by:

OCR0B = OCR0A * 10% = 250 * .1 = 25

The configuration is complete when OCR0B is assigned:

OCR0B = 25; 

Changing the output compare control setting (COM0B1,COM0B0) = (0,1) inverts the waveframe (i.e., the output will be off for 10% and on for 90%).

What is the setting for a 75% duty cycle? How would the period be changed to 250 uS, and how would this affect the precison of the duty cycle?

Python Script For PWM

The script that controls the period and duty cycle of the PWM mode is:

elif cmd == "pwm" :
    period = 250
    duty = 25 
    range = 3
    if len(sys.argv) > 2 :
        period = int( sys.argv[2] )
        if len(sys.argv) > 3 :
            duty = float( sys.argv[3] ) / 100.0
            duty = int(duty * period )
            if len(sys.argv) > 4 :
                range = int( sys.argv[4] )
    write_reg( "ddrb", 0x18 )
    write_reg( "tccr0a", 0x23 )
    write_reg( "tccr0b", (range & 0x7) | 0x08 )
    write_reg( "ocr0a", period )
    write_reg( "ocr0b", duty )

Available Periods When Using Maximun Count

What are the available periods if the maximum is used and the systems clock is 8Mhz?

Using two PWM outputs from timer0

If the operating mode is set to (WGM2,WGM1,WGM0) = (0,1,1), then the PWM is set/clear when the maximum count value (255) is reached. In this mode, both compare A and compare B outputs can be used.

The following configuration can be used to set output A to 25% duty cycle and output B to 60%.

// fast PWM, compare a and b outputs 
TCCR0A = _BV(COM0A1) | |_BV(COM0B1) | _BV(WGM01) | _BV(WGB00);
TCCR0B =  _BV(CS01); // prescale of 8 
OCR0A = 64; // 256 * 25% = 64
OCR0B = 154; // 256 * 60% = 153.6

Python Script For Two Output PWM

The script that controls the duty cycle for two outputs in PWM mode is:

elif cmd == "pwm2" :
    dutyA = 64
    dutyB = 154
    range = 2
    if len(sys.argv) > 2 :
        dutyA = float( sys.argv[2] ) / 100.0
        dutyA = int(dutyA * 256 )
        if len(sys.argv) > 3 :
            dutyB = float( sys.argv[3] ) / 100.0
            dutyB = int(dutyB * 256 )
            if len(sys.argv) > 4 :
                range = int( sys.argv[4] )
    write_reg( "ddrb", 0x18 )
    write_reg( "tccr0a", 0xa3 )
    write_reg( "tccr0b", range & 0x7 )
    write_reg( "ocr0a", dutyA )
    write_reg( "ocr0b", dutyB )

Complete Python Script

The full script is:

timer_test.py
import serial
import sys

def write_reg( reg, value ) :
    ser.write( "%s %x\n" % (reg,value) )
    l = ser.readline()
    w = l.split()
    v = int(w[2],16)
    if v != value :
        print "register write failed %s %x %x\n" % (reg,value,v)

ser = serial.Serial('/dev/ttyS0', 9600)
cmd = sys.argv[1]

if cmd == "flash" :
    write_reg( "ddrc", 0xff );
    for x in xrange(0,256) :
        write_reg( "portc", 255-x );
elif cmd == "freq" :
    freq = 118
    range = 3
    if len(sys.argv) > 2 :
        freq = int( sys.argv[2] )
        if len(sys.argv) > 3 :
            range = int( sys.argv[3] )
    write_reg( "ddrb", 0x08 )
    write_reg( "tccr0a", 0x42 )
    write_reg( "tccr0b", range & 0x7 )
    write_reg( "ocr0a", freq )
elif cmd == "pwm" :
    period = 250
    duty = 25 
    range = 3
    if len(sys.argv) > 2 :
        period = int( sys.argv[2] )
        if len(sys.argv) > 3 :
            duty = float( sys.argv[3] ) / 100.0
            duty = int(duty * period )
            if len(sys.argv) > 4 :
                range = int( sys.argv[4] )
    write_reg( "ddrb", 0x18 )
    write_reg( "tccr0a", 0x23 )
    write_reg( "tccr0b", (range & 0x7) | 0x08 )
    write_reg( "ocr0a", period )
    write_reg( "ocr0b", duty )
elif cmd == "pwm2" :
    dutyA = 64
    dutyB = 154
    range = 2
    if len(sys.argv) > 2 :
        dutyA = float( sys.argv[2] ) / 100.0
        dutyA = int(dutyA * 256 )
        if len(sys.argv) > 3 :
            dutyB = float( sys.argv[3] ) / 100.0
            dutyB = int(dutyB * 256 )
            if len(sys.argv) > 4 :
                range = int( sys.argv[4] )
    write_reg( "ddrb", 0x18 )
    write_reg( "tccr0a", 0xa3 )
    write_reg( "tccr0b", range & 0x7 )
    write_reg( "ocr0a", dutyA )
    write_reg( "ocr0b", dutyB )

ser.close()

Controlling RC Servos

Most RC toys are controlled by servo motors. A servo motor can position a shaft with in an arch of 300 degrees. A PWM signal is used to control the position. The pulse width varies from 1mS to 2ms, with a required repeat rate of around 40 times a second. The shaft is at the 0 position for a pulse width of 1mS and at the 300 degree position with a pulse width of 2mS (the degree of rotation varies across server motors).

A repeat rate of around 40 times a second gives a period of 1/40 or 25mS. The closest period is 32mS when the prescaler is set to 1024. The repeat rate is 32 times per second for the 32mS period. If 40 was required, then timer 0 and timer 2 would be required, each timer would handle one output.

How many steps would this setup allow for the 1mS to 2mS period?

The calculation is:

(2mS - 1mS) / (1024/8000000) = 7.812

Thus, this setting only allows 7 different servo motor positions.

Solutions to Controlling RC Servos

What are the pros can cons of the above two techniques?

Generating an arbitary waveform.

Use timer 0 to generate the following waveform:

Solution Approach

A possible solution approach using timers for waveform generation is:

Prescaler choices and issues

The times in the example are all multiples of 104. Since 104 is the product of 13 and 8, a timer whose clock is 8 uS can be used to generate all multiples of 104. For a 8Mhz system clock, timer 0 with a prescaler of 64 gives a 8 uS tick rate. The 8-bit timer can measure the time upto 19 multipes of 104 (i.e., 1976uS).

Unfortunately the prescaler on timer 0 cannot be directly controlled. This results in an 8uS uncertainty to when the next tick will occur.

Timer 2 has the ability to reset the prescaler counter, and thus eliminate the uncertainty.

Forcing the compare output, resetting prescaler

The output driven by the counter must be initialized to the require state. The output is set to a 0 with:

TCCR2A = (TCCR2A & ~0xc0) | 0x80;
TCCR2B |= _BV(FOC0A);

The output is driven high with:

//TCCR2A = (TCCR2A & ~0xc0) | 0xc0;
TCCR2A |= 0xc0;
TCCR2B |= _BV(FOC0A);

Strobing the FOC0A bit causes, a compare match operation to occur.

The prescaler for timer2 is reset with:

GTCCR = _BV(PSRASY);

Timer 2 and Timer 0

Timers 0 and 2 are both 8-bit timers with almost identically features.

The differences for timer 2 are:

Timer 2 can be used for a real time clock.

The differences for timer 0 are:

Timer 0 can be used to count the changes for an external signal.

Timer 1, a 16-bit timer

The 16-bit timer in the AVR is very similar to the 8-bit timers.

Timer 1, 16-bit access

Since a shared 8-bit temporary register is used to access the upper byte of the 16-bit counter, compare and capture registers, interrupts should be disabled for all accesses. An example of a safe access is:

uint16_t counter = 0xff01;
uint8_t sreg = SREG;
cli();
TCNT1 = counter; // 16-bit write 
SREG = sreg;

Servo Controller

The connections for this servo motor are:

Timer 1 - servo controller

If the prescaler of timer 1 is set to 8, then the time between increments of the counter is 1 uS for a 8Mhz clock. The maximum duration for this timer is 65,536 uS or 65.536 mS.

A duration of 1 mS is 1000 counts, and 2 mS is 2000 counts. Thus the 16-bit counter can produce 1000 distinct duration from 1 mS to 2 mS.

Timer 1 configuration for a PWM with a period of 20 mS is:

static void
timer1_init( void )
{
    /* Both outputs are clear on match, and set on top,
     * top is ICR2.
     */
    TCCR1A = 0xa2; // clear
    /* Mode configuration continued,
     * prescalar clock set to divide by 8.
     */
    TCCR1B = (3<<3) | (2);

    /* assuming a 8Mhz system clock,
     * a 0.020 millisecond period is
     * 0.020 / 0.000001 = 20000
     */
    ICR1 = 20000;

    DDRD |= _BV(5) | _BV(4); // enable timer1 pwm outputs
}

Timer 1 - Duty Cycle

The duty cycle of the PWM mode for both outputs is controlled with:

static void
timer1_pwm( uint16_t a, uint16_t b )
{
    OCR1A = a;
    OCR1B = b;
}

For a servo motor with a 1 mS to 2 mS control pulse, the values for A and B should range from 1000 to 2000. Note: that interrupts must be turned off when OCR1A and OCR1B are updated.

Servo Demonstration

A simple demonstration of the servo motor motions can be performed by varying the pulse width from 1 mS to 2 mS and then from 2 mS to 1 mS. If the pulse width is change every 100 mS, then a visible motion of the motors is apparent.

Timer 2 can be configured to generate a 20 mS periodic interrupt. The 100 mS interval is acheived by skipping 5 interrupts. The configuration code for timer 2 is:

static void
timer2_init( uint8_t clockCnt )
{
    OCR2A = clockCnt;
    // CTC, OCR2A is top, TCNT2 cleared with OCR2A reached
    // prescaler set to 1024
    TCCR2A = _BV(WGM21);
    TCCR2B = _BV(CS22) | _BV(CS21) | _BV(CS20);
    // enable compare A interrupts
    TIMSK2 |= _BV( OCIE2A );

    timer2_state.pwm_a = PWM_A_MAX;
    timer2_state.a_dir = 0;
    timer2_state.pwm_b = PWM_B_MIN;
    timer2_state.b_dir = 1;
}

Servo Motion Code

The PWM is changed in timer 2's interrupt routine. The code is:

ISR(TIMER2_COMPA_vect)
{
    if ( timer2_state.skips != 0 ) {
        timer2_state.skips--;
        return;
    }
    cnt++;
    PORTC = ~cnt;
    timer2_state.skips = TIMER2_SKIPS;
    if ( timer2_state.a_dir ) {
        timer2_state.pwm_a += DELTA;
        if ( timer2_state.pwm_a > PWM_A_MAX ) {
            timer2_state.pwm_a = PWM_A_MAX;
            timer2_state.a_dir = 0;
        }
    }
    else {
        timer2_state.pwm_a -= DELTA;
        if ( timer2_state.pwm_a < PWM_A_MIN ) {
            timer2_state.pwm_a = PWM_A_MIN;
            timer2_state.a_dir = 1;
        }
    }
    if ( timer2_state.b_dir ) {
        timer2_state.pwm_b += DELTA;
        if ( timer2_state.pwm_b > PWM_B_MAX ) {
            timer2_state.pwm_b = PWM_B_MAX;
            timer2_state.b_dir = 0;
        }
    }
    else {
        timer2_state.pwm_b -= DELTA;
        if ( timer2_state.pwm_b < PWM_B_MIN ) {
            timer2_state.pwm_b = PWM_B_MIN;
            timer2_state.b_dir = 1;
        }
    }
    timer1_pwm( timer2_state.pwm_a, timer2_state.pwm_b );
}

How can the code size be reduced? What is the fastest rate that the motor's position can be changed?

Complete Servo Code

servo.c
#include <avr/io.h>
#include <stdint.h>
#include <avr/power.h>
#include <avr/sleep.h>
#include <avr/pgmspace.h>
#include <avr/interrupt.h>

static void
timer1_init( void )
{
    /* Both outputs are clear on match, and set on top,
     * top is ICR2.
     */
    TCCR1A = 0xa2; // clear
    /* Mode configuration continued,
     * prescalar clock set to divide by 8.
     */
    TCCR1B = (3<<3) | (2);

    /* assuming a 8Mhz system clock,
     * a 0.020 millisecond period is
     * 0.020 / 0.000001 = 20000
     */
    ICR1 = 20000;

    DDRD |= _BV(5) | _BV(4); // enable timer1 pwm outputs
}

static void
timer1_pwm( uint16_t a, uint16_t b )
{
    OCR1A = a;
    OCR1B = b;
}

#define TIMER2_SKIPS (5)
#define PWM_A_MAX (2000)
#define PWM_A_MIN (1000)
#define PWM_B_MAX (2000)
#define PWM_B_MIN (1000)
#define DELTA (25)

static struct {
    uint8_t skips;
    uint16_t pwm_a;
    uint8_t a_dir;
    uint16_t pwm_b;
    uint8_t b_dir;
} timer2_state;


static void
timer2_init( uint8_t clockCnt )
{
    OCR2A = clockCnt;
    // CTC, OCR2A is top, TCNT2 cleared with OCR2A reached
    // prescaler set to 1024
    TCCR2A = _BV(WGM21);
    TCCR2B = _BV(CS22) | _BV(CS21) | _BV(CS20);
    // enable compare A interrupts
    TIMSK2 |= _BV( OCIE2A );

    timer2_state.pwm_a = PWM_A_MAX;
    timer2_state.a_dir = 0;
    timer2_state.pwm_b = PWM_B_MIN;
    timer2_state.b_dir = 1;
}

uint8_t cnt = 0;

ISR(TIMER2_COMPA_vect)
{
    if ( timer2_state.skips != 0 ) {
        timer2_state.skips--;
        return;
    }
    cnt++;
    PORTC = ~cnt;
    timer2_state.skips = TIMER2_SKIPS;
    if ( timer2_state.a_dir ) {
        timer2_state.pwm_a += DELTA;
        if ( timer2_state.pwm_a > PWM_A_MAX ) {
            timer2_state.pwm_a = PWM_A_MAX;
            timer2_state.a_dir = 0;
        }
    }
    else {
        timer2_state.pwm_a -= DELTA;
        if ( timer2_state.pwm_a < PWM_A_MIN ) {
            timer2_state.pwm_a = PWM_A_MIN;
            timer2_state.a_dir = 1;
        }
    }
    if ( timer2_state.b_dir ) {
        timer2_state.pwm_b += DELTA;
        if ( timer2_state.pwm_b > PWM_B_MAX ) {
            timer2_state.pwm_b = PWM_B_MAX;
            timer2_state.b_dir = 0;
        }
    }
    else {
        timer2_state.pwm_b -= DELTA;
        if ( timer2_state.pwm_b < PWM_B_MIN ) {
            timer2_state.pwm_b = PWM_B_MIN;
            timer2_state.b_dir = 1;
        }
    }
    timer1_pwm( timer2_state.pwm_a, timer2_state.pwm_b );
}

int
main( void )
{
    DDRC = 0xff;
    clock_prescale_set( clock_div_1 );

    timer1_init();
    timer2_init( 156 ); // approximately 0.02 seconds for 8MHz 

    sei(); // enable interrupts
    while ( 1 ) {
        sleep_mode();
    }
    return 0;
}

Pin change interrupts and time

The counter values of the timers can be read to determine the current time. If the timers are read in the pin change interrupt routine, then the time between pin interrupts can be measured. How can this be used to decode an asynchronous serial signal? How should protocol errors be handled?