Pulse Width Modulation (PWM) of the SAMA5D2 Series ARM® Cortex®-A5 Microprocessor Unit (MPU) in the Linux® Kernel

Last modified by Microchip on 2023/11/10 11:03


In this article, you will learn how to configure and control the Pulse Width Modulation (PWM) pins of the SAMA5D2 Series ARM® Cortex®-A5 Microprocessor Unit (MPU) in the Linux® kernel to control LEDs, motors, and other devices.

You will use the ATSAMA5D27-SOM1-EK1. The ATSAMA5D27 SOM1 contains two 3-channel 32-bit Timer/Counters (TC), supporting basic PWM modes, and one full-featured 4-channel 16-bit PWM.

By default, the Device Trees configure the PWM controller to the RGB LED D5. We show you how to edit the Device Trees to control the PWM pins of the mikroBUS connectors.

Since the generic PWM framework was introduced into the Linux kernel, it is easy to implement the driver for a PWM device and access it in user space via sysfs.

Back to Top


You'll need to load a bootable image for the ATSAMA5D27-SOM1-EK1 development platform:

Reference Material

For this article, you will be using the:

PWM Overview

The PWM controller can control either the RGB LED D5 (default) or the PWM_mBUS1 and PWM_mBUS2 pins of the mikroBUS 1 expansion socket.

Note in the connection accompanying tables, that two of the PWM pins, PB1 and PA31, are used for both the mikroBUS connectors and the RGB LED D5. By default, the Device Trees connect the PWM pins to the RGB LED. You can configure which device is controlled by the PWM pins by editing and recompiling the Device Trees. We will show you how.

This figure shows the expansion capability of the SOM1-EK1.

Expansion features of the ATSAMA5D27-SOM1-EK1

For more details of the package and pinout of the SAMA5D2, refer to “Table 6-2. Pinouts” in the SAMA5D2 series datasheet.

Back to Top


You can control pins PB1 and PA31 from the ATSAMA5D27 SOM1, which connects to J25 pin 1 and J30 pin 1 respectively, of the mikroBUS 1 and mikroBUS 2 connector (labeled PWM_mBUS1 and PWM_mBUS2 on the schematic).

mikroBUS pinSchematic NamePackage PinComments
J25 pin 1PWM_mBUS1PB1Conflict with LED_Green
J30 pin 1PWM_mBUS2PA31Conflict with LED_blue

mikroBUS 1

mikroBUS 1 schematic

mikroBUS 2

mikroBUS 2 schematic

Red-Green-Blue (RGB) LED (D5)

You can also control pins PB1, PA31, and PA10, which connect to the RGB LED D5, Green, Red, and Blue, respectively.

User Interface features of the ATSAMA5D27-SOM1-EK1

Schematic NamePackage PinComment
LED_GreenPB1Conflict with PWM_mBUS1
LED_bluePA31Conflict with PWM_mBUS2

RGB LED Schematic

Back to Top

Buildroot Configuration

Objective: Using Buildroot, build a bootable image and FLASH onto an SD Memory Card for the ATSAMA5D27-SOM1-EK1 development board.

Follow the steps for building the image in the "Create Project with Default Configuration" article. You will use the default configuration file, atmel_sama5d27_som1_ek_mmc_dev_defconfig.

As already mentioned, the default configuration of the Device Trees connects the PWM pins to the RGB LED D5. To configure the PWM pins for the mikroBUS connectors, enter the edits as shown.

Back to Top

Device Tree

Objective: Observe how the PWM controller was configured for PWM in the Device Tree. Note the changes shown to the PWM controller connection from the RGB LED D5 to the PWM_mBUS1 and PWM_mBUS2 pins of the mikroBUS connectors.

Once Buildroot has completed its build, the PWM definitions for the ATSAMA5D27-SOM1-EK1 were configured by a Device Tree. The device tree source include files (DTSI and DTS), are located in the Buildroot output directory, /output/build/linux-linux4sam_6.0/arch/arm/boot/dts/.

Examine the sama5d2.dtsi file and observe the PWM0 device assignments:

769   pwm_clk: pwm_clk {
770      #clock-cells = <0>;
771      reg = <38>;
772      atmel,clk-output-range = <0 83000000>;
773   };
1232  pwm0: pwm@f802c000 {
1233     compatible = "atmel,sama5d2-pwm";
1234     reg = <0xf802c000 0x4000>;
1235     interrupts = <38 IRQ_TYPE_LEVEL_HIGH 7>;
1236     #pwm-cells = <3>;
1237     clocks = <&pwm_clk>;
1238  };

