The Pico/W In C: Servos
Written by Harry Fairhead   
Tuesday, 05 November 2024
Article Index
The Pico/W In C: Servos
Complete Program

Servo motors are basic to many an IoT project and they are easy to work with and there is no need for a driver. This is an extract from a recent book in the I Programmer Library, all about the Pico/W in C.

Programming the Raspberry Pi Pico In C

By Harry Fairhead

picoC2E360

Buy from Amazon.

Contents

  • Preface
  • Chapter 1 The Raspberry Pi Pico – Before We Begin
  • Chapter 2 Getting Started
  • Chapter 3 Getting Started With The GPIO
  • Chapter 4 Simple Output
  • Chapter 5 Some Electronics
  • Chapter 6 Simple Input
        Extract:   GPIO Input ***NEW!
  • Chapter 7 Advanced Input – Events and Interrupts
  • Chapter 8 Pulse Width Modulation
        Extract: Basic PWM
  • Chapter 9 Controlling Motors And Servos
  • Chapter 10 Getting Started With The SPI Bus
  • Chapter 11 A-To-D and The SPI Bus
  • Chapter 12 Using The I2C Bus
  • Chapter 13 Using The PIO
        Extract: A 1-Wire PIO Program  
  • Chapter 14 The DHT22 Sensor Implementing A Custom Protocol
  • Chapter 15 The 1‑Wire Bus And The DS1820
  • Chapter 16 The Serial Port
  • Chapter 17 Using the Pico W
       Extract: Simple Web Client
       Extract:A Better Connect
  • Chapter 18 The Pico/W In C: Direct To Hardware 

Extra: Adding WiFi To The Pico 2

<ASIN:1871962803>

<ASIN:187196279X>

Controlling Motors And Servos

Controlling motors is an obvious use for the low cost Pico, but it is important to understand the different types of motor that you can use and exactly how to control them using PWM. In addition to PWM control, we also look at the very useful stepper motor which doesn’t make use of PWM.

The simplest division among types of motor is AC and DC. AC motors are generally large and powerful and run from mains voltage. As they are more difficult to work with, and they work at mains voltages, these aren’t used much in IoT applications. DC motors generally work on lower voltage and are much more suitable for the IoT. In this chapter we will only look at DC motors and how they work thanks to pulse width modulation. The parts used are listed in the Resources section of the book’s webpage at www.iopress.info.

In Chapter but not in this extract

  • DC Motor
  • Brushed Motors
  • Unidirectional Brushed Motor
  • Unidirectional PWM Motor Controller
  • Bidirectional Brushed Motor
  • Bidirectional Motor Software
  • Using A Single Full H-Bridge As Two Half H-Bridges

Controlling a Servo

Hobby servos, of the sort used in radio control models, are very cheap and easy to use and they connect via a standard PWM protocol. Servos are not drive motors, but positioning motors. That is, they don’t rotate at a set speed, they move to a specified angle or position.

A servo is a motor, usually a brushed DC motor, with a feedback sensor for position, usually a simple variable resistor (potentiometer) connected to the shaft. The output is usually via a set of gears which reduces the rotation rate and increases the torque. The motor turns the gears, and hence the shaft, until the potentiometer reaches the desired setting and hence the shaft has the required angle/position.
A basic servo has just three connections, ground, a power line and a signal line. The colors used vary, but the power line is usually red, ground is usually black or brown and the signal line is white, yellow or orange. If a standard J‑connector is fitted then the wire nearest the notch, pin 3, is Signal, the middle wire, pin 2, is 5V and outer wire, pin 1, is Ground.

servomotor

The power wire has to be connected to a 5V supply capable of providing enough current to run the motor - anything up to 500mA or more depending on the servo. The good news is that the servo’s signal line generally needs very little current, although it does, in theory, need to be switched between 0 and 5V using a PWM signal.

servo

You can assume that the signal line needs to be driven as a voltage load and so the appropriate way to drive the servo is:

  • The servo's + line needs to be connected to an external 5V power supply.

  • The 10K resistor R1 can be a lot larger for most servos - 47K often works. The 5.6K resistor limits the base current to slightly less than 0.5mA.

Notice, however, that if you are using a single BJT driver, like the one shown above, the input is inverted.

This is the correct way to drive a servo, but in nearly all cases you can drive the servo signal line directly from the 3.3V GPIO line with a 1K resistor to limit the current if anything goes wrong with the servo. Some servos will even work with their motor connected to 3.3V, but at much reduced torque.

Now all we have to do is set the PWM line to produce 20ms pulses with pulse widths ranging from 0.5ms to 2.5ms – i.e. a duty cycle of 2.5 to 12.5%.

Once again, it is easier to use a struct to represent the current state of a servo and create functions to change the state:

typedef struct
{
    uint gpio;
    uint slice;
    uint chan;
    uint speed;
    uint resolution;
    bool on;
    bool invert;
} Servo;

This struct includes a field to specify an inversion of the output. Initialization is easy:

void ServoInit(Servo *s, uint gpio, bool invert)
{
    gpio_set_function(gpio, GPIO_FUNC_PWM);
    s->gpio = gpio;
    s->slice = pwm_gpio_to_slice_num(gpio);
    s->chan = pwm_gpio_to_channel(gpio);
    pwm_set_enabled(s->slice, false);
    s->on = false;
    s->speed = 0;
    s->resolution = pwm_set_freq_duty(s->slice,
s->chan, 50, 0); pwm_set_duty(s->slice, s->chan, 250); if (s->chan) {
pwm_set_output_polarity(s->slice, false, invert); } else { pwm_set_output_polarity(s->slice, invert, false); } s->invert = invert; }

Notice that we set the frequency to 50 Hz. If you want to work with a non-standard servo you can change this or make it settable. We also need a higher accuracy version of the function to set the duty cycle.

The function given earlier works with percentages, but as the percentage range for a servo is just 2.5% to 12.5% we need to specify two decimal places and so we need to work with percent*100:

void pwm_set_dutyH(uint slice_num, uint chan, int d)
{
pwm_set_chan_level(slice_num, chan, pwm_get_wrap(slice_num) * d / 10000);
}

With this in place we can write on/off and positioning functions:

void ServoOn(Servo *s)
{
 pwm_set_enabled(s->slice, true);
 s->on = true;
}
void ServoOff(Servo *s)
{
 pwm_set_enabled(s->slice, false);
 s->on = false;
}
void ServoPosition(Servo *s,uint p) { pwm_set_dutyH(s->slice, s->chan, p*10+250); }

The ServoPosition function sets the position in terms of percentages. That is ServoPosition(&s,50) sets the servo to the middle of its range. This assumes that the servo has a standard positioning range and most don’t. In practice, to get the best out of a servo you need to calibrate each servo and discover what range of movement is supported.



Last Updated ( Tuesday, 05 November 2024 )