I am currently working on controlling servo motors with an ATtiny85. I have a hard time understanding how to achieve a fine granularity for the angles of the servo.

I am using a similar technique as the one stated by KyranF.

A servo angle is determined by the pulse width between 1-2ms of an 50Hz PWM.

I am using Timer 1 with a prescaler of 1 with a 8 Mhz clock. This means a timer overflow(tick) takes (1/8MHz) * 256 = 32 microseconds.

A 20ms period takes 625 overflows. This means my 1ms granularity is 625/20ms = 31,25 = ~31 ticks per ms.

Because a servo angle is controlled by setting the pulse width somewhere between 1-2ms, I can only have a precision of 31/180°= ~6 degrees.

Is there a way to achieve 1° precision ?

  • \$\begingroup\$ Switch to a device with a 16-bit timer. \$\endgroup\$ Apr 22, 2017 at 12:13
  • \$\begingroup\$ Does another solution come to your mind, if the option to switch to a 16-bit timer device does not exist? \$\endgroup\$
    – elhe
    Apr 22, 2017 at 12:22
  • \$\begingroup\$ Nothing I would trust a device running at less than 32MHz to be able to pull off. \$\endgroup\$ Apr 22, 2017 at 12:23
  • \$\begingroup\$ @IgnacioVazquez-Abrams With a 4MHz PIC (1MHz CPU cycle) and no hardware timers I can generate 2 simultaneous 10 bit servo pulses with 1us resolution. Should be no problem for an 8MHz AVR! \$\endgroup\$ Apr 22, 2017 at 17:00
  • \$\begingroup\$ @BruceAbbott: But we're not doing it all in software, we're doing it with an 8-bit timer and handling the overflow in software. \$\endgroup\$ Apr 22, 2017 at 17:05

2 Answers 2


Because a servo angle is controlled by setting the pulse width somewhere between 1-2ms, I can only have a precision of 31/180°= ~6 degrees.

Is there a way to achieve 1° precision ?

Most servos only move ~120º with 1-2ms. However assuming 1-2ms = 180º you need 180 counts of 5.55us per count.

The Attiny85 has an 8 bit PWM generator that can do up to 256 counts, but doesn't have a prescaler division ratio that can do 5.55us steps at 8MHz. The nearest is 8us per count with a 1/64 prescale, which corresponds to 125 counts in 1ms for a resolution of 1.44º per count (if 1ms = 180º). To get this you could set up Timer0 to produce a one-shot PWM pulse of 125-250 which equals 1-2ms, and use timer1 to repeat the pulse at approximately 20ms intervals. Most servos should get close to 1º resolution with these 8us steps.

If 8us is not fine enough then you will have to use a software delay. At 8Mhz 44 CPU cycles take 5.5us, which is 99% of the desired 5.55us and provides enough instructions to make an accurate variable timing loop of 1-2ms. You could try writing the code for this in C (with a few NOPs to adjust the timing) but it might be easier to do in assembler.

To generate an entire frame you would first set up a timer to interrupt at ~20ms intervals. In the timer ISR you would start the servo pulse, wait for a variable 1-2ms using your software timer, then end the servo pulse. During this time the CPU can't do anything else, but you still have ~18-19ms available per 20ms frame.

  • \$\begingroup\$ Although I don't like having a fully blocking routine, I think this is appropriate for what I want to do. It is still better then the idea I came up with. Which is using CTC1 with OCR1C loaded with 42 in PWM1A mode, which generates a overflow interrupt every 5.25us. \$\endgroup\$
    – elhe
    Apr 22, 2017 at 16:29
  • \$\begingroup\$ AVR is has high interrupt latency when coded in C (so many registers that have to be saved and restored!) so you might not save many (or any) cycles using a timer interrupt, and it would have more jitter. Other tasks would still be 'blocked' because there wouldn't be enough time between interrupts to do much else, and all other interrupts would have to be disabled to avoid upsetting the timing. \$\endgroup\$ Apr 22, 2017 at 17:12
  • \$\begingroup\$ A couple other variations of this idea: you might be able to choose a crystal/resonator frequency that gave a better fit (if you can spare the pins), and you could do the interval between pulses by counting timer overflows in software if you wanted to save the second timer for something else. \$\endgroup\$ Apr 22, 2017 at 17:39

some of your math is messed up.

if you set 1:1 prescaler, 8Mhz crystal + 8:1 prescaler, timer1 can have a top at 20,000, and from 1ms - 2ms, you have 1000 counts or 180 degrees / 1000 count = 0.18 degree / count.

far more than you need.

here is one approach to the same problem that you may find helpful: https://dannyelectronics.wordpress.com/2017/02/18/driving-multiple-servos-off-a-pic-timer2-ranged-extended/

edit: one of the links above showed the basic approach -> you repeatedly load the target, or in this case, advance the comp match register until the desired length is reached.

The examples I linked to earlier use the overflow interrupts but the same applies to compare match as well.

    //check for _OCR2A exhaustion
    if (_OCR2A) {                   //target exhaused?
        _OCR2A -= 0x100;            //decrement
        OCR2A = 0;
    } else {                        //compValue has been exhausted
        //do user stuff
        positiveTimeoutCheck = false;
        digitalWrite( DEBUG_PIN, LOW );
        TCCR2B = 0;                     //stop timer2

the code above follows the basic logic, and clear DEBUG_PIN once _OCR2A (a 16-bit type set by the user) has been exhausted.

Here is an example of its implementation on TIMER2 of an ATMEGA328p (running 1MIPS) where the timer2's prescaler is set to 1:1, and _OCR2A to 25000 -> yielding a 25ms delayed on for DEBUG_PIN on every transition on PCINT0. enter image description here

different ways of doing the same thing.

  • 1
    \$\begingroup\$ How can the timer1 count to 20000 when the timer counter register (TCNT1) on page 91 of the datasheet is an 8 bit register. \$\endgroup\$
    – elhe
    Apr 22, 2017 at 14:28
  • 1
    \$\begingroup\$ In your answer you used the atmega328, which has 16 bit timers, but the attiny85 doesn't. So the maximum value I can set for compare registers are 255 \$\endgroup\$
    – elhe
    Apr 22, 2017 at 15:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.