PWM Problems with BeagleBone Black Debian 8.4 Kernel 4.4

Hello Guys,

I am trying to control the PWM Output over a C++ program. However I cannot get it to work. It seems like I am unable to activate the PWM output.

Here is what I already tried:
I loaded the following capes:

0: PF---- -1
1: PF---- -1
2: PF---- -1
3: PF---- -1
4: P-O-L- 0 Override Board Name,00A0,Override Manuf,BB-UART1
5: P-O-L- 1 Override Board Name,00A0,Override Manuf,BB-UART5
6: P-O-L- 2 Override Board Name,00A0,Override Manuf,am33xx_pwm
8: P-O-L- 4 Override Board Name,00A0,Override Manuf,BB-PWM0
9: P-O-L- 5 Override Board Name,00A0,Override Manuf,BB-PWM1
10: P-O-L- 6 Override Board Name,00A0,Override Manuf,BB-PWM2

After that several pwmchip directories appear in /sys/class/pwm.
All of those contain a export file which I can use to create pwm0 and in some pwmchip’s also a pwm1 directory.
Inside these pwm[01] subfolders are files for duty_cycle, period and similar but writing duty_cycle=5000, period=10000 and enable=1 into them does not cause any output to occur.
A run file like in some other tutorials does not exist in our case.

I also tried to follow the instructions of the Derek Molloy book, and different guides in this google groups forum. However none of these seem to apply to my kernel.

What am I missing? It seems like the outputs are not activated, or something similiar? Does anybody know how to get the PWMs to work, and how to interface them correctly?

Best Regards,
Phil

You have not mentioned which kernel you’re using.

Also seeing your code would help.

Hello,

thanks very much for your quick reply.

  • Concerning the Kernel: If I enter the command uname -a I get the following output: Linux beaglebone 4.4.9-ti-r25 #1 SMP Thu May 5 23:08:13 UTC 2016 armv7l GNU/Linux. Therefore I assume I am using Kernel 4.4.9

  • Concerning the Code: Well there is no code yet. I assumed that the PWM could be controlled over files, therefore just as I control GPIOS by writing the desired outputs in the appropriate files. I tried to control the PWM by writing the desired duty_cycle in the file. So I hoped that I could access the PWM from my c++ code by simple file access. However as I mentioned, writing the setting duty_cycle=5000, period=10000 and enable=1 in the files I described in my original post, I could not produce any outputs.

  • I also tried to use a custom device tree overlay. I generated it using this Website: http://kilobaser.com/blog/2014-07-28-beaglebone-black-devicetreeoverlay-generator#dtogenerator

    with the parameters: P8_36, fastslew, output, pulldown, mode2.
    This gave me the overlay code:

    /*

    • Copyright © 2013 CircuitCo
      À * 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.

The only overlay I know for a fact that works with recent kernels is universaln. Which is one of the universal io overlays. But here is one I personally made that works for all ehrpwms . . . keep in mind the file name has to be univ-wph-00A0.dts . . . But this file is what universaln has it in for pwm exactly. I just stripped out the pwm stuff and put it into this file.

/dts-v1/;
/plugin/;

/ {
compatible = “ti,beaglebone”, “ti,beaglebone-black”, “ti,beaglebone-green”;

/* identification */
part-number = “univ-wph”;
version = “00A0”;

/* state the resources this cape uses /
exclusive-use =
/
the pin header uses /
“P8.13”, /
EHRPWM2B - ZONE 6 PWM /
“P8.19”, /
EHRPWM2A - ZONE 4 PWM /
“P9.14”, /
EHRPWM1A - ZONE 3 PWM /
“P9.16”, /
EHRPWM1B - ZONE 5 PWM /
“P9.21”, /
EHRPWM0A - ZONE 2 PWM /
“P9.22”, /
EHRPWM0B - ZONE 1 PWM /
/
the hardware ip uses */
“ehrpwm0A”,
“ehrpwm0B”,
“ehrpwm1A”,
“ehrpwm1B”,
“ehrpwm2A”,
“ehrpwm2B”;

