wiki:linux/pwm

Pulse Width Modulation (PWM)

Pulse Width Modulation refers to a modulation technique where a digital output signal will alternate high and low with possibly differing on/off times. PWM signals are commonly used in embedded systems for:

  • controlling analog devices (by using an RC circuit to convert the digital PWM signal to an analog voltage)
  • controlling the brightness of an LED
  • controlling the position of an RC hobby stepper motor

When working with PWM's the following terminology is commonly used:

  • period - represents the period the PWM signal alternates between its off/on times
  • duty-cycle - represents the on time as a percentage of the duty cycle (ie 10% duty-cycle means the signal is on for 10% of the period and off for 90% of the period)

Many Gateworks boards have on-board PWM controllers and offer one or more PWM outputs with 3.3V TTL voltage levels.

References:

PWM mapping

The various product hardware manuals will call out what PWM devices are available on each board.

The hardware mapping of the PWM controllers is defined in the Linux kernel device-tree for Ventana (ie http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/arch/arm/boot/dts/imx6qdl-gw54xx.dtsi).

If debugfs is enabled in the kernel (as it is on Gateworks BSP's) you can see a nice table of PWM mapping via /sys/kernel/debug/pwm showing each PWM controller by name, a list of the PWM's it provides, what driver (if any) has control over them, and their state. For example:

root@ventana:~# cat /sys/kernel/debug/pwm
platform/208c000.pwm, 1 PWM device
 pwm-0   (backlight           ): requested enabled

platform/2088000.pwm, 1 PWM device
 pwm-0   ((null)              ):

platform/2084000.pwm, 1 PWM device
 pwm-0   (sysfs               ): requested enabled

platform/2080000.pwm, 1 PWM device
 pwm-0   ((null)              ):
  • The above is an IMX6 showing its 4 controllers available, each controller providing 1 PWM pin. The first one shown (0x208c000 maps to IMX PWM4) is used by the backlight driver. The 2nd (0x2088000 maps to IMX PWM3) is unused. The third one (0x2084000 maps to IMX PWM2) is exported for use by sysfs. The fourth (0x2080000 maps to IMX PWM1) is unused.

Ventana PWM mapping

The IMX6 SoC has 4 PWM controllers each having a single PWM signal:

IMX6 controller address IMX6 pwm
pwm@02080000 pwm1
pwm@02084000 pwm2
pwm@02088000 pwm3
pwm@0208c000 pwm4

Only the controllers enabled in the device-tree will be registered therefore you won't always see pwmchip0-3 and can't depend on that mapping to be consistent as it varies board to board and is also dependent on which (if any) DIO's you may have configured as PWM via the bootloader hwconfig variable. You must look at the symlink /sys/class/pwm/pwmchip*/device to see what controller is linked to.

The Ventana bootloader hwconfig env variable must be properly set to configure PWM mode for the DIO pin, otherwise the pin will be muxed to the GPIO controller and the pwmchip controller will be disabled. For example, to configure DIO1 as a PWM and DIO0, DIO2, DIO3 as GPIO:

Ventana> setenv hwconfig 'dio0:mode=gpio;dio1:mode=pwm;dio2:mode=gpio;dio3:mode=gpio'
Ventana> saveenv

For example a GW53xx which has DIO1 configured as a GPIO, and DIO2 configured as a PWM (PWM3) shows:

  • bootloader hwconfig
    root@ventana:~# fw_printenv | grep hwconfig
    hwconfig=dio0:mode=gpio;dio1:mode=pwm;dio2:mode=gpio;dio3:mode=gpio
    
  • /sys/kernel/debug/pwm:
    root@ventana:~# cat /sys/kernel/debug/pwm
    platform/208c000.pwm, 1 PWM device
     pwm-0   (backlight           ): requested enabled
    
    platform/2088000.pwm, 1 PWM device
     pwm-0   ((null)              ):
    
    platform/2080000.pwm, 1 PWM device
     pwm-0   ((null)              ):
    
  • /sys/class/pwmchip* linkage:
    root@ventana:~# ls -l /sys/class/pwm/pwmchip*/device
    lrwxrwxrwx    1 root     root             0 Dec 18 15:33 /sys/class/pwm/pwmchip0/device -> ../../../2080000.pwm
    lrwxrwxrwx    1 root     root             0 Dec 18 15:33 /sys/class/pwm/pwmchip1/device -> ../../../2088000.pwm
    lrwxrwxrwx    1 root     root             0 Dec 18 15:33 /sys/class/pwm/pwmchip2/device -> ../../../208c000.pwm
    
    • shows that IMX6 pwm1 is /sys/class/pwm/pwmchip0 (because its linked to the controller address 0x2080000). This is unused but it is present because its controller's node was not specifically 'disabled' in the device-tree.
    • shows that IMX6 pwm3 is /sys/class/pwm/pwmchip1 (because its linked to the controller address 0x2088000). This is PWM3 which on the GW53xx is mapped to DIO2 coming out J4.3. This is present because the bootloader hwconfig specified DIO2 as a PWM.
    • shows that IMX6 pwm4 is /sys/class/pwm/pwmchip2 (because its linked to the controller address 0x208c000). This is PWM4 which on the GW53xx is mapped to the backlight controller present on the LVDS connector.

You can use the following script will provide the sysfs path for an IMX PWM:

#!/bin/sh

# match IMX PWM{$1} with available pwm controllers
for i in $(ls /sys/class/pwm); do
        link=$(readlink /sys/class/pwm/$i)
        IMXPWM=
        case "$link" in
                *2080000*) IMXPWM=1;;
                *2084000*) IMXPWM=2;;
                *2088000*) IMXPWM=3;;
                *208c000*) IMXPWM=4;;
        esac
        [ $IMXPWM -a $IMXPWM == $1 ] && { chip=$i; break; }
