Pulse-width modulation (PWM) is a technique that modifies the duty cycle of a pulsing signal to encode information or to control the amount of energy provided to a charge.

On the ConnectCore MP25 system-on-module:

  • Up to three Advanced-control timers signals (TIM1, TIM8 and TIM20 ) available from the STM32MP25 system-on-chip (multiplexed with other signals) that can be used to generate PWM output waveform.

  • Up to twelve General-purpose timers signals (from TIM2 to TIM5 and from TIM10 to TIM17) available from the STM32MP25 system-on-chip (multiplexed with other signals) that can be used to generate PWM output waveform.

On the ConnectCore MP25 Development Kit:

  • PWM20 channel 4 (Port B GPIO0) is used for display backlight.

  • PWM2 channel 4 (Port F GPIO0) is available at PWM pin of MikroBus connector.

Kernel configuration

You can manage the STM32MP25 PWM driver support through the following kernel configuration options:

  • PWM support (CONFIG_PWM)

  • STM32 PWM support (CONFIG_PWM_STM32)

  • STM32 PWM low power support (CONFIG_PWM_STM32_LP)

These options are enabled as built-in on the default ConnectCore MP25 kernel configuration file.

Kernel driver

The driver for the STM32MP25 PWM is located at:

File Description

drivers/pwm/pwm-stm32.c

STM32 PWM driver

drivers/pwm/pwm-stm32-lp.c

STM32 PWM LP driver

Device tree bindings and customization

The STM32MP25 PWM interface is documented at Documentation/devicetree/bindings/mfd/st,stm32-timers.yaml.

The STM32MP25 PWM LP interface is documented at Documentation/devicetree/bindings/mfd/st,stm32-lptimer.yaml.

STM32MP25 PWM interfaces

The common STM32MP25 CPU device tree file contains entries for all the timers that can have PWM functionality (excerpt shows a few):

STM32MP25 device tree
timers2: timer@40000000 {
	compatible = "st,stm32mp25-timers";
	reg = <0x40000000 0x400>;
	interrupts = <GIC_SPI 105 IRQ_TYPE_LEVEL_HIGH>;
	interrupt-names = "global";
	clocks = <&rcc CK_KER_TIM2>;
	clock-names = "int";
	#address-cells = <1>;
	#size-cells = <0>;
	feature-domains = <&rifsc STM32MP25_RIFSC_TIM2_ID>;
	power-domains = <&CLUSTER_PD>;
	status = "disabled";

	pwm {
		compatible = "st,stm32mp25-pwm";
		#pwm-cells = <3>;
		status = "disabled";
	};

	timer@1 {
		compatible = "st,stm32mp25-timer-trigger";
		reg = <1>;
		status = "disabled";
	};

	counter {
		compatible = "st,stm32mp25-timer-counter";
		status = "disabled";
	};
};

lptimer1: timer@40090000 {
	compatible = "st,stm32mp25-lptimer";
	reg = <0x40090000 0x400>;
	interrupts = <GIC_SPI 166 IRQ_TYPE_LEVEL_HIGH>;
	clocks = <&rcc CK_KER_LPTIM1>;
	clock-names = "mux";
	#address-cells = <1>;
	#size-cells = <0>;
	feature-domains = <&rifsc STM32MP25_RIFSC_LPTIM1_ID>;
	power-domains = <&RET_PD>;
	status = "disabled";

	counter {
		compatible = "st,stm32mp25-lptimer-counter";
		status = "disabled";
	};

	pwm {
		compatible = "st,stm32mp25-pwm-lp";
		#pwm-cells = <3>;
		status = "disabled";
	};

	timer {
		compatible = "st,stm32mp25-lptimer-timer";
		status = "disabled";
	};

	trigger@0 {
		compatible = "st,stm32mp25-lptimer-trigger";
		reg = <0>;
		status = "disabled";
	};
};

PINCTRL configuration

You must configure the pads that are to be used as STM32MP25 PWMs. See Pin multiplexing (IOMUX).

STM32MP25 pads should only have one IOMUX configuration. Remove other configurations for those pads, like GPIO, when configuring them as PWMs.

