PWM
From ArmadeusWiki
On this page, you will find usefull informations to configure and use the Pulse Width Modulation (PWM) capabilities of your APF9328/APF27 boards.
The i.MX processor is capable of driving 2 PWM signals:
- one is linked to the LCD controller and allow to control the LCD backlight
- the other can be used to generate a PWM signal in several mode:
- audio playback
- real PWM
Contents |
Hardware
i.MX PWM module is using PWMO (pin 2 of PortA on APF9328 and pin 5 of PortE on the APF27) as ouput.
- On APF9328DevLight this pin is accessible from TIMER zone under the board (See datasheets for more infos)
- On APF9328DevFull development boards this pin is connected to pin 4 (TIM2) of "Timers" connector (X21)
- On APF27Dev development boards this pin is connected to pin 4 of J22
Linux driver installation
i.MX PWM driver is installed by default in standard Armadeus rootfs.
Usage
- Load the module:
# modprobe imx-pwm Initializing PWM class. Initializing PWM#0...PWM major number = 251 PWM driver v0.7 done.
- If you plan to use sound, create /dev/pwm device file if not already existing (Major is dynamically allocated)
# DEV=`cat /proc/devices | grep imx-pwm | cut -d " " -f 1`; mknod /dev/pwm c $DEV 0
PWM Mode
Remark:
- The pwm has a resolution of 1uS
After driver loading, you have access to /sys/class/pwm/... functionnalities:
- to change frequency (for example 500 Hz):
# echo 500 > /sys/class/pwm/pwm0/frequency
- to change period (for example 100us):
# echo 100 > /sys/class/pwm/pwm0/period
- to change duty cycle (for example 50.0%)
# echo 500 > /sys/class/pwm/pwm0/duty
- to activate PWM:
# echo 1 > /sys/class/pwm/pwm0/active
- to de-activate PWM:
# echo 0 > /sys/class/pwm/pwm0/active
Bash test script
- A Bash test script is available given below. Just enter the desired frequency and duty cycle.
#!/bin/sh # # script to command imx pwm # usage : pwm_imx frequency duty # if [ $# -lt 2 ] then echo "Provide two arguments to this script !" echo "pwm_imx frequency dutycycle" exit 1 fi SYS_DIR="/sys/class/pwm/pwm0/" FREQUENCY=$1 DUTY=$2 DUTY=`(echo $DUTY | awk '{ print $1*10}')` if [ ! -d "$SYS_DIR" ]; then echo "Can't find /sys/ interface for PWM" exit 1 fi echo "Starting PWM" echo 1 > $SYS_DIR/active #SET FREQUENCY echo $FREQUENCY > $SYS_DIR/frequency echo "Setting PWM to $FREQUENCY Hz" #SET DUTY CYCLE echo $DUTY > $SYS_DIR/duty echo "Setting DUTY to $2 %" exit 0
PWM with Java
The code bellow shows how to directly use iMX-PWM from Java. The main method provides example of using it. PWM control is done by reading and writing pseudo-files in /sys/class/pwm/pwm0/ directory.
import java.io.*; public class Imxpwm { private FileReader activeReader; private FileWriter activeWriter; private FileReader frequencyReader; private FileWriter frequencyWriter; private FileReader periodReader; private FileWriter periodWriter; private FileReader dutyReader; private FileWriter dutyWriter; public boolean isActive() { try { int result = this.activeReader.read(); if (result == 0) { return false; } else { return true; } } catch (Exception e) { e.printStackTrace(); return false; } } public void setActive(boolean active) { try { if (active) { this.activeWriter.write("1"); this.activeWriter.flush(); } else { this.activeWriter.write("0"); this.activeWriter.flush(); } } catch (Exception e) { e.printStackTrace(); } } public double getDuty() { try { return this.dutyReader.read()/10.0; } catch (Exception e) { e.printStackTrace(); return -1.0; } } public void setDuty(double duty) { try { this.dutyWriter.write(Double.toString(duty * 10)); this.dutyWriter.flush(); } catch (Exception e) { e.printStackTrace(); } } public int getFrequency() { try { return this.frequencyReader.read(); } catch (Exception e) { e.printStackTrace(); return -1; } } public void setFrequency(int frequency) { try { this.frequencyWriter.write(Integer.toString(frequency)); this.frequencyWriter.flush(); } catch (Exception e) { e.printStackTrace(); } } public int getPeriod() { try { return this.periodReader.read(); } catch (Exception e) { e.printStackTrace(); return -1; } } public void setPeriod(int period) { try { // en µs this.periodWriter.write(Integer.toString(period)); this.periodWriter.flush(); } catch (Exception e) { e.printStackTrace(); } } /** * @param frequency * @param duty * @param period * @param active */ public Imxpwm(int frequency, int duty, boolean active) { try { activeReader = new FileReader("/sys/class/pwm/pwm0/active"); } catch (Exception e) { e.printStackTrace(); } try { activeWriter = new FileWriter("/sys/class/pwm/pwm0/active"); } catch (Exception e) { e.printStackTrace(); } try { frequencyReader = new FileReader("/sys/class/pwm/pwm0/frequency"); } catch (Exception e) { e.printStackTrace(); } try { frequencyWriter = new FileWriter("/sys/class/pwm/pwm0/frequency"); } catch (Exception e) { e.printStackTrace(); } try { periodReader = new FileReader("/sys/class/pwm/pwm0/period"); } catch (Exception e) { e.printStackTrace(); } try { periodWriter = new FileWriter("/sys/class/pwm/pwm0/period"); } catch (Exception e) { e.printStackTrace(); } try { dutyReader = new FileReader("/sys/class/pwm/pwm0/duty"); } catch (Exception e) { e.printStackTrace(); } try { dutyWriter = new FileWriter("/sys/class/pwm/pwm0/duty"); } catch (Exception e) { e.printStackTrace(); } this.setActive(active); this.setDuty(duty); this.setFrequency(frequency); } public static void main(String[] args) throws Exception { Imxpwm maPWM = new Imxpwm(50, 75, true); double d = 0.0; for (int j = 0; j < 100; j++) { for (int i = 5; i <= 999; i++) { d = i * 1.0 / 10.0; maPWM.setDuty(d); } } maPWM.setFrequency(500); maPWM.setPeriod(50000); } }
Audio Mode
Audio playback mode is working since v0.5 of PWM driver (thanks to SebastienR). You can play 8, 16 & 32 KHz 8bit linear RAW sound. It's recommended to play 8kHz one, as they use less CPU ressources. To convert a sound to the right format we recommend to use SoX (on Linux). For exemple:
[host]$ sox /usr/share/sounds/KDE_Chimes_1.ogg -r 8192 -b -u -c 1 Chimes.raw -r -> sampling rate -b -> byte/8bit signal -u -> linear /not compressed -c 1 -> Mono / 1 channel
Then you can test it using target/linux/modules/pwm/test/testplaymode, which can be compiled that way:
$ make shell_env $ . armadeus_env.sh $ export PATH=$PATH:$ARMADEUS_TOOLCHAIN_PATH $ make -C target/linux/modules/pwm/test/ $ cp target/linux/modules/pwm/test/testplaymode /tftpboot/ $ cp target/linux/modules/pwm/test/KDE_Startup_2.raw /tftpboot/
on target do:
# testplaymode
or
# testplaymode KDE_Startup_2.raw
Going further
If you need more than one PWM at a time, you can use the APF's FPGA with the following project.