There are currently 0 users and 3 guests online.
PWM - an overview
05 - Example :- Driving a servo via PWM
Submitted by Webbot on November 30, 2008 - 4:46pm.
In order to drive a servo we need to understand the mechanics of a servo:-
So the first PWM point is that the PWM repeat frequency should be every 20ms.
Secondly we note that the active pulse length is between 1ms and 2ms. This represents a duty cycle between 1ms per 20ms (5%) and 2ms per 20ms (10%) hence a 5% fluctuation between minimum and maximum. So out of our total comparator values then only 5% of them will actually make any difference to how the servo behaves.
For an 8 bit timer with 2^8 (ie 256) different values then this 5% fluctuation represents 5% of the total of 256 (= 12.8). So you could only set the servo to move to any one of 12 different positions. This is a much smaller value than a servo is capable of. But you may decide it is still enough for a given application.
For a 16 bit timer with 2^16 (65,536) different values then this 5% fluctuation represents 5% of the total of 65,536 (=3276.8). So you could set the servo to move to one any of 3,276 different positions. This is more steps than most servos can actually cope with but does, at least, cover all the physically achievable positions even though there will be a lot of duplicates ie position 1,234 may well be the same as position 1,235.
Since we are not driving a DC Motor then we will go with Fast PWM mode.
So lets make a decision, we will go with the 16 bit PWM because:-
So we are going to use 16 bit PWM via Timer1.
In order to setup the hardware we first have to tell Timer1 to repeat every 20ms or 50Hz. To calculate the value for TOP we will use the formula from the previous section:
TOP = (Clock_Speed / (Prescaler * Output_PWM_Frequency)) - 1
Since we are using a 16 bit timer then the value of TOP must be in the range 0 to 65,535. If the result is bigger than this then we need to bump up the pre-scaler in order to reduce the TOP to fit this range. So starting with a prescaler value of 1:
For a 1MHz controller then
TOP = (1,000,000 / (1 * 50)) - 1 = 19,999 This is 'in-range' so our answer is: prescaler=1 and TOP=19,999
For a 8MHz controller then
TOP = (8,000,000 / (1 * 50)) - 1 = 159,999 This is not 'in-range' so lets try again unsing the next available prescaler value of 8
TOP = (8,000,000 / (8 * 50)) - 1 = 19,999 This is 'in-range' so our answer is: prescaler=8 and TOP=19,999
Here is our code so far for an ATMega8 with 1Mhz clock speed:
or for an 8Mhz processor:
We can generalise by using compiler directives to insert the correct values by using:-
The Timer1 is now set up to be able to provide up to 2 PWM outputs on pins OC1A and OC1B respectively. At the moment our statement 'TCCR1A=0;' means that we have disabled both of them.
To activate the first channel on OC1A, which is also port B1, we must do the following:-
we can now adjust the duty cycle of the output pin by setting OCR1A to a value between 0 and ICR1.
To activate the second channel on OC1B, which is also port B2, we must do the following:-
we can now adjust the duty cycle of the output pin by setting OCR1B to a value between 0 and ICR1.
So we now have a repeating 20ms pulse on our output pins whose duration is set by either OCR1A or OCR1B. What values do we need to put in these registers in order to drive the servo?
Well ICR1 holds the value of TOP which represents 20ms. We know that a centered servo requires a pulse of 1.5ms so we need to write a comparator value to OCR1A or OCR1B of: ICR1 * 1.5 / 20.
The coolest thing about PWM is that it is all taken care of in the hardware. All we have to is change the values of the comparator in OCR1A and/or OCR1B to change the position of the servo (with a modified servo this means its speed).
So assuming a differential drive robot that has two modified servos then we can demonstrate this as follows for a 1Mhz controller:-