A kernel module for generating PWM signals

Our beloved Fox G20 is nice and all in itself, but where it really shines is in being connected to external peripherals. Obviously, I'm not talking USB and ethernet - any Linux desktop could do that, so let's do more!

Assuming you have already read the basic, like GPIO lines, you might wonder how you can hook up something fancier than a LED: what about driving a servo, for instance?

It turns out that the G20 CPU is indeed capable of generating the required PWM (Pulse Width Modulation) signal, directly in hardware. There's a catch, though: you can only have a couple of pins for that (not enough for many applications - like making an hexapod walk, for instance). Worse still, it seems that there are no Linux drivers (at least, not yet).

Since the generic Linux driver for controlling GPIO pins seems well established and functional, I've decided to write a similar interface for PWM too - you can download my code here: soft_pwm-20100511_0804.tgz. Decompress the archive somewhere, and compile it with

    KDIR=<position of your kernel tree> make

You can also change the variable KDIR in Makefile to point where your kernel tree resides (by default I assume soft_pwm/ and linux-2.6.32.2/ are in the same directory) and then simply type make. You might want to have a look at Compiling the Linux Kernel 2.6.32.2 and Compiling Linux Device Drivers where the kernel and module compilation procedures are explained in full detail. As a side note, my G20 is a Debian system with Lee's COMPLEX kernel; I made the one single modification of activating option CONFIG_PREEMPT, which gives better overall latency. When the compilation completes, transfer the resulting soft_pwm.ko file to your Fox and enjoy.

The control interface is modeled after the standard GPIO library of the kernel. Here is a trivial example of how to make it work:

     #load module
     insmod soft_pwm.ko
     #export pin 82 for PWM usage
     echo 82 > /sys/class/soft_pwm/export
     #use a wave period of 1000 usecs (thus, a 1kHz wave)
     echo 1000 > /sys/class/soft_pwm/pwm82/period
     #use a pulse width of 500 usecs (a 50% duty cycle)
     echo 500 > /sys/class/soft_pwm/pwm82/pulse
     #have a look at the running counter
     cat /sys/class/soft_pwm/pwm82/counter

Both period and pulse accept a number in microseconds: but don't go too low with period or you risk locking you board.

Using a multimeter, I've been checking the resulting voltage on kernel GPIO pin 82 (J7.3 on the board): modulating the pulse width you get a simple DAC converter in the range 0-3.3V.

Now, if I only had a scope I'd love to have a look at how precise the signal generation really is, and how it is affected by the G20 system load.

Example usage

I have a low cost Modelcraft RS-2 servo; the red wire should go to +5V, the black one to GND, while the orange wire will be attached to one G20 soft-generated PWM signal. Since I don't know how much current the servo will draw during operation, I've opted for powering it through an external source; I've connected the orange servo control wire to pin J7.3 on the G20, which to the kernel is known as pin number 82.

To control the servo rotation, we need to produce a wave with a period of 20ms; most servos will be centered with a pulse of 1.5ms, turn full right at about 1ms, and turn full left at about 2ms pulse respectively:

     #load module
     insmod soft_pwm.ko
     #export pin 82 for PWM usage
     echo 82 > /sys/class/soft_pwm/export
     #set a 20ms wave period
     echo 20000 > /sys/class/soft_pwm/pwm82/period
     #drive servo full right
     echo 1000 > /sys/class/soft_pwm/pwm82/pulse
     sleep 5
     #drive servo full left
     echo 2000 > /sys/class/soft_pwm/pwm82/pulse
     sleep 5
     #center servo
     echo 1500 > /sys/class/soft_pwm/pwm82/pulse
     sleep 5
     #release pin 82
     echo 82 > /sys/class/soft_pwm/unexport

A few measures

As you can expect, the driver will have an impact on the latency of the OS running on the G20; after all, we are executing the PWM code up to a few thousands times per second. I've devised a simplistic test to measure the response times of the kernel when confronted with a bunch of udelay calls (udelay(x) is used to wait for x microseconds). The code is a written as a kernel module, since I wish to test latency in kernel space. On initialization, it simply repeats 10 times a 200k loop around udelay() (and then fails, so it won't require an rmmod to be unloaded). To use it, simply insert the module with:

     insmod udelay.ko

and look at dmesg output - or better yet at the debug console on /dev/ttyS0. Without soft_pwm installed, the response times on my G20 are as follows:

     debarm:~# insmod udelay.ko
     uDelay starting.
     pass 0: expected time 1000000 usec, elapsed time 1000000 usec
     pass 1: expected time 1000000 usec, elapsed time 990000 usec
     pass 2: expected time 1000000 usec, elapsed time 980000 usec
     pass 3: expected time 1000000 usec, elapsed time 1030000 usec
     pass 4: expected time 1000000 usec, elapsed time 990000 usec
     pass 5: expected time 1000000 usec, elapsed time 980000 usec
     pass 6: expected time 1000000 usec, elapsed time 990000 usec
     pass 7: expected time 1000000 usec, elapsed time 990000 usec
     pass 8: expected time 1000000 usec, elapsed time 990000 usec
     pass 9: expected time 1000000 usec, elapsed time 1030000 usec
     insmod: error inserting 'udelay.ko': -1 No such device

The script test.sh in the above archive uses 4 different GPIO pins to generate 50% duty-cycles at 1kHz; on my Fox I obtain these numbers:

     debarm:~# ./test.sh
     SoftPWM v0.1 initializing.
     Clock resolution is 1ns
     SoftPWM initialized.
     Registered device pwm82
     Starting timer (period).
     Starting timer (pulse).
     Registered device pwm83
     Starting timer (period).
     Starting timer (pulse).
     Registered device pwm84
     Starting timer (period).
     Starting timer (pulse).
     Registered device pwm85
     Starting timer (period).
     Starting timer (pulse).
     Registered device pwm86
     Starting timer (period).
     Starting timer (pulse).
     uDelay starting.
     pass 0: expected time 1000000 usec, elapsed time 1220000 usec
     pass 1: expected time 1000000 usec, elapsed time 1160000 usec
     pass 2: expected time 1000000 usec, elapsed time 1150000 usec
     pass 3: expected time 1000000 usec, elapsed time 1150000 usec
     pass 4: expected time 1000000 usec, elapsed time 1150000 usec
     pass 5: expected time 1000000 usec, elapsed time 1170000 usec
     pass 6: expected time 1000000 usec, elapsed time 1180000 usec
     pass 7: expected time 1000000 usec, elapsed time 1150000 usec
     pass 8: expected time 1000000 usec, elapsed time 1150000 usec
     pass 9: expected time 1000000 usec, elapsed time 1160000 usec
     insmod: error inserting '/root/udelay.ko': -1 No such device

I've repeated my measurements quite a few times and under different conditions, and found that a single 1kHz wave produces about a 4% increase in latency; on the other hand, a 50Hz wave(the frequency needed for driving servos) does not visibly impact the response times to udelay.

 
contributes/antoniogalea/soft_pwm.txt · Last modified: 2010/05/11 10:04 by galea
 
Except where otherwise noted, content on this wiki is licensed under the following license:CC Attribution-Noncommercial-Share Alike 3.0 Unported
Recent changes RSS feed Donate Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki