As discussed in last week’s Off-The-Shelf Hacker column, digital output devices simply turn on or off according to the state of a microcontroller pin. A zero is off, one is on. With the fundamentals behind us, it’s time to move along and explore pulse width modulation (PWM) output. My examples use the Arduino micro-controller and IDE, although the Raspberry Pi has digital and PWM outputs, as well. I use Python to code the GPIO part of my projects, on that platform.
PWM is a Natural Extension of Digital
So, why do we need PWM output, anyway?
Take your common everyday LED. The way LEDs work is that you supply a fairly narrow range of voltages to them and they light up. They’re digital because they’re either lit or not. That behavior is quite different than an incandescent lamp. The lamp changes brightness as the voltage rises and falls. LEDs light at a certain minimum voltage and burn out if the voltage goes too high. The brightness is more or less constant throughout the range.
You can, however, change an LED’s brightness by varying the “on” and “off” times. Use a short “on/off” interval and the LED will be dim. If you change the “on/off” times, particularly the “on” time, it will look brighter as the value goes up.
Check out this Arduino code from last week.
1 2 3 4 5 6 7 8 9 10 11 12 |
void setup() { // initialize digital pin 13 as an output. pinMode(13, OUTPUT); } // the loop function runs over and over again forever void loop() { digitalWrite(13, HIGH); // turn the LED on delay(500); // wait for 500 milliseconds digitalWrite(13, LOW); // turn the LED off delay(500); // wait for 500 milliseconds } |
Suppose we change the both delays to 10. That means that the length the LED on time would be 10 milliseconds. The off time for the LED would also be 10 milliseconds. The new loop would look like this:
1 2 3 4 5 6 |
void loop() { digitalWrite(13, HIGH); // turn the LED on delay(10); // wait for 10 milliseconds digitalWrite(13, LOW); // turn the LED off delay(10); // wait for 10 milliseconds } |
The LED would go from slowly flashing on and off, to looking continuously on, but probably somewhat dim. Now replace both delay statements with their own variables.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
void setup() { // initialize digital pin 13 as an output. pinMode(13, OUTPUT); int ledon = 10; int ledoff = 10; } // the loop function runs over and over again forever void loop() { digitalWrite(13, HIGH); // turn the LED on delay(ledon); // wait for “ledon” milliseconds digitalWrite(13, LOW); // turn the LED off delay(ledoff); // wait for “ledoff” milliseconds } |
It’s then possible to change the on and off times of the LED, independently, to anything you wanted, under programmatic control. So now you control the width of the on/off pulse. That’s the basics of PWM.
It’s Even Better with Libraries
You don’t have to write all the control code, using endless digitalWrite()-delay() function combinations, to change the Arduino pulse width modulation output. Occasionally you might do it that way for special circumstances. For everyday PWM tasks, just use the analogWrite() command.
Oh, sure, I know what you’re saying. See Doc, there IS an analog output. Well, it’ really is just a little library function, that mimics our variable delay on/off cycles, with a sort-of appropriate name. Whatever.
The point is that working with PWM is pretty painless, using library functions.
Here’s the FADE example from the Arduino IDE.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
int led = 9; // PWM pin attached to the LED int brightness = 0; // LED brightness int fadeAmount = 5; // LED fade amount void setup() { pinMode(led, OUTPUT); // declare pin 9 as output: } void loop() { analogWrite(led, brightness); // init pin 9 brightness brightness = brightness + fadeAmount; // increment brightness // reverse fade at the ends if (brightness == 0 || brightness == 255) { fadeAmount = -fadeAmount ; } // wait 30 ms to see the dimming effect delay(30); } |
analogWrite() works great with LEDs. You can also use if for other PWM devices if you like.
If you want to use a servo, another PWM output device, take a look at the KNOB example under the servo tab in the Arduino IDE.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
#include <Servo.h> Servo myservo; // create servo object for servo int potpin = 0; // analog pin - connect to potentiometer int val; // variable for analog pin value void setup() { myservo.attach(9); // attaches the servo to pin 9 } void loop() { val = analogRead(potpin); // reads potentiometer val = map(val, 0, 1023, 0, 180); // scale value myservo.write(val); // sets the servo pos delay(15); // waits for the servo } |
You guessed it, the servo.write() function takes care of the appropriate timing for making a servo move. In this case the map() function converts the analog input values of 8-bit (0 to 1023) to correspond to the movement of the servo motor (0 to 180 degrees). The calculated value is used with the servo.write() function to rotate the servo to the correct position.
Cool, huh?
What’s Next
We’ve looked at straight digital outputs and pulse width modulation. Digital is simply turning an output pin on or off. PWM, not only turns the pin on and off, it also allows varying the width of the on and off parts. PWM makes it easy to dim a LED or change the position of a servo motor.
Digital and PWM functions and their libraries are available in all current micro-controllers, from the Arduino and its clones to the BeagleBone Black, the Raspberry Pi and the CHIP computer. Keep in mind that the Arduino simply reads inputs, perhaps does some calculations and sets outputs. It’s fast and the code runs in firmware. The BBB, Pi and CHIP are multi-tasking, multi-user Linux machines and so using digital and PWM outputs behave a little differently than on the Arduino speed demons.
Next time, we’ll look at the hardware side of digital and PWM outputs. I’ll summarize which device works with which control technique and discuss a few engineering aspects you’ll need to keep in mind when actually using digital outputs in your physical computing projects.
The New Stack is a wholly owned subsidiary of Insight Partners, an investor in the following companies mentioned in this article: Shelf.