Cubieboard-1 - powered by the Allwinner A10 Arm SOC
When I received my first Allwinner A10 board it lacked support for hardware PWM. Without a driver for hardware PWM it’s very hard to use this very powerful SOC for controlling motors
This article describes the design of one such hardware pwm driver interface.
The Arduino has a simple to use PWM interface and other implementations should strive to have a similarly developer friendly interface. The “analogWrite” Arduino interface is both easy to understand and implement. For instance the following code implements a 50% duty cycle on digital pin 5:
1 2 |
|
One problem with the Arduino interface is the learning curve required to venture beyond the primary interface. For instance, the above code doesn’t specify the PWM period. Many motors (servos..) function best within a specific PWM period.
This implementation doesn’t allow specification of the signal polarity either. If the hardware interface requires an inverted signal, then duty will have to be computed as 1/duty manually.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
*example code from Timer One Library for Arduino
The Linux OS and expanded ram of the cubieboard make possible a much more rich interface than the arduino affords. File based programming interfaces are available to extend the PWM control to many more languages. One possible implementation might include the following interface (examples in shell script):
/sys/class/pwm-sunxi/pwmX
This is the sysfs directory that contains the PWM interface. X is the PWM channel. The Allwinner A10 has 2period (r/w)
period that makes up a cycle. Can be expressed as hz, khz, ms, or us. Whole numbers only. Examples:
echo 10hz > /sys/class/pwm-sunxi/pwm0/period
echo 1khz > /sys/class/pwm-sunxi/pwm0/period
echo 100ms > /sys/class/pwm-sunxi/pwm0/period
echo 100us > /sys/class/pwm-sunxi/pwm0/period
echo 150khz > /sys/class/pwm-sunxi/pwm0/period
duty (r/w)
portion of the period above that is “active” or on. Same units as duty.
echo 100ms > /sys/class/pwm-sunxi/pwm0/period
# Period is 100 milliseconds
echo 25ms > /sys/class/pwm-sunxi/pwm0/duty
# 25% duty
echo 50ms > /sys/class/pwm-sunxi/pwm0/duty
# 50% duty
echo 75ms > /sys/class/pwm-sunxi/pwm0/duty
# 75% dutyduty_percent (r/w)
duty expressed as a percentage. Whole numbers only
echo 100ms > /sys/class/pwm-sunxi/pwm0/period
# Period is 100 milliseconds
echo 25 > /sys/class/pwm-sunxi/pwm0/duty_percent
# 25% duty
echo 50 > /sys/class/pwm-sunxi/pwm0/duty_percent
# 50% duty
echo 75 > /sys/class/pwm-sunxi/pwm0/duty_percent
# 75% dutypolarity (r/w)
Polarity of the pin during the duty portion. Note that the opposite state will be for the non-duty portion.
1 = high, 0 = low
pulse (r/w)
Output one pulse at the specified period and duty
echo 1 > /sys/class/pwm-sunxi/pwm0/puls
# Output 1 pulse when run is 1pin (r/o)
Name of the A10 pin this pwm outputs on. This is hardwired and informational only. Example:
cat /sys/class/pwm-sunxi/pwm0/pin
# output: PB2run (r/w)
Enable the PWM with the previously set parameters. Example:
echo 1 > /sys/class/pwm-sunxi/pwm0/run
# Start the pwm interface
echo 0 > /sys/class/pwm-sunxi/pwm0/run
# Stop the pwm interface
So, the above interface as a Linux kernel module can implement a 50% duty cycle hardware pwm with the following code:
1 2 |
|
Or a 25% duty cycle at 250hz with the following code:
1 2 3 |
|
Or a 25% duty cycle at 500hz with negative polarity with the following code:
1 2 3 4 |
|
So, the design goal of a simple interface is achieved, with incremental functionality requiring only incremental amounts of additional code.
Next article – developing the pwm-sunxi pwm module