Line 771: The Peripheral Identification (PID) of PWM0 is 38, this definition of the offset will be used to enable the PWM0 clock in Power Management Controller (PMC).

Line 1233: Specifies which driver will be used for this PWM device.

Line 1234: The PWM0 base address is 0xf802c000, size of register block is 0x4000.

Line 1235: The PID of PWM0 is 38, high level triggered, priority seven.

Line 1237: The definition of the PWM0 clock source.

Back to Top

Examine the at91-sama5d27_som1_ek.dts file and observe the PWM0 device assignments:

173   pwm0: pwm@f802c000 {
174      pinctrl-names = "default";
175      pinctrl-0 = <&pinctrl_mikrobus1_pwm &pinctrl_mikrobus2_pwm>;
176      status = "okay"; /* Conflict with leds. */
177   };
490   pinctrl_mikrobus1_pwm: mikrobus1_pwm {
491      pinmux = <PIN_PB1__PWML1>;
492      bias-disable;
493   };
495   pinctrl_mikrobus2_pwm: mikrobus2_pwm {
496      pinmux = <PIN_PA31__PWML0>;
497      bias-disable;
498   };
560   leds {
561      compatible = "gpio-leds";
562      pinctrl-names = "default";
563      pinctrl-0 = <&pinctrl_led_gpio_default>;
564      status = "disabled"; /* Conflict with pwm0. */
566      red {
567         label = "red";
568         gpios = <&pioA PIN_PA10 GPIO_ACTIVE_HIGH>;
569      };
571      green {
572         label = "green";
573         gpios = <&pioA PIN_PB1 GPIO_ACTIVE_HIGH>;
574      };
576      blue {
577         label = "blue";
578         gpios = <&pioA PIN_PA31 GPIO_ACTIVE_HIGH>;
579         linux,default-trigger = "heartbeat";
580      };
581   };

Line 175: Assigns PWM0 pin definitions to the mikroBUS connectors.

Line 176 (change 1): Change the status to “okay” to enable the PWM0 controller.

Line 491: The mux of PB1 will be switched to PWML1.

Line 492: The pull-up/down feature will be disabled.

Line 496: The mux of PA31 will be switched to PWML0.

Line 497: The pull-up/down feature will be disabled.

Line 564 (change 2): Change the status to “disabled” to disable the RGB LED. 

Back to Top


Objective: Observe how the PWM functionality was configured in the Linux kernel.

From the buildroot directory, run the Linux kernel menuconfig:

$ make linux-menuconfig

The top-level menu will be displayed.

Linux menuconfig top menu

Back to Top

Device Driver

Select Device Drivers ---->

Select [*] Pulse-Width Modulation (PWM) Support ---->

PWM Support

Observe that <*> Atmel® PWM Support is selected.

Atmel PWM Support

Back to Top

sysfs File System Support

Back up to the Kernel Configuration top-level menu (ESC-ESC twice).

Select File systems ---->

Select File systems

Select Pseudo filesystems ---->

Select Pseudo filesystem

Observe that -*- sysfs file system support is selected.

By default, the sysfs support has been selected. As there is no device node file for the PWM controller, you will access the PWM driver via the sysfs in User Space.

Back to Top


You can access the PWM driver via the following sysfs path in user space, /sys/class/pwm.

Several files and sub-folders can be found in this path.

​For more details on PWM in Linux, see kernel_dir/documentation/pwm.txt.

  • /sys/class/pwm/pwmchipN: Each probed PWM controller will be exported as pwmchipN, where N is the base of the PWM controller.
  • /sys/class/pwm/pwmchipN/npwm: The number of PWM channels which this controller supports (read-only).
  • /sys/class/pwm/pwmchipN/export: Exports a PWM channel with sysfs (write-only). (The PWM channels are numbered using a per-controller index from 0 to npwm-1.)
  • /sys/class/pwm/pwmchipN/unexport: Un-exports a PWM channel from sysfs (write-only).

When a PWM channel is exported, a pwmX directory will be created in the pwmchipN directory, where X is the number of the channel that was exported.