done
[ $chip ] || { echo "chip not found for IMX PWM$1"; exit; }

path="/sys/class/pwm/$chip"

# export first pwm for chip
echo "$path/pwm0"
[ -d $path/pwm0 ] || { echo 0 > $path/export; }
[ -d $path/pwm0 ] || { echo "Export failed - not PWM$1 not available"; }

Example use:

root@ventana:/ # /data/test 2 # get path for IMX PWM2
/sys/class/pwm/pwmchip1/pwm0

The following table shows what PWM's are available on the various Ventana baseboards:

Board DIO(1) Connector(2) IMX6_PAD(3) IMX6 GPIO(4) linux gpio(5) linux PWM(6)
GW51xx DIO1 J11.7 PAD_SD1_DATA2 GPIO1_IO19 gpio-19 PWM2 (0x02084000)
GW51xx DIO2 J11.6 PAD_SD1_DATA1 GPIO1_IO17 gpio-17 PWM3 (0x02088000)
GW51xx DIO3 J11.8 PAD_SD1_CMD GPIO1_IO18 gpio-18 PWM4 (0x0208C000)
GW52xx DIO1 J4.2 PAD_SD1_DATA2 GPIO1_IO19 gpio-19 PWM2 (0x02084000)
GW52xx DIO2 J4.3 PAD_SD1_DATA1 GPIO1_IO17 gpio-17 PWM3 (0x02088000)
GW52xx - J6.5 PAD_SD1_CMD GPIO1_IO18 gpio-18 PWM4 (0x0208C000)
GW53xx DIO1 J4.2 PAD_SD1_DATA2 GPIO1_IO19 gpio-19 PWM2 (0x02084000)
GW53xx DIO2 J4.3 PAD_SD1_DATA1 GPIO1_IO17 gpio-17 PWM3 (0x02088000)
GW53xx - J6.5 PAD_SD1_CMD GPIO1_IO18 gpio-18 PWM4 (0x0208C000)
GW54xx DIO0 J16.1 PAD_GPIO_9 GPIO1_IO09 gpio-9 PWM1 (0x02080000)
GW54xx DIO1 J16.2 PAD_SD1_DATA2 GPIO1_IO19 gpio-19 PWM2 (0x02084000)
GW54xx DIO2 J16.3 PAD_SD4_DATA1 GPIO2_IO09 gpio-41 PWM3 (0x02088000)
GW54xx DIO3(7) J16.4 PAD_SD4_DATA2 GPIO2_IO10 gpio-42 PWM4 (0x0208C000)
GW54xx - J6.5 PAD_SD1_CMD GPIO1_IO18 gpio-18 PWM4 (0x0208C000)
GW553x DIO1 J10.7 PAD_SD1_DATA2 GPIO1_IO19 gpio-19 PWM2 (0x02084000)
GW553x DIO2 J10.6 PAD_SD1_DATA1 GPIO1_IO17 gpio-17 PWM3 (0x02088000)
GW53xx DIO3 J10.8 PAD_SD1_CMD GPIO1_IO18 gpio-18 PWM4 (0x0208C000)
GW552x DIO1 J8.11 PAD_SD1_DATA2 GPIO1_IO19 gpio-19 PWM2 (0x02084000)
GW552x DIO2 J8.10 PAD_SD1_DATA1 GPIO1_IO17 gpio-17 PWM3 (0x02088000)
GW551x DIO1 J3.19 PAD_SD1_DATA2 GPIO1_IO19 gpio-19 PWM2 (0x02084000)
GW551x DIO2 J3.20 PAD_SD1_DATA1 GPIO2_IO17 gpio-17 PWM3 (0x02088000)
  1. This is the signal name from the Ventana hardware manuals
  2. This is the connector pinout. The 5th pin on 5-pin connectors is GND
  3. Consult the IMX6 Reference manuals for PAD info: IMX6DQRM IMX6SDLRM
  4. This is the GPIO block and IO the PAD is pinmux'd to when hwconfig is configured for GPIO
  5. This is the gpio mapped in linux accessible via /sys/class/gpio. Note that the pinmux and IO configuration are done by the bootloader, but you will need to manually export the GPIO to use it (see here for details)
  6. This is the PWM that the PAD is pinmux'd to by the bootloader with hwconfig is conifgured for PWM. u-boot hwconfig to configure DIO pinmux/config
  7. The GW5400 PWM4 can be routed to two different pads/connectors. By default the Ventana kernel device-tree's map PWM4 to the J6.5 backlight connector. If you want to use PWM4 as DIO3 on J16.4 you need to change the kernel device-tree and should contact support@… for details.

See also:

Linux PWM and pwm class

The Linux kernel con provide pwm functionality to userspace via a pwm class accessed via sysfs (/sys/class/pwm/). For userspace access this requires sysfs kernel support (CONFIG_SYSFS) and the sysfs filesystem mounted which is standard for most linux systems including Gateworks BSP's). This framework allows boards to define PWM controllers and allow userspace to make use of them by adjusting their period, duty-cycle, and enable.

Note that any pwm that is registered for use by a driver is not available for exporting for userspace control. You can cat /sys/kernel/debug/pwm to see what pwm's are currently registered with the kernel (those shown to be owned by sysfs are exported for userspace, those shown as null are not exported or in use by any driver).

Note that the pwm sysfs API is slightly different from the gpio sysfs API:

  • the individual pwm's from a pwmchip (controller) are in the subdir of the pwmchip and all start with index 0 (whereas gpio's are exported to /sys/class/gpio and keep increasing in number)

Each exported pwm will be in the sub-directory of it's controller chip and will be 0 based. For example, the 2nd pwm of a pwm controller that has 16 pwm's which was the 3rd pwm controller to be registered will be /sys/class/pwmchip2/pwm1 (0 based).

You can determine which pwmchip is which by either looking at /sys/kernel/debug/pwm or the symlink of the device file in the pwmchip directory.

Here are some example use cases for using pwm-class via syfs:

  • to see pwm names and state: (requires debugfs kernel support and fs mounted)
    cat /sys/kernel/debug/pwm
    
  • to export a PWM that is not currently in use:
    echo 0 > /sys/class/pwm/pwmchip0/export # export the first pwm (pwm0) on the first pwm controller (pwmchip0)
    
  • to change the period of the PWM signal (value is in nanoseconds and is the sum of the active and inactive time of the PWM)
    echo 1000000 > /sys/class/pwm/pwmchip0/pwm0/period # set 1ms period
    cat /sys/class/pwm/pwmchip0/pwm0/period # see period
    
  • to change the duty cycle of the PWM signal (value is in nanoseconds refers to the time the signal is active and must be less than the period)
    echo 500000 > /sys/class/pwm/pwmchip0/pwm0/duty_cycle # set 500us (50% duty-cycle if period is 1ms)
    cat /sys/class/pwm/pwmchip0/pwm0/duty_cycle # see duty_cycle
    
  • to change the polarity of the PWM signal: (*Note not supported on i.MX6 PWM)
    echo normal > /sys/class/pwm/pwmchip0/pwm0/polarity # set normal polarity
    echo inversed > /sys/class/pwm/pwmchip0/pwm0/polarity # set inverted polarity
    cat /sys/class/pwm/pwmchip0/pwm0/polarity # see polarity
    
  • to enable/disable a PWM signal:
    echo 1 > /sys/class/pwm/pwmchip0/pwm0/enable # enable output
    echo 0 > /sys/class/pwm/pwmchip0/pwm0/enable # disable output
    

References:

PWM based LEDs

See wiki:linux/led

PWM based Backlight

See wiki:linux/backlight

Last modified 4 weeks ago Last modified on 07/19/17 21:23:28