Connecting Piezo Speaker to ATmega32
An ATMega32 sound generator code is extremely simple to implement. Almost any GPIO pin can drive a piezo buzzer, and the output quality is fine for producing some beeps. The code shown here is the simplest one I remember using basic physics, and since it does not use PWM it could be implemented on almost any microcontroller.
The program simply toggles the output of a pin, between 5 V and 0 V, very fast. The square wave generated drives a piezoelectric speaker directly. Here is a sample of the output I recorded; I Hope your computer has sound switched ON.
Example Sound
Here is a MP3 recording to show that this code actually works. I test all the code thoroughly before publishing.
Programming Terminology
SPEAKER_PORT |= (1 << SPEAKER_PIN)
The programming terminology is very simple as you can see. This will simply send a 1 to pin 7 on my development system where the speaker is connected.
SPEAKER_PORT &= ~(1 << SPEAKER_PIN)
This will send a 0 to the speaker pin. As you can see, by sending ones and zeros very quickly in a loop, the speaker makes a sound!
Delay Function
_delay_ms(x)
Luckily, some built-in functions such as delay are useful to use. In this function the parameter x is in milliseconds.
Prototype Sound Function
PLAYNOTE(float duration, float frequency)
Here is my own prototype function that works really well. I decided to call it PlayNote because essentially that is what it does, it plays a note. The note frequency in Hertz, and the duration in milliseconds, passes to the function.
Sound Generator Principle
The sound principle is very simple and anyone with basic knowledge should understand it. Wavelength (w) and frequency (f) are related, by the equations below.
f =1/w, and w=1/f
A complete period is one wavelength (w); therefore, a half period is w/2. The number of cycles is simply the duration of the note divided by w. This value is used in a "for" loop to count to the number of cycles required.
In this case, w is the wavelength calculated from frequency using the formula w = 1 / f. We output 5 V for a length of time determined by w/2, and then output 0 V, for the same length of time.
Do not expect a symphony; at most, this will sound like R2D2. This circuit uses a cheap 50-cent piezo, and square waves to generate sound. At most, it will output beeps of different frequencies and durations. This is very useful, in error debugging to produce beeps similar to BIOS beep codes used on PC motherboards.
Portability
This program does not use Pulse Width Modulation (PWM), or internal timers to generate the sound. As a result, almost any microcontroller and any pin could work, and it should even work with PIC microcontrollers!
Generating Sound Program Code
1: /********************************************
2: Author: Peter J. Vis
3: Title: R2D2
4:
5: Microcontroller: ATmega32
6: Crystal: 16 MHz
7: Platform: Development System
8:
9: LIMITATIONS:
10: No psrt of this work may be used in commercial
11: applications without prior written permission.
12:
13: This work cannot be reproduced for blogging
14: Copyright Protected. All Rights Reserved.
15:
16: PURPOSE:
17: This program is used to test the Piezo
18: speaker by generating musical notes.
19:
20: CIRCUIT:
21: Piezo Speaker connected to PortC pin PC7.
22: An 8 Ω voice coil speaker could also be
23: used with a 2 Ω resistor in series.
24:
65: *********************************************/
66:
67: #define F_CPU 16000000UL
68:
69: #include <avr/io.h>
70: #include <util/delay.h>
71:
72: #define SPEAKER_PORT PORTC
73: #define SPEAKER_DDR DDRC
74: #define SPEAKER_PIN 7
75:
76: // My Prototype is simply called PLAYNOTE.
77: void PLAYNOTE(float duration, float frequency);
78:
79: int main(void)
80: {
81:
82: PLAYNOTE(400,880); // Musical note 880 Hz
83: PLAYNOTE(400,932);
84: PLAYNOTE(400,988);
85: PLAYNOTE(400,1047);
86: PLAYNOTE(400,1109);
87: PLAYNOTE(400,1175);
88: PLAYNOTE(400,1244);
89: PLAYNOTE(400,1319);
90: PLAYNOTE(400,1397);
91: PLAYNOTE(400,1480);
92: PLAYNOTE(400,1568);
93: PLAYNOTE(400,1660); // Musical note 1660 Hz
94:
95: }
96:
97: // ---------------------------------------
98: // The PLAYNOTE function must be given the
99: // duration, and frequency values.
100: // The duration is how long the note
101: // is played for. The frequency value
102: // determines the musical note.
103: // ---------------------------------------
104:
105:
106: void PLAYNOTE(float duration, float frequency)
107: {
108: // Physics variables
109: long int i,cycles;
110: float half_period;
111: float wavelength;
112:
113: wavelength=(1/frequency)*1000;
114: // Standard physics formula.
115: cycles=duration/wavelength;
116: // The number of cycles.
117: half_period = wavelength/2;
118: // The time between each toggle.
119:
120: // Data direction register Pin 7
121: // is set for output.
122: SPEAKER_DDR |= (1 << SPEAKER_PIN);
123:
124: for (i=0;i<cycles;i++)
125: // The output pin 7 is toggled
126: // for the 'cycles'number of times.
127: // --------------------------------
128:
129: {
130: _delay_ms(half_period);
131: // Wait 1 half wavelength.
132: SPEAKER_PORT |= (1 << SPEAKER_PIN);
133: // Output 5 V to port Pin 7.
134: _delay_ms(half_period);
135: // Wait 1 half wavelength.
136: SPEAKER_PORT &= ~(1 << SPEAKER_PIN);
137: // 0 V at port pin 7.
138: }
139:
140: return;
141: // Return to main()
142:
143: }
Limitations: Notice that in this code, half_period is a float variable that feeds the standard built-in _delay_ms function, which beginners usually use with an integer constant. Before you lurch up on your desks to press the "hit" button or send me an email, let me assure you that it is not an error. You simply need to understand the operation of the delay function. In reality, the avr-gcc toolchain usually links functions from the library so that you can pass a variable to it. There are limitations to this method such as memory usage and accuracy of sound; however, this code is just an example to make a beep sound. You can always work around the limitations by making your own delay function if you wish.
The official Atmel site also suggests that the new implementation of _delay_ms(double __ms) with __builtin_avr_delay_cycles(unsigned long) support is not backward compatible. Therefore, please make sure you are using the correct version of the function and library.
Here is the listing together with the code.