Author Topic: Digital sine wave creation (DAC)  (Read 6083 times)

Offline UncleThadius

  • Global Moderator
  • Advanced Poster
  • *****
  • Posts: 41
    • View Profile
Digital sine wave creation (DAC)
« on: February 06, 2011, 07:30:16 PM »
Here is a project for creating sine waves from the PWM output OC0A on  a Butterfly (AtMega 169). I took most of the code from:

http://aquaticus.info/pwm-sine-wave

This code works for many different ATMega micro's, but was originally coded for ATMega 32. All AtMega processors are very similar in that the hardware is all identical, just that the header files use different names for some of the peripherals on the different micro's. For instance, in this original code they listed the output pin as OC0, and yet in the larger processors they include both A and B outputs of the same register I.E. OC0A and OC0B, and OC1A and OC1B. So this was a very simple conversion to the larger processor format of the Mega 169, versus the Mega 32.

The code in this case takes a lookup table of 256 values (8 bits) and produces a sine wave from a digital signal. This is called Digital to Analog conversion and is very handy for creating music and warning sounds and also converting DC current to AC current (which is my intent for this project). With a small amount of filtering ( A 10k Ohm resistor and .1uF capacitor) we can see a clean sine wave develop on the output of the butterfly. I played with values for the lookup table to see how the amount of pulses would effect the smoothness of the output. 128 values is still a very clean sine wave but 64 or less starts to become jagged if the filter is not modified.

In this project we are sending gate signals to a set of mosfets, either a full H-Bridge which consists of 4 mosfets or other switches, or two mosfets or other types of switches (this is called a half bridge). Both of these setups pulse the primary of a transformer in order to create an AC current from DC current. Any level of output can be created in this manner including and up to 100's of kilowatts of energy. In my case I am creating 4000 watts of 120V 60 Hz AC power from a 1000 amp/hour battery bank charged to 24 volts DC (166 amperes for 4000 watts max output) .

Now since this is only a single output we also need a second output to handle the other half of the bridge to created both the positive and negative portion of the 60Hz sine wave (16.66 milliseconds per full cycle). This will be added shortly to obtain a full 360 degree sine wave as you would find on a normal grid connection. Also to be added is a grid synchronization circuit which will tell the main processor when to connect to the grid connection for grid-tie back feeding of power. This will physically make the power meter turn backwards assuming you have enough alternative energy to fulfill all of your household needs with some excess to feed back into the grid.

 

Below is the modified code:


/*
   Generate sine wave.
   Modified for Butterfly
   Microcontroller: ATmega169
   Clock: 8 MHz
   External hardware: RC filter on PB4 (OC0A)
   Compiler: AVR GCC

   http://aquaticus.info/pwm-sine-wave

   $Revision: 128 $
*/

#include <stdlib.h>
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <math.h>
#include <stdio.h>

/// if defined sine wave table is computed every time program starts
//#define COMPUTE_SINE_WAVE
#undef COMPUTE_SINE_WAVE

/// Number of probes for one period of sine wave
#define WAVE_PROBES 256 // was 256

/// Wave probes.

#ifdef COMPUTE_SINE_WAVE
/* InitSinTable() used to initiate the table */
uint8_t  wave[WAVE_PROBES];
#else
/* predefined values */
uint8_t  wave[WAVE_PROBES] =
{
128,131,134,137,140,143,146,149,152,155,158,162,165,167,170,173,176,179,182,
185,188,190,193,196,198,201,203,206,208,211,213,215,218,220,222,224,226,228,
230,232,234,235,237,238,240,241,243,244,245,246,248,249,250,250,251,252,253,
253,254,254,254,255,255,255,255,255,255,255,254,254,254,253,253,252,251,250,
250,249,248,246,245,244,243,241,240,238,237,235,234,232,230,228,226,224,222,
220,218,215,213,211,208,206,203,201,198,196,193,190,188,185,182,179,176,173,
170,167,165,162,158,155,152,149,146,143,140,137,134,131,128,124,121,118,115,
112,109,106,103,100,97,93,90,88,85,82,79,76,73,70,67,65,62,59,57,54,52,49,
47,44,42,40,37,35,33,31,29,27,25,23,21,20,18,17,15,14,12,11,10,9,7,6,5,5,
4,3,2,2,1,1,1,0,0,0,0,0,0,0,1,1,1,2,2,3,4,5,5,6,7,9,10,11,12,14,15,17,18,
20,21,23,25,27,29,31,33,35,37,40,42,44,47,49,52,54,57,59,62,65,67,70,73,
76,79,82,85,88,90,93,97,100,103,106,109,112,115,118,121,124,
};
#endif

/// Current wave sample number
static uint8_t sample = 0;

/**
   Generate sin table in range 0-359 deg.

   In fact only values 0-89 could be computed, but to make things
   simpler entire range is calculated.
*/

void InitSinTable()
{
   //sin period is 2*PI
   const float step = (2*M_PI)/(float)WAVE_PROBES;
   float s;
   float zero = 128.0; //was 128.0

   //in radians
   for(int i=0;i<WAVE_PROBES;i++)
   {
      s = sin( i * step );

      //calculate OCR value (in range 0-255, timer0 is 8 bit)
      wave = (uint8_t) round(zero + (s*127.0));

   }
}

/**
 Initializes timer0 for PWM generation
*/
void InitPWM()
{
   DDRB |= _BV(PB4); //OC0A pin as output

   TCCR0A |= (0<<WGM01) | (1<<WGM00); //Phase correct PWM

   TCCR0A |= _BV(COM0A1); //Clear OC0 on compare match, set OC0 at BOTTOM

   TCCR0A |= _BV(CS00); //prescaler divider 1

   TIMSK0 |= _BV(TOIE0); //Timer/Counter0 Overflow Interrupt Enable
}

int main()
{
   InitPWM();
   
#ifdef COMPUTE_SINE_WAVE
   InitSinTable();
#endif
   
   sei(); //enable global interruprts

   //go to sleep mode
   while(1)
   {
      sleep_mode();
   }
}

/**
   Interrupt routine. Needed to synchronize change of OCR0 with the end of period.
*/

ISR(TIMER0_OVF_vect)
{
   OCR0A = wave[sample];

   sample++;

   if( sample >= WAVE_PROBES-1 )
      sample = 0;
}
« Last Edit: February 06, 2011, 07:47:22 PM by UncleThadius »