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

Firmware

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);

	}

}

4 thoughts on “ATtiny13 – tone generator

  1. 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.

  2. 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

Leave a Comment