This is really a continuation of my last post. This post carries Pulse Width Modulation further by considering the control of a servo.
There are two types of small servos: a standard type and continuous type. This post describes the standard servo. This is the servo I purchased from Adafruit.
The standard servo has a shaft that rotates to a desired position and stops rotating once that position is attained. It can rotate 180°, or 90° in each direction from a neutral position.
It has a DC motor that connects through a series of gears to the shaft of a potentiometer, a device producing a variable electrical resistance. An extension of the potentiometer shaft is the output shaft of the servo. The set of gears between the motor and the potentiometer significantly reduces the speed of the motor and significantly increases its torque.
As the motor turns the potentiometer shaft, the resulting change in resistance produces a change in voltage. Let's call this voltage "A". Another voltage is produced that comes from translating the width of input pulses to voltage. The input pulses come from your ATmega microcontroller and represent the desired shaft position of the servo. Let's call this voltage "B". Voltages "A" and "B" are compared in a feedback circuit within the servo. As long as voltage "A" does not equal voltage "B", the feedback circuit produces a voltage to turn the motor either CW or CCW. When the motor has rotated the potentiometer to the point where the voltage "A" equals voltage "B", the control circuit stops the motor. As long as voltage "B" does not change, the output shaft will not rotate. A small servo usually can prevent a fair amount of external torque from turning the shaft.
Photo Found On Several Sites | Servo As Found On Adafruit's Website |
The other type is the continuous servo. This type turns continuously and the PWM controls the direction and speed of rotation. This may be a good alternative to a toy motor as it will be geared down for a more manageable speed range.
The ATmega328P will produce the pulses required by the servo using its Pulse Width Modulation features. In my last post, controlling a DC motor, the pulse repetition frequency was not very critical. This is not the case with the servo. The servo requires a pulse repetition frequency of 50Hz. We need a pulse every 20ms. The pulse duration is also specified and is in the range of 0.5ms. to 2.5ms. All the documentation I had seen for this type of servo specified the range to be 1.0ms. to 2.0ms. My particular servo, purchased from Adafruit, did not come with a data sheet, but apparently has better resolution than the typical unit. Programming from 1.0ms. to 2.0ms. did not turn the shaft 180°. Programming from 0.5ms. to 2.5ms. (confirmed by my oscilloscope) did produce 180° of rotation.
Forget about using the Arduino AnalogWrite() function. This function provides a fixed frequency of either 490Hz or 980Hz depending on the I/O pin. We need 50Hz. for servos.
The discussion below describes my system with a 12MHz clock frequency, and my servo that has a 0.5ms. to 2.5ms. pulse duration range. If your system is different you will have to make the appropriate adjustments.
The first decision we have to make is what timer/counter and prescaler value to use. The decision will be based on having adequate resolution of the angle of rotation. Since we know the frequency of pulse repetition we can rearrange the formula found in my last post. This gives us two unknowns: the maximum count and the prescaler value.
pulse repetition freq = clock speed / (2 * prescaler * max count)
Substituting the known values:
50 = 12,000,000 / (2 * prescaler * max count)
Rearranging and simplifying:
max count = 120,000 / prescaler
The range in pulse width is 2.0ms (2.5ms. at +90° minus 0.5ms. at -90°). This is one tenth of the 20ms. pulse repetition rate (1/50 x 1000). Therefore, the range of programmable count values is one tenth of the maximum count. The rotation resolution is the range of motion of the servo, 180°, divided by the count range. This is summarized below:
Prescaler | Max Count | Pulse Width Count Range | Rotation Resolution |
1 | 120,000 | N/A | N/A |
8 | 15,000 | 1500 | 0.12° |
64 | 1875 | 187.5 | 0.96° |
256 | 468.8 | 46.8 | 3.84° |
1024 | 117.2 | 11.7 | 15° |
From the chart above we can eliminate the prescaler value of 1 because the ATmega328 does not provide a timer/counter that can count to 120,000 (65535 or 255). The only way to employ an eight bit timer/counter (timer/counter 0, or 2) is to use the prescaler value of 1024 - the only choice where the maximum count is under 255. However, the resolution of 15° is poor. This means that we will use timer/counter 1 because it can count to 65535. We should go for the best resolution which means that we will use the divide by 8 prescaler. This gives a resolution of 0.12° per count
Next we select the mode. We will select mode 8, the "Phase and Frequency Correct PWM" mode. The value of the maximum count will be programmed into register 1CR1.
The script I wrote is below and the details of register programming is included within the comments. The script requires you to open a terminal window on the host computer - the Raspberry Pi in my case. You are asked to input a floating point number which is the desired degrees of rotation from the middle position. The script accepts values from -90° to +90°. Once it rotates the servo shaft to the angle you requested, it will ask for your input, again.
Inputting a number from the terminal is not a trivial endeavor. The terminal handles one character at a time and it is up to the programmer to make sense of those characters. I did not want the script to be encumbered with the handling of the character inputs so I wrote a library to do that task. I wanted to learn how to do that, anyway, having not done that before. There are only three lines dealing with the character input: line 35 where we include the library dot h file, line 37 where we create an instance of the class created in the library, and line 59 where we call the function to get our number. My next post will report on creating that library function.
The servo has three pins, two go the battery to power the device, and provide a ground reference, and the third is the control pin which receives the output from the ATmega328. Don't forget to connect the ground pin from the battery to one of the grounds of the Gertboard.
The script:
No comments:
Post a Comment