fragment@0 {
target = <&am33xx_pinmux>;
overlay {

/* P8_13 (ZCZ ball T10) /
P8_13_pwm_pin: pinmux_P8_13_pwm_pin {
pinctrl-single,pins = <0x024 0x24>; }; /
Mode 4, Pull-Down, RxActive */

/* P8_19 (ZCZ ball U10) /
P8_19_pwm_pin: pinmux_P8_19_pwm_pin {
pinctrl-single,pins = <0x020 0x24>; }; /
Mode 4, Pull-Down, RxActive */

/* P9_14 (ZCZ ball U14) /
P9_14_pwm_pin: pinmux_P9_14_pwm_pin {
pinctrl-single,pins = <0x048 0x26>; }; /
Mode 6, Pull-Down, RxActive */

/* P9_16 (ZCZ ball T14) /
P9_16_pwm_pin: pinmux_P9_16_pwm_pin {
pinctrl-single,pins = <0x04c 0x26>; }; /
Mode 6, Pull-Down, RxActive */

/* P9_21 (ZCZ ball B17) /
P9_21_pwm_pin: pinmux_P9_21_pwm_pin {
pinctrl-single,pins = <0x154 0x33>; }; /
Mode 3, Pull-Up, RxActive */

/* P9_22 (ZCZ ball A17) /
P9_22_pwm_pin: pinmux_P9_22_pwm_pin {
pinctrl-single,pins = <0x150 0x33>; }; /
Mode 3, Pull-Up, RxActive */

};
};

fragment@1 {
target = <&ocp>;
overlay {

P8_13_pinmux {
compatible = “bone-pinmux-helper”;
status = “okay”;
pinctrl-names = “default”;
pinctrl-0 = <&P8_13_pwm_pin>;
};

P8_19_pinmux {
compatible = “bone-pinmux-helper”;
status = “okay”;
pinctrl-names = “default”;
pinctrl-0 = <&P8_19_pwm_pin>;
};

P9_14_pinmux {
compatible = “bone-pinmux-helper”;
status = “okay”;
pinctrl-names = “default”;
pinctrl-0 = <&P9_14_pwm_pin>;
};

P9_16_pinmux {
compatible = “bone-pinmux-helper”;
status = “okay”;
pinctrl-names = “default”;
pinctrl-0 = <&P9_16_pwm_pin>;
};

P9_21_pinmux {
compatible = “bone-pinmux-helper”;
status = “okay”;
pinctrl-names = “default”;
pinctrl-0 = <&P9_21_pwm_pin>;
};

P9_22_pinmux {
compatible = “bone-pinmux-helper”;
status = “okay”;
pinctrl-names = “default”;
pinctrl-0 = <&P9_22_pwm_pin>;
};
};
};

fragment@20 {
target = <&epwmss0>;
overlay {
status = “okay”;
};
};

fragment@21 {
target = <&ehrpwm0>;
overlay {
status = “okay”;
pinctrl-names = “default”;
pinctrl-0 = <>;
};
};

fragment@23 {
target = <&epwmss1>;
overlay {
status = “okay”;
};
};

fragment@24 {
target = <&ehrpwm1>;
overlay {
status = “okay”;
pinctrl-names = “default”;
pinctrl-0 = <>;
};
};

fragment@25 {
target = <&epwmss2>;
overlay {
status = “okay”;
};
};

fragment@26 {
target = <&ehrpwm2>;
overlay {
status = “okay”;
pinctrl-names = “default”;
pinctrl-0 = <>;
};
};
};

You’ll still have to manually export pwm0 pwm1 per ehrpwm module, and make sure you set period before duty_cycle. As you’ll get an error otherwise. As for code, I’ll have to look on the board which I wrote the code for, but I believe I wrote javascript for nodejs as a quick proof of concept test. The same basic principles would be the same for C or C++ though . . .

