PWM and Arduino: fading an LED and playing melodies on a Piezo Speaker

Submitted by fabio on Mon, 2010-07-26 12:49.

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:

Pulse Width Modulation (PWM) with Arduino analogWrite()

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.

Picture of the circuit for using analogWrite to fading an LED

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:

Circuit to connect a Piezo Speaker to the Arduino board

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!

References

Posted in:

PWN necessary in 2nd example?

Submitted by blaise (not verified) on Mon, 2011-09-12 10:30.

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

Submitted by fabio on Mon, 2011-09-12 11:07.

Good observation. Looks like the code implement PWM in software, without using analogWrite() thus it should work on any digital pin.

Post new comment

The content of this field is kept private and will not be shown publicly.
If you have a personal or company website insert its address in the form http://www.example.com/ .
  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd> <pre> <del> <img> <h2> <h3> <h4> <b> <video> <sub> <sup>
  • Lines and paragraphs break automatically.
  • Images can be added to this post.
  • You may use [inline:xx] tags to display uploaded files or images inline.
  • You may insert videos with [video:URL]
  • Each email address will be obfuscated in a human readable fashion or (if JavaScript is enabled) replaced with a spamproof clickable link.

More information about formatting options