Hi,
I've written a kernel driver for the Pi which lets you drive multiple servos, without having to resort to additional hardware such as micro-controllers. It seems to work very nicely, giving nice stable outputs even when the Pi is busy with other tasks; here's a screenshot of Panalyzer showing four servo outputs, of varying widths:

You can control the pulse widths by writing commands to /dev/servoblaster. For example, to set servo 2 pulse width to 1200us, you'd do:

This is a Linux kernel driver for the RaspberryPi, which provides an interface
to drive multiple servos via the GPIO pins. You control the servo postions by
sending commands to the driver saying what pulse width a particular servo
output should use. The driver maintains that pulse width until you send a new
command requesting some other width.

Currently is it configured to drive 8 servos. Servos typically need an active
high pulse of somewhere between 0.5ms and 2.5ms, where the pulse width controls
the position of the servo. The pulse should be repeated approximately every
20ms, although pulse frequency is not critical. The pulse width is critical,
as that translates directly to the servo position.

The driver creates a device file, /dev/servoblaster, in to which you can send
commands. The command format is "<servo-number>=<servo-position>", where servo
number is a number from 0 to 7 inclusive, and servo position is the pulse width
you want in units of 10us. So, if you want to set servo 3 to a pulse width of
1.2ms you could do this at the shell prompt:

echo 3=120 > /dev/servoblaster

120 is in units of 10us, so that is 1200us, or 1.2ms.

When the driver is first loaded the GPIO pins are configure to be outputs, and
their pulse widths are set to 0. This is so that servos don't jump to some
arbitrary postion when you load the driver. Once you know where you want your
servos positioned, write a value to /dev/servoblaster to enable the respective
output. When the driver is unloaded it attempts to shut down the outputs
cleanly, rather than cutting some pulse short and causing a servo position to
jump.

The driver allocates a timeslot of 2.5ms to each output (8 servos resulting in
a cycle time of 20ms). A servo output is set high at the start of its 2.5ms
timeslot, and set low after the appropriate delay. There is then a further
delay to take us to the end of that timeslot before the next servo output is
set high. This way there is only ever one servo output active at a time, which
helps keep the code simple.

The driver works by setting up a linked list of DMA control blocks with the
last one linked back to the first, so once initialized the DMA controller
cycles round continuously and the driver does not need to get involved except
when a pulse width needs to be changed. For a given servo there are four DMA
control blocks; the first transfers a single word to the GPIO 'set output'
register, the second transfers some number of words to the PWM FIFO to generate
the required pulse width time, the third transfers a single word to the GPIO
'clear output' register, and the fourth transfers a number of words to the PWM
FIFO to generate a delay up to the end of the 2.5ms timeslot.

While the driver does use the PWM peripheral, it only uses it to pace the DMA
transfers, so as to generate accurate delays. The PWM is set up such that it
consumes one word from the FIFO every 10us, so to generate a delay of 1.2ms the
driver sets the DMA transfer count to 480 (1200/10*4, as the FIFO is 32 bits
wide). The PWM is set to request data as soon as there is a single word free
in the FIFO, so there should be no burst transfers to upset the timing.

I used Panalyzer running on one Pi to mointor the servo outputs from a second
Pi. The pulse widths and frequencies seem very stable, even under heavy SD
card use. This is expected, because the pulse generation is effectively
handled in hardware and not influenced by interrupt latency or scheduling
effects.

Please read the driver source for more details, such as which GPIO pin maps to
which servo number. The comments at the top of servoblaster.c also explain how
to make your system create the /dev/servoblaster device node automatically when
the driver is loaded.

The driver uses DMA channel 0, and PWM channel 1. It makes no attempt to
protect against other code using those peripherals. It sets the relevant GPIO
pins to be outputs when the driver is loaded, so please ensure that you are not
driving those pins externally.

I would of course recommend some buffering between the GPIO outputs and the
servo control inputs, to protect the Pi. That said, I'm living dangerously and doing
without.. If you just want to experiment with a small servo you can probably
take the 5 volts for it from the header pins on the Pi, but I find that doing
anything non-trivial with four servos connected pulls the 5 volts down far
enough to crash the Pi (no surprise there).

"..here's a screenshot of Panalyzer ...".
Is that the logic analyzer running on the raspberry-Pi measuring the output waveform generated by the Raspberry-Pi?
Somehow I have a feeling there is a flaw in there somewhere...

What I really wanted to say is:
Well done but beware that you might have artifacts in there which are not visible due to my remark above.

