Buzzer Decision
Hardware testing for the buzzer was more of a choice than checking if it actually worked. Getting it to work was fairly simple by just setting a PWM (Pulse Width Modulation) pin to a certain frequency and duty cycle and listening for the tone that the buzzer outputted. The real choice came when I had to decide whether to use an analogue pin or a PWM pin to create the tones for the buzzer. Firstly I weighed up the pros and cons of each method:
PWM signal
Pros:
- PWM is able to produce an output while the microcontroller is executing other parts of the code which would be crucial when developing a game where audio and visual need to happen simultaneously.
- It is very simple to alter the frequency and therefore the pitch of the tone.
- There are several ways of altering the volume of the output both with their own benefits and disadvantages. The first is to alter the duty cycle of the PWM signal and while this does change the volume of the buzzer to some extent it does not directly alter the amplitude of the PWM output. This means that the relationship between duty cycle and volume will not be linear and so not easily controllable. There is no way to alter the amplitude of the PWM pin output itself but the second method would be to place a potentiometer (variable resistor) between the buzzer and output pin. This method provides us with a good level of control over the volume, however the excess energy is dissipated over the potentiometer thereby wasting some power (albeit a very small amount). Also, by using this method we cannot alter the volume in the code itself and this must be done externally. This should not prove a problem though considering the audio fidelity of the buzzer is clearly not the best so small alterations in the amplitudes of certain frequencies would likely have little to no effect on the audible output of the buzzer (It would also create more work to be done in code).
Cons:
- As well as not having a linear behavior with volume, altering the duty cycle changes the sound of each tone in the same way that a sine wave sounds different to a square wave.
- Using PWM we are not able to accurately reproduce different sounds i.e. only a square waveform is ever outputted
DAC signal (Digital to Analogue Converter)
Pros:
- The DAC is able to produce any range of waveforms with different sounds including Sine, Square, Triangle, Sawtooth and even random noise! Much more versatile in that it could theoretically be used to get the buzzer to create several tones at once which would be invaluable when generating chords and similar sounds.
- It is able to alter the amplitudes of any signals in order to vary the volume of each sound and so no external components would be required (Unless the user should be able to vary the volume)
Cons:
- In order to be able to output such a variety of sounds the analog output needs to be changed regularly and so it is a lot harder to create sounds using the DAC whilst simultaneously executing other code.
- Using the DAC increases the complexity of changing the frequency and waveform of the output
Testing and Conclusions
To make the decision between which output method should be used I wrote some simple code that would use a potentiometer to select the frequency each method would output at and a button that would change the type of waveform being outputted by the DAC. The relevant information is displayed on the Nokia LCD screen I have been using. Also, another potentiometer is used to alter the duty cycle of the PWM signal to test the sound quality when increasing or reducing the volume. I did not implement the same volume altering behaviour in the DAC as this has the same effect as having a potentiometer between the buzzer and pin. When testing each method the connection is physically changed by hand from the PWM pin at address PTC10 and the DAC pin at DAC0_OUT.
I encountered several problems during testing one of which is immediately visible from the pictures above. I set the potentiometer range to be from 1Hz to 20kHz in the code (20Hz to 20kHz is the range of human hearing), however this seems to be too large and the variation of voltage being detected across the potentiometer that determines the frequency is quite large. This can be seen on the LCD display as rapidly changing values of the frequency in a range of about 10Hz each side of the desired value. The variation in frequency gave the tone that the buzzer was outputting a noisy sound so no clear tones could be heard. This was remedied after changing the code to only set a new frequency when a button was pressed, causing an interrupt to occur. Now that the buzzer was outputting clear tones I came to the conclusion quite quickly that changing the waveform that the DAC outputted did not really alter the sound of the tone much which is to be expected from a piezo-electric buzzer as it isn't created to have the best audio fidelity
Another conclusion I came to after listening to the buzzer was that altering the duty cycle to affect the volume of it did not work very effectively at all, however it was noted that the signal could be stopped entirely if a duty cycle of 0.0 or 1.0 was applied which makes sense because if the voltage was either always on or always off then there would be no frequency and the buzzer would not vibrate and create an audible tone.
In the end the choice to use PWM or DAC relied heavily on its ability to create tones while executing other code at the same time such as changing the LCD display and while I could use multi-threading in my application and have the audio on one thread using the DAC and the main code on another, this would require a lot more work than it would to just edit the properties of a PWM signal when its frequency needed to be changed. There is also practically no audible difference between a PWM pin and the DAC pin when using this particular buzzer so the only sacrifice made is the ability to output several frequencies at once. Even without testing I can already say from the quality of the buzzer that it is unlikely to output good quality chordal sounds anyway so for the rest of my project I will be using the buzzer with PWM.
Notes, Tunes and their Implementation
At this point we need more information in order to accurately produce tones of different pitch. The information we need links the note to its corresponding frequency and can be found on http://www.phy.mtu.edu/~suits/notefreqs.html (If the link does not work a quick google search will provide the information). A header file was created with notes from B0 to DS8 (D sharp) defined with each of their frequencies e.g #define NOTE_C4 262 . In the same header file below the definitions, const float arrays are defined to hold different tunes as shown in this example: const float test1[] = {NOTE_C4,0.5,NOTE_D4,0.5,NOTE_E4,0.5,NOTE_F4,0.5,NOTE_G4,0.5,NOTE_A4,0.5,NOTE_B4,0.5,NOTE_C5,0.5,NULL}; . This array is defined in a specific way so that the first component is the note frequency, then the second component is how long in seconds that frequency should be held for, and finally the NULL at the end lets our code know that we have reached the end of this specific tune. In the main code I include the header file containing the notes and tunes and then setup a Timeout interrupt. This Timeout is the crucial part of this code as it sets the next interrupt to happen after the time for a note has passed; using interrupts in this way is what allows me to play tunes while continuing to execute other code. For plagiarism purposes I will not be posting the code here as I plan to use it in the final project, however I can describe the core details that allow it to work. Firstly I call a function and pass the array pointer of the tune I wish to play to it, this function then copies the array pointer to a volatile global variable that stores it safely as it is likely to be altered in an interrupt service routine. Then still within the first called function I attach the ISR (interrupt service routine) function to the Timeout to occur immediately, then this function ends. The purpose of the first function is just to make calling the tune file easier than having to attach a Timeout myself in code and having to copy the array pointer to the global variable.
The ISR function is what does all the legwork in this setup. Firstly it checks to see if what the array pointer is pointing at is a NULL and if so then it turns the PWM duty cycle to 0 and ends, however if it is not NULL then it sets the PWM period equal to one over what the array pointer is pointing at which will be the frequency of the note. Next the ISR is attached to the Timeout again and the time of the Timeout is the next component of the array and to find this we increment the array pointer and find what it is pointing to. Then once the Timeout is setup we increment the array pointer once more before ending the function. This effectively allows us to run through the array of notes and delays using only interrupts and all with only one line of code in the main function ( playSound("soundArrayName"); ).
I can imagine that there are probably much more elegant ways of doing exactly the same thing probably utilizing threads to concurrently run two sets of code, however I have very little knowledge of how to multi-thread an application. Any feedback on how to do this or any improvements I could make are much appreciated!
Top Comments