Home / Tutorials / BeagleBone Tutorial / Beaglebone Black PWM on Ubuntu 16.04 Using Device Tree Overlay

Beaglebone Black PWM on Ubuntu 16.04 Using Device Tree Overlay

pcbway

Now we’ll use the Beaglebone Black PWM to control a servo motor. PWM, which is short for pulse width modulation, is widely used for controlling motors (dc and servomotors).  PWM is also used in controlling power delivered to a load or device without using “power-eating” rheostat.

Beaglebone Black PWM on Ubuntu

While there are a lot of tutorials out there about using the BBB’s PWM module, this tutorial is slightly different since I am using Ubuntu 16.04 LTS with kernel 4.4.9. There are a few changes in the way you can use the PWM using this kernel as compared to earlier ones like 3.8, etc. If you’re not sure which version you are using, type cat /etc/issue for the distro name and version and uname -r for the kernel version in your BBB’s ssh terminal. This is what mine looks like:

olan@arm:~$ cat /etc/issue 
Ubuntu 16.04 LTS 
\n \l rcn-ee.net console Ubuntu Image 2016-05-12 
Support/FAQ: http://elinux.org/BeagleBoardUbuntu 
default username:password is [ubuntu:temppwd] 
The IP Address for usb0 is: 192.168.7.2 
olan@arm:~$ uname -r 
4.4.9-ti-r2

Instructions on how to get Ubuntu to your BBB is found here.

You also need to know what device tree overlays are before pushing through this tutorial. Basically, a device tree is a way to access the hardware components of a device such as a BBB. A device tree overlay is a file that we can use to modify the hardware without recompiling the device’s kernel.  I would suggest you read this tutorial by Adafruit if to get you acquainted with device tree overlays.

If you are not using Ubuntu or if you find this tutorial too advanced, you can still use PWM using bonescript.

Installing the Device Tree Overlay

Preliminaries

First, login as root user so that you wouldn’t need to use sudo on all commands (you’d have to enter your user password):

olan@arm:~$ sudo su 
[sudo] password for olan: 
root@arm:/home/olan#

Next is to install the device tree overlay. I have scourged the web looking for the correct device tree overlay for my device and my kernel version. The one that worked for me can be found here: https://github.com/beagleboard/bb.org-overlays/tree/master/src/arm. You can clone this repository to your BBB.

root@arm:/home/olan# git clone https://github.com/beagleboard/bb.org-overlays/tree/master/src/arm

The repository has three pwm device tree overlays. The one that you’ll be using depends on which pwm output you will use. The BBB has six pwm outputs spread out to 12 pins (two pins per pwm output):

PWM Module Pin
EHRPWM0A P9_22, P9_31
EHRPWM0B P9_21, P9_29
EHRPWM1A P9_14, P8_36
EHRPWM1B P9_16, P8_34
EHRPWM2A P8_19, P8_45
EHRPWM2B P8_13, P8_46

For this tutorial, I’ll be using EHRPWM1A at pin P9_14. That means I’ll be using the device tree overlay BB-PWM1-00A0.dts. Here is its contents:

/*
* Copyright (C) 2016 Seeed Studio.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
/dts-v1/;
/plugin/;
/{
compatible = "ti,beaglebone", "ti,beaglebone-black", "ti,beaglebone-green";
part-number = "BB-PWM1";
version = "00A0";

fragment@0 {
target = <&am33xx_pinmux>;
       __overlay__ {
              pinctrl_spec: Panel_Pins {
                 pinctrl-single,pins = < 0x48 0x06 /* (U14) gpmc_a2.ehrpwm1A */ 0x4c 0x06 /* (T14) gpmc_a3.ehrpwm1B */ >;
              };
       };
};
fragment@1 {
target = <&ocp>;
       __overlay__ {
              test_helper: helper {
                   compatible = "bone-pinmux-helper";
                   pinctrl-names = "default";
                   pinctrl-0 = <pinctrl_spec>;
                   status = "okay";
              };
       };
};
fragment@2 {
target = <&epwmss1>;
       __overlay__ {
              status = "okay";
              };
       };
fragment@3 {
target = <&ehrpwm1>;
       __overlay__ {
              status = "okay";
              };
      };
};

