PWM issue in Beagle Bone Black using C program

Hi, I am trying to execute a C program which should increase and decrease the brightness of an LED connected to P9 pin 22 of Beaglebone Black. When I am executing it, the LED’s brightness doesn’t change and the LED is always on with semi power and am not sure if the program is working. Please help me with the correct code. The code that I have used is as below:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#define PWM_PERIOD "/sys/class/pwm/pwmchip0/pwm-0:0/period"
#define PWM_DUTY_CYCLE "/sys/class/pwm/pwmchip0/pwm-0:0/duty_cycle"
#define PWM_ENABLE "/sys/class/pwm/pwmchip0/pwm-0:0/enable"

void set_pwm_period(int period)
{
    FILE *fp = fopen(PWM_PERIOD, "w");
    if (fp == NULL) {
        perror("Failed to open PWM period");
        exit(1);
    }
    fprintf(fp, "%d", period);
    fclose(fp);
}

void set_pwm_duty_cycle(int duty_cycle)
{
    FILE *fp = fopen(PWM_DUTY_CYCLE, "w");
    if (fp == NULL) {
        perror("Failed to open PWM duty cycle");
        exit(1);
    }
    fprintf(fp, "%d", duty_cycle);
    fclose(fp);
}

void enable_pwm()
{
    FILE *fp = fopen(PWM_ENABLE, "w");
    if (fp == NULL) {
        perror("Failed to open PWM enable");
        exit(1);
    }
    fprintf(fp, "1");
    fclose(fp);
}

void disable_pwm()
{
    FILE *fp = fopen(PWM_ENABLE, "w");
    if (fp == NULL) {
        perror("Failed to open PWM enable");
        exit(1);
    }
    fprintf(fp, "0");
    fclose(fp);
}

int main()
{
    int duty_cycle = 0;
    int increment = 1000;

    set_pwm_period(1000000); // Set PWM period to 1ms (1 kHz)

    enable_pwm();

    while (1) {
        set_pwm_duty_cycle(duty_cycle);
        usleep(1000000); // Sleep for 10ms
        printf("Changing\n");
        duty_cycle += increment;
        if (duty_cycle >= 1000000 || duty_cycle <= 0) {
            increment = -increment;
        }
    }

    disable_pwm();

    return 0;
}
1 Like

Hello,

Depending on the kernel and image used, one might find the Compatibility Layer used or the /dev/bone/pwm/ files used.

For older files and firmware for the BBB, I would say that the using gpioinfo to track down what is what may be useful.

