- Development Software Suite
- Version Control
- Embedded C and IAR Primer
- Firmware System
- LED Basic
- Button Interface
- Debug Interface (for Module Exercise)
A simple and common audio source in an embedded system is a piezoelectric buzzer since they can be easily driven with a square wave directly from the processor with very minimal current draw. They will reproduce tones in the full audible range of a human ear which is about 50Hz to 15kHz.
The challenge in creating audio with a microcontroller is the various calculations needed to get the actual frequency correct, and then generating a constant frequency square wave without impacting the rest of the program. Therefore an API that does these things is very useful.
If you are following along sequentially in the EiE modules, this is the first task that will make use of one of the SAM3U2’s functional peripherals. If you explore the firmware, you will not find any code that toggles the output lines to the buzzer directly. All output is handled by the peripheral that is simply set up with the frequency and duty cycle required to create the audio signal.
There are no unique types defined for the audio API, but constants are defined in configuration.h that assign names BUZZER1 and BUZZER2 to the buzzers and the appropriate hardware pins. BUZZER2 is on the left side of the ASCII development board.
The following functions are defined in the board-specific firmware file (eief1-pcb-01.c). They are quite straight forward so they do not require their own files. The functions may be used by any application in the system.
- void PWMAudioSetFrequency(u32 u32Channel_, u16 u16Frequency_) – Configures the buzzer with the desired frequency. Parameter u32Channel_ is either BUZZER1 or BUZZER2; u16Frequency_ is in Hertz and should be in the range 100 – 20,000 though higher and lower frequencies are allowed. The buzzer remains in its current on or off state.
- void PWMAudioOn(u32 u32Channel_) – Turns on the buzzer specified in u32Channel_. The buzzer will run at whatever frequency it was last configured to. If the buzzer is already on, it will stay on.
- void PWMAudioOff(u32 u32Channel_) – Turns off the buzzer specified in u32Channel_. If the buzzer is already off, it will stay off.
Start with the Master code branch from GitHub and create a new Buzzer branch. The first task is to toggle a 500Hz tone on when BUTTON0 is pressed.
In UserApp1Initialize() add this line:
In UserAppSM_Idle(), add the following:
Music (sort of)
Now you will assign a tone to each of the system buttons that plays while the button is held. Two tones may NOT be played at the same time on the same buzzer, however the ASCII LCD board can play two different tones at the same time (one on each speaker). To keep this example simple, we will only use BUZZER1. If more than one button is pressed, the last button pressed will set the current tone.e.
Modify the first example code in UserApp1SM_Idle() by placing a call to WasButtonPressed(BUTTON0) before the code to turn on/off the buzzer. In the function include a call to PWMAudioSetFrequency() to set BUZZER1 frequency to 262Hz.
Build and test the code. The only difference at this point from the previous example is that the tone is lower frequency. Try halting the code while the buzzer is on. You should notice that with the processor halted, the buzzer is still active. Even though the peripheral is on the microcontroller, it is not halted when the main processor is halted so it will keep generating the tone.
Copy the WasButtonPressed() code and update for BUTTON1 immediately after the BUTTON0 check but before activating the buzzer. Use 294Hz for the frequency set here.
Copy the code twice more for BUTTON2 and BUTTON3 and set frequencies 330Hz and 392Hz, respectively.
Replace the code that checks if BUTTON0 is pressed to play the sound, to a statement that checks if ANY of the BUTTONS are on. This ensures the tone is active for any button. The actual frequency that gets played will depend on the last button that was pressed. It is nearly impossible to press two buttons at exactly the same time, so if you try you might not get exactly the result you expect.
Build and test the code. You should be able to play “Mary had a little Lamb” on the ASCII board with the frequencies that have been set.
BUTTON2, BUTTON1, BUTTON0, BUTTON1, BUTTON2, BUTTON2, BUTTON2 (hold)
BUTTON1, BUTTON1, BUTTON1 (hold)
BUTTON2, BUTTON3, BUTTON3 (hold)
BUTTON2, BUTTON1, BUTTON0, BUTTON1, BUTTON2, BUTTON2, BUTTON2, BUTTON2, BUTTON1, BUTTON1, BUTTON2, BUTTON1, BUTTON0 (hold)
This exercise requires the use of the Debug API.
Add a full octave (12 notes including sharps and flats) of notes by using keyboard keys through the debug interface. Use keyboard keys Z, X, C, V, B, N, and M for the musical notes C, D, E, F, G, A, B. Use keyboard keys S, D, G, H, and J for the sharps/flats. With each letter input, play the corresponding note for 500ms. The frequencies of the notes can be found in music.h. To make the second part of the exercise easier, use the third octave frequencies (C3 is 131Hz).
Use BUTTON0 through BUTTON3 to set the octave so you can play 4 octaves of notes. The buttons can be used to set a multiplier to a base set of frequencies for the full scale of notes. It’s probably a good idea to indicate the current octave with an LED.
Try to play a song, or at least try to play every note from C3 to B6 in order.
[STATUS: RELEASED. LAST UPDATE: 2023-NOV-08]