Hello William,
thank you very much for your reply, your help is very appreciated. I have not tried your solution yet, however:

  • you say that I still have to manually export pwm0 and pwm1. You mean after mounting your cape, I will have to write a 0 and 1 into the export file of the pwmchip directories in /sys/class/pwm?

  • Also which Chip corresponds to which ehrpwm?

  • And last but not least, what is the unit of duty_cycle and period? Is it ns?

Best regards,
Phil

   - you say that I still have to manually export pwm0 and pwm1. You mean
   after mounting your cape, I will have to write a 0 and 1 into the export
   file of the pwmchip<number> directories in /sys/class/pwm?

Yes.

   - Also which Chip corresponds to which ehrpwm?

This site has a table most of the way down that explains it. However do
keep in mind that it's outdated material, and that now we only have pwm0,
and pwm 1 per module. No, pwm0, pwm1, pwm2, pwm3, etc. Also the pwm
directories will no longer show in /sys/class/pwm, but will now show in
/sys/class/pwm/pwmchipX where X is the chip number.

   - And last but not least, what is the unit of duty_cycle and period? Is
   it ns?

I'm not 100% sure, but ns sounds reasonable. However, I'm not sure the time
period matters so much, as duty_cycle is a percentage of period. So in our
circuit here, which is reverse logic. A period of 2000 with a duty_cycle of
2000 would be 100% duty_cycle. BUt I was just driving LEDs with it, not a
stepper motor or anything period critical.

Anyway, you can always hook the output up to a scope . . .

heh, sorry the link . . https://briancode.wordpress.com/2015/01/06/working-with-pwm-on-a-beaglebone-black/

Yeah, the code I wrote was Javascript. Basically I created a sysfs bit of wrapper code in Javascript for Nodejs on the Beaglebone.

I’ll probably be adding it to my Bonejs project on github. If you care to look at it for reference. But I need to write a readme.md for it first, and I’ve no idea how long that will take. Lots of stuff to do, and not enough time to do it all.

Hello William,

so I tried your approach however without success. Here is how I did it:

  • I exported the capes to sys/devices/platform/bone_capemgr/slots. This reads now as follows:
    0: PF---- -1 1: PF---- -1 2: PF---- -1 3: PF---- -1 4: P-O-L- 0 Override Board Name,00A0,Override Manuf,BB-UART1 5: P-O-L- 1 Override Board Name,00A0,Override Manuf,BB-UART5 6: P-O-L- 2 Override Board Name,00A0,Override Manuf,univ-wph
  • I changed directory and exported all pwms
    cd /sys/class/pwm for chip in *; do for i in 0 1; do echo ${i} > ${chip}/export; done ; done
  • This is what I see in one of the directories

`
root@beaglebone:/sys/class/pwm/pwmchip0/pwm0# ls

duty_cycle enable period polarity power uevent
`

  • After that i change to the /sys/class/pwm folder and set all pwms to 50% duty cycle and 100000 ns Period via the following command:

cd /sys/class/pwm for chip in *; do for i in 0 1; do echo 100000 > ${chip}/pwm${i}/period; echo 50000 > ${chip}/pwm${i}/duty_cycle; echo 1 > ${chip}/pwm${i}/enable ; done ; done

I checked, and all directories read as follows

`
root@beaglebone:/sys/class/pwm/pwmchip0/pwm0# cat enable period duty_cycle
1
100000
50000

`

However I could not produce any outputs. I measure all channels with an oscilloscope, but unfortunately no success. Do you know what I did wrong?

Best regards,
Phil

However I could not produce any outputs. I measure all channels with an oscilloscope, but unfortunately no success. Do you know what I did wrong?

No. Did you test the correct pins ? Also what is the output of:

$ ls /sys/devices/platform/ocp/

?

Hello William,

the output is:

