Enabling PWM on Beagle Bone Black

So I decided to try and get 4 independent PWM ports up and running on the Beagle Bone Black. The process is fairly straightforward with a couple of caveats.

Firstly, the kernel which you are using on the device must be 3.8 (at the time of this writing) and support capemgr, which is essentially the manager for runtime device tree overlays. I previously tried the 3.13 kernel but this had no support for overlays and you will have to edit the dts files after pulling the kernel. This was a little too involved for me so I just changed the kernel version. These overlays enable runtime changes to the device tree blob passed at boot by the bootloader.

By enabling these runtime overlays we can essentially change the hardware configuration on the BBB. The reason why we need to change this hardware configuration is to re-direct the P9 and P8 port muxes to the appropriate hardware block, in our case, the PWM controllers.

The pins which I selected are
Port 8 Pin 13
Port 9 Pins 14, 28 and 42

beaglebone_pinout

I avoided selecting the pairs P8 13/19 or P9 14/16 since it seems that changing the PWM period is not allowed with an error of:
sh: echo: write error: Invalid argument”. Having any one from the pair seems to not yield that error, however having both does not seem to work. I even tried first changing the duty cycle to a value much lower than the period but to no avail.

Without further ado, this is what my boot arguments are in my uEnv.txt file which can be edited by mounting

sudo mount /dev/mmcblk0p1 /media/bootfs/ 

optargs=quiet capemgr.enable_partno=am33xx_pwm,bone_pwm_P8_13,bone_pwm_P9_28,bone_pwm_P9_14,bone_pwm_P9_42,BB-UART1,BB-UART4,BB-UART4,BB-UART5

There are several other PWM pins we can select as well with the caveat mention above.

Above I am also enabling all the UARTs as my project needs them.

To setup all the PWM pins, I created symlinks in my home directory from /sys/devices/ocp.3/pwm_test_P* then I wrote a little script:

#!/bin/sh

pwm_port=p8_13
period_ns=1000
duty=750
echo "Setting ${pwm_port} Period: ${period_ns} Duty Cycle: ${duty}"
echo 0 > pwm_${pwm_port}/duty
echo ${period_ns} > pwm_${pwm_port}/period
echo ${duty} > pwm_${pwm_port}/duty

pwm_port=p9_14
period_ns=1000
duty=500
echo "Setting ${pwm_port} Period: ${period_ns} Duty Cycle: ${duty}"
echo 0 > pwm_${pwm_port}/duty
echo ${period_ns} > pwm_${pwm_port}/period
echo ${duty} > pwm_${pwm_port}/duty

pwm_port=p9_28
period_ns=1000
duty=250
echo "Setting ${pwm_port} Period: ${period_ns} Duty Cycle: ${duty}"
echo 0 > pwm_${pwm_port}/duty
echo ${period_ns} > pwm_${pwm_port}/period
echo ${duty} > pwm_${pwm_port}/duty

pwm_port=p9_42
period_ns=1000
duty=125
echo "Setting ${pwm_port} Period: ${period_ns} Duty Cycle: ${duty}"
echo 0 > pwm_${pwm_port}/duty
echo ${period_ns} > pwm_${pwm_port}/period
echo ${duty} > pwm_${pwm_port}/duty

Remember to first chmod 660 the contents of /sys/devices/ocp.3/pwm_test_P* or you will have to invoke the above script with sudo, which is rather annoying.
This will set each PWM pin to a different duty cycle on a 1uS period. the 1uS period is far too agressive, but it was just a test.
Hooking up a scope probe to each pin seems to yield satisfactory results.

Leave a Reply