The following external pads are configured as PWMs on the default ConnectCore MP25 Development Kit device tree:

  • On the LVDS connector:

    Pad Signal PWM

    16

    BCKL_PWM

    PWM20 channel 4 (PWM20_CH4)

  • On the mikroBUS™ connector J40:

    Pad Signal PWM

    1

    MIKROBUS_PWM

    PWM2 channel 4 (PWM2_CH4)

Example: enable PWM2 on ConnectCore MP25 Development Kit

For example, PWM2 is available on the ConnectCore MP25 Development Kit MikroBus connector which corresponds to pad Port F GPIO0 (PWM2_CH4) of the CPU. In order to enable it, you must:

  • Configure the pad Port F GPIO0 (PWM2_CH4)

  • Enable the PWM node.

ConnectCore MP25 Development Kit device tree
&timers2 {
	/delete-property/dmas;
	/delete-property/dma-names;
	status = "okay";
	pwm_mikrobus: pwm {
		pinctrl-0 = <&ccmp25_pwm2_pins>;
		pinctrl-1 = <&ccmp25_pwm2_sleep_pins>;
		pinctrl-names = "default", "sleep";
		status = "okay";
	};
	timer@1 {
		status = "okay";
	};
};

&pinctrl {
	ccmp25_pwm2_pins: ccmp25-pwm2-0 {
		pins {
			pinmux = <STM32_PINMUX('F', 11, AF7)>; /* TIM2_CH4 */
			bias-disable;
			drive-push-pull;
			slew-rate = <0>;
		};
	};

	ccmp25_pwm2_sleep_pins: ccmp25-pwm2-sleep-0 {
		pins {
			pinmux = <STM32_PINMUX('F', 11, ANALOG)>; /* TIM2_CH4 */
		};
	};
};

Using the PWM channels

Control PWM signal from sysfs

Each of the {number-of-timers} TIM interfaces is registered in the system as a standalone PWM controller.

On the ConnectCore MP25 Development Kit:

  • PWM2 is enabled (used for backlight control of LCD and LVDS displays)

  • PWM2 is enabled on pin 1 of MikroBus™ connector (J40)

  • All other PWM channels are disabled by default

The PWM interfaces appear under /sys/class/pwm:

# ls /sys/class/pwm/
pwmchip0

The PWM interfaces begin numbering with an index of 0.

Each PWM interface can manage several PWM channels. Check the number of channels of an interface by printing the value of npwm:

# cat /sys/class/pwm/pwmchip0/npwm
4

To access a channel of the PWM interface, write the channel index to the export descriptor. Notice the hardware manuals name the channels starting from 1 but Linux indexes start at 0. To access channel 1, write 0 to the export descriptor:

# echo 0 > /sys/class/pwm/pwmchip0/export
To get a PWM output on a given pad, the selected channel must match the pinctrl functionality for that pad. For instance, in the example above, pad at Port F GPIO0 can function as PWM 2 channel 4, thus the 0 on the command. Exporting a different channel would require to use a different pinctrl on a different pad.
You won’t be able to request PWM channels that are in use by other drivers, like the one used by the backlight.

Now you can access the PWM channel and configure its settings:

# ls /sys/class/pwm/pwmchip0/pwm0/
duty_cycle  enable      period      polarity    power       uevent

Period and duty cycle must be given in nanoseconds. For example, to configure a 100kHz signal with 20% duty cycle:

# echo 10000 > /sys/class/pwm/pwmchip0/pwm0/period
# echo 2000 > /sys/class/pwm/pwmchip0/pwm0/duty_cycle

To enable the PWM signal:

# echo 1 > /sys/class/pwm/pwmchip0/pwm0/enable

The default polarity is normal (active high for the duty cycle). To invert the polarity:

# echo inversed > /sys/class/pwm/pwmchip0/pwm0/polarity

Using Digi APIx library from a C application

An example application called apix-pwm-example is included in the dey-examples-digiapix recipe (part of dey-examples package) of the meta-digi layer. This application shows how to generate a PWM signal using Digi APIx library on the ConnectCore MP25 platform.

Go to GitHub to see the application instructions and source code.

See PWM API for more information about the PWM APIx.

See also