`
root@beaglebone:/lib/firmware# ls /sys/devices/platform/ocp/
40300000.ocmcram 47400000.usb 48046000.timer 4819c000.i2c 48302000.epwmss 4a100000.ethernet modalias ocp:P9_22_pinmux
40302000.ocmcram_nocache 48022000.serial 48048000.timer 481a0000.spi 48304000.epwmss 4a300000.pruss ocp:l4_wkup@44c00000 of_node
44e07000.gpio 48030000.spi 4804a000.timer 481aa000.serial 48310000.rng 4c000000.emif ocp:P8_13_pinmux power
44e09000.serial 48038000.mcasp 4804c000.gpio 481ac000.gpio 49000000.edma 53100000.sham ocp:P8_19_pinmux subsystem
44e0b000.i2c 4803c000.mcasp 48060000.mmc 481ae000.gpio 49800000.tptc 53500000.aes ocp:P9_14_pinmux uevent
44e35000.wdt 48042000.timer 480c8000.mailbox 48200000.interrupt-controller 49900000.tptc 56000000.sgx ocp:P9_16_pinmux
44e3e000.rtc 48044000.timer 480ca000.spinlock 48300000.epwmss 49a00000.tptc driver_override ocp:P9_21_pinmux

`

I compiled your overlay with this command:

dtc -O dtb -o /lib/firmware/univ-wph-00A0.dtbo -b 0 -@ /lib/firmware/univ-wph-00A0.dts
Is that the correct way to do it?

