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
freq[] - frequencies of each note
length[] - length of each note
delay[] - pause between each note
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:
The attiny13A has an internal oscillator set to 9.6MHz
The internal clock for IO is the oscillator divded by 8 or 1.2MHz
An internal timer is setup in a 8bit register to count up every clock cycle with prescale of 8.
This results in one tick equal to 1 / (1.2MHz / 8) = .006667ms
The attiny13A is configured to compare what is in the 8bit TCCR0B register with the timer and toggle a pin when they match.
For example, in order to generate a square wave at 524Hz (one octave above middle C) which has a period of 1.908ms.
1.908ms = 286 clock ticks (1.908/.0067)
Divide 286 by 2 to toggle the pin at t/2 (286/2 = 143)
Put 143 in the TTCR0B register to generate this note.
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:
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)