Pinmux on BBB

The above overlay is for both EHRPWM1A and EHRPWM1B. Notice these lines under fragment0:

0x48 0x06 /* (U14) gpmc_a2.ehrpwm1A */
0x4c 0x06 /* (T14) gpmc_a3.ehrpwm1B */

Each pin has its own 32 bit address which starts with 44e10, followed by three unique numbers. The whole address of P9_14 is 44e10848 so it’s offset by 848. You can see the addresses of the pin yourself:

root@arm:/sys/kernel/debug/pinctrl/44e10800.pinmux# more pins | grep 848
pin 18 (44e10848.0) 00000027 pinctrl-single

The number with the pin address is the pin mode. P9_14’s current mode is 27h . A reference table from AM335X technical documentation provided by TI will explain the mode number:

Bit Field Reset Description
6 conf_<module>_<pin>_slewctrl X Slew Control. Slew Rate: Fast is 0, Slow is 1
5 conf_<module>_<pin>_rxactive 1h Receiver Active. Input Enable: Receiver Disable 0, Receiver Enable 1
4 conf_<module>_<pin>_putypesel X Pad Pullup/Pulldown Type. Pulldown is 0, Pullup is 1
3 conf_<module>_<pin>_puden X Pad Pullup/Pulldown enable. Enabled is 0, Disabled is 1
2-0 conf_<module>_<pin>_mmode X Mode. Pad functional mux select. A number between 0 and 7 i.e. 000 and 111. This depends on which mode we require.

27 hex (00100111 binary) means that P9_14 is currently set to Receiver Active. Input Enable: Receiver Disable 0, Receiver Enable 1 and in Mode 7. According to this image here (click to enlarge):

Mode 7 for P9_14 is GPIO mode. This means we can’t use it to multiplex pwm yet unless we change the mode. The device tree overlay does the mode changing!.

Look back at the dts file, there is a line that says:

pinctrl-single,pins = < 0x48 0x06 /* (U14) gpmc_a2.ehrpwm1A */ 0x4c 0x06 /* (T14) gpmc_a3.ehrpwm1B */ >;

The device tree overlay sets the mode of P9_14 (0x48) and P9_16 (0x4c) to mode 6 (0x06). Mode 6 is PWM mode!

Compiling the Device Tree Overlay

In order for the BBB to understand what the .dts file is for, we need to compile the .dts file to the binary object .dtbo. Here’s how to do that:

root@arm: ~# dtc -O dtb -o BB-PWM1-00A0.dtbo -b 0 -@ BB-PWM1-00A0.dts

For the newer debian images, the .dtbo file is already in the /lib/firmware folder so no need to compile the .dts

Note that my .dts file is at the root directory. After that, the .dtbo file will now have been created. The next step is to copy that .dtbo file to /lib/firmware

root@arm:~# move -i BB-PWM1-00A0.dtbo /lib/firmware

Once the dtbo is in the /lib/firmware folder, it’s now ready to be loaded to the cape manager. To do that, just do:

root@arm:~# echo BB-PWM1 > /sys/devices/platform/bone_capemgr/slots

Next, check if it’s loaded:

root@arm:~# cat /sys/devices/platform/bone_capemgr/slots 
0: PF---- -1 
1: PF---- -1 
2: PF---- -1 
3: PF---- -1 
4: P-O-L- 
0 Override Board Name,00A0,Override Manuf,BB-PWM1

If everything is successful, doing this:

root@arm:~# cd /sys/kernel/debug/pinctrl/44e10800.pinmux 
root@arm: /sys/kernel/debug/pinctrl/44e10800.pinmux# more pins | grep 848
pin 18 (44e10848.0) 00000006 pinctrl-single

Should show that P9_14 will now have 6 as its pin mode. Now we are ready to use PWM!

Using the Beaglebone Black PWM Module

The PWM Chips

Go to /sys/class/pwm 

