Lab 4: Digital Audio
Introduction
This lab was an introduction to working with our STM32 microcontroller boards. After learning how to read a reference manual and datasheet and learning how clocks work on a microcontroller, we were tasked with configuring clocks, timers, and I/O in order to drive a speaker to play a song. A lot of this lab required close reading and attention to detail. It was a good lesson in taking my time and using the Segger Embedded Studio debugger to my advantage. I had a lot of fun with this lab, in the end playing "September" by Earth, Wind, and Fire on the speaker! See the E155 Lab 4 Page for a more detailed overview of the specifications and instructions for this lab.
All code written for this lab can be found on my E155 Lab 4 Repository on Github.
Setup and Circuitry
The circuit diagram for this circuit is shown to the left. The circuit is relatively simple: it contains an LM386 audio amplifier to drive the speaker
while ensuring that current draw stays within allowable levels for the microcontroller as well as a potentiometer to allow for volume control. The potentiometer especially came in handy while testing the
code over and over again!
Software and Timing
In order to output a square wave signal from the microcontroller, I took advantage of the Timer 15 and Timer 16 registers. Both are able to be configured to a PWM mode and connected to a GPIO pin
in alternate function mode in order to produce a square wave output. Each Timer has its own counter that is compared to a value set when configuring the register. When the counter is less than this
compare/contrast value, it outputs a high signal (or 1). When the counter is greater or equal to this capture/compare value, it outputs a low signal (or 0). Additionally, an auto-reload value is set
such that when the counter is equal to this value, it resets to zero. Therefore, the auto-reload value will set the period of our signal and the capture/compare value will set the duty cycle.
In this lab, I used a duty cycle of 0.5 for all sound outputs so the capture/compare value was always set to half of the auto-reload value.
For this lab, we were tasked with producing pitches with accuracies within 1% across the frequency range of 220-1000 Hz. To achieve this constraint, I ran a timing analysis using Excel and the equations listed below. Because I used phase-locked-loop to clock the microcontroller at 80 MHz, I needed to use a prescaler to produce these lower frequencies. The equations I used are detailed below. \[ f_{CNT} = \frac{f_{clk}}{PSC + 1} \] \[ \frac{1}{f_{sound}} = \frac{1}{f_{CNT}} * cycles \] \[ cycles = \frac{f_{clk}}{f_{sound} (PSC + 1)} \] \[ f_{sound} = \frac{f_{clk}}{cycles (PSC + 1)} \]
Where \(f_{CNT}\) is the frequency at which the counter changes, \(f_{sound}\) is the frequency of the output sound signal, \(cycles\) is the number of times the counter counts up or the auto-reload value, \(f_{clk} \) is the system clock frequency, and \(PSC\) is the system clock pre-scaling factor. The minimum and maximum duration and frequency supported can be calculated by considering when the auto-reload value is set to 0 and when it is set to its max value, which is \(2^{16} - 1 = 65535\) for a 16 bit register.
Below, I have included the table detailing the values I calculated to ensure that the frequencies outputted by Timer 16 produced pitches in the range of 220-1000 Hz within 1% accuracy. I chose to use a prescaler value of 999 for Timer 16, which output a PWM signal to the speaker. Given a prescaler value of 999, the maximum frequency able to be produced is 80 kHz using an auto-reload value of 0. The minimum frequency able to be produced is 1.22 Hz using an auto-reload value of 65535.
I performed a similar analysis in order to determine the prescaler value needed to ensure proper durations for all notes. A table is shown below detailing the calculated values. I chose to use a prescaler of 1999 for Timer 15, which was used to produce delays in milliseconds. Given a prescaler value of 1999, the maximum duration able to be produced is 1.64 seconds using an auto-reload value of 65535. The minimum duration able to be produced is 0.025 milliseconds.
Verification
I am proud to present a video of my final results below! First the microcontroller plays Fur Elise, then it plays September by Earth, Wind, and Fire. I used the website Hook Theory to get the notes for the song, and then transcribed it using the chart on the Lab 4 Page.
I successfully compiled and uploaded my code to the STM32 microcontroller. The design meets all of the requirements.
This lab took me an estimated 15 hours to complete.