Hi Gert, thanks Actually that is Panalyzer running on one Raspberry-Pi monitoring the servo control outputs from a second Raspberry-Pi.

I should have credited you with the idea of using dummy DMA cycles to generate a delay. In this case I DMA to the PWM FIFO, which is configured to consume one word every 10us. So, there isn't really any reason the servo pulse timing should have jitter of more than whatever contention the DMA controller might see on the bus (unless you know differently).

I did have a question for you... are you sure the register accesses in the gertboard example code for setting the PWM clock rate are correct? I used your values which gives a frequency of 600KHz, then set the range to 6, so one FIFO word is consumed every 10us (600/6 = 100KHz = 10us cycle time). However, if I try changing the (32<<12) it seems to have no effect on how quickly the FIFO empties. The registers are clearly valid registers that only take a new value when you set the top byte to 5A, but I'm wondering if they are controlling some other clock. I tried values such as (16<<12) or (8<<12), expecting the FIFO to then consume a word every 5us or 2.5us. Maybe there is something else I need to do to make a new clock rate active. It's not really important; I'm just curious.

I received a question about the PWM clock frequency yesterday.
You are not the first to have an 'issue' where the clock/frequency/output signal are not what is expected.
I have not had time to look into the details. For my code I was relying in the data sheet and my general knowledge
how most circuits on the chip work.
I think the original designer of the PWM module no longer works with use which
means I have to delve deep into the Verilog code or run a simulation to see what is going on.
I am afraid it will be while before I have time for that.
Don't hesitate to poke me again in two weeks time if you have not received an answer.

rgh wrote:Hi,
I've written a kernel driver for the Pi which lets you drive multiple servos, without having to resort to additional hardware such as micro-controllers. It seems to work very nicely, giving nice stable outputs even when the Pi is busy with other tasks; here's a screenshot of Panalyzer showing four servo outputs, of varying widths:

Neat stuff

With a small modifications, it should be possible to generate pulses for stepper motors as well. A 10us resolution is plenty enough for most applications. The user code would request "send N pulses at rate f with direction d on output x", and the device driver does the rest. I assuming here there is an external stepper driver with a Step and Dir input, like the Allegro A4988, which is used on Pololu boards for about £10.

If someone can hook this up with LinuxCnc, it might be a way to get that running on the Pi. It would also be an interesting project to start from scratch, LinuxCnc is a bit of a monster.

I don't supose you could give some visual reference for the layman on how to connect the servo's to the board?

Also, how could the servo's be buffered to protect the Pi?

Nothing visual at the moment, but I used these servos: http://www.hobbytronics.co.uk/robotics/ ... mall-servo. It has three wires, black goes to GND, red goes to 5V, white goes to the relevant GPIO pin. You can find all of those signals on the P1 header on the Pi. There seems to be enough current available to drive at least one servo from there. If you carefully remove the connectors from the black plastic bit on the end of the wires, they'll even plug direct on to the P1 header pins. To protect the Pi I'd probably use something like a 74LS241 as I have them kicking around and they'll buffer all 8 lines in one chip, but I'm sure you could find examples that use a transistor instead via google. If your circuit ends up inverting the signal you can always change the driver to invert also to compensate.

I just updated github; the servoblaster.ko is now built against a 3.2.27+ kernel so will probably work for anyone who ran rpi-update recently. I also had one report of the pulse timing being out by about a factor of 8, presumably because I'm not programming the PWM clock correctly, and for some reason that system happens to have it set to some different frequency. Anyway, I added a couple of module parameters you can use to tweak the timing, see the README.txt for details. https://github.com/richardghirst/PiBits ... rvoBlaster.

Hi bulldog5046. 'make' decides what to do based on the rules in the Makefile and the timestamps on the files. In this case the target for 'all' is servoblaster.ko, and if you already have a file by that name with a timestamp newer than the things it is created from, then make will conclude that it doesn't have to do anything. You could try 'make clean' and then 'make', which works for me. However, the easiest way to get going is probably to update to the latest firmware and kernel by running rpi-update, and then simply use the servoblaster.ko module I put on github; no need to compile anything. You still have to follow the instructions in the top of servoblaster.c to get the system to create the /dev/servoblaster device file automatically for you when you load the module. I didn't see what confused you with the current README, but I'm happy to update it if you point me at the problem.

I created the 2 files but it dodnt creat /dev/servoblaster so i had to do it manually, then load the driver with 'sudo insmod servoblaster.ko' and i'm now able to successfully issue commands to the servo