root@arm:~# cd /sys/class/pwm
root@arm:/sys/class/pwm# ls -al
total 0
drwxr-xr-x 2 root root 0 Jan 1 2000 .
drwxr-xr-x 57 root root 0 Jan 1 2000 ..
lrwxrwxrwx 1 root root 0 Jan 23 04:59 pwmchip0 -> ../../devices/platform/ocp/48302000.epwmss/48302200.pwm/pwm/pwmchip0
lrwxrwxrwx 1 root root 0 Jan 23 04:59 pwmchip2 -> ../../devices/platform/ocp/48300000.epwmss/48300200.pwm/pwm/pwmchip2
lrwxrwxrwx 1 root root 0 Jan 23 04:59 pwmchip4 -> ../../devices/platform/ocp/48304000.epwmss/48304200.pwm/pwm/pwmchip4
lrwxrwxrwx 1 root root 0 Jan 23 04:59 pwmchip6 -> ../../devices/platform/ocp/48300000.epwmss/48300100.ecap/pwm/pwmchip6
lrwxrwxrwx 1 root root 0 Jan 23 04:59 pwmchip7 -> ../../devices/platform/ocp/48304000.epwmss/48304100.ecap/pwm/pwmchip7

You can see the pwmchips inside this folder.

EHRPWM1 is under PWMSS1 which has address 0x48302000 and can be seen under pwmchip0. Hence we should go to this folder and echo 0 to export (0 for EHRPWM1A, 1 for EHRPWM1B).

root@arm:/sys/class/pwm/pwmchip0# echo 0 > export

Once 0 is echoed to export, pwm0 will now become part of folder pwmchip0. Go to this newly created directory to find:

root@arm:/sys/class/pwm/pwmchip0# cd pwm0 
root@arm:/sys/class/pwm/pwmchip0/pwm0# ls -al total 0 
drwxr-xr-x 3 root root 0 Jan 23 05:04 . 
drwxr-xr-x 4 root root 0 Jan 23 04:53 .. 
-rw-r--r-- 1 root root 4096 Jan 23 05:04 duty_cycle 
-rw-r--r-- 1 root root 4096 Jan 23 05:04 enable 
-rw-r--r-- 1 root root 4096 Jan 23 05:04 period 
-rw-r--r-- 1 root root 4096 Jan 23 05:04 polarity 
drwxr-xr-x 2 root root 0 Jan 23 05:04 power 
-rw-r--r-- 1 root root 4096 Jan 23 05:04 uevent

Echo, Echo

You can give values (in nano seconds) to period and duty_cycle. Consequently, give 1 or 0 to enable to turn the Beaglebone Black PWM on or off respectively. For example, if a 100 Hz pulse with a duty_cycle of 80% is desired, do:

root@arm:/sys/class/pwm/pwmchip0/pwm0# echo 1000000000 > period 
root@arm:/sys/class/pwm/pwmchip0/pwm0# echo 800000000 > duty_cycle 
root@arm:/sys/class/pwm/pwmchip0/pwm0# echo 1 > enable

Connecting a Servo Motor

Now that we know how to use the Beaglebone Black PWM module, it’s time to control a servo motor.

To start, wire the circuit as shown:

beaglebone black pwm connection to servo

The direction of the servo motor’s arm can be controlled by changing the duty cycle of the Beaglebone Black PWM signal applied to the pulse (yellow) line.  In theory, the minimum pulse width should be 1 ms while the maximum pulse width is 2 ms. A 1.5 ms pulse width will turn the arm to the center position. But not all servo motors follow the exact same pulse widths. Some will have center positions at less than or greater than 1.5 ms. Thus you need to experiment with your own servo meter to know the exact pulse width for the center position. As for me, I’ll stick with the standard 1.5 ms pulse width for the center position.

To begin with, we need to setup the Beaglebone Black PWM’s period. The period should be larger than the max pulse width of 2 ms. We also need to set the polarity to a positive pulse. I wanted to set it to 20 ms so here’s what I did:

root@arm:/sys/devices/class/pwm/pwmchip0/pwm0# echo 0 > polarity 
root@arm:/sys/devices/class/pwm/pwmchip0/pwm0# echo 20000000 > period 
root@arm:/sys/devices/class/pwm/pwmchip0/pwm0# echo 1 > enable

I sent a 1 ms pulse to turn it to -90° (left most position when facing the arm):

