PWM

From ArmadeusWiki
Revision as of 19:45, 27 September 2009 by OlivierR (Talk | contribs) (PWM with Java)

Jump to: navigation, search

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

Hardware

i.MX PWM module is using PWMO (pin 2 of PortA on APF9328 and pin 5 of PortE on the APF27) as ouput.

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 here :
#!/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.

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 true;
			} else {
				return false;
			}
		} 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.

Links