Alchitry

Navigation: Main menu

This tutorial will explain how servos work and how to use them in your Lucid projects. We will be using a pre-written module (found in the Components Library in the Mojo IDE) but the module will be fully explained. Enjoy!

Servos, for those unfamiliar, are typically little boxes that have a spline that a servo horn can be attached to. Inside the box is a set of gear, a motor, a potentiometer and some control electronics. What makes a servo useful is that you can simply specify a position and the electronics inside will drive the motor to move the spline to that position. A typical servo has a travel range of a little over 180 degrees. However, you can find servos with more. There are even special servos that act more as a motor and spin continuously. Similar to servos would be electronic speed controllers, or ESC, which can be used to accept the same signal as a servo but will drive a motor for you. These are commonly used in RC hobbies (cars, planes, helicopters, etc.) and can be great for robots.

The PWM Signal

Servos require that you send them a PWM signal, but the signal has some special requirements. Typically, a PWM signal will have a duty cycle that ranges from 0% (always off) to 100% (always on). However, a servo uses a much narrower range and it is less helpful to think of duty cycle as it is pulse width.

A typical servo expects a pulse every 20ms, or 50 times per second. The width of these pulses specifies the position of the servo. The center, or neutral point, is generally set with a 1.5ms pulse. It is pretty safe to assume that any servo will then accept +/-0.5ms. In other words, 1ms to 2ms pulses.

It's important not to exceed the servo's range as servos are typically stupid and will try to drive the motor past a fixed stopping point potentially burning it out.

Lucky for us, there's a component that will make this super easy.

Controlling Servos

Make a new Lucid project based on the Base Project and open the Components Library. Add the servo controller, which can be found under Controllers. While you are there, also add the Counter component to your project (found under Miscellaneous).

First, I added the servo output. You will also need to add the following to mojo.ucf.

NET "servo" LOC = P50 | IOSTANDARD = LVTTL;

This will be the pin we use to control the servo.

The servo module has a few parameters, but the one you will change most is probably RESOLUTION. This sets how precise you can control the width of the pulse. Most of the time 8 bits will be plenty, but, we will use 16 here just to show off the FPGA (buttery smooth servo motion).

We then have a counter that outputs 17 bits with 10 bits of extra division to slow things down.

The reason we need 17 instead of simply 16 is because of the neat little trick on line 40 that allows you to use the 17th bit to make the counter count up then count down.

servo_controller.position = ctr.value[15:0]^(16x{ctr.value[16]});

Here we take bit 16 (the 17th bit) and duplicate it 16 times. We then XOR this with the lower 16 bits of the counter. When bit 16 is 0, the XOR with 0 does nothing and the counter behaves exactly as you would expect. However, once bit 16 is 1, all the bits of the output are XORed with 1. XOR with 1 will invert the bits. Inverting the bits is the same as taking the max value and subtracting the counter value from it, so in effect it will start counting down. What will happen is the counter will count from 16h0000 to 16hFFFF then back to 16h0000.

Here are some images to help explain what is happening.

This is what a basic counter looks like. As it reaches the max value, it resets back to 0. The areas in gray are where the MSB is 1.

If we now simply ignore the MSB, we get the same thing but with twice the frequency and half the max value.

Finally, if we use the MSB to invert every other section, we get a nice saw-tooth wave without any discontinuities.

You can now build the project and load it to your Mojo.

Plug a servo into P50, RAW (make sure the Mojo is powered with 5V), and GND. The servo should move back and forth. You should notice that the servo stops at both extremes for a short period. This will be explained in the next section.

This module is pretty dense due to all the stuff to make it easily configurable.

Let's first talk about the parameters. CLOCK_FREQ is used to specify the clock frequency (who would have guessed?). For the vast majority of projects, this will be 50MHz, or 50,000,000Hz. CENTER_WIDTH is used to specify the neutral point for the servo. As mentioned before this is typically 1.5ms. All the time parameters are specified in micro seconds so this is 1500 by default. MIN_MAX_DIFF is used to set how much the pulse can vary off center. Since most servos accept 1-2ms pulses, 500us gives us exactly that (1500 +/- 500 = 1000 to 2000). PERIOD is used to specify how often a pulse occurs. Again, 20ms is typical so 20000us is used.

Finally, RESOLUTION we already talked about. It is the number of bits used to specify the servo's position. Note that there are some kinda crazy looking restrictions on this parameter. This is because there's a limit to how high this can be. It's the number of bits needed to represent the number of clock cycles in the difference between the min and max pulse widths. By default, the difference is 1ms and we have a clock of 50MHz. This means there are 50,000 clock cycles, or 50,000 possible steps we can use for the pulse width. Since you need 16 bits to represent this, 16 is the maximum value RESOLUTION can take.

Note that the way this is setup is very different than is you use PWM on something like an Arduino to control a servo. There if you use a 16bit counter, you will only get a small sliver of that in actual usable resolution. Most of it will be wasted in waiting for the reset of the pulse period. The FPGA can achieve a MUCH higher resolution than any microcontroller I've ever seen.

The constants on lines 61 to 67 are just some more calculations for all the flexibility. TOP is the value to reset the timer at so we get a period of exactly PERIOD. MIN_MAX is the number of clock cycles to allow in either direction of the center. SHIFT is the number of bits to shift our input position by if we aren't operating at maximum resolution.OFFSET is the offset to use to make sure the pulse width is CENTER_WIDTH when the input position is half of its max value.

When the counter reaches its maximum value (TOP - 1), we reset the counter and update the position value. The reason we update the value here is because it prevents us from updating when the pulse could be active. If the pulse could be active and we change the desired position, we could get half a pulse which causes servos to glitch (jitter).

Here we also cap the values of the position so that we never output a pulse outside the set bounds. This is why the servo pauses at either extreme as it sweeps back and forth. The two extremes have values that aren't being used but the counter sweeps them anyways. If we wanted to open these up, we could simply set MIN_MAX_DIFF to be a bit larger.

Finally, we output a 1 only when the shifted position plus offset is greater than the counter.

That's it for this module! You could totally use the PWM component instead, but you would have to do a lot of calculations to set the TOP value correctly and then you would need to be careful about what duty cycle you requested to not damage your servo. With this module, you don't have to think about the details and it won't let you send values outside the range you specify.