root@arm:/sys/devices/class/pwm/pwmchip0/pwm0# echo 1000000 > duty_cycle 
root@arm:/sys/devices/class/pwm/pwmchip0/pwm0# echo 1 > enable

To turn it to 90° (all the way to the right), I sent a 2 ms pulse:

root@arm:/sys/devices/class/pwm/pwmchip0/pwm0# echo 2000000 > duty_cycle 
root@arm:/sys/devices/class/pwm/pwmchip0/pwm0# echo 1 > enable

To turn it to the center position, I sent a 1.5 ms pulse:

root@arm:/sys/devices/class/pwm/pwmchip0/pwm0# echo 1500000 > duty_cycle 
root@arm:/sys/devices/class/pwm/pwmchip0/pwm0# echo 1 > enable

TL, DR:

  • To begin with beaglebone black pwm, download/copy/create a suitable device tree overlay
  • Then compile the device tree overlay
  • Then move the compiled device tree overlay to cape manager
  • Next, identify the correct pwm chip
  • Finally, echo values to polarity, period, duty cycle and enable!

Was this post useful? Drop a commment below!

Check Also

Raspberry Pi Zero vs. PocketBeagle

Raspberry Pi Zero vs. PocketBeagle

Last year, both Raspberry Pi and Beaglebone Black released mini versions of their popular microcomputers: …

