blakelivingston / avr_tinytune

Audio Synthesis and Song Playback Library for Atmel ATtiny85 microcontrollers
MIT License
17 stars 4 forks source link

Non-PLL AVR support. #1

Open lberezy opened 9 years ago

lberezy commented 9 years ago

Hi there,

I just stumbled upon this project and found it quite interesting. I'd like to port it to the ATMega8 and higher, of which do not have the built-in PLL for fast PWM and a different configuration of timers. I'm only new to AVRs, but have been thoroughly reading datasheets all day. I've got an ATMega8 and to try and get it working with this code I've modified the timer related code in tinytune.c as follows:

Sample interrupt now comes from Timer1 (16 bit timer on ATMega) @ F_CPU/8/SAMPLE_CLOCK_DIVIDER = SAMPLE_RATE? I changed to the appropriate interrupt vector (TIMER1_OVF_vect) and can see some activity on PORTB[0:1] as should be in the interrupt code, so I /assume/ it's doing the right thing.

void initSampleInterrupt(void) {
  /* OLD CODE
  // Clock : Sys/8 (2mhz)
  // Fast PWM mode (|WGM0-2)
  TCCR0A = (1 << WGM00) | (1 << WGM01);
  TCCR0B = (1 << CS01) | (1 << WGM02);
  // Enable interrupt on overflow.
  TIMSK |= (1 << TOIE0);
  // In fast PWM mode, the timer overflows at OCR0A
  // Our timer / OCR0A = SAMPLE RATE
  // Do make sure this is under 255..
  OCR0A = SAMPLE_CLOCK_DIVIDER;
  sei();
  // Enable interrupts
  */

  TIMSK |= (1 << TOIE1);
  // fast pwm ( WGM13, 12, 11,10) for TOP controlled by OCR1A
  TCCR1B |= (1 << WGM12 ) | (1 << WGM13);
  TCCR1A |= (1 << WGM10) | (1 << WGM11);
  TCCR1B |= (1 << CS11); // CS11 for /8 divider
  OCR1A = SAMPLE_CLOCK_DIVIDER;
  sei();
}

The next change I made was to get 8-pwm going as fast as I can out of the other timer that supports it on the ATMega (Timer2) using the following code:

This should set up 8-bit PWM at a frequency of F_CPU / (prescale * 256).

void initPWMB(void) {
  /*TCCR1 = (1 << CS10); // Run at PCK/1
  GTCCR = (1 << PWM1B) | (1 << COM1B0); //Enable PWMB (pb4)
  DDRB |= (1 << PB4) | (1 << PB0) | (1 << PB1); // Output on pb4
  OCR1C = 0xff; */

  // set up timer 2 - this will be the output PWM
  TCCR2 |= ((1 << WGM21) | (1 << WGM20)); // Fast-PWM (p.115)
  TCCR2 |= (1 << COM21); // non-inverting OC2 pin output (p115)
  TCCR2 &= ~(1 << COM20);
  TCCR2 |= (1 << CS10); // prescale 1x
  OCR2 = 0;
}

I've also changed the first line of the interrupt routine to change the PWM duty cycle: OCR2 = sample_buffer[sample_buf_clock++];

Everything should be in order, but alas, I get no sound. I've tried a basic test sine table using the same timers and that seems to work fine, but here I get dead silence.

Would you happen to have some extra knowledge on how this code would be best ported to the ATMega hardware (looks like there's a hardware multiply instruction) or anything on minimum timings (perhaps an interrupt is being overflowed)? I've tried running from the internal 8 MHz RC as well as an external 16 MHz crystal as well lowering the sample rate.

Thanks!

lberezy commented 9 years ago

I made the egregious error of not including this important bit of code when I initialised the Timer2 PWM:

    DDRB |= (1 << DDB3); // Enable OC2 (PB3) as output
    PORTD &= ~(1 << DDB3);

Plays like a champ using the internal 8 MHz RC oscillator on an ATMega8, albeit at half the speed it should I think. (I set F_CPU to 8000000 and I'm using the Zelda tune to test - it seems to be at half tempo and pitch to your video)

lberezy commented 9 years ago

Okay now it's playing at full speed. Double checked the fuses and everything and it took a make clean && make program to get it all working. The buffer is very useful for sections that are close to the worst case of the ISR length.

blakelivingston commented 9 years ago

Hi Lucas, This is really great! Thank you for trying out the project and porting it to ATMega - I had intended to give this a shot for a long time but hadn't gotten around to it. It's gratifying to see that it went pretty smoothly and there weren't any attiny-specific snags in the synthesis logic. As far as I know, you're the second person from GitHub to get the project running.

I think I know why it needed a make clean to get the speed correct - I was neglectful in my Makefile dependencies and it wasn't checking settings.h. Apologies if that caused any frustration.

I have a few ATMega parts in my bin, and I'm going to try this change out tonight. Getting it running on ATMega also goes a long distance towards packaging it up for Arduino.

Would it be alright with you if I included your changes, with credit in README and code of course, in a future update or ATMega specific fork of the project?

Cheers, ~Blake

On Mon, Apr 27, 2015 at 5:09 AM, Lucas Berezy notifications@github.com wrote:

Okay now it's playing at full speed. Double checked the fuses and everything and it took a make clean && make program to get it all working. The buffer is very useful for sections that are close to the worst case of the ISR length.

— Reply to this email directly or view it on GitHub https://github.com/blakelivingston/avr_tinytune/issues/1#issuecomment-96623223 .

lberezy commented 9 years ago

For sure! Just wrap them up in some #if defined (__AVR_ATmega8__) preprocessor and it should be all good. I feel like there's some optimisations that could definitely be made thanks to the hardware multiply on the ATMega series too.

blakelivingston commented 9 years ago

Hi, just wanted to get back to you: Successfully got your patches running on an Atmega8! I'm going to takes some time this week to tidy up and check in a few other changes I've been meaning to make for a long time, namely a better random number algorithm for the noise channel.

The hardware multiplier on the ATMega really opens up a lot of optimization options as well as some features that just wouldn't be possible without it. The first thing I'd like to try if I can find some time to get back into the project is to see if I can get some kind of band-limited synthesis working so that the higher pitches sound less... awful.

Cheers, ~Blake

On Mon, Apr 27, 2015 at 3:04 PM, Lucas Berezy notifications@github.com wrote:

For sure! Just wrap them up in some #if defined (AVR_ATmega8) preprocessor and it should be all good. I feel like there's some optimisations that could definitely be made thanks to the hardware multiply on the ATMega series too.

— Reply to this email directly or view it on GitHub https://github.com/blakelivingston/avr_tinytune/issues/1#issuecomment-96834939 .

lberezy commented 9 years ago

Ah that's just great, I'll be sure to keep tabs on this project and test anything you push.

As for the noise function, have a look here, might prove useful. You could probably chunk that down to a maximal 16-bit Galois LFSR. Still pretty nice considering byte-wide XORs are single cycle on the AVR.

Regards, Lucas.