I have tried the dtoverlay=pwm but couldn't seem to get PWM working. It puts the pin/s in PWM mode and creates /sys/class/pwm/pwmchip0 but no signal.

The dtoverlay description states 4) Currently the clock must have been enabled and configured by other means. but I have failed to find any "other means" or documentation explaining how to get it to generate a PWM signal.

1 Answer 1


I recently discovered some kernel documentation Pulse Width Modulation (PWM) interface

Like much kernel documentation this is inscrutable. It states This API controls both the PWM period/duty_cycle config and the enable/disable state.

I will attempt to explore this in future but there is also Using PWMs with the sysfs interface which enabled me to get it working.

  1. Load the overlay pwm (or pwm-2chan) with dtoverlay=pwm in config.sys or on-the-fly with sudo dtoverlay pwm
    You can use sudo dtoverlay pwm pin=12 func=4 to use GPIO12 rather than the default GPIO18.

  2. Export a PWM channel for use with sysfs.

  3. Set the period (in nanoseconds)

  4. Set the duty_cycle (in nanoseconds)

  5. Enable the PWM signal

The code below is testing code as part of my development process.
All listed code works but has minimal error checking and may not be the most elegant or efficient.
Only PWM0 code is listed but PWM1 also works (with appropriate changes).

The following script sets a single PWM output on pwm0 (pin12 GPIO18 or pin32 GPIO12).

#! /bin/sh
# Export channel 0
# Set the period 1,000,000 nS (1kHz)
# Set the duty_cycle 50%
# Enable the PWM signal
# 2023-07-06

cd /sys/class/pwm/pwmchip0
echo 0 > export
sleep 0.1
echo 10000000 > pwm0/period
echo  5000000 > pwm0/duty_cycle 
echo 1 > pwm0/enable

echo 0 > /sys/class/pwm/pwmchip0/pwm0/enable can be used to stop PWM.

I actually used a c program to setup both channels

#include <unistd.h>

#define PWM_SYSFS "/sys/class/pwm/pwmchip0/"
#define PWM_PATH0 "/sys/class/pwm/pwmchip0/pwm0/"
#define PWM_PATH1 "/sys/class/pwm/pwmchip0/pwm1/"

void writeSYS(char filename[], char value[]){
    FILE* fp;           // create a file pointer fp
    fp = fopen(filename, "w");  // open file for writing
    fprintf(fp, "%s", value);   // send the value to the file
    fclose(fp);         // close the file using fp

int main(int argc, char* argv[]){
    int chan = 0;
    writeSYS(PWM_SYSFS "export", "0");  // export PWM channel (0)
    writeSYS(PWM_SYSFS "export", "1");  // export PWM channel (1)

    usleep(100000);                 // sleep for 100ms
    writeSYS(PWM_PATH0 "period" , "1000000");   // set period
    writeSYS(PWM_PATH0 "duty_cycle" , "250000");    // set duty_cycle
    writeSYS(PWM_PATH0 "enable" , "1");     // enable
    writeSYS(PWM_PATH1 "period" , "50000");     // set period
    writeSYS(PWM_PATH1 "duty_cycle" , "25000"); // set duty_cycle
    writeSYS(PWM_PATH1 "enable" , "1");     // enable

To use the following python code on PWM1 change pwm_channel = '1'

#! /usr/bin/env python3
# 2023-07-09
Export channel (0 or 1)
Set the period 1,000,000 ns (1kHz)
Set the duty_cycle 50%
Enable the PWM signal
pwm_channel = '0'
PERIOD = 1000000 

import sys, os, time
PWM_SYSFS = "/sys/class/pwm/pwmchip0"

if not os.path.isdir(PWM_SYSFS) :
    print("Overlay not loaded")

pwm_dir = f"{PWM_SYSFS}/pwm{pwm_channel}"
if(not os.access(pwm_dir, os.F_OK)) :
    with open(f"{PWM_SYSFS}/export", "w") as f:

with open(os.path.join(pwm_dir, "period"), "w") as f:
with open(os.path.join(pwm_dir, "duty_cycle"), "w") as f:
with open(os.path.join(pwm_dir, "enable"), "w") as f:

I have recently been advised that only the GPIO sysfs has been deprecated but the PWM sysfs interface is still supported.
The sysfs has been deprecated and "replaced" by a userspace char device interface introduced with Linux 4.8 which is intended to replace the Sysfs interface /sys/class/gpio. Unfortunately this has limited functionality and is little used for gpio.
As of kernel 6.1 sysfs remains but the possibility of its removal still exists. There seems to be no userspace functionality for PWM like I²C and SPI.


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.