PWM and Arduino: fading an LED and playing melodies on a Piezo Speaker
In the past blog posts, we already saw how to read and write digital signals on the Digital Input Output pins of the Arduino. We also used the analog reading capabilities of Arduino to read values from variable resistance components such as potentiometers, thermistors, or LDRs.
We still don't know anything about how to produce an Analog Output signal with Arduino. In this post, will cover it.
Pulse Width Modulation (PWM): analog outputs with digital means
Digital boards and processors, like the Arduino board and its ATMega 328 microcontroller, usually have some problems providing an Analog Output, a variable signal which can range from eg 0 to 5V. This is a consequence of the fact that they are digital components, so they work using 0 and 1, they are not capable of such a variable output.
Fortunately, there is the Pulse Width Modulation or PWM technique which permits getting an analog result using digital means.
The Arduino PWM tutorial describes how PWM works:
Digital control is used to create a square wave, a signal switched between on and off. This on-off pattern can simulate voltages in between full on (5 Volts) and off (0 Volts) by changing the portion of the time the signal spends on versus the time that the signal spends off. The duration of "on time" is called the pulse width. To get varying analog values, you change, or modulate, that pulse width. If you repeat this on-off pattern fast enough with an LED for example, the result is as if the signal is a steady voltage between 0 and 5v controlling the brightness of the LED.
In Arduino, we have the function analogWrite() which implements PWM. It gets a parameter on a value between 0 and 255. You can see some examples of square waves generated with analogWrite() in the picture below:
Fading an LED using PWM with Arduino analogWrite()
Let's try the PWM analogWrite() implementation of Arduino with a simple example. Let's try fading on and off an LED. We just need a 1K Ohm resistor in series with an LED connected to PWM capable digital pin on Arduino. We'll use pin 9.
We'll use the code coming from the Fading tutorial on Arduino.cc
/* Fading This example shows how to fade an LED using the analogWrite() function. The circuit: * LED attached from digital pin 9 to ground. Created 1 Nov 2008 By David A. Mellis Modified 17 June 2009 By Tom Igoe http://arduino.cc/en/Tutorial/Fading */ int ledPin = 9; // LED connected to digital pin 9 void setup() { // nothing happens in setup } void loop() { // fade in from min to max in increments of 5 points: for(int fadeValue = 0 ; fadeValue <= 255; fadeValue +=5) { // sets the value (range from 0 to 255): analogWrite(ledPin, fadeValue); // wait for 30 milliseconds to see the dimming effect delay(30); } // fade out from max to min in increments of 5 points: for(int fadeValue = 255 ; fadeValue >= 0; fadeValue -=5) { // sets the value (range from 0 to 255): analogWrite(ledPin, fadeValue); // wait for 30 milliseconds to see the dimming effect delay(30); } }
This is what it produce:
PWM to play music on a Peizo Speaker
The same ideas behind the PWM are used in this interesting program from the PlayMusic tutorial to play music on a Piezo Speaker.
It will use the following circuit:
Here is the code:
/* Play Melody
* -----------
*
* Program to play a simple melody
*
* mytones are created by quickly pulsing a speaker on and off
* using PWM, to create signature frequencies.
*
* Each note has a frequency, created by varying the period of
* vibration, measured in microseconds. We'll use pulse-width
* modulation (PWM) to create that vibration.
* We calculate the pulse-width to be half the period; we pulse
* the speaker HIGH for 'pulse-width' microseconds, then LOW
* for 'pulse-width' microseconds.
* This pulsing creates a vibration of the desired frequency.
*
* (cleft) 2005 D. Cuartielles for K3
* Refactoring and comments 2006 clay [dot] shirky [at] nyu [dot] edu
* See NOTES in comments at end for possible improvements
*/
// mytoneS ==========================================
// Start by defining the relationship between
// note, period, & frequency.
#define c 3830 // 261 Hz
#define d 3400 // 294 Hz
#define e 3038 // 329 Hz
#define f 2864 // 349 Hz
#define g 2550 // 392 Hz
#define a 2272 // 440 Hz
#define b 2028 // 493 Hz
#define C 1912 // 523 Hz
// Define a special note, 'R', to represent a rest
#define R 0
// SETUP ============================================
// Set up speaker on a PWM pin (digital 9, 10 or 11)
int speakerOut = 9;
// Do we want debugging on serial out? 1 for yes, 0 for no
int DEBUG = 1;
void setup() {
pinMode(speakerOut, OUTPUT);
if (DEBUG) {
Serial.begin(9600); // Set serial out if we want debugging
}
}
// MELODY and TIMING =======================================
// melody[] is an array of notes, accompanied by beats[],
// which sets each note's relative length (higher #, longer note)
int melody[] = { C, b, g, C, b, e, R, C, c, g, a, C };
int beats[] = { 16, 16, 16, 8, 8, 16, 32, 16, 16, 16, 8, 8 };
int MAX_COUNT = sizeof(melody) / 2; // Melody length, for looping.
// Set overall tempo
long tempo = 10000;
// Set length of pause between notes
int pause = 1000;
// Loop variable to increase Rest length
int rest_count = 100; //<-BLETCHEROUS HACK; See NOTES
// Initialize core variables
int mytone = 0;
int beat = 0;
long duration = 0;
// PLAY mytone ==============================================
// Pulse the speaker to play a mytone for a particular duration
void playmytone() {
long elapsed_time = 0;
if (mytone > 0) { // if this isn't a Rest beat, while the mytone has
// played less long than 'duration', pulse speaker HIGH and LOW
while (elapsed_time < duration) {
digitalWrite(speakerOut,HIGH);
delayMicroseconds(mytone / 2);
// DOWN
digitalWrite(speakerOut, LOW);
delayMicroseconds(mytone / 2);
// Keep track of how long we pulsed
elapsed_time += (mytone);
}
}
else { // Rest beat; loop times delay
for (int j = 0; j < rest_count; j++) { // See NOTE on rest_count
delayMicroseconds(duration);
}
}
}
// LET THE WILD RUMPUS BEGIN =============================
void loop() {
// Set up a counter to pull from melody[] and beats[]
for (int i=0; ihttp://www.arduino.cc/en/Tutorial/PlayMelody
* ADD code to make the tempo settable by pot or other input device
* ADD code to take tempo or volume settable by serial communication
* (Requires 0005 or higher.)
* ADD code to create a mytone offset (higer or lower) through pot etc
* REPLACE random melody with opening bars to 'Smoke on the Water'
*/
This is the result:
Conclusions
analogWrite() is really a handy way to produce an analog output using Arduino. Looking forward to control some motors with it!






PWN necessary in 2nd example?
Hi,
Thanks for your blog.
In the 2nd example, you do not use analogWrite() so I am wondering whether a PWM pin is necessary or whether this would work on any Arduino pin.
thanks,
Blaise
Good observation. Looks like
Good observation. Looks like the code implement PWM in software, without using analogWrite() thus it should work on any digital pin.
Post new comment