A competition robot should be able to play a victory march!
Here is an example of how to use Timer/Counter 2 on the Orangutan (ATmega168, 8 MHz clock assumed) to generate frequencies and output to the buzzer. Below, I’ve pasted in a self-contained program that plays a tune array with a range of three octaves. It sounds better than you think! The tune array for “Fuer Elise” was stolen from the AVR Butterfly code. Thanks, ATMEL Norway!
I’ll post some more general buzzer control routines later, but this was so much fun that I couldn’t wait. Please post some suitable tune arrays!
Code with tabs (for readability) can be downloaded from uoxray.uoregon.edu/orangutan/buzzer_tunes.c
See also buzzer_tunes2.c which is an interrupt driven version.
Jim
/*
Play music with your robot!
Buzzer example for the Orangutan/ATmega168.
This example uses Timer2 in "Clear Timer/Counter on Compare Match" mode to generate the buzzer frequency.
The counter is incremented at 125 kHz, and resets when count = OCR2A. Upon reset, buzzer port pin is toggled.
The longest period before overflow (when OCR2A=255) corresponds to 488 Hz or buzzer frequency=244 Hz.
Higher frequencies are set by reducing the value of OCR2A.
In this example, notes are defined according to the equal interval twelve tone scale.
CPU clock frequency assumed to be 8 MHz. If different, TCCR2 divider should be changed appropriately.
Produces about 1312 bytes of loaded code with -O3
sjames_remington at yahoo dot com
*/
#define F_CPU 8000000UL
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
// TIMER 2 Interrupt service vector
// This routine is called with TIMER2 count (TCNT2) = OCR2A, which is set on the fly
// Operation is to simply toggle the buzzer line
//
ISR(TIMER2_COMPA_vect)
{
PORTB ^= 1; //toggle buzzer output line
}
/*
Set up buzzer to play tones. Timer/Counter 2 is used in CTC mode, clear timer on compare/match
Clocked at 125 kHz, = 8MHz/64
OCR2A overflow buzzer
value freq freq
255 488 244 ("concert A" = 440 Hz)
128 976 488
64 1953 976
32 3906 1953
For other clock ratios, multiply or divide accordingly
Individual notes are = base frequency*2^(n/12), on the twelve tone scale (1.05946 multiplicative factor)
Here, we define notes by the value of OCR2A:
A0 255 A2 63 A3 31
A# 241 67 33
B 227 71 35
C 214 75 37
C# 202 80 40
D 191 85 42
D# 180 90 44
E 170 95 47
F 161 101 50
F# 152 107 53
G 143 113 56
G# 135 120 59
A1 127 127 A2 63
*/
/*
define three octaves of notes for coding convenience. x=sharp
*/
#define p 0 //pause, no sound
#define a0 255
#define ax0 241
#define b0 227
#define c0 214
#define cx0 202
#define d0 191
#define dx0 180
#define e0 170
#define f0 161
#define fx0 152
#define g0 143
#define gx0 135
#define a1 127
#define ax1 120
#define b1 113
#define c1 107
#define cx1 101
#define d1 95
#define dx1 90
#define e1 85
#define f1 80
#define fx1 75
#define g1 71
#define gx1 67
#define a2 63
#define ax2 59
#define b2 56
#define c2 53
#define cx2 50
#define d2 47
#define dx2 44
#define e2 42
#define f2 40
#define fx2 37
#define g2 35
#define gx2 33
#define a3 31
void init_buzzer(void)
{
DDRB |= 1; //set data direction reg bit 0 to output
PORTB &=~(1); //set buzzer output low
TCCR2A= (1<<WGM21); //mode 2, clear timer on compare match
TCCR2B=4; //CS22=1,CS21=0,CS20=0 => counter increments at 8 MHz/64 = 125 kHz,
// ... Counter overflows at 125kHz/256 = 488 Hz => lowest buzzer freq = 244 Hz
TCNT2=0; //clear counter
OCR2A=255; //set both compare match registers to MAX
OCR2B=255; //set both compare match registers to MAX
TIMSK2=(1<<OCIE2A); //enable interrupt on compare match with OCR2A
}
int main(void)
{
int i,j;
double delay;
unsigned char note,dur;
/*
tune array "Fuer Elise" stolen from AVR Butterfly code example. Thanks, ATMEL Norway!
Tune array entries are byte pairs: [duration (8=whole note,4=half etc.), note (or p=pause=0)]
duration=0 ends the tune
The final byte is intended to be the repeat flag, but this feature has not been implemented
*/
unsigned char tune[]= //Fuer Elise, with apologies
{
8,e1, 8,dx1, 8,e1, 8,dx1, 8,e1, 8,b0, 8,d1, 8,c1, 4,a0, 8,p,
8,c0, 8,e0, 8,a0, 4,b0, 8,p, 8,e0, 8,gx0, 8,b0, 4,c1, 8,p, 8,e0,
8,e1, 8,dx1, 8,e1, 8,dx1, 8,e1, 8,b0, 8,d1, 8,c1, 4,a0, 8,p, 8,c0,
8,e0, 8,a0, 4,b0, 8,p, 8,e0, 8,c1, 8,b0, 4,a0,
8,p,8,p,8,p,8,p, //pause and go up one octave for test!
8,e2, 8,dx2, 8,e2, 8,dx2, 8,e2, 8,b1, 8,d2, 8,c2, 4,a1, 8,p,
8,c1, 8,e1, 8,a1, 4,b1, 8,p, 8,e1, 8,gx1, 8,b1, 4,c2, 8,p, 8,e1,
8,e2, 8,dx2, 8,e2, 8,dx2, 8,e2, 8,b1, 8,d2, 8,c2, 4,a1, 8,p, 8,c1,
8,e1, 8,a1, 4,b1, 8,p, 8,e1, 8,c2, 8,b1, 4,a1,
0, 0
};
init_buzzer();
DDRB &= ~(1); //turn off buzzer for now by setting PORTB.0 = input
i=0;
sei(); //enable interrupts or timer2 won't kick buzzer
do {
dur=tune[i++]; //march through the tune array, picking up duration,note pairs
note=tune[i++];
if (note) { //if note=0 (p) skip next step
OCR2A=note; //set OCR2A for compare match
DDRB |=1; //turn on buzzer
}
else DDRB &=~(1); // silence for this period
delay=(double) (dur * 32); //increase note duration by maximum constant factor
for (j=1; j<8; j++) _delay_ms(delay); //delay=262. max ~ 1/4 second. The "8" was chosen to sound OK
}
while (dur>0);
return 0;
}