The following properties will be available:

  • /sys/class/pwm/pwmchipN/pwmX/period – The total period of the PWM signal (read/write). Value is in nanoseconds and is the sum of the active and inactive time of the PWM.
  • /sys/class/pwm/pwmchipN/pwmX/duty_cycle – The active time of the PWM signal (read/write). Value is in nanoseconds and must be less than the period.
  • /sys/class/pwm/pwmchipN/pwmX/polarity – Change the polarity of the PWM signal (read/write). This property only works if the PWM controller supports changing the polarity. The polarity can only be changed if the PWM is not enabled. Value is the string "normal" or "inversed".
  • /sys/class/pwm/pwmchipN/pwmX/enable – Enable/disable the PWM signal (read/write) where 0 = disabled and 1 = enabled.

Back to Top


Lab 1: Hands-on with PWM_mBUS1

Reset the target board.

# reboot

The following will setup a 10 kHz 90% duty cycle PWM output:

# cd /sys/class/pwm/
# ls
# cd pwmchip0/
# echo 1 > export
# ls
device     npwm       pwm1       uevent
export     power      subsystem  unexport
# cd pwm1/
# ls
capture     enable      polarity    uevent
duty_cycle  period      power
# echo 100000 > period
# echo 90000 > duty_cycle
# echo 1 > enable

An oscilloscope capture of the PWM waveforms to mbus1

Back to Top

Lab 2: Hands-on with LED_Green

Reset the target board.

# reboot

Turn off LED_Red.

# echo 10 > /sys/class/gpio/export
# echo out > /sys/class/gpio/PA10/direction
# echo 0 > /sys/class/gpio/PA10/value

Turn off LED_Blue.

# echo 0 > /sys/class/pwm/pwmchip0/export
# echo 10000000 > /sys/class/pwm/pwmchip0/pwm0/period
# echo 10000000 > /sys/class/pwm/pwmchip0/pwm0/duty_cycle
# echo 1 > /sys/class/pwm/pwmchip0/pwm0/enable

Set up PWM channel 1 output.

# cd /sys/class/pwm/pwmchip0/
# echo 1 > export
# cd pwm1/

Adjust LED_Green brightness.

# echo 10000000 > period
# echo 10000000 > duty_cycle
# echo 1 > enable
# echo 9000000 > duty_cycle
# echo 8000000 > duty_cycle
# echo 7000000 > duty_cycle
# echo 6000000 > duty_cycle
# echo 5000000 > duty_cycle
# echo 4000000 > duty_cycle
# echo 3000000 > duty_cycle
# echo 2000000 > duty_cycle
# echo 1000000 > duty_cycle

Set LED_Green blink.

# echo 0 > enable
# echo 100000000 > period
# echo 50000000 > duty_cycle
# echo 1 > enable

Back to Top

Lab 3: Hands-on with Synchronous Channels

Some channels can be linked together as synchronous channels.

​Refer to the “ Synchronous Channels” chapter in the SAMA5D2 datasheet for more detailed information about this feature.

In Linux, the current PWM device model doesn’t support this feature. We can test this feature through a direct register setting via the devmem2 command.

The following shows the command sequence for using PWM0 and PWM1 as synchronous channels.

Reset target board.

# reboot

These are commands for PWM channel initialization:

Enable the PWM0 and PWM1 SYNC feature.

# devmem2 0xF802C020 w 0x3
# cd /sys/class/pwm/pwmchip0/
# echo 0 > export
# echo 1 > export
# echo 10000000 > pwm1/period
# echo 9000000 > pwm1/duty_cycle

This is dummy enabling for PWM1, used for writing duty cycle configurations:

# echo 1 > pwm1/enable

Since the SYNC feature of PWM1 has been enabled, all SYNC channels are enabled together by enabling channel 0.

# echo 10000000 > pwm0/period
# echo 2000000 > pwm0/duty_cycle

Here the output of PWM0 and PWM1 will be enabled together:

# echo 1 > pwm0/enable

These are commands for updating channel configuration:

Change the PWM duty cycle.

# echo 5000000 > pwm1/duty_cycle

Write UPDULOCK bit in PWM_SCUC after adjusting the configuration of PWM channel.

# devmem2 0xF802C028 w 1

An oscilloscope capture of the PWM waveforms to green LED

Back to Top


In this article, you used Buildroot to build an image with PWM support for the ATSAMA5D2 series MPU. You walked through the Device Tree and Kernel to observe how the embedded Linux system configures the source code for building. You also edited the Device Trees to control the PWM pins of the mikroBUS connectors. Finally, you tried some hands-on exercises to see the PWM controller in action.

Additional Information

Back to Top