Applications - Pulse Width Modulation (PWM)

Introduction

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.


Prerequisites

This application is developed for the ATSAMA5D27-SOM1-EK1 development platform:

This application is developed using the Buildroot build system:


Hardware

For this article, you will be using the ATSAMA5D27-SOM1-EK1.

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 tables below, 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.

ATSAMA5D27_SOM1_EK1_expansion_01.png

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

mikroBUS

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 pin Schematic Name Package Pin Comments
J25 pin 1 PWM_mBUS1 PB1 Conflict with LED_Green
J30 pin 1 PWM_mBUS2 PA31 Conflict with LED_blue

mikroBUS 1

mikrobus1_R1.png

mikroBUS 2

mikrobus1_R2.png

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.

ATSAMA5D27_SOM1_EK1_user_interface.png
Schematic Name Package Pin Comment
LED_Green PB1 Conflict with PWM_mBUS1
LED_blue PA31 Conflict with PWM_mBUS2
LED_Red PA10
LED_D5_Schematic.png

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 below.


Device Tree

Objective: Observe how the PWM controller was configured for PWM in the Device Tree. Note the changes shown below to change the connection of the PWM controller 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/.

1

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.

2

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   };
494
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. */
565
566      red {
567         label = "red";
568         gpios = <&pioA PIN_PA10 GPIO_ACTIVE_HIGH>;
569      };
570
571      green {
572         label = "green";
573         gpios = <&pioA PIN_PB1 GPIO_ACTIVE_HIGH>;
574      };
575
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.


Kernel

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

1

From the buildroot directory, run the Linux kernel menuconfig:

$ make linux-menuconfig

The top-level menu will be displayed.

linux-config-top-level.png

Device Driver

2

Select Device Drivers —->.

3

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

pwm_support.png

4

Observe that <*> Atmel PWM Support is selected.

atmel_pwm_support.png

sysfs File System Support

1

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

2

Select File systems —->.

file_systems.png

3

Select Pseudo filesystems —->.

pseudo_filesystems.png

4

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.

sysfs_support.png

Rootfs

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.

Hands-On

1. Hands-on with PWM_mBUS1

1

Reset the target board.

# reboot

2

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

# cd /sys/class/pwm/
# ls
pwmchip0
# 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
pwm_mbus1_oscope.png

2. Hands-on with LED_Green

1

Reset target board.

# reboot

2

Turn off LED_Red.

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

3

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

4

Set up PWM channel 1 output.

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

5

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

6

Set LED_Green blink.

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

3. Hands-on with Synchronous Channels

Some channels can be linked together as synchronous channels.

Refer to the “55.6.2.9 Synchronous Channels” chapter in the SAMA5D2 data sheet 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.

1

Reset target board.

# reboot

2

These are commands for PWM channel initialization:

a

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

b

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

# echo 1 > pwm1/enable

c

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

d

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

# echo 1 > pwm0/enable

3

These are commands for updating channel configuration:

a

Change the PWM duty cycle.

# echo 5000000 > pwm1/duty_cycle

b

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

# devmem2 0xF802C028 w 1
pwm_synchronous.png

Summary

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.

© 2020 Microchip Technology, Inc.
Notice: ARM and Cortex are the registered trademarks of ARM Limited in the EU and other countries.
Information contained on this site regarding device applications and the like is provided only for your convenience and may be superseded by updates. It is your responsibility to ensure that your application meets with your specifications. MICROCHIP MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND WHETHER EXPRESS OR IMPLIED, WRITTEN OR ORAL, STATUTORY OR OTHERWISE, RELATED TO THE INFORMATION, INCLUDING BUT NOT LIMITED TO ITS CONDITION, QUALITY, PERFORMANCE, MERCHANTABILITY OR FITNESS FOR PURPOSE. Microchip disclaims all liability arising from this information and its use. Use of Microchip devices in life support and/or safety applications is entirely at the buyer's risk, and the buyer agrees to defend, indemnify and hold harmless Microchip from any and all damages, claims, suits, or expenses resulting from such use. No licenses are conveyed, implicitly or otherwise, under any Microchip intellectual property rights.