I have a simple 8 Ohm speaker which has been gathering dust for quite a while, so I figured I should start using it in some way. The most obvious project was building a simple WAV player, which actually turned out to be simpler than I expected.
You will need some mikrocontroller with PWM capabilities and SPI (not very hard requirements); I will use an Atmega328p since a) I have some lying around and b) they are the controllers used in the Arduino, so you can use the Arduino hardware, which is really convenient for debugging since it has an integrated UART-USB-converter. However, we will program in C, bypassing the Arduino IDE and libraries - after all, including a library and calling a "play_wav()" function is not very teaching.
On the hardware side, besides the mikrocontroller and the speaker, you will need:
- A bipolar npn transistor.
- A resistor, depending on the amplification of your transistor - 470 Ohm should easily do it. For the beginning, you may also want to use another resistor to limit the current through the speaker, just to be on the safe side.
- An SD card for storing the WAV files and some way to connect it to your breadboard. You can for example use a micro SD card, plug it into a microSD-to-SD adapter and then solder wires onto the adapter.
- SD cards DO NOT WORK with 5V. If your microcontroller runs with 5 Volt, you need a level shifter.
- some capacitors are useful to improve sound quality.
- some wires.
Let us start with the wiring of the speaker. It is fairly simple: Connect one side of the speaker to the supply voltage, the other side to ground via the collector and emitter of the transistor. Then connect the base of the transistor via a resistor to the PWM pin on your mikrocontroller you want to use, and we are done.
Now we can already play some simple sounds. We only have to toggle the PWM pin at an audible frequency, say something between 200 and 1000 Hertz. You can either achieve this by setting up suitable delays or timers and switch the pin whenever a delay or timer runs out; that approach has the advantage that you can create arbitrary frequencies. Since we will later on have to use the PWM functionalities anyway, we will use a dedicated PWM pin. For this part, you should add a small resistor between the speaker and VCC - the sine wave sound we will play can be quite loud.
Here is the source code for the Atmega328 used on the Arduino, taken right from Atmel Studio:
Pin B1 (or Pin 9 on the Arduino) is the PWM pin for timer 1; we set timer 1 to 8-bit PWM mode by setting WGM10 and WGM12, and setting CS12 sets the prescaler to 256. Finally, the OCR1A register contains the length of the duty cycle; we set it to 128 so that the pin is high half of the time.
These settings correspond to a frequency of
16 MHz (frequency of the Arduino) / (256 (length of a PWM cycle) * 256 (prescaler)) = 244 Hz
If you are using an Atmega328p, you probably have to tweak these numbers since it may not be clocked at 16 MHz. By default, the internal oscillator clocks the Atmega at 1 MHz, so using a prescaler of 16 instead of 256 should play the same sound.
Compiling the code and uploading it, you should hear the speaker make a - rather unpleasant - tone. In case you don't know, you upload the hex file to the Arduino by navigating to the directory containing the hex file via the command line and then executing the following command while pressing Reset on the Arduino:
Ok, we have (hopefully) sound - even though its rather annoying sound at this point...
Before explaining what we have to do to play actual sound, let me show you a program for the Atmega328p which plays actual sound. Wiring is as before, except that you should remove the resistor between speaker and VCC. If you want to run this code on an Arduino, you have to account for the higher frequency (16 MHz), compared to the 8 MHz of the internal oscillator of an Atmega (I remove the standard prescaler of 8 in the first two lines of main(), switching from default 1 MHz clockrate to 8 MHz). Just change all lines "sample_count = 2" into "sample_count = 4" and you should be fine. The PWM pin used is Pin B1, or Pin 9 on the Arduino layout.
Be careful when copying this program - the array extends quite far to the right.
The HTML version of this sourcecode is unfortunately so large that either the blogger software or (more likely) my browser refuse to handle it properly while editing, so the explanation of the source code will be postponed to an upcoming post.