Regarding your question whether I used the correct pins: I set all pwm outputs with the nested for commands described in the my last post and used the oscilloscope to measure all the pwm pins given in this image (http://beagleboard.org/static/images/cape-headers-pwm.png) against a GND pin. So at least one of them should have caused some output on the oscilloscope.
The oscilloscope is working correctly as it gives me 3.3V and 5V for P9_3 and P9_5.

Best regards
Phil

Hello Phil,

As I encountered the same behaviour as yours when switching to kernel 4.4, I resolved mine with the cape_universal (the default with the official image provided) with a line ‘config-pin P9_14 pwm’ for the PWM1A for ex.
But, the numbering of the PWMs differ at each booting of the bbb ! not very easy to handle, so after I saw the PWM with npwm == 2 are not the ECAP ones , I wrote this :
`
import os
import glob
from collections import OrderedDict
from pprint import pprint

os.chdir(’/sys/class/pwm/’)
chips = glob.glob(‘pwmchip*’)

pwms = [‘PWM0A’, ‘PWM0B’, ‘PWM1A’, ‘PWM1B’, ‘PWM2A’, ‘PWM2B’]
nbpwm = 0
pwm_dict = OrderedDict()

for chip in chips:
npwm = int(open(’{}/npwm’.format(chip)).read())
if npwm == 2:
for i in [0, 1]:
path = ‘/sys/class/pwm/{}/’.format(chip)
open(path+‘export’, ‘w’).write(str(i))
pwm_dict[pwms[nbpwm+i]] = ‘{}/pwm{}/’.format(path, i)
nbpwm += 2

pprint(pwm_dict)
`

which wil give for ex. :
`

`
root@beaglebone:~# ./PWM_4.4.py
{‘PWM0A’: ‘/sys/class/pwm/pwmchip0//pwm0/’,
‘PWM0B’: ‘/sys/class/pwm/pwmchip0//pwm1/’,
‘PWM1A’: ‘/sys/class/pwm/pwmchip2//pwm0/’,
‘PWM1B’: ‘/sys/class/pwm/pwmchip2//pwm1/’,
‘PWM2A’: ‘/sys/class/pwm/pwmchip4//pwm0/’,
‘PWM2B’: ‘/sys/class/pwm/pwmchip4//pwm1/’}

root@beaglebone**:~****# config-pin P9_14 pwm**
root@beaglebone:~# cd /sys/class/pwm/pwmchip2/pwm0/
root@beaglebone:/sys/class/pwm/pwmchip2/pwm0# ls
duty_cycle enable period polarity power uevent
root@beaglebone:/sys/class/pwm/pwmchip2/pwm0# echo 1000000 > period
root@beaglebone:/sys/class/pwm/pwmchip2/pwm0# echo 200000 > duty_cycle
root@beaglebone:/sys/class/pwm/pwmchip2/pwm0# echo 1 > enable
`

and I got the correct PWM on my oscilloscope, hope it helps, good luck !

Samuel

`

Hello Samuel

I too faced came issues in 4.4 and managed to get PWM working.

Now, I need to implement this in C++. The pwmchipX number keeps on changing for my desired PWM channel.

Could you please suggest me a way to overcome this?

Easy:

don't use /sys/pwm/pwmchipX, instead use the /sys/devices directory:

/sys/devices/platform/ocp/48300000.epwmss/48300200.pwm/pwm/pwmchip*/

and ignore the "X"...

https://github.com/beagleboard/pocketbeagle/wiki/Peripherals

Regards,

Thanks !
This will do better.

Hi

Thanks for you help.

I entered the following path in my c++ program :

PWM red_pwm("48304000.epwmss/48304200.pwm/pwm/pwmchip*/pwm0");

When I execute the code, I get this error:

GPIO: read failed to open file : No such file or directory

I guess its due to the wildcard * used in file path, because when I replace * with the corresponding number, the code works and I get PWM signal.

What should I do to solve this?

Thanks & Regards,
Chirag Pooniwala

I am using PWM library by Derek Molloy with few modifications in the file path.

They are as follows

PWM.h

`
#ifndef PWM_H_
#define PWM_H_
#include
using std::string;

#define PWM_PATH “/sys/devices/platform/ocp/”
#define PWM_PERIOD “period”
#define PWM_DUTY “duty_cycle”
#define PWM_POLARITY “polarity”
#define PWM_RUN “enable”

namespace exploringBB {

class PWM {
public:
enum POLARITY{ ACTIVE_LOW=0, ACTIVE_HIGH=1 };

private:
string name, path;
float analogFrequency; //defaults to 100,000 Hz
float analogMax; //defaults to 3.3V

public:
PWM(string pinName);

virtual int setPeriod(unsigned int period_ns);
virtual unsigned int getPeriod();
virtual int setFrequency(float frequency_hz);
virtual float getFrequency();
virtual int setDutyCycle(unsigned int duration_ns);
virtual int setDutyCycle(float percentage);
virtual unsigned int getDutyCycle();
virtual float getDutyCyclePercent();

virtual int setPolarity(PWM::POLARITY);
virtual void invertPolarity();
virtual PWM::POLARITY getPolarity();

virtual void setAnalogFrequency(float frequency_hz) { this->analogFrequency = frequency_hz; }
virtual int calibrateAnalogMax(float analogMax); //must be between 3.2 and 3.4
virtual int analogWrite(float voltage);

virtual int run();
virtual bool isRunning();
virtual int stop();

virtual ~PWM();
private:
float period_nsToFrequency(unsigned int);
unsigned int frequencyToPeriod_ns(float);
};

} /* namespace exploringBB */

#endif /* PWM_H_ */
`

PWM.cpp

`
#include “PWM.h”
#include “util.h”
#include

namespace exploringBB {

PWM::PWM(string pinName) {
this->name = pinName;
this->path = PWM_PATH + this->name + “/”;
this->analogFrequency = 100000;
this->analogMax = 3.3;
}

int PWM::setPeriod(unsigned int period_ns){
return write(this->path, PWM_PERIOD, period_ns);
}

unsigned int PWM::getPeriod(){
return atoi(read(this->path, PWM_PERIOD).c_str());
}

float PWM::period_nsToFrequency(unsigned int period_ns){
float period_s = (float)period_ns/1000000000;
return 1.0f/period_s;
}

unsigned int PWM::frequencyToPeriod_ns(float frequency_hz){
float period_s = 1.0f/frequency_hz;
return (unsigned int)(period_s*1000000000);
}

int PWM::setFrequency(float frequency_hz){
return this->setPeriod(this->frequencyToPeriod_ns(frequency_hz));
}

float PWM::getFrequency(){
return this->period_nsToFrequency(this->getPeriod());
}

int PWM::setDutyCycle(unsigned int duty_ns){
return write(this->path, PWM_DUTY, duty_ns);
}

int PWM::setDutyCycle(float percentage){
if ((percentage>100.0f)||(percentage<0.0f)) return -1;
unsigned int period_ns = this->getPeriod();
float duty_ns = period_ns * (percentage/100.0f);
this->setDutyCycle((unsigned int) duty_ns );
return 0;
}

unsigned int PWM::getDutyCycle(){
return atoi(read(this->path, PWM_DUTY).c_str());
}

float PWM::getDutyCyclePercent(){
unsigned int period_ns = this->getPeriod();
unsigned int duty_ns = this->getDutyCycle();
return 100.0f * (float)duty_ns/(float)period_ns;
}

int PWM::setPolarity(PWM::POLARITY polarity){
return write(this->path, PWM_POLARITY, polarity);
}

void PWM::invertPolarity(){
if (this->getPolarity()==PWM::ACTIVE_LOW) this->setPolarity(PWM::ACTIVE_HIGH);
else this->setPolarity(PWM::ACTIVE_LOW);
}

PWM::POLARITY PWM::getPolarity(){
if (atoi(read(this->path, PWM_POLARITY).c_str())==0) return PWM::ACTIVE_LOW;
else return PWM::ACTIVE_HIGH;
}

int PWM::calibrateAnalogMax(float analogMax){ //must be between 3.2 and 3.4
if((analogMax<3.2f) || (analogMax>3.4f)) return -1;
else this->analogMax = analogMax;
return 0;
}

int PWM::analogWrite(float voltage){
if ((voltage<0.0f)||(voltage>3.3f)) return -1;
this->setFrequency(this->analogFrequency);
this->setPolarity(PWM::ACTIVE_LOW);
this->setDutyCycle((100.0f*voltage)/this->analogMax);
return this->run();
}

int PWM::run(){
return write(this->path, PWM_RUN, 1);
}

bool PWM::isRunning(){
string running = read(this->path, PWM_RUN);
return (running==“1”);
}

int PWM::stop(){
return write(this->path, PWM_RUN, 0);
}

PWM::~PWM() {}

} /* namespace exploringBB */
`

util.cpp
`
#include “util.h”
#include
#include
#include
using namespace std;

namespace exploringBB {

/**

  • Helper write function that writes a single string value to a file in the path provided
  • @param path The sysfs path of the file to be modified
  • @param filename The file to be written to in that path
  • @param value The value to be written to the file
  • @return
    /
    int write(string path, string filename, string value){
    ofstream fs;
    fs.open((path + filename).c_str());
    if (!fs.is_open()){
    perror("GPIO: write failed to open file ");
    return -1;
    }
    fs << value;
    fs.close();
    return 0;
    }
    /
    *
  • Helper read function that reads a single string value to a file from the path provided
  • @param path The sysfs path of the file to be read
  • @param filename Filename The file to be written to in that path
  • @return
    */
    string read(string path, string filename){
    ifstream fs;
    fs.open((path + filename).c_str());
    if (!fs.is_open()){
    perror("GPIO: read failed to open file ");
    }
    string input;
    getline(fs,input);
    fs.close();
    return input;
    }

/**

  • Private write method that writes a single int value to a file in the path provided
  • @param path The sysfs path of the file to be modified
  • @param filename The file to be written to in that path
  • @param value The int value to be written to the file
  • @return
    */
    int write(string path, string filename, int value){
    stringstream s;
    s << value;
    return write(path,filename,s.str());
    }

} /* namespace exploringBB */

`

Maybe the server changed just as you were doing an `apt-get update`?
(Or maybe just as the mirror you're using updated?) Try again tomorrow
and see if it still happens, or maybe try a different mirror.

If you're using a proxy, check that isn't caching a stale result.