ATtiny13 – tone generator

There is a set of projects that require some kind of audio output. For example a simple alarm bell. This project shows how we can produce different kinds of audio effects by just using an AVR MCU and a speaker/buzzer.  We will use 8-bit Timer, PWM technique and some pre-calculations to generate right sounds. Tu run this project just hook a small speaker/buzzer with resistor connected in series between PB0 and GND and you are ready. The code is on Github, click here.

Parts Required

Circuit Diagram

tone-generator

Frequency Table (A4 = 440 Hz)

Frequencies for equal-tempered scale, where A4 = 440 Hz. Column 3 contains a real frequencies of first octave which has been measured for ATtiny13 @1.2MHz on PB0 (see oscilloscope screenshots)

Note Frequency (Hz)  ATtiny13 frequency (Hz) Wavelength (cm)
C0 16.35 16.63 2109.89
 C#0/Db0 17.32 17.61 1991.47
D0 18.35 18.59 1879.69
 D#0/Eb0 19.45 19.56 1774.20
E0 20.60 20.54 1674.62
F0 21.83 21.53 1580.63
 F#0/Gb0 23.12 23.48 1491.91
G0 24.50 24.46 1408.18
 G#0/Ab0 25.96 26.42 1329.14
A0 27.50 27.40 1254.55
 A#0/Bb0 29.14 29.35 1184.13
B0 30.87 31.31 1117.67
C1 32.70 1054.94
 C#1/Db1 34.65 995.73
D1 36.71 939.85
 D#1/Eb1 38.89 887.10
E1 41.20 837.31
F1 43.65 790.31
 F#1/Gb1 46.25 745.96
G1 49.00 704.09
 G#1/Ab1 51.91 664.57
A1 55.00 627.27
 A#1/Bb1 58.27 592.07
B1 61.74 558.84
C2 65.41 527.47
 C#2/Db2 69.30 497.87
D2 73.42 469.92
 D#2/Eb2 77.78 443.55
E2 82.41 418.65
F2 87.31 395.16
 F#2/Gb2 92.50 372.98
G2 98.00 352.04
 G#2/Ab2 103.83 332.29
A2 110.00 313.64
 A#2/Bb2 116.54 296.03
B2 123.47 279.42
C3 130.81 263.74
 C#3/Db3 138.59 248.93
D3 146.83 234.96
 D#3/Eb3 155.56 221.77
E3 164.81 209.33
F3 174.61 197.58
 F#3/Gb3 185.00 186.49
G3 196.00 176.02
 G#3/Ab3 207.65 166.14
A3 220.00 156.82
 A#3/Bb3 233.08 148.02
B3 246.94 139.71
C4 261.63 131.87
 C#4/Db4 277.18 124.47
D4 293.66 117.48
 D#4/Eb4 311.13 110.89
E4 329.63 104.66
F4 349.23 98.79
 F#4/Gb4 369.99 93.24
G4 392.00 88.01
 G#4/Ab4 415.30 83.07
A4 440.00 78.41
 A#4/Bb4 466.16 74.01
B4 493.88 69.85
C5 523.25 65.93
 C#5/Db5 554.37 62.23
D5 587.33 58.74
 D#5/Eb5 622.25 55.44
E5 659.25 52.33
F5 698.46 49.39
 F#5/Gb5 739.99 46.62
G5 783.99 44.01
 G#5/Ab5 830.61 41.54
A5 880.00 39.20
 A#5/Bb5 932.33 37.00
B5 987.77 34.93
C6 1046.50 32.97
 C#6/Db6 1108.73 31.12
D6 1174.66 29.37
 D#6/Eb6 1244.51 27.72
E6 1318.51 26.17
F6 1396.91 24.70
 F#6/Gb6 1479.98 23.31
G6 1567.98 22.00
 G#6/Ab6 1661.22 20.77
A6 1760.00 19.60
 A#6/Bb6 1864.66 18.50
B6 1975.53 17.46
C7 2093.00 16.48
 C#7/Db7 2217.46 15.56
D7 2349.32 14.69
 D#7/Eb7 2489.02 13.86
E7 2637.02 13.08
F7 2793.83 12.35
 F#7/Gb7 2959.96 11.66
G7 3135.96 11.00
 G#7/Ab7 3322.44 10.38
A7 3520.00 9.80
 A#7/Bb7 3729.31 9.25
B7 3951.07 8.73

See equations for the Frequency Table

SDS00002 SDS00003 SDS00004 SDS00005 SDS00006 SDS00007 SDS00008 SDS00009 SDS00010 SDS00011 SDS00012 SDS00013

Software

This code is written in C and can be compiled using the avr-gcc. More details on how compile this project is here.

/**
 * Copyright (c) 2016, Łukasz Marcin Podkalicki <lpodkalicki@gmail.com>
 * ATtiny13/007
 * Simple tone generator.
 * --
 * Settings:
 *  FUSE_L=0x6A
 *  FUSE_H=0xFF
 *  F_CPU=1200000
 */

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

#define	BUZZER_PIN	PB0


#define	N_1	(_BV(CS00))
#define	N_8	(_BV(CS01))
#define	N_64	(_BV(CS01)|_BV(CS00))
#define	N_256	(_BV(CS02))
#define	N_1024	(_BV(CS02)|_BV(CS00))

typedef struct s_note {
	uint8_t OCRxn; // 0..255
	uint8_t N;
} note_t;

typedef struct s_octave {
	note_t note_C;
	note_t note_CS;
	note_t note_D;
	note_t note_DS;
	note_t note_E;
	note_t note_F;
	note_t note_FS;
	note_t note_G;
	note_t note_GS;
	note_t note_A;
	note_t note_AS;
	note_t note_B;
} octave_t;

/*
 All calculations below are prepared for ATtiny13 default clock source (1.2MHz)
 F = F_CPU / (2 * N * (1 + OCRnx)), where:
 - F is a calculated PWM frequency
 - F_CPU is a clock source (1.2MHz)
 - the N variable represents the prescaler factor (1, 8, 64, 256, or 1024).
 */

