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.
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
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.