Skip to main content
Version: Torizon OS 6.x.y

How to Use PWM on Torizon OS

Introduction

PWMs are frequently used to control the power delivered by electrical signals to LEDs and motors. That requires a software layer to connect the OS system calls to the hardware. This article describes how to use PWMs on Torizon OS from user space through the sysfs interface. Therefore, it does not cover specific device driver usage. For further information, see Peripheral Access Overview.

This article provides the following sections:

This article complies with the Typographic Conventions for Torizon Documentation.

Prerequisites

Connect Devices to PWM pins

Toradex SoMs offer multiple PWM pins for connecting peripherals, with some pins having PWM as their default or alternative function. Prioritize using pin functions compatible with all modules within the same family, ensuring the best compatibility with standard software and other modules in the family. To change pin functionalities, refer to the Pin Multiplexing article.

In addition, some standard PWM interfaces may be dedicated to specific uses, such as controlling LCD backlight intensity. To ensure compatibility and prevent issues, use PWM designed for general purposes. If necessary, you can modify the behavior of these interfaces. For detailed information, see Device Tree Overlays Overview.

You can find pin information in the SoM and carrier board datasheets.

danger

Always check the IO voltage level of your modules and peripherals before connecting them. The TTL level for Colibri and Apalis IO is 3.3V, while for Verdin it is 1.8V. Do not apply 3.3V signals to Verdin IO.

The Verdin iMX8M Plus module provides multiple PWM buses, including the PWM_1, which is designed for general use and is the one we will use in examples throughout this article. You can find this information in the PWM section of your module datasheet as follows:

X1 Pin#Verdin Std Functioni.MX 8MP Ball Namei.MX 8MP FunctionI/ODescription
15PWM_1SPDIF_EXT_CLKPWM1_OUTOGeneral-purpose PWM
16PWM_2GPIO1_IO11PWM2_OUTOGeneral-purpose PWM

To connect your device, search for the pin that carries the PWM_1 signal in your carrier board schematics or datasheet.

Backlight Control

Toradex modules have all PWM channels enabled by default. Some modules have dedicated a PWM channel for backlight control. You can use that channel for a different purpose if the backlight is not required.

To free up the PWM channel, you can remove backlight support by deleting the backlight node in the device tree file.

/delete-node/ backlight;

For detailed information, refer to the Device Tree Technical Overview article.

PWM Usage from Kernel Space

Depending on your use case, you may need to activate a device driver for controlling a peripheral connected to a PWM bus. In such cases, look for available drivers or add drivers to OS images. For additional information, see Device Drivers.

To activate a driver, proceed with the following steps:

  1. Identify and test a PWM bus on Torizon OS by following the steps presented on PWM Usage from User Space.

  2. Apply a device tree overlay that modifies the PWM bus node.

PWM Usage from User Space

Torizon OS, as a Linux-based distribution, treats peripheral devices as files. The PWM files are exposed through the sysfs interface in the /sys/class/pwm directory. This section explains how to locate PWM buses by their address and interact with them using the sysfs interface. You can also use multimeters or oscilloscopes to ensure that pins work as expected.

The steps below describe how to locate the PWM_1 bus of the Verdin iMX8M Plus in the OS:

  1. Identify the pin control group that contains the PWM pin: You can find this information in the module's device tree.

    The following code snippet indicates that the pinctrl_pwm_1 control group contains the SPDIF_EXT_CLK pin with the PWM1_OUT function.

    imx8mp-verdin.dtsi
    pinctrl_pwm_1: pwm1grp {
    fsl,pins =
    <MX8MP_IOMUXC_SPDIF_EXT_CLK__PWM1_OUT 0x6>; /* SODIMM 15 */
    };
  2. Locate the bus node that corresponds to the identified pin control group.

    The snippet below shows that the pwm1 node uses the pinctrl_pwm_1 pin control group.

    imx8mp-verdin.dtsi
    /* Verdin PWM_1 */
    &pwm1 {
    pinctrl-names = "default";
    pinctrl-0 = <&pinctrl_pwm_1>;
    #pwm-cells = <3>;
    };
  3. Search for the corresponding register address in the root node: The reg property contains the PWM address.

    In the following example, the address is 0x30660000.

    imx8mp.dtsi
    pwm1: pwm@30660000 {
    compatible = "fsl,imx8mp-pwm", "fsl,imx27-pwm";
    reg = <0x30660000 0x10000>;
    interrupts = <GIC_SPI 81 IRQ_TYPE_LEVEL_HIGH>;
    clocks = <&clk IMX8MP_CLK_PWM1_ROOT>,
    <&clk IMX8MP_CLK_PWM1_ROOT>;
    clock-names = "ipg", "per";
    #pwm-cells = <2>;
    status = "disabled";
    };
  4. List the available PWM pins: In your module's terminal, run the following command:

    # sudo cat /sys/kernel/debug/pwm

    The output below shows there are three PWMs available. Among them, it is possible to identify the PWM_1 bus, which has the 0x30660000 address.

    platform/30680000.pwm, 1 PWM device
    pwm-0 ((null) ): period: 0 ns duty: 0 ns polarity: normal

    platform/30670000.pwm, 1 PWM device
    pwm-0 ((null) ): period: 0 ns duty: 0 ns polarity: normal

    platform/30660000.pwm, 1 PWM device
    pwm-0 ((null) ): period: 0 ns duty: 0 ns polarity: normal

    Those shown as null are either not exported or in use by a driver.

  5. Export a PWM using the sysfs interface: PWM bus files are named pwmchip<number>.

    For example:

    # echo 0 > /sys/class/pwm/pwmchip0/export

    For additional information, see Using PWMs with the sysfs interface.

  6. Check if the address of the exported PWM matches the selected bus: List the available PWM pins again.

    # sudo cat /sys/kernel/debug/pwm
    platform/30680000.pwm, 1 PWM device
    pwm-0 ((null) ): period: 0 ns duty: 0 ns polarity: normal

    platform/30670000.pwm, 1 PWM device
    pwm-0 ((null) ): period: 0 ns duty: 0 ns polarity: normal

    platform/30660000.pwm, 1 PWM device
    pwm-0 (sysfs ): requested period: 2730667 ns duty: 0 ns polarity: normal

    The PWM_1 appears as sysfs, which means it is exported to userspace control and ready to use.

