Version 3 (modified by 4 years ago) ( diff ) | ,
---|
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 | Desc(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 | LVDS backlight | 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 | LVDS backlight | 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 | LVDS backlight | 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) |
- This is the signal name from the Ventana hardware manuals
- This is the connector pinout. The 5th pin on 5-pin connectors is GND
- Consult the IMX6 Reference manuals for PAD info: IMX6DQRM IMX6SDLRM
- This is the GPIO block and IO the PAD is pinmux'd to when hwconfig is configured for GPIO
- 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)
- 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
- 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