PROGMEM const octave_t octaves[8] = {
	{ // octave 0
	.note_C = {142, N_256}, // 16.35 Hz
	.note_CS = {134, N_256}, // 17.32 Hz
	.note_D = {127, N_256}, // 18.35 Hz
	.note_DS = {120, N_256}, // 19.45 Hz
	.note_E = {113, N_256}, // 20.60 Hz
	.note_F = {106, N_256}, // 21.83 Hz
	.note_FS = {100, N_256}, // 23.12 Hz
	.note_G = {95, N_256}, // 24.50 Hz
	.note_GS = {89, N_256}, // 25.96 Hz
	.note_A = {84, N_256}, // 27.50 Hz
	.note_AS = {79, N_256}, // 29.14 Hz
	.note_B = {75, N_256} // 30.87 Hz
	},
	{ // octave 1
	.note_C = {71, N_256}, // 32.70 Hz
	.note_CS = {67, N_256}, // 34.65 Hz
	.note_D = {63, N_256}, // 36.71 Hz
	.note_DS = {59, N_256}, // 38.89 Hz
	.note_E = {56, N_256}, // 41.20 Hz
	.note_F = {53, N_256}, // 43.65 Hz
	.note_FS = {50, N_256}, // 46.25 Hz
	.note_G = {47, N_256}, // 49.00 Hz
	.note_GS = {44, N_256}, // 51.91 Hz
	.note_A = {42, N_256}, // 55.00 Hz
	.note_AS = {39, N_256}, // 58.27 Hz
	.note_B = {37, N_256} // 61.74 Hz
	},
	{ // octave 2
	.note_C = {142, N_64}, // 65.41 Hz
	.note_CS = {134, N_64}, // 69.30 Hz
	.note_D = {127, N_64}, // 73.42 Hz
	.note_DS = {120, N_64}, // 77.78 Hz
	.note_E = {113, N_64}, // 82.41 Hz
	.note_F = {106, N_64}, // 87.31 Hz
	.note_FS = {100, N_64}, // 92.50 Hz
	.note_G = {95, N_64}, // 98.00 Hz
	.note_GS = {89, N_64}, // 103.83 Hz
	.note_A = {84, N_64}, // 110.00 Hz
	.note_AS = {79, N_64}, // 116.54 Hz
	.note_B = {75, N_64} // 123.47 Hz
	},
	{ // octave 3
	.note_C = {71, N_64}, // 130.81 Hz
	.note_CS = {67, N_64}, // 138.59 Hz
	.note_D = {63, N_64}, // 146.83 Hz
	.note_DS = {59, N_64}, // 155.56 Hz
	.note_E = {56, N_64}, // 164.81 Hz
	.note_F = {53, N_64}, // 174.61 Hz
	.note_FS = {50, N_64}, // 185.00 Hz
	.note_G = {47, N_64}, // 196.00 Hz
	.note_GS = {44, N_64}, // 207.65 Hz
	.note_A = {42, N_64}, // 220.00 Hz
	.note_AS = {39, N_64}, // 233.08 Hz
	.note_B = {37, N_64} // 246.94 Hz
	},
	{ // octave 4
	.note_C = {35, N_64}, // 261.63 Hz
	.note_CS = {33, N_64}, // 277.18 Hz
	.note_D = {31, N_64}, // 293.66 Hz
	.note_DS = {29, N_64}, // 311.13 Hz
	.note_E = {27, N_64}, // 329.63 Hz
	.note_F = {26, N_64}, // 349.23 Hz
	.note_FS = {24, N_64}, // 369.99 Hz
	.note_G = {23, N_64}, // 392.00 Hz
	.note_GS = {22, N_64}, // 415.30 Hz
	.note_A = {20, N_64}, // 440.00 Hz
	.note_AS = {19, N_64}, // 466.16 Hz
	.note_B = {18, N_64} // 493.88 Hz
	},
	{  // octave 5
	.note_C = {142, N_8}, // 523.25 Hz
	.note_CS = {134, N_8}, // 554.37 Hz
	.note_D = {127, N_8}, // 587.33 Hz
	.note_DS = {120, N_8}, // 622.25 Hz
	.note_E = {113, N_8}, // 659.25 Hz
	.note_F = {106, N_8}, // 349.23 Hz
	.note_FS = {100, N_8}, // 369.99 Hz
	.note_G = {95, N_8}, // 392.00 Hz
	.note_GS = {89, N_8}, // 415.30 Hz
	.note_A = {84, N_8}, // 440.00 Hz
	.note_AS = {79, N_8}, // 466.16 Hz
	.note_B = {75, N_8} // 493.88 Hz
	},
	{  // octave 6
	.note_C = {71, N_8}, // 1046.50 Hz
	.note_CS = {67, N_8}, // 1108.73 Hz
	.note_D = {63, N_8}, // 1174.66 Hz
	.note_DS = {59, N_8}, // 1244.51 Hz
	.note_E = {56, N_8}, // 1318.51 Hz
	.note_F = {53, N_8}, // 1396.91 Hz
	.note_FS = {50, N_8}, // 1479.98 Hz
	.note_G = {47, N_8}, // 1567.98 Hz
	.note_GS = {44, N_8}, // 1661.22 Hz
	.note_A = {42, N_8}, // 1760.00 Hz
	.note_AS = {39, N_8}, // 1864.66 Hz
	.note_B = {37, N_8} // 1975.53 Hz
	},
	{  // octave 7
	.note_C = {35, N_8}, // 2093.00 Hz
	.note_CS = {33, N_8}, // 2217.46 Hz
	.note_D = {31, N_8}, // 2349.32 Hz
	.note_DS = {29, N_8}, // 2489.02 Hz
	.note_E = {27, N_8}, // 2637.02 Hz
	.note_F = {26, N_8}, // 2793.83 Hz
	.note_FS = {24, N_8}, // 2959.96 Hz
	.note_G = {23, N_8}, // 3135.96 Hz
	.note_GS = {22, N_8}, // 3322.44 Hz
	.note_A = {20, N_8}, // 3520.00 Hz
	.note_AS = {19, N_8}, // 3729.31 Hz
	.note_B = {18, N_8} // 3951.07 Hz
	}
};

static void
tone(uint8_t octave, uint8_t note)
{
	uint32_t ret;
	note_t *val;
	ret = pgm_read_word_near((uint8_t *)&octaves + sizeof(octave_t) * octave + sizeof(note_t) * note);
	val = (note_t *)&ret;
	TCCR0B = (TCCR0B & ~((1<<CS02)|(1<<CS01)|(1<<CS00))) | val->N;
  	OCR0A = val->OCRxn - 1; // set the OCRnx
}

static void
stop(void)
{

	TCCR0B &= ~((1<<CS02)|(1<<CS01)|(1<<CS00)); // stop the timer
}

int
main(void)
{
	uint8_t i, j;

	/* setup */
	DDRB = 0b00000001; // set BUZZER pin as OUTPUT
	PORTB = 0b00000000; // set all pins to LOW
	TCCR0A |= (1<<WGM01); // set timer mode to Fast PWM
	TCCR0A |= (1<<COM0A0); // connect PWM pin to Channel A of Timer0

	/* Walk throwgh all octaves */
	for (i = 0; i < 8; ++i) {
		for (j = 0; j < 12; ++j) {
			tone(i, j);
			_delay_ms(80);
		}
	}

	stop();
	 _delay_ms(1500);


	/* loop */
	while (1) {


		/* Polish song "Wlazł kotek na płotek" in loop */
		tone(4, 7); // G
		_delay_ms(500);
		tone(4, 4); // E
		_delay_ms(500);
		tone(4, 4); // E
		_delay_ms(500);
		tone(4, 5); // F
		_delay_ms(500);
		tone(4, 2); // D
		_delay_ms(500);
		tone(4, 2); // D
		_delay_ms(500);
		tone(4, 0); // C
		_delay_ms(200);
		tone(4, 4); // E
		_delay_ms(300);
		tone(4, 7); // G
		_delay_ms(1000);

		stop();
		_delay_ms(2000);

		tone(4, 7); // G
		_delay_ms(500);
		tone(4, 4); // E
		_delay_ms(500);
		tone(4, 4); // E
		_delay_ms(500);
		tone(4, 5); // F
		_delay_ms(500);
		tone(4, 2); // D
		_delay_ms(500);
		tone(4, 2); // D
		_delay_ms(500);
		tone(4, 0); // C
		_delay_ms(200);
		tone(4, 4); // E
		_delay_ms(300);
		tone(4, 0); // C
		_delay_ms(1000);

		stop();
		_delay_ms(5000);

	}

}

18 thoughts on “ATtiny13 – tone generator

  1. Thanks – I re-programmed the fuses for CPU/8 and it works nicely 🙂
    I managed to squeeze in code for the ADC and set up a basic 0-5V scaling for tuned VCO with 1V/oct using the bottom 5 octaves.
    This is great – thanks for the code.
    🙂

  2. Hi – thanks for this project.
    I have copied your code to a ATtiny13a and it very nearly works for me.
    The tones play and the Polish song plays.
    However the lowest note I can achieve is C in Octave 3 (approx 131 Hz)
    I have checked that the ATtiny is set for 1.2 MHz.
    If I hard code tone (0, 0); then I get approx 131 Hz, not the expected 16.35 Hz.

    In the comments, you have FUSE_L=0x6A and FUSE_H=0xFF
    I’m not sure what to do with this information – can you please advise?
    Maybe the problem is caused because I haven’t set these?

    I am using Arduino IDE for programming and to flash the ATtiny.

  3. I’m having trouble getting this circuit to work properly . It produces a repeating series of “clicks” in the speaker instead of tones.
    I am using WinAVR with avr-gcc to program the Attiny13a PU. I have set the fuses to l = 0x6a and h = 0xff using avrdude and verified them. Everything compiles without error and programs to the Attiny13a. I have tried a different mini speaker with the same result. It seems like the Attiny is running very slow, but I can’t figure out how to change it. I use an F_CPU = 1200000 in my Makefile.
    Can you tell me how to troubleshoot this. Thank you

    • Hi, I guess it’s something wrong with CPU clock. Attiny13a runs with default clock F_CPU = 1200000. Mayby the chip is broken, or the chip is running with very slow clock – like 128kHz? From that point it happens to be not trivial to restore fuse bits.

      /LP

  4. Hi thank you for sharing this method to play tones in the tiny mcu. I used a buzzer instead of a piezo on my setup and found out the stop() method is not complete. For a piezo it is ok to stop the timer only since it has a very high impedance. However for a buzzer or inductive device (or if using the code to drive some other low impedance device, like an LED) only stopping the timer can leave the output high and running a steady current through the device because some combinations of the pwm waveform will stop on the high portion of the duty cycle. The device will get hot. So the stop() function should stop the timer and disconnect the OCxn output to the pin:

    TCCR0A &= ~(1<<COM0A0); // disconnect PWM pin to Channel A of Timer0

    This also requires that the tone() function reconnect the OCxn output to the pin on every call. Just add this above the TCCR0B line:

    TCCR0A |= (1<<COM0A0); // connect PWM pin to Channel A of Timer0

    • Hi,

      I’m sure your code lines will help other readers to handle this situation. The comments like yours are very helpfull. Thank you for sharing this!

      /L

  5. you said “TCCR0A |= (1<<WGM01); // set timer mode to Fast PWM"
    but its wrong, the only "WGM01" does not enable fast pwm, insted put timer in CTC mode,

  6. Hi, good job!
    You can operate OC0B (PB1) in a push-pull manner to OC0A. This increases the volume. If you want to use a low resistance speaker, remove R1. AVRs have an internal current limit at about 40 mA. Because of the internal inductance of these speakers, the waveform is significantly more sinusoidal.

    Kind Regards

  7. Nice project! Just wondering how you did the wavelength computation? Shouldn’t the wavelength of 440Hz be something around 681km (and the 78cm you computed rather map to a frequency of 382MHz)?

    I was planning to run a dozen of these at a time, so for pitch stability, it’d probably be better to use an external XTAL. Do you have any experiences on this (i.e., do I have to find one at 1.2MHz)?

    • Hi, thanks! Computation is described in ATtiny13 manual. It’s good you pointed that missing thing. I added this info to a source code. All calculations has been prepared for ATtiny13 default internal clock source (1.2MHz)

      F = F_CPU / (2 * N * (1 + OCRnx))

      Where:
      F is a calculated PWM frequency
      F_CPU is a clock source (1.2MHz)
      – the N variable represents the prescaler factor (1, 8, 64, 256, or 1024).
      – the OCRnx is a counter limit.

      If you need high precision, then I recommend to use other AVR chip, that accept external crystal input directly.

      Unfortunately, AVRs internal oscillator has a low precision. What’s worst, ATtiny13 doesn’t accept a crystal input directly. The simplest approach to a stable external clock for that chip is to use a crystal clock generator IC. These are IC-sized metal boxes that put out a precision clock signal. They are available in a wide range of frequencies.

Leave a Comment