-
Notifications
You must be signed in to change notification settings - Fork 356
PWM Output
This discussion assumes you understand the principles of digital output. If not, read about them here.
A Pulse Width Modulation (PWM) signal is a kind of a digital output, which has a certain structure in its temporal behavior. Specifically, the signal has a fixed period. Every time a period starts, the signal moves from LOW to HIGH. Then it stays HIGH for a certain duration, called the pulse width, after which it becomes LOW again for the rest of the period. The pulse width may change over time, but the period stays the same. The figure below presents a typical waveform of a PWM signal.
The ratio between the pulse width and the period is called "duty cycle" and is normally presented in percent. In the example above, the first five periods have a duty cycle of 50% and the last five period have a duty cycle of 10%. Also, it is very common to talk about the frequency rather than the period. The frequency is simply the number of periods per second, so the translation between period and frequency is very simple. If the X-axis units in the example above are microseconds, we can see that the period is 100us, thus the frequency is 10KHz, or 10,000 cycles per second.
Roughly speaking, PWM signals have two distinct uses. The first one is to simulate or approximate an analog output. The way it works is that if we consider the average voltage of a PWM signal of 0V / 3.3V, it will be (duty cycle) * 3.3V. For example, if the duty cycle is 10%, the average output voltage will be 0.33V. If we were to drive an LED with a 10% duty cycle PWM signal, it would be on 10% of the time and off 90% of the time. If the toggling is fast enough (i.e. the frequency is high enough), we would not notice the blink, and would get an impression of an LED shining dimly at 10% of its power. The surprising fact is that there are many other devices for which 10% duty cycle PWM at 3.3V would produce the same result as constant 0.33V. Most notably, motors often won't tell the difference between two, and for many kinds of motors this means that a 25% duty cycle PWM signal will cause them to turn at 25% their full speed! Just a side note, don't connect motors directly to the IOIO pins, you need a little circuit called driver in between. But you can safely experiment PWM with an LED connected through a 220ohm resistor.
The second use of PWM signals is as means to encode information. The device consuming the PWM signal will measure the width of the pulses and will behave differently for different pulse widths. The most notable example is a hobby servo motor (the kind often seen in RC toys). Those servos expect a PWM control signal with a frequency of 100Hz (10ms period). They decode this signal by measuring the pulse width and adjust their rotation angle accordingly. Typically, a 1ms pulse width will take the servo arm to one extreme, a 2ms pulse to the other extreme, a 1.5ms pulse to the center, etc. When controlling servos with the IOIO, it is not a problem to connect the PWM signal directly to the motor control wire - the servo does not draw a lot of current from this wire, unlike a DC motor.
The IOIO supports generation of up to 9 simultaneous PWM signals. Not every pin can serve as PWM output, but rather only the pins that can function as "peripheral" outputs. You can find out which these are according to the "P" symbol at the bottom of the board or by checking out the table at the bottom of this page. Note that on the first revision of boards the letter "P" has unfortunately been emitted from some of the pins that support peripheral output. These are pins 34-40 and 45-48. The on-board 'stat' LED can also be used with PWM. Just note that it is connected in such a way that LOW turns it on and HIGH turns it of, so driving it with a 75% duty cycle PWM will actually turn it on at 25% intensity.
Using the IOIO pins as PWM outputs is done via the PwmOutput
interface. An instance of this interface corresponds to a physical pin on the board, configured to work in PWM output mode as well as to one PWM module out of the 9 available ones. PwmOutput
instances are obtained by calling one of the overloads of IOIO.openPwmOutput()
. The simplest form is:
PwmOutput pwm = ioio.openPwmOutput(pinNum, freq);
This opens pin number pinNum for PWM output, in the normal (i.e. not open-drain) mode, running at a frequency of freq
Hz. It is required that at the time of call, this pin is not being used for anything else, and that there is at least one free PWM module. In order to open a pin for PWM output in open-drain mode (e.g. for generating a 5V PWM signal, as discussed here), the following form can be used:
PwmOutput pwm = ioio.openPwmOutput(new DigitalOutput.Spec(pinNum, Mode.OPEN_DRAIN), freq);
Once an instance of PwmOutput
is obtained, the duty cycle can be controlled by calling:
pwm.setDutyCycle(dc);
where dc
is a floating point number between 0 and 1. This is normally the convenient method when the PWM signal is used to simulate an analog output.
Alternately, you can control the pulse width directly (e.g. for controlling a servo): pwm.setPulseWidth(pw); where 'pw` is the pulse width in microseconds. That is, a servo would take here a number between 1000-2000, which is 1ms-2ms in order to utilize its full range.
When you are done using the pin, call:
out.close();
in order to return the pin to a "floating" state, free the PWM module and possibly be able to re-open it in the same or in a different mode. The PwmOutput
instance becomes useless after this call - it is illegal to do anything with it.