Aquaticus

Projects for geeks

Play music

11 Aug 2009

Playing music is a bit more complicated then generating simple “beep”. To play a sound AVR must generate music notes in special order. Hardware is the same as for beep example.

A note in the simplest form is just a PWM signal with specific frequency. Because AVR can generate only square wave, the sound is similar to the sound generated by the early ’80 home computers.
As we need variable frequency of the signal, it is best to use Phase/frequency correct or CTC PWM mode.
Those modes offer easy method of changing frequency by ICRx register. In this example phase/frequency correct mode is used (timer1), frequency of the wave is:

TOP value is taken from ICR1 register, so to change frequency a new value must be written to ICR1.

Below are frequencies of notes and appropriate ICR1 values for one octave.
Note that listed ICR values are only valid for 1MHz system clock and 1 prescaler divider or
8MHz system clock and 8 prescaler divider.

Note Frequency ICR1 value
C4 261.63 Hz 1911
D4 293.66 Hz 1703
E4 329.63 Hz 1517
F4 349.23 Hz 1432
G4 392.00 Hz 1276
A4 440.00 Hz 1136
B4 493.88 Hz 1012

ICR1 value is computed from:

fnote — note frequency
N — prescaler divider
fclock — system clock

For full list of frequencies look at list of notes. The frequency formula can be found in Wikipedia article

ICR values

You must compute ICR values every time clock or prescaler is changed. To make life easier, you can use special Perl script (located in pwm_music.zip) to automatically do the dirty job.
The script creates C language header file that contains human-friendly note symbols with appropriate ICR1 values. You do not need to manually compute ICR values for all notes.
To use script you need Perl interpreter installed.

Usage is as follows:

perl notegen.pl 1 1 >notes.h

The above generates a new file notes.h for 1MHz system clock (first argument) and prescaler divider 1 (second argument).
The output file looks like:

#define C4   1911 	/* PWM: 261.64 Hz, note freq: 261.63 Hz, error 0.01% */
#define D4   1703 	/* PWM: 293.60 Hz, note freq: 293.66 Hz, error 0.02% */
#define E4   1517 	/* PWM: 329.60 Hz, note freq: 329.63 Hz, error 0.01% */
#define F4   1432 	/* PWM: 349.16 Hz, note freq: 349.23 Hz, error 0.02% */
#define G4   1276 	/* PWM: 391.85 Hz, note freq: 392.00 Hz, error 0.04% */
#define A4   1136 	/* PWM: 440.14 Hz, note freq: 440.00 Hz, error 0.03% */
#define B4   1012 	/* PWM: 494.07 Hz, note freq: 493.88 Hz, error 0.04% */

In the comments you can see the frequency of the note and the PWM frequency that will be generated when playing this sound and the error.

By default values for 5 octaves are generated. You get ICR values from C3 (130.81 Hz) to B7 (3951.07 Hz).

For one note, the script generates more then one #define line with the variations of note symbol. The table shows example preprocessor definitions to give you an idea. General rule is that lower and upper case names are available, x is for ♯ and b is for ♭. The x and b letters can be used as suffix of prefix.

Note C preprocessor defs
C3 C3 c3
C♯3 C3x xC3 c3x xc3 Cis3 cis3
D♭3 D3b bD3 d3b bd3

Because in some countries H is used instead of B, appropriate H names are generated as well.
For more information see script documentation (execute it without any parameters).

Playing music

As mentioned earlier, to play music a sequence of PWM signals with appropriate frequency must be generated.

Here is the beginning of popular Frère Jacques melody:

To play the song, AVR must generate notes: G4 A4 B4 G4. Technically speaking it must generate PWM signals with frequencies [Hz]: 392.00, 440.00, 493.88, 392.00.
In practice it means appropriate values have to be written to ICRx register. PWM signal have to be generated as long as note duration.
When silence is required, PWM generation must be stopped for appropriate period of time.

Steps to play a sound:

  1. Convert note name to frequency, e.g. C4 to 261.63 Hz
  2. Convert note frequency to corresponding ICR1 value, e.g. 261.63 Hz to 1911
  3. Write appropriate value to ICR1 register, e.g. ICR1=1911;

Code snippets

Configure OC1B pin as output

DDRD |= _BV(DDD4); //OC1B as output

Configure timer1 for PWM

TCCR1A |= _BV(COM1B1);  //Clear OC1A/OC1B on compare match
TCCR1B |= _BV(WGM13) //mode 8, PWM, Phase and Frequency Correct (TOP value is ICR1) | _BV(CS11); //prescaler(8)

Set 16 bit ICR1 register.

const int note = 1911;
 
ICR1H = (note >> 8); //first set the high byte
ICR1L = note;        //now the low byte

Play note C4

//ICR value for 523Hz (C4)
#define C4   1911
 
//set duty cycle (affects volume)
OCR1B = 100;
 
//set frequency
ICR1H = (C4 >> 8);
ICR1L = C4;

Sample code

Chip ATmega16
System clock 8MHz
Compiler AVR Studio + GCC
AVR hardware timer1
External hardware piezo buzzer

See attached pwm_music.zip sample code. There are two main functions. First InitMusic() which initialize timer1 for PWM in Phase and Frequency Correct mode. The second one PlayMusic() plays melody stored in the table.

The table should contain pairs of integers. The first number is the value to be written to ICR register (note pitch, preprocessor defs like C4 can be used), the second value is note duration, 8 means 1/8, 16 means 1/16 and so on. Value of 1 (or P or PAUSE preprocessor defs) indicates a silence of an equivalent duration. The last element of the table must be 0 or MUSIC_END preprocessor def.

For example to play first 4 notes of Frère Jacques you could declare the following table:

#include "notes.h" //notegen.pl output
 
//Table consists of 4 notes (1/4 duration each)
const int Music[] = {G4, 4, A4, 4, B4, 4 G4, 4, MUSIC_END};

Using hardware PWM generation it is quite easy to play music using interrupts. This makes it easy to play music in the background not disturbing primary tasks of microcontroller. In the example interrupts were not used to make it simpler.

Files: 
AttachmentSize
pwm_music.zip8.87 KB