Also…what does the output of this command state? ls -l /sys/class/pwm/pwmchip0/*

There has been some changes made recently and throughout history while I have been using their boards to learn onward. I still use them.

Seth

Update

Also…

  1. ls -l /dev/bone/pwm/* will provide extra support for people looking to help.
  2. cat /etc/dogtag
  3. uname -a

The output of those commands will help too. config-pin -q p9.22 may show you what is going on for that pin and then config-pin p9.22 pwm will mux that pin to pwm.

Here’s my take on getting that pwm running. I have an amp and piezo element attached to it, so ignore that. What you probably are looking for can be found in the summary block. And it isn’t perfect. I have on my “wouldn’t it be nice” list to figure out why It sometimes quits when I start pushing the boundaries regarding the period and duty cycle.

Oh, and take note that duty cycle is really the on time for each period and not a percentage of the period.

///


/// This section covers the functionality of the sounder
/// The following commands will perform action(s) similar to this function:
/// config-pin p9.42 pwm
/// echo 0 /sys/class/pwm/pwmchip0/export
/// echo 1000000 > /sys/class/pwm/pwmchip0/pwm0/period
/// echo 500000 > /sys/class/pwm/pwmchip0/pwm0/duty_cycle
/// echo 1 > /sys/class/pwm/pwmchip0/pwm0/enable
/// [at this point the sounder squeals like a square wave]
/// echo 0 > /sys/class/pwm/pwmchip0/pwm0/enable
/// [at this point the sounder should go quiet]
///

/// The frequency to drive the sounder at
/// The duration of time to drive the sounder
/// reports the current board temp to stdout
void do_beep(float frequency,float duration)
{
long int period;
period = (long int)((float)1000000000.0 / frequency);
struct timespec sNapPeriod, sNapRemaining;
char periodstring[20],dutystring[20];
snprintf(periodstring,80, “%ld”, period);
snprintf(dutystring, 20, “%ld”, (period / 2));
// set up a nap time to pace pwm configuration
sNapPeriod.tv_sec = 0;
sNapPeriod.tv_nsec = (long) 10000000; // 10 million nanosecond equals 0.01 seconds
sNapRemaining.tv_sec = 0;
sNapRemaining.tv_nsec = 0;

// The PWM is controlled through the /sys interface.  The following is a link to
// a source for one of the stock beaglebone libraries that involve PWM, not to mention
// a cape that we're not using.  The bad news is that the controlling path(s) for these
// devices changes relative to the version of the kernel.
// For example: going from 4.19.94-ti-r42 to 4.19.232-bone75, the controlling paths change
// /sys/class/pwm/pwmchip0/pwm-0:0/ changes to /sys/class/pwm/pwmchip0/pwm0/
// There are libraries that involve accessing them.  Reviewing their source, however, shows that
// they are tied to the kernel version being used at the time  the following link shows how
// the librobotcontrol does their manipulation of the PWM.
// https://git.beagleboard.org/beagleboard/librobotcontrol/-/blob/master/library/src/io/pwm.c

// Set P9.42 to PWM mode, if not already done.
//This is effectively what system("config-pin P9_42 pwm") does
int pwmfio;
if (access("/sys/devices/platform/ocp/ocp:P9_42_pinmux/state", F_OK) != 0)
{
    printf("ERROR! Pinmux control for the sounder's gpio line is absent!");
}
else
{
    if ((access("/sys/devices/platform/ocp/ocp:P9_42_pinmux/state", W_OK) != 0)
        || (access("/sys/class/pwm/pwmchip0/export", W_OK) != 0))
    {
        printf("Sorry, write permissions denied for pwm setup, Try running as root\n");
    }
    else
    {
        nanosleep(&sNapPeriod, &sNapRemaining);
        pwmfio = open("/sys/devices/platform/ocp/ocp:P9_42_pinmux/state", O_WRONLY);
        write(pwmfio, "pwm", 3);
        close(pwmfio);


        nanosleep(&sNapPeriod, &sNapRemaining);
        pwmfio = open("/sys/class/pwm/pwmchip0/export", O_WRONLY);
        write(pwmfio, "0", 1);
        close(pwmfio);

        // Set the period in nanoseconds.  1 million should give 1 kHz
        pwmfio = open("/sys/class/pwm/pwmchip0/pwm-0:0/period", O_WRONLY);
        write(pwmfio, periodstring, 20);
        close(pwmfio);

        nanosleep(&sNapPeriod, &sNapRemaining);
        // set the duty cycle to half the period to get 50% square wave
        pwmfio = open("/sys/class/pwm/pwmchip0/pwm-0:0/duty_cycle", O_WRONLY);
        write(pwmfio, dutystring, 20);
        close(pwmfio);

        // set enable to 1 to get it to start making noise.
        pwmfio = open("/sys/class/pwm/pwmchip0/pwm-0:0/enable", O_WRONLY);
        write(pwmfio, "1", 1);
        close(pwmfio);

        // set up a nap and wait a couple seconds to annoy the user
        sNapPeriod.tv_sec = (time_t)trunc(duration);
        sNapPeriod.tv_nsec = (long)(fmod(duration, 1.0) * 1000000000.0); // 100 million nanosecond equals 0.1 seconds 
        sNapRemaining.tv_sec = 0;
        sNapRemaining.tv_nsec = 0;

        nanosleep(&sNapPeriod, &sNapRemaining);

        // shut off the pwm to quiet the speaker
        pwmfio = open("/sys/class/pwm/pwmchip0/pwm-0:0/enable", O_WRONLY);
        write(pwmfio, "0", 1);
        close(pwmfio);
    }
}

} // end of do_beep()