# ATtiny85 Servo PWM angle granularity

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 ?

• Switch to a device with a 16-bit timer. – Ignacio Vazquez-Abrams Apr 22 '17 at 12:13
• Does another solution come to your mind, if the option to switch to a 16-bit timer device does not exist? – AronC Apr 22 '17 at 12:22
• Nothing I would trust a device running at less than 32MHz to be able to pull off. – Ignacio Vazquez-Abrams Apr 22 '17 at 12:23
• @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! – Bruce Abbott Apr 22 '17 at 17:00
• @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. – Ignacio Vazquez-Abrams Apr 22 '17 at 17:05

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.

• 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. – AronC Apr 22 '17 at 16:29
• 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. – Bruce Abbott Apr 22 '17 at 17:12
• 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. – Chris Stratton Apr 22 '17 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.

//TIMER 2 COMPARE match A
ISR(TIMER2_COMPA_vect){
//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 );