Tag Archives: Music using micro-controller

Playing Music using a Micro-controller

This 11th article in the series of “Do It Yourself: Electronics”, demonstrates playing various audio frequncies using timer module of a micro-controller.

<< Previous Article

After seeing the dimming power of the timer module of a micro-controller, Rohit was excited enough to experiment with the various other modes of the timer module. The idea of generating waveforms of various frequencies was generating all kind of random thoughts in his head. “Sound is also waves with some frequency – so can I generate sound using the timer?”, was one of the thoughts getting stronger and stronger. He finally decided to try it out.

As he has already tried with the Timer0, this time he thought of giving a try to another similar timer.

“Hey Pugs! Timer2 seems to be similar to Timer0. Can I try similar experiments with that as well?”, quizzed Rohit.

“Yes. Why not? Timer2 is also 8-bit with similar registers and modes”, replied Pugs.

“Also, the ATmega16 datasheet pg 122 mentions that the clear timer on compare match aka CTC mode gives more control to (compare match) output frequency.”

“Yes, it does. So, what are you trying?”

“Just thought of creating some sound.”

“Hmmm! That would be cool. Let’s give it a try.”

Unlike the earlier tried fast PWM mode, the timer counter clears to zero on match with value of OCR2 in CTC mode (WGM2[1:0] = 2), instead of hitting the maximum value. Also, by setting the COM2[1:0] to 1 the output on OC2/PD7 would keep on toggling on every match, thus generating a 50% duty cycle square wave. As per the ATmega16 datasheet pg 123, the waveform frequency would be given by

f = \frac{f_{cpu}}{2 . N . (1 + OCR2)}

where N represents the prescaler, to be set in the same register TCCR2, as the above settings.

“Based on the frequency formula, seems like so many ways to get the same frequency using the various prescaler values. How do we choose the prescaler?”, asked Rohit puzzled with the possibilities.

“Don’t get swayed by that. Note that both OCR2 and prescaler are integral and hence can’t generate all frequencies”, replied Pugs after a keen observation on the formula. “Also, the various N values would give the varying frequency resolutions. Hence, depending on our required resolution and may be range, we’d have to carefully choose the prescaler”, completed Pugs.

“That looks complicated and also interesting. Let me try a few combinations to get a feel of it.”

“Just keep in mind that for your sound generation to be audible, you’d need frequencies only between 20Hz to 20KHz.”

“Yes. Yes. I know that”, quipped Rohit.

Meanwhile, Pugs gave a shot to his shell scripting prowess, and ran the following shell command:

$ for ps in 1 8 32 64 128 256 1024
>do
>	echo -e "\nprescaler = $ps"
>	for ocr2 in `seq 0 255`
>	do
>		echo -n $((500000 / $ps / (1 + $ocr2)))
>		echo -n " "
>	done
>done

And from its output, he got well-interspersed frequencies in the audio frequency range for a prescaler of 8, as follows:

...
prescaler = 8
62500 31250 20833 15625 12500 10416 8928 7812 6944 6250 5681 5208 4807 4464 4166 3906
3676 3472 3289 3125 2976 2840 2717 2604 2500 2403 2314 2232 2155 2083 2016 1953 1893
1838 1785 1736 1689 1644 1602 1562 1524 1488 1453 1420 1388 1358 1329 1302 1275 1250
1225 1201 1179 1157 1136 1116 1096 1077 1059 1041 1024 1008 992 976 961 946 932 919
905 892 880 868 856 844 833 822 811 801 791 781 771 762 753 744 735 726 718 710 702
694 686 679 672 664 657 651 644 637 631 625 618 612 606 600 595 589 584 578 573 568
563 558 553 548 543 538 534 529 525 520 516 512 508 504 500 496 492 488 484 480 477
473 469 466 462 459 456 452 449 446 443 440 437 434 431 428 425 422 419 416 413 411
408 405 403 400 398 395 393 390 388 385 383 381 378 376 374 372 369 367 365 363 361
359 357 355 353 351 349 347 345 343 341 339 337 336 334 332 330 328 327 325 323 322
320 318 317 315 314 312 310 309 307 306 304 303 301 300 299 297 296 294 293 292 290
289 288 286 285 284 282 281 280 279 277 276 275 274 272 271 270 269 268 267 265 264
263 262 261 260 259 258 257 256 255 254 253 252 251 250 249 248 247 246 245 244
...

So, they decided to go ahead with the prescaler of 8, which translates to CS2[2:0] = 2, and this is what buzzer.c Rohit coded:

/*
 * This example uses CTC mode of 8-bit Timer/Counter2 for generating various
 * audio frequencies on OC2/PD7 (Pin 21).
 * Hence, Buzzer/Speaker is to be connected between OC2/PD7 (Pin 21) & GND.
 */

#include <avr/io.h>
#include <util/delay.h>

#define HUMAN_AUDIBLE_MAX_FREQ 20000

#define PRESCALER (2 << CS20) /* Prescaler N = 8 */
#define N 8
#define MAX_FREQ (F_CPU / (2 * N)) /* F_CPU / (2 * N * (1 + (OCR2 = 0))) */
#define MIN_FREQ (F_CPU / (2 * N * 256)) /* F_CPU / (2 * N * (1 + (OCR2 = 255))) */

void set_frequency(unsigned long freq)
{
	if (freq < MIN_FREQ)
	{
		freq = MIN_FREQ;
	} else if (freq > MAX_FREQ)
	{
		freq = MAX_FREQ;
	}
	/* f = MAX_FREQ / (1 + OCR2), i.e. OCR2 = MAX_FREQ / f - 1; */
	OCR2 = MAX_FREQ / freq - 1;
}
void init_timer(unsigned long freq)
{
	DDRD |= (1 << PD7); // OC2 is PD7 - setting it as output

	set_frequency(freq);

	/*
	 * Setting the Timer/Counter2 in CTC (Clear Timer on Compare) (non-PWM) mode.
	 * Toggling on Match to generate square wave for a particular frequency,
	 * as per the prescaler and OCR2 setting.
	 * Output would come on OC2/PD7 (Pin 21).
	 */
	TCCR2 = (1 << WGM21) | (0 << WGM20) | (1 << COM20) | PRESCALER;
}
void stop_timer(void)
{
	TCCR2 = 0;
}

int main(void)
{
	unsigned long freq = MIN_FREQ;

	init_timer(freq);

	while (1)
	{
		_delay_ms(100);
		freq += MIN_FREQ;
		if (freq > HUMAN_AUDIBLE_MAX_FREQ)
		{
			freq = MIN_FREQ;
		}
		set_frequency(freq);
	}

	stop_timer();

	return 0;
}

Then, Rohit compiled the program as follows (as in the previous articles):

$ avr-gcc -mmcu=atmega16 -DF_CPU=1000000 -Os buzzer.c -o buzzer.elf
$ avr-objcopy -O ihex buzzer.elf buzzer.hex

And finally, downloaded the buzzer.hex into the ATmega16 with J1 shorted (same as in the previous articles), using the following command:

$ avrdude -c ponyser -P /dev/ttyUSB0 -p m16 -U flash:w:buzzer.hex:i

And … removing the jumper J1, … nothing happened. Aha! forgot to connect the buzzer. Connecting the buzzer between PD7 (Pin 21) and GND, echoed the varying sound tones going from flat (high bass) to shrill (high treble), repeatedly.

Next Article >>

   Send article as PDF