Sinewave generation using ATtiny85

I’m working on a very small, simple tone generator using the ATtiny85 chip. Examples using timers and interrupts work great on the regular Arduino boards, but since the ATtiny only has one timer, examples like this (http://makezine.com/projects/make-35/advanced-arduino-sound-synthesis) don’t work.

I did find an example (http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&p=1137195) that does generate a sinewave using the ATtiny’s limited hardware. When connected to an LED it does a nice fade in/out, but connected to a speaker it plays a very high-frequency tone, fading the volume in/out.

I’m still foggy on the way timers, ports, and bitwise operations work. Can anyone help me? Thanks!

#include <avr/pgmspace.h>      // for accessing PROGMEM
#include <avr/interrupt.h>     // for timer/interrupts

// basic settings
uint16_t frequency =   440;        // output frequency in Hz
#define cpuSpeed       8000000L   // 8MHz clock (CPU speed)


// sine wavetable
static const uint8_t sineTable[] PROGMEM =
{
  0x80,0x83,0x86,0x89,0x8c,0x8f,0x92,0x95,
  0x98,0x9c,0x9f,0xa2,0xa5,0xa8,0xab,0xae,
  0xb0,0xb3,0xb6,0xb9,0xbc,0xbf,0xc1,0xc4,
  0xc7,0xc9,0xcc,0xce,0xd1,0xd3,0xd5,0xd8,
  0xda,0xdc,0xde,0xe0,0xe2,0xe4,0xe6,0xe8,
  0xea,0xec,0xed,0xef,0xf0,0xf2,0xf3,0xf5,
  0xf6,0xf7,0xf8,0xf9,0xfa,0xfb,0xfc,0xfc,
  0xfd,0xfe,0xfe,0xff,0xff,0xff,0xff,0xff,
  0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xfe,
  0xfd,0xfc,0xfc,0xfb,0xfa,0xf9,0xf8,0xf7,
  0xf6,0xf5,0xf3,0xf2,0xf0,0xef,0xed,0xec,
  0xea,0xe8,0xe6,0xe4,0xe2,0xe0,0xde,0xdc,
  0xda,0xd8,0xd5,0xd3,0xd1,0xce,0xcc,0xc9,
  0xc7,0xc4,0xc1,0xbf,0xbc,0xb9,0xb6,0xb3,
  0xb0,0xae,0xab,0xa8,0xa5,0xa2,0x9f,0x9c,
  0x98,0x95,0x92,0x8f,0x8c,0x89,0x86,0x83,
  0x80,0x7c,0x79,0x76,0x73,0x70,0x6d,0x6a,
  0x67,0x63,0x60,0x5d,0x5a,0x57,0x54,0x51,
  0x4f,0x4c,0x49,0x46,0x43,0x40,0x3e,0x3b,
  0x38,0x36,0x33,0x31,0x2e,0x2c,0x2a,0x27,
  0x25,0x23,0x21,0x1f,0x1d,0x1b,0x19,0x17,
  0x15,0x13,0x12,0x10,0x0f,0x0d,0x0c,0x0a,
  0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x03,
  0x02,0x01,0x01,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,
  0x02,0x03,0x03,0x04,0x05,0x06,0x07,0x08,
  0x09,0x0a,0x0c,0x0d,0x0f,0x10,0x12,0x13,
  0x15,0x17,0x19,0x1b,0x1d,0x1f,0x21,0x23,
  0x25,0x27,0x2a,0x2c,0x2e,0x31,0x33,0x36,
  0x38,0x3b,0x3e,0x40,0x43,0x46,0x49,0x4c,
  0x4f,0x51,0x54,0x57,0x5a,0x5d,0x60,0x63,
  0x67,0x6a,0x6d,0x70,0x73,0x76,0x79,0x7c
};


// settings and frequency conversion for PWM
static uint8_t osc =         0;                                // outputed table value
uint16_t phaseAccumulator =  0;                                // 16-bit accumulator
uint8_t index =              0;                                // wavetable index
#define sampleFreq           (cpuSpeed / (1 << 8))             // sample rate
#define accumWidth           (sizeof(phaseAccumulator) * 8)

// final output timing frequency (called "fine-tuning word" or FTW)
//#define FTW                  ((frequency * (1UL << accumWidth)) / sampleFreq)


// run! note that (for some reason beyond me) using the traditional 
// setup() and loop() breaks the sketch :(
int main(void) {
  initTimer0();
  sei();

  for ( ;; ) {
    // run forever
  }
}


// setup timer0
void initTimer0(void) {
  DDRB |= (1<<PB0);                            // set PORTB0 pin as output
  TCCR0A |= (1<<COM0A1) | (0<<COM0B1);         // set to non-inverted fast PWM

  // enable fast pwm mode with no prescaler
  TCCR0A |= (1<<WGM01) | (1<<WGM00);
  TCCR0B |= (0<<WGM02) | (0<<CS02) | (0<<CS01) | (1<<CS00);

  TIMSK |= (1<<TOIE0);                         // enable overflow interrupt for timer0
}


// TIMER0 will overflow at a 31.25KHz (our sampling frequency)
// OCR0A value and the accumulator are updated
ISR (TIMER0_OVF_vect) {
  OCR0A = osc;                                 // send oscillator output to PWM
  phaseAccumulator += FTW;                     // increment accumulator
  index = phaseAccumulator >> 8;               // update index
  osc = pgm_read_byte( &sineTable[index] );    // get sine value for next interrupt
}

When connected to an LED it does a nice fade in/out, but connected to a speaker it plays a very high-frequency tone, fading the volume in/out.

It is functioning correctly. You are using PWM to generate an analog signal, and need a low pass filter on the output to remove the high frequency components. The filter component values will depend on your application and circuitry. For a very complete discussion see http://www.ti.com/lit/an/spraa88a/spraa88a.pdf

I don't think it is - even with a very aggressive low-pass filter (cutting off at ~500Hz) the pitch stays very high. It is possible I have the RC filter wrong, but it's so simple I don't think so.

I'm using a 3.3K resistor and a 0.1uF cap.

A 1st order filter is not "aggressive".

It is, in that I'm cutting down to just above the desired frequency. If you could suggest an alternative, or changes in the code that might produce less noise, that would be helpful.