projects | about

Marioman

Using an attiny13A and some leds and a speaker create a musical marioman.

If you have trouble seeing the video above download the movie direct here.

Construction

Materials

multivibrator multivibrator multivibrator
Total cost of materials ~ $5

The two LEDs were attached directly to two pins each of the attiny13A. Two pins are used for each LED, the second pin is set low to use as a ground connection. The current limit of the I/O pins on the AVR will prevent the LEDs from drawing too much so a resistor is not necessary to connect in series. The speaker used is typical of one found in a musical greeting card, any small speaker will do, given that this is outputting a square wave tone, it's not too important to worry about driving the speaker or sound quality.

Software

The attiny13A has 1K of programmable flash, and 64bytes of SRAM.

Firmware for the attiny13A as well as the source can be downloaded here - attiny13_marioman.tar

Three arrays in the c code were used to generate the music

The frequency array does not have the actual frequencies but rather the value to put in the TTCROB register to generate the square wave off the PB0 pin.

Here is a brief summary of the calculations and pin configuration for square wave generation:

This is all the code that is necessary to set up the timer, do the compare and output a square wave:

    TCCR0A |= (1<<WGM01);  // configure timer 1 for CTC mode
    TCCR0A |= (1<<COM0A0); // toggle OC0A on compare match
    TCCR0B |= (1<<CS01);   // clk/8 prescale

    TTCR0B = 143;  // generate a square wave at 524Hz

To delay the tones and the pauses between them a simple delay function was used:

void sleep(int ms) {
	int cnt;
	for (cnt=0; cnt<(ms); cnt++) {
		int i = 150;
		while(i--) {
			__asm("NOP");
		}
	}
}
This counts down from 150 where each NOP cycle is approximately .006667ms.

The last thing the code does is loop through the arrays, generate the music and blink the two LEDs.

This is done in a continuous for loop with the following code:


const uint8_t freq[] PROGMEM = { ... data };
const uint8_t length[] PROGMEM = { ... data };
const uint8_t delay[] PROGMEM = { ... data };

...
while (1) {
		for (cnt=0; cnt<156; cnt++) {

   		    OCR0A=pgm_read_byte(&freq[cnt]);
		    output_toggle(PORTB,PB3);
		    output_toggle(PORTB,PB4);
		    sleep( pgm_read_byte(&length[cnt]) );
		    output_toggle(PORTB,PB3);
		    output_toggle(PORTB,PB4);
		    // stop timer
		    TCCR0B = 0;
		    sleep ( pgm_read_word(&delay[cnt]) );
		    // start timer
		    TCCR0B |= (1<<CS01);   // clk/8 prescale
		}
}
There are 156 elements in the frequencies/lengths/delay arrays, this loop traverses them. Pin PB3 and PB4 are each toggled so they will alternate with each note The first sleep is the length of the note we play after setting the OCR0A register to the appropriate value. The second sleep is the pause between the notes we play.

Large amount of read-only data and pgmspace.h

In the code above you might have notice the two functions pgm_read_byte() and pgm_read_word() as well as the keyword PROGMEM.

With an embedded chip like the attiny the amount of SRAM is very limited, in this case only 64bytes. The arrays we are using for all of the frequency/delay/length data are much larger than 64bytes and therefore can not be loaded into memory. By using the special PROGMEM avr-gcc directive these large data arrays are prevented from loading into memory, instead they are read from flash.




Copyright (c) jarv.org Verbatim copying and redistribution of this entire page are permitted provided this notice is preserved.
Validate (nerd)