This tutorial reuses code developed in earlier tutorials and explains how to control a standard servomotor. Standard means here servos that can be bouhgt off the shelf at radio control toys shops.

About servomotors

A servomotor is a motor with angular control. It is therefore a positon control.

ServoThe principle is quite simple. It consists in one motor with gears to increase the torque while decreasing the speed. At the end of the axis, a potentiometer transmits the angular position to a comparator / amplifier. The amplifier spins the motor until the angle matches the command.

This can be completely analog, in which case the analog value of the potentiometer feeds the input of an analog comparator that also receives an analog command.

It could also be partly digital, and it would be possible to make a servo (and even several servos) with REOBoard. In this case, the analog value of the potentiometer would be read using REOBoard's analog inputs, the analog command could also be read with another analog input, both compared, and the method explained in Tutorial 6 would allow to spin the motor in the right direction.

However, this is not our purpose. We are going to control an existing servo.

HYP DH13Many small servo motors exist in the radiocontrol domain. We are using a medium metal gear servomotor, but any cheap servo (some would cost less than 10 Euros) could also be used .

The advantage of using such a device is tha the control loop is integrated. That kind of servo has 3 wires. One power, one ground, and one signal. This latter is the signal we are going to generate as a function of the potentiometer position, as we already did in previous tutorials for LED and motor control.

The implementation is rather simple because the input signal is a kind of PWM that we alreday know how to generate.

 

The diagram below shows detail about the timing. This kind of PWM has a period of 20 ms, and a ON width between 1 and 2 ms, 1.5 being the cental value.

 

HYP Timing

If we want an easy adjustment, one solution is to divide the 20 ms interval in 20000 parts. In this case, we would set the CCR0 value of the timer to 20 000 (19999 to be accurate), and the CCRx corresponding to the servo line would be set between 1000 (min value) and 2000 (max value).

A signed integer for the value adjustment would be for instance a value between 0 and 1000. Therefore, setting CCRx to 1000 + input_value would make exactly what we want.

 

 

 

Summary of this method

  • Divide the 20 ms interval in 20000 parts. One part is therefore 1 µs. As a result, we have to feed the timer with a 1 MHz clock
  • Use 19 999 as a value for the timer period
  • Use 1000+command_value as a value for the high part of the PWM signal

The program

The only really new part of this program will be to generate the 1MHz timer clock. As we have seen previously, we can select either ACLK which is 32kHz and MCLK (master clock) which is 24 MHz. Fortunately the timer clock can be predivided before entering the timer. If we can find a way to divide the clock by 24, we will have as a result a 1MHz timer.

There are 2 dividers. One can divide by 2, 3, 4, 5, 6, 7, 8, and the other can divide by 2, 4, 8. Dividing by 24 means dividing by 3, then 8.

Here is the function to setup the PWM for servo control

--------------------

void EnableServoPWM(void) {
    P4DIR |= 0x02;                            //    P4.2 output
    P4SEL |= 0x02;                            //    P4.2 as timer output
    TB0CCR0 = 19999;                          //    PWM Period
    TB0CCTL1 = OUTMOD_7;                      //    CCR1 reset/set
    TB0CCR1 = 1500;                           //    CCR1 average vakye
    TB0EX0 = 2;                               //    Divider by 3
    TB0CTL = TBSSEL_2 + MC_1 + TBCLR + ID_3;  //    SMCLK, up mode, clear TAR, divider by 8
}

--------------------

What is changed compared to the previous LED control is the clock division at the 2 last lines.

The next new function is the way to set the angle. As we receive a value coming from the 12 bit AD converter, it is a value between 0 and 4095. If we want to set something between 0 and 1000, and quick and possibly dirty way is to first shift by 2 bits, therefore making a 0 ~ 1023 value.

--------------------void SetAngle(uint16 angle) {
    angle >>= 2;
    if(angle > 1023) angle = 1023;
    TB0CCR1 = 1000 + angle;
}

--------------------

And this all works!

Download the C code

A summary of the first tutorials has been added as a downloadable zip file here.