The Pico/W In C: Servos |
Written by Harry Fairhead | |||
Tuesday, 05 November 2024 | |||
Page 1 of 2 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 CBy Harry FairheadBuy from Amazon. Contents
Extra: Adding WiFi To The Pico 2 <ASIN:1871962803> <ASIN:187196279X> Controlling Motors And ServosControlling 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
Controlling a ServoHobby 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. 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. 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:
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, 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) { 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; } 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 ) |