Container Access

To access the PWM files created by the sysfs interface within the container, you have to mount the directory into the container using the -v flag as follows:

# docker run --rm -it -v sys/class/pwmchip<number>:<container-path> <container-image>

For comprehensive information, see Manage data in Docker.

Shell Example

This section shows an example of changing a PWM frequency and duty cycle using Linux shell commands from within a container.

  1. Run a Debian Container for Torizon: Do not forget to mount the device data folder into the container.

    # docker run -it --rm \
    -v /sys/class/pwm/pwmchip2/:/PWM_1 \
    --mount type=bind,source='/sys/kernel/debug/pwm',target='/pwm' \
    torizon/debian:${CT_TAG_DEBIAN}
  2. Once inside the container, locate the device files.

    ## ls /PWM_1
    device export npwm power subsystem uevent unexport
  3. Configure the PWM parameters:

    Export a PWM channel for use with sysfs.

    ## echo 0 > /PWM_1/export

    List the PWM channel files.

    ## ls /PWM_1/pwm0
    capture duty_cycle enable period polarity power uevent

    Set the PWM period and duty cycle, expressed in nanoseconds. The following commands set a period of 1 kHz and a duty cycle of 50%.

    ## echo 1000000 > /PWM_1/pwm0/period
    ## echo 500000 > /PWM_1/pwm0/duty_cycle

    Enable the PWM.

    ## echo 1 > /PWM_1/pwm0/enable

    Finally, if you list the PWMs, you will get the following output:

    ## sudo cat /pwm
    platform/30680000.pwm, 1 PWM device
    pwm-0 ((null) ): period: 0 ns duty: 0 ns polarity: normal

    platform/30670000.pwm, 1 PWM device
    pwm-0 ((null) ): period: 0 ns duty: 0 ns polarity: normal

    platform/30660000.pwm, 1 PWM device
    pwm-0 (sysfs ): requested period: 1000000 ns duty: 500000 ns polarity: normal

    The preceding output confirms that the PWM bus is enabled and working with the configured parameters.

C Example

This section provides a step-by-step sample-based guide on using the sysfs interface for interacting with PWM buses in C. Note that while the steps in this approach vary by languages/frameworks and use cases, they provide a general idea of working with peripherals in user space.

The application uses C file-handling functions to configure and enable the PWM. If you set the frequency to 1 kHz and the duty cycle to 50%, you will get the following output:

frequency is 1000000 Hz
duty cycle is 50.00%

You can also check the results by listing the available PWMs as explained in the PWM Usage from User Space section. For example:

# sudo cat /sys/kernel/debug/pwm
platform/30660000.pwm, 1 PWM device
pwm-0 (sysfs ): requested period: 1000000 ns duty: 500000 ns polarity: normal

There are two ways to set the PWM parameters:

  • Modify the source code. The following code snippet shows the creation of frequency and duty cycle macros:

  • Pass the frequency and duty cycle as program arguments:

    pwmC <Frequency (Hz)> <DUTY_CYCLE (0-100 %)>

To use this sample, perform the following steps:

  1. Download the source code from GitHub: This application is part of the Toradex-provided Samples.

  2. Modify the source code to match the PWM bus you are using: The second commit shows the modifications made to a Torizon IDE Extension template to use PWM buses.

    For example, if using pwmchip1, you would modify the code as follows:

    - char pwm[25] = "/sys/class/pwm/pwmchip0/";
    + char pwm[25] = "/sys/class/pwm/pwmchip1/";
  1. Build and run the container. On Torizon OS, there are two possible approaches:


Send Feedback!