Research for Generating Tunes Using Arduino:
In order to create audible sound with code we are going to be using Pulse Width Modulation (PWM) techniques to turn our speaker on and off (by sending a series of square waves to the speaker). Each tune is composed of many notes that collectively define what the tune sounds like.
Each note within the tune has two components to it: pitch (frequency) and tempo (duration). Combining these two aspects together creates an audible note that we can hear.
There are many examples online that show how to implement tunes with the arduino using busy-waiting (polling method) to pause between the differnt notes in the tune. I used the following example as reference: PlayMelody.
The problem with busy waiting is that it essentially wastes processing time by doing nothing when the processsor could be doing more important things. Our project requires that we be able to quickly transition between the various states in our Finite State Machine (FSM). Implementing busy-waiting into our project would cause our FSM to stay in states longer than it should and it would take away the ability for our project to effectively transition between its multiple states. This is why it is beneficial to use a timer interrupt here instead of using busy-waiting.
Using Arduino Timer Interrupts:
In order to get the timer interrupt to work, I used the following reference: Timers. This particular link was very helpfull in understanding how timer interrupts work with the Arduino.
There are a couple of things that I had to keep in mind while figuring out which timer to use:
- Timer0 and Timer2 are 8-bit timers (256 values), whereas Timer1 is a 16-bit timer (65536 values). This means that a higher count value can be stored into timer1.
- For Arduino, timer0 is used for the timer functions such as delay(), millis(), and micros(). Changing timer0 register values can thus negatively impact the Arduino timer function. Because I am using the millis() function in the FSM implementation and I do not want to mess with these values, I am not going to use timer0.
- The tone() function uses timer2. Since I am going to be using the tone() function to output the sound, I do not want to mess with these values and as a result I am not going to be using timer2 either.
Because I do not want to mess with the values in timer0 or timer2, I am going to be using timer1 for the timer interrupt.
Timers can run in either PWM mode or CTC mode. In PWM mode, the OCxy outputs are used to generate PWM signals. In CTC mode, the timer value is compared to the value in the match register. When these values match up, the timer is cleared.
In order to configure the timer1 interrupt I need to modify the following values in the Timer Control Register:
TCNTx – Timer/Counter Register. The actual timer value is stored here.
OCRx – Output Compare Register
ICRx – Input Capture Register (only for 16bit timer)
TIMSKx – Timer/Counter Interrupt Mask Register. To enable/disable timer interrupts.
TIFRx – Timer/Counter Interrupt Flag Register. Indicates a pending timer interrupt.
I want to create an interrupt that uses the CTC mode with a frequency of 2KHz. In order to select this timer frequency it is important to follow these steps as outlined in the guide that I linked to above.
1. CPU frequency 16Mhz for Arduino
2. maximum timer counter value (256 for 8bit, 65536 for 16bit timer)
3. Divide CPU frequency through the chosen pre-scaler (16000000 / 256 = 62500)
4. Divide result through the desired frequency (62500 / 2000Hz = 31.250 = 31)
5. Verify the result against the maximum timer counter value (31 < 65536 success) if fail, choose bigger pre-scaler.
In the ATmega328 datasheet (on pages 131-140) it decribes the TCCR1A and TCCR1B registers in detail. Pages 111-139 on the datasheet give an overview of timer1 and describe its different features in great detail. Following this datasheet along with the guidelines above, I would need to initialize my timer1 to the following values:
//set timer1 interrupt at 2KHz (2000 Hz)
TCCR1A = 0;// set entire TCCR1A register to 0
TCCR1B = 0;// same for TCCR1B
TCNT1 = 0;//initialize counter value to 0
// set compare match register for 2KHz increments
OCR1A = 15624;// = (16*10^6) / (2000*256) – 1 (must be <65536)
TCCR1B |= (1 << WGM12); // turn on CTC mode
TCCR1B |= (1 << CS12); // Set CS12 bit for 256 prescaler
TIMSK1 |= (1 << OCIE1A); // enable timer compare interrupt
Now that I have my timer interrupt set up correctly, the next step is to determine
the sounds that our group wants to output and generate those sounds using Arduino.
Check back next week for an update on this next step.