9 comments

  1. Could i get a little clarification on how you knew to do echo pwm0 > export.. I’m a little confused on that. Also how do i can only input the value for the period that you gave. If i try any other numbers i get invalid argument.. what am i doing wrong?

    • Hi,

      Page 182 of the AM335x Technical Reference Manual gives the address of PWMSS1 (where EHRPWM1 belongs) as 0x4830_2000. Once the device tree overlay is installed and that pwmchips are accessible, you just need to find that address. You can see above that it’s on pwmchip0 (in the line: pwmchip0 -> ../../devices/platform/ocp/48302000.epwmss/48302200.pwm/pwm/pwmchip0) . So I just changed my current directory to pwmchip0. Then to choose between EHRPWM1A or EHRPWM1B, you need to echo 0 > export (for 1A) or echo 1 >export (for 1B). Only then will pwm0 subfolder will appear.

    • Can you show me the error that appeared when you put values into the period?

      • Hey! thanks for the reply.. It kept saying “Invalid argument” but I rebooted my BB and tried again and it worked great!

        I have been trying to write a C++ program that:
        1. Loads the cape overlay
        2. configures the pins
        3. Changes the period, duty cycle, and enables/disables

        I have managed to accomplish everything but I can’t get the part where you have to echo 0 > export.. I am getting a segmentation fault.

        Do you have any idea of how to get around this?
        I am trying to use:
        configureP9_14 = fopen(“/sys/class/pwm/pwmchip0/export”, “w”);
        fwrite(pwm0,1,sizeof(pwm0), configureP9_14);
        fclose(configureP9_14);
        and its no good.

        Also, do you know of any good IDE’s to develop c++ code for the BeagleBone?

        • Hi,

          The segmentation fault is due to the pwm0 not created yet. I’m not sure if pwm0 in your code refers to the string “pwm0” or the char “0”. In any case, you should export “0” and not “pwm0” like this:

          char str[] = “0”
          configureP9_14 = fopen(“/sys/class/pwm/pwmchip0/export”, “w”);
          fwrite(str,1,sizeof(str), configureP9_14);
          fclose(configureP9_14);

          Only then will the pwm0 folder appear. You can then do this:

          char str2[] = “20000000”;
          setPeriod = fopen (“/sys/class/pwm/pwmchip0/pwm0/period”, “w”);
          fwrite (str2, 1, sizeof(str2), setPeriod);
          fclose (setPeriod);

          to set the period. Setting the duty_cycle and enabling it follows the same approach.

          As for the IDE, you can use any Linux C++ IDE if you have a monitor connected to your BBB. I suggest Eclipse CDT, although I really don’t use it. Linux itself is an IDE 🙂

  2. #include
    #include
    using namespace std;

    int main() {
    FILE *loadOverlay = NULL;
    FILE *configureP9_14 = NULL;
    FILE *configureP9_16 = NULL;
    FILE *period_file = NULL;
    FILE *dutyCycle_file = NULL;
    FILE *enable_file = NULL;

    char overlay[] = “BB-PWM1”;
    char pwm0[] = “0”;

    char period[] = “100000000”;
    char dutyCycle[] = “5000000”;
    char enable[] = “1”;
    char disable[] = “0”;

    /* Load the DTO to enable PWM */
    loadOverlay = fopen(“/sys/devices/platform/bone_capemgr/slots”, “w”);
    fwrite(overlay,1,sizeof(overlay), loadOverlay);
    fclose(loadOverlay);

    /*We are using pins P9_14 and and P9_16 pins for this PWM */
    configureP9_14 = fopen(“/sys/class/pwm/pwmchip0/export”, “w”);
    fwrite(pwm0,1,sizeof(pwm0),configureP9_14);
    fclose(configureP9_14);

    period_file = fopen(“/sys/class/pwm/pwmchip0/pwm0/period”, “w”);
    fwrite(period,1,sizeof(period),period_file);
    fclose(period_file);

    dutyCycle_file = fopen(“/sys/class/pwm/pwmchip0/pwm0/duty_cycle”, “w”);
    fwrite(period,1,sizeof(dutyCycle),dutyCycle_file);
    fclose(dutyCycle_file);

    enable_file = fopen(“/sys/class/pwm/pwmchip0/pwm0/enable”, “w”);
    fwrite(enable,1,sizeof(enable),enable_file);
    fclose(enable_file);

    usleep(10000000);

    enable_file = fopen(“/sys/class/pwm/pwmchip0/pwm0/enable”, “w”);
    fwrite(disable,1,sizeof(disable),enable_file);
    fclose(enable_file);
    }

    You are no able to write to the /sys/class/pwm/pwmchip0/export file.. That is what give you the segmentation fault…

    If you see any error in my code please let me know. I also tried to set config_P9 = /sys/class/pwm/pwmchip0 while pwm0 = echo 0 > export.

    I still received the segmentation fault in both cases

    • You’re code works on mine! I suggest you switch to super user first (sudo su) then run the same code then see what happens.

  3. Sir,
    Thanks for this article to describe how to setup PWM step-by-step. So far, I could just raise up only one PWM DTO file from BB-PWM0, BB-PWM1 and BB-PWM2.

    When I tried to echo the second PWM to slots, the error always happened.

    root@arm:/sys/devices/platform/bone_capemgr# cat slots
    0: PF—- -1
    1: PF—- -1
    2: PF—- -1
    3: PF—- -1
    root@arm:/sys/devices/platform/bone_capemgr# echo “BB-PWM0” > slots
    root@arm:/sys/devices/platform/bone_capemgr# cat slots
    0: PF—- -1
    1: PF—- -1
    2: PF—- -1
    3: PF—- -1
    4: P-O-L- 0 Override Board Name,00A0,Override Manuf,BB-PWM0
    root@arm:/sys/devices/platform/bone_capemgr# echo “BB-PWM1” > slots
    bash: echo: write error: File exists

    dmesg log:
    [ 302.574185] bone_capemgr bone_capemgr: slot #4: override
    [ 302.590414] bone_capemgr bone_capemgr: slot #4: ‘Override Board Name,00A0,Override Manuf,BB-PWM0’
    [ 308.127210] bone_capemgr bone_capemgr: slot #5: override
    [ 308.140355] bone_capemgr bone_capemgr: slot #5: ‘Override Board Name,00A0,Override Manuf,BB-PWM1’
    [ 308.168899] OF: changeset: add_property failed @/__symbols__/pinctrl_spec
    [ 308.177980] OF: Error applying changeset (-17)
    [ 308.183042] bone_capemgr bone_capemgr: slot #5: Failed to create overlay

    I followed the introduction in bb.org-overlays to update DTOS, but the problem still there.
    Could you kindly provide any suggestion about this error?

  4. Hi, can we use all the pwm at the same time or only one overlay at a time ?
    Thank you for the tutorial, it works very well.

Leave a Reply

Your email address will not be published. Required fields are marked *

Index