Close

Timer math

A project log for Portable North American Power

220 VAC 50 Hz -> 120 VAC 60 Hz 15W

nick-sayerNick Sayer 06/15/2019 at 17:491 Comment

The ATTiny85 can run at 8 MHz without an external crystal. Of course, that 8 MHz isn't terribly accurate, but it doesn't have to be. Recall that we have a 256 byte sine table and we want to run the whole table through at 60 Hz. That's a sample rate of 60 * 256, or 15.36 kHz. If we clock the timer at the 8 MHz system clock, that's a nominal counter value of 520.8333. Unfortunately, the timers are only 8 bit, so we have to prescale the clock by 4, which means the nominal counter value will be 130.208333. So the PLL should, if the clock frequency is accurate, flip between 130 and 131. The counter overflow will trigger an interrupt that will update the other timer, which will be configured for PWM output of the sine wave. So there will be an in-memory value that counts through all 256 samples of the sine table.

Successive 50 Hz interrupts should see the sample pointer value move 6/5th of the way through the sample table, or 307.2 places (we can track the 15.36 kHz interrupts with an int and mask off the lower byte to index into the sine table). If the actual value is higher than 307, then we can increase the cycle length of the main counter and if it's 307 or lower, we can decrease it. The result will have some amount of jitter in it, but that jitter should be buried in the 256 samples of the 60 Hz sine output.

Discussions

agp.cooper wrote 06/16/2019 at 12:46 point

You can use the direct digital synthesis accumulator method here. The code is shorter than the name (well the code it is pretty short).

I am using the Arduino IDE and the code is for a Nano but it is not hard to port to a ATtiny85.

Here is a cut down example of the ISR for one channel (you can have more than one concurrent channel):

/* ISR: Tone on A0 */
volatile unsigned int magic0=0;
volatile unsigned int phase0=0;
ISR(TIMER0_OVF_vect) {
  phase0+=magic0;
  if (phase0<0x8000) PORTC&=B11111110 else PORTC|=B00000001;
}

The "magic" number is equal to:

   OutputFrequency*PhaseAccumulator/InterruptFrequency

You can scale the phase accumulator to look up a sine wave table for a PWM value.

It your interest I can send you some code.

Regards AlanX

  Are you sure? yes | no