--- /dev/null
+*.hex
+*.elf
+*.o
--- /dev/null
+# -*- Makefile -*-
+
+PROJECT := tiny_sound
+DEVICE := attiny85
+F_CPU := 8000000UL
+INC := -I.
+#AVRDUDE := avrdude -c usbasp -p $(DEVICE)
+AVRDUDE := avrdude -c atmelice_isp -p $(DEVICE)
+
+CSRCS = $(PROJECT).c
+OBJS = $(CSRCS:.c=.o)
+
+uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')
+
+CC := avr-gcc
+LD := avr-gcc
+OBJCOPY := avr-objcopy
+ifeq ($(uname_S),Linux)
+RM := rm -f
+else
+RM := del >NUL
+endif
+CFLAGS :=-Wall -Wcast-align -Wshadow \
+ -std=gnu99 -fshort-enums -pedantic-errors -Os -mcall-prologues \
+ -mmcu=$(DEVICE) -DF_CPU=$(F_CPU)
+LDFLAGS := -Wall -mmcu=$(DEVICE)# -Wl,-v
+LIBS :=
+
+V := @
+Q := $(V:1=)
+QUIET_CC = $(Q:@=@echo CC $@ &)$(CC)
+QUIET_LD = $(Q:@=@echo LD $@ &)$(LD)
+QUIET_OBJCOPY = $(Q:@=@echo OBJCOPY $@ &)$(OBJCOPY)
+QUIET_AVRDUDE = $(Q:@=@echo AVRDUDE $@ &)$(AVRDUDE)
+
+all: $(PROJECT).hex
+
+%.hex: %.elf
+ $(QUIET_OBJCOPY) -j .text -j .data -O ihex $< $@
+
+%.elf: $(OBJS)
+ $(QUIET_LD) $(LDFLAGS) $^ $(LIBS) -o $@
+
+%.o: %.c
+ $(QUIET_CC) $(CFLAGS) $(INC) -c $<
+
+flash: $(PROJECT).hex
+ $(QUIET_AVRDUDE) -U flash:w:$<:i
+
+clean:
+ -@$(RM) $(addprefix $(PROJECT), .elf .hex .net .bom .cmd)
+
+.PHONY: clean
+.SECONDARY: $(addsuffix .elf, $(PROJECT)) $(OBJS)
+
+# Fuse high byte:
+# 0xdd = 1 1 0 1 1 1 0 1
+# ^ ^ ^ ^ ^ \-+-/
+# | | | | | +------ BODLEVEL 2..0 (brownout trigger level -> 2.7V)
+# | | | | +---------- EESAVE (preserve EEPROM on Chip Erase -> not preserved)
+# | | | +-------------- WDTON (watchdog timer always on -> disable)
+# | | +---------------- SPIEN (enable serial programming -> enabled)
+# | +------------------ DWEN (debug wire enable)
+# +-------------------- RSTDISBL (disable external reset -> enabled)
+#
+# Fuse low byte:
+# 0xe1 = 1 1 1 0 0 0 0 1
+# ^ ^ \+/ \--+--/
+# | | | +------- CKSEL 3..0 (clock selection -> HF PLL)
+# | | +--------------- SUT 1..0 (BOD enabled, fast rising power)
+# | +------------------ CKOUT (clock output on CKOUT pin -> disabled)
+# +-------------------- CKDIV8 (divide clock by 8 -> don't divide)
+# For 16.5MHz internal: -U hfuse:w:0xdd:m -U lfuse:w:0xe1:m
+# For 12MHz crystal: -U hfuse:w:0xdd:m -U lfuse:w:0b11111111:m
+# For 8MHz internal: -U hfuse:w:0xd7:m -U lfuse:w:0xe2:m
+fuse:
+ $(AVRDUDE) -e -U hfuse:w:0xd7:m -U lfuse:w:0xe2:m
--- /dev/null
+v 20140308 2
+C 55800 41700 0 0 0 title-A4-2.sym
+{
+T 63200 43000 15 30 1 1 0 4 1
+Title=Low pass audio filter
+T 62300 42000 15 16 1 1 0 4 1
+filename=filter.sch
+T 66750 43200 15 16 1 1 0 4 1
+revision=1
+T 66950 41800 15 16 1 1 0 6 1
+page=1
+T 67100 41800 15 16 1 1 0 0 1
+number_of_pages=1
+T 66750 42550 15 12 1 1 0 4 1
+date=21/02/2016
+T 65350 42150 15 16 1 1 0 4 1
+author=Pat Thoyts
+T 56000 50700 15 8 0 0 0 0 1
+symversion=1.0
+}
+C 59400 46800 1 0 0 res_horiz.sym
+{
+T 59700 49800 5 8 0 0 0 0 1
+device=resistor
+T 59700 47000 5 10 1 1 0 0 1
+refdes=R1
+T 59700 46800 5 10 1 1 0 0 1
+value=1k
+T 59700 46600 5 8 0 1 0 0 1
+footprint=0805
+T 59700 48800 5 8 0 0 0 0 1
+symversion=1.0
+}
+C 60500 46100 1 0 0 cap_vert.sym
+{
+T 60900 46500 5 10 1 1 0 0 1
+value=100nF
+T 60900 46300 5 8 0 1 0 0 1
+footprint=0805
+T 60800 48200 5 8 0 0 0 0 1
+symversion=1.0
+T 60900 46900 5 10 1 1 0 0 1
+refdes=C1
+}
+C 63400 46600 1 90 0 elko.sym
+{
+T 61200 46900 5 8 0 0 90 0 1
+device=POLARIZED_CAPACITOR
+T 63300 47100 5 8 0 1 90 0 1
+footprint=elko_RM5_D10
+T 62900 47100 5 10 1 1 0 0 1
+refdes=C2
+T 62900 46400 5 10 1 1 0 0 1
+value=10uF
+T 61800 46900 5 8 0 0 90 0 1
+symversion=2
+}
+C 61600 46300 1 0 0 potentiometer_vert.sym
+{
+T 61700 49300 5 8 0 0 0 0 1
+device=potentiometer
+T 61900 46900 5 8 0 1 0 0 1
+footprint=panel_mount_BI898
+T 61900 46900 5 10 1 1 0 0 1
+refdes=R3
+T 61700 48300 5 8 0 0 0 0 1
+symversion=1.0
+T 61900 46500 5 10 1 1 0 0 1
+value=10k
+}
+C 57900 47200 1 0 0 input-2.sym
+{
+T 57900 47400 5 10 0 0 0 0 1
+net=AUDIO:1
+T 58500 47900 5 10 0 0 0 0 1
+device=none
+T 59000 47500 5 10 1 1 0 7 1
+value=AUDIO
+}
+N 59300 47300 59600 47300 4
+N 60200 47300 61700 47300 4
+N 60800 47300 60800 47100 4
+N 61700 47300 61700 47100 4
+C 61200 45600 1 0 0 gnd-1.sym
+N 61700 46500 61700 45900 4
+N 60800 45900 61700 45900 4
+N 60800 46500 60800 45900 4
+N 62800 46800 62000 46800 4
+C 63700 46700 1 0 0 output-2.sym
+{
+T 64600 46900 5 10 0 0 0 0 1
+net=OUTPUT:1
+T 63900 47400 5 10 0 0 0 0 1
+device=none
+T 63900 47000 5 10 1 1 0 1 1
+value=OUTPUT
+}
+N 63700 46800 63400 46800 4
--- /dev/null
+/* Copyright (C) 2016 Pat Thoyts <patthoyts@users.sourceforge.net>
+ *
+ * Demonstration of simple sound output using PWM on an ATtiny85.
+ *
+ * Based on the example at:
+ * http://www.technoblogy.com/show?QVN
+ * Modified to play a tune.
+ *
+ * The sound output is on PB4 and should be passed through a low
+ * pass filter.
+ *
+ */
+
+#include <avr/io.h>
+#include <avr/interrupt.h>
+#include <avr/pgmspace.h>
+#include <avr/power.h>
+#include <avr/sleep.h>
+#include <avr/wdt.h>
+#include <util/delay.h>
+
+/*
+ * counter values to generate specific notes
+ * tone_count = (freq * 65536) / 20000
+ */
+unsigned int Tones[] = {
+ /* freq */
+ /* A3 220.00 */ 721,
+ /* B3 246.94 */ 809,
+ /* C4 261.63 */ 857,
+ /* D4 293.00 */ 960,
+ /* E4 329.63 */ 1080,
+ /* F4 349.23 */ 1144,
+ /* G4 392.00 */ 1285,
+ /* A4 440.00 */ 1442,
+};
+
+volatile unsigned int acc;
+static unsigned int note = 857; /* middle C */
+static int tune_index = 0;
+static char tune[] = "CCGGAAG FFEEDDC GGFFEED GGFFEED CCGGAAG FFEEDDC ";
+
+#define SQUARE_WAVE
+//#define SAWTOOTH_WAVE
+ISR(TIMER0_COMPA_vect)
+{
+ acc += note;
+
+#ifdef SQUARE_WAVE
+ OCR1B = (acc >> 8) & 0x80;
+#elif defined(SAWTOOTH_WAVE)
+ OCR1B = acc >> 8;
+#else
+ signed char temp, mask;
+ temp = acc >> 8;
+ mask = temp >> 7;
+ OCR1B = temp ^ mask;
+#endif
+}
+
+ISR(TIMER0_OVF_vect)
+{
+ PORTB ^= _BV(PB1);
+}
+
+ISR(TIMER1_OVF_vect)
+{
+ PORTB ^= _BV(PB0);
+}
+
+/* Section 12.3.9: enable the 64Mhz PLL as asynchronous clock source
+ * for timer 1 allowing the PLL to settle.
+ */
+static void enable_pll()
+{
+ PLLCSR = _BV(PLLE);
+ _delay_us(100);
+ while (!(PLLCSR & _BV(PLOCK)))
+ ;
+ PLLCSR |= _BV(PCKE);
+}
+
+/* use timer 0 and PWM on OC1B for sound generation */
+static void init_sound()
+{
+ cli();
+
+ DDRB |= _BV(DDB4);
+ PORTB &= ~_BV(PB4);
+
+ enable_pll();
+
+ /*
+ * Configure timer 0 to call the COMPA interrupts at 20 kHz
+ * In the compare match interrupt handler, OCR1B gets set to
+ * a value defined by the current note
+
+ * WGM = 0b111: Fast PWM (0->OCRA)
+ * CS = 0b010: CLK / 8
+ * OCR0A = 49 causes a further /50 so 8e6/8/50 == 20 kHz
+ */
+ TCCR0A = _BV(WGM01) | _BV(WGM00);
+ TCCR0B = _BV(WGM02) | _BV(CS01);
+ OCR0A = 49;
+
+ /*
+ * CTC1 = 0: continuous counter (does not restart on compare match)
+ * COM1A = 0b00: OC1A disconnected
+ * COM1B = 0b01: OC1B clear on compare match, set at BOTTOM, OC1B# enabled
+ * PWM1A = 0: disabled modulator A (attached to PB0 and PB1)
+ * PWM1B = 1: enable PWM mode for OCR1B and 0 when match OCR1C
+ * CS = 0b0001: PCK/1
+ *
+ * timer 1 has PWM enabled for B and toggles the output line on
+ * a match with OCR1B which is set by the note.
+ * OCR1B sets the duty cycle (when to switch the line low) and
+ * OCR1C (defaults to 0xFF) sets the TOP value and controls the frequency
+ * so in this case, 64e6/1/256 == 250kHz
+ *
+ * The tone is created in the OCR1B interrupt handler by setting
+ * the 16bit accumulator and using the top bit to control the PWM
+ */
+ TCCR1 = _BV(CS10);
+ GTCCR = _BV(PWM1B) | /*_BV(COM1B1) |*/ _BV(COM1B0);
+
+ /* Enable the compare match interrupt for A and add a overflow
+ * interrupt for testing the clock frequency with the oscilloscope
+ * (could use this as a 20kHz counter instead of _delay_ms later)
+ *
+ * For some reason, setting TOIE1 causes continual resets.
+ */
+ TIMSK = _BV(OCIE0A) | _BV(TOIE0); /* | _BV(TOIE1); */
+
+ sei();
+}
+
+/* flash the hearbeat led (PB2) on startup */
+static void indicate_startup()
+{
+ DDRB |= _BV(PB2);
+ PORTB &= ~(PB2);
+
+ for (int n = 0; n < 20; ++n)
+ {
+ PORTB ^= _BV(PB2);
+ _delay_ms(50);
+ }
+}
+
+/* emit silence for ms milliseconds by disabling the output line */
+#define PAUSE(x) \
+ DDRB &= ~_BV(DDB4); \
+ _delay_ms((x)); \
+ DDRB |= _BV(DDB4)
+
+int
+main(void)
+{
+ const int tempo = 400;
+
+ wdt_enable(WDTO_1S);
+
+ power_adc_disable();
+ power_usi_disable();
+
+ indicate_startup();
+ init_sound();
+
+ /* some test outputs for oscilloscope probing */
+ DDRB |= _BV(DDB1) | _BV(DDB0);
+ PORTB &= ~(_BV(PB1) | _BV(PB0));
+
+ for(;;)
+ {
+ wdt_reset();
+
+ int x = (int)tune[tune_index++];
+ if (x == 0)
+ {
+ tune_index = 0;
+ continue;
+ }
+ if (x == 32) /* space means silence for 1 note */
+ {
+ PAUSE(tempo);
+ continue;
+ }
+
+ x = x - (int)'A';
+ if (x < 2)
+ x = x + 7;
+ note = Tones[x];
+
+ PAUSE(50); /* provide a break after each note */
+
+ _delay_ms(tempo); /* play each note for tempo milliseconds */
+ PORTB ^= _BV(PB2);
+ }
+}