Orangutan plays music!

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;
}
1 Like

HAHAHAHAHA! OMG! That’s awesome!

Ten points to anyone who takes Flight of the Valkyries and puts it into array form!

Man, this rocks. Ok, no more excuses. I build an Orangutan-based mini-sumo NOW. The tune it plays depends on how it thinks it’s doing. Flight of the Valkyries when it starts to move, Rocky themesong when it makes contact and begins the pushing match, and We are the Champions if it lives to stay in the ring and fight another day.

This is a RIOT! You rock, dude!

Tom

My bad. It’s “Ride of the Valkyries”, and I’m on it. I’ll post the array as soon as I’ve got the score translated.

Tom

Glad you like it! Keep in mind that the program currently has the robot mostly in a wait loop while playing a tune, so it can’t do much else. However, there is no reason not to put everything into an interrupt routine – if only we had more timers!

“Ride of the Valkyries” would be perfect.

Best, Jim

Man, I know! I just finished writing some code to do countdown timers and generic “call this routine every 1ms” style interrupt stuff that uses the same timer you used for that code. ARGH!

TIMER2 can do compares on both OCR2A and OCR2B. I’d be willing to bet I can munge my countdown timer code to use the same prescale value as your music code. That would let the two play nicely on the same timer, with one using A and one using B.

I really do wish the AVRs had more timers. But the three that are there are still a big step up from what I’m used to.

Tom

At first I thought the above was a great idea but after trying to code it, I realized that in CTC mode, OCR2A resets the timer. OCR2B interrupts never occur if OCR2B>OCR2A. Also, interrupts occur at a variable frequency, so it seems you can’t have a steady clock tick out of the same timer for other timing purposes.

The simplest approach is to use two timers to implement an “all-interrupt” music player. I’ve done so, using timer 0 to provide steady 1 millisecond ticks, and play the tune, while timer 2 is used for frequency generation. I also changed the note scale so that it accurately corresponds to the International Tempered Scale.

This frees up the bot to do a victory dance while playing an appropriate tune.

New buzzer code, which plays the first 28 bars of “The Sorcerer’s Apprentice” can be downloaded at:
uoxray.uoregon.edu/orangutan/buzzer_tunes2.c

If someone can figure out how to use one timer to generate both variable frequencies and steady clock ticks, let us know!

Onward, Jim

PS To generate the tune array for the Sorcerer’s Apprentice, I downloaded a MIDI file for a flute score from borg.com/~jglatt/
to decode the MIDI file and create a human-readable text file. I transposed the score up one octave for the Orangutan. The note timings were a bit tricky, but otherwise, easy as 3.14159.

hi i’m pretty new to all this avr stuff, but how can i change it so it would work with the mega8, cos otherwise i get alot of errors, when compiling and also, if i want it to play my voice, how do i change that into the array?

thanks
alex

Hello.

One of the main differences between the mega8 and the mega168 is that the mega168 has an extra eight-bit timer (TIMER2) upon which Jim Remington’s code relies. To make this code work on the mega8, you will need to convert it to use TIMER0 instead of TIMER2. In most places this should be as simple as replacing “2” with “0”, but there might be some subtle differences between the timers that complicate things. Using the mega168 and mega8 datasheets as references, compare the mega168’s TIMER2 to the mega8’s TIMER0 as you make the conversion. It should be relatively straightforward if you’re not afraid to use the datasheets.

Also, the Orangutan is probably never going to sound much like your voice. It produces sound using a single, simple buzzer that isn’t going to do a very good job of emulating the complexities of the human vocal system. Nevertheless, you could always use a computer program to record yourself saying something and then try to send pulses to the buzzer that roughly match the signal displayed by the program. After a lot of trial and error you might come away with something that sounds very remotely like a recognizable word.

- Ben

but if i put a 3.5mm jack output circuit on one of the pins and out put to that (somehow???) could i get something pretty similar?

Maybe, but it seems to me like the programming for outputing speech would be pretty complicated. I’d look into interfacing with a dedicated speech synthesizer/MP3 if you want to get much more complicated than playing simple midi files. Such things exist, but I am not familiar enough with them to tell you anything more specific (e.g. part numbers).

- Ben