Sinusoidal PWM(SPWM) using AVR interrupt.

Hi all,

I’m basically working on programming for generating Sinusoidal PWM(SPWM) and simple PWM output using Arduino Uno ATMEGA 328p. To be honest, i’m new in Arduino and have little knowledge in programming. My program is mostly taken from another and not totally done by me from scratch.

My SPWM code is basically based on this link:
http://interface.khm.de/index.php/lab/experiments/arduino-dds-sinewave-generator/

My objective is to produce PWM output like figure below:

For the past few week, i have been working on this and able to produce the required PWM. Figure below shows the generated PWM using Proteus:

My problem is, when running the program after a few minutes, the square waves generated start to shifted from the filtered SPWM. FYI, the square wave is running on 50 Hz freq and the filtered SPWM is producing 100 Hz. Figure below shows the square waves are shifted when running after a few minutes:

Based on my opinion, this problem occur due to timer1 and timer2 are not oscillating in synchronize and delayed with few nano seconds. Below is my code:

/*
* DDS Sine Generator using Arduino Uno ATMEGA328P
*/

#include "avr/pgmspace.h"

// table of 256 sine values / half sine period / stored in flash memory
PROGMEM  prog_uchar sine256[]  = {
  1,3,6,8,11,14,17,20,23,25,28,31,34,37,39,42,45,48,50,53,56,59,61,64,67,69,72,75,77,80,83,85,
88,91,93,96,98,101,103,106,108,111,113,116,118,121,123,125,128,130,132,135,137,139,142,144,
146,148,150,152,154,157,159,161,163,165,167,169,170,172,174,176,178,180,181,183,185,186,188,
190,191,193,194,196,197,199,200,201,203,204,205,207,208,209,210,211,212,214,215,216,217,217,
218,219,220,221,222,222,223,224,224,225,226,226,227,227,228,228,228,229,229,229,229,230,230,
230,230,230,230,230,230,230,230,230,229,229,229,229,228,228,228,227,227,226,226,225,224,224,
223,222,222,221,220,219,218,217,217,216,215,214,212,211,210,209,208,207,205,204,203,201,200,
199,197,196,194,193,191,190,188,186,185,183,181,180,178,176,174,172,170,169,167,165,163,161,
159,157,154,152,150,148,146,144,142,139,137,135,132,130,128,125,123,121,118,116,113,111,108,
106,103,101,98,96,93,91,88,85,83,80,77,75,72,69,67,64,61,59,56,53,50,48,45,42,39,37,34,31,28,
25,23,20,17,14,11,8,6,3,

};
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))

double dfreq;
// const double refclk=320000;    // =16MHz / 50Hz
const double refclk=312500;      // measured

// variables used inside interrupt service declared as voilatile
volatile byte icnt;              // var inside interrupt
volatile byte icnt1;             // var inside interrupt
volatile byte c4ms;              // counter increment
volatile unsigned long phaccu;   // phase accumulator
volatile unsigned long tword_m;  // dds tuning word m
boolean toggle1 = 0;

void setup()
{
  pinMode(9, OUTPUT);      // sets the digital pin as output
  pinMode(10, OUTPUT);      // sets the digital pin as output
  pinMode(11, OUTPUT);     // pin11= PWM  output / frequency output

  Setup_timer1();
  cli();                   //stop interrupts
  
  //set timer1 interrupt at 100Hz
  TCCR1A = 0;              // set entire TCCR1A register to 0
  TCCR1B = 0;              // same for TCCR1B
  TCNT1  = 0;              //initialize counter value to 0
  // set compare match register for 100hz increments
  OCR1A = 155.25;            // = (16*10^6) / (100*1024) - 1 (must be <65536)
  // turn on CTC mode
  TCCR1B |= (1 << WGM12);
  // Set CS12 and CS10 bits for 1024 prescaler
  TCCR1B |= (1 << CS12) | (1 << CS10);  
  // enable timer compare interrupt
  TIMSK1 |= (1 << OCIE1A);
  
  sei();//allow interrupts
  
  
  Setup_timer2();

  // disable interrupts to avoid timing distortion
  cbi (TIMSK0,TOIE0);              // disable Timer0 !!! delay() is now not available
  sbi (TIMSK2,TOIE2);              // enable Timer2 Interrupt
  

  dfreq=1000.0;                    // initial output frequency = 1000.o Hz
  tword_m=pow(2,32)*dfreq/refclk;  // calulate DDS new tuning word 
  
}
void loop()
{
  while(1) {
     if (c4ms > 256) {                 // timer / wait fou a full second
      c4ms=0;

      cbi (TIMSK2,TOIE2);              // disable Timer2 Interrupt
      tword_m=pow(2,32)*dfreq/refclk;  // calulate DDS new tuning word
      sbi (TIMSK2,TOIE2);              // enable Timer2 Interrupt 
    }
    
  }
 }
//******************************************************************
// timer1 setup
void Setup_timer1(){
}

// timer2 setup
// set prscaler to 1, PWM mode to phase correct PWM,  16000000/510 = 31372.55 Hz clock
void Setup_timer2() {

// Timer2 Clock Prescaler to : 1
  sbi (TCCR2B, CS20);
  cbi (TCCR2B, CS21);
  cbi (TCCR2B, CS22);

  // Timer2 PWM Mode set to Phase Correct PWM
  cbi (TCCR2A, COM2A0);  // clear Compare Match
  sbi (TCCR2A, COM2A1);

  sbi (TCCR2A, WGM20);  // Mode 1  / Phase Correct PWM
  cbi (TCCR2A, WGM21);
  cbi (TCCR2B, WGM22);
}

//******************************************************************
// Timer1 Interrupt Service at 100 Hz
//generates pulse wave of frequency 100Hz/2 = 50Hz (takes two cycles for full wave- toggle high then toggle low)
ISR(TIMER1_COMPA_vect){   //timer1 interrupt 100Hz toggles pin 9,10

  if (toggle1){
    digitalWrite(9,HIGH);
    digitalWrite(10,LOW);
    toggle1=0;
  }
  else{
    digitalWrite(9,LOW);
    digitalWrite(10,HIGH);
    toggle1= 1;
  }
}

// Timer2 Interrupt Service at 320 KHz = 3.125uSec
// this is the timebase REFCLOCK for the DDS generator
// FOUT = (M (REFCLK)) / (2 exp 32)
ISR(TIMER2_OVF_vect) {

  phaccu=phaccu+tword_m; // soft DDS, phase accu with 32 bits
  icnt=phaccu >> 24;     // use upper 8 bits for phase accu as frequency information
                         // read value fron ROM sine table and send to PWM DAC
  OCR2A=pgm_read_byte_near(sine256 + icnt);    

  if(icnt1++ == 1) {  // increment variable c4ms
    c4ms++;
    icnt1=0;
   } 

}

I’m hoping that someone could help me to solve this problem or at least give me some idea on how to solve this. TQ ;D

First thing that strikes me:

  OCR1A = 155.25;            // = (16*10^6) / (100*1024) - 1 (must be <65536)

You don’t have decimal places in the timer fields (they are integers). So it won’t be 155.25. It will be 155.

pow(2,32)

That is a slow way of doing a bit shift.

Well, i guess that is the problem. since the program only read 155, it cannot oscillate 100 Hz accurately, but instead 100.16 Hz.

i’m thinking on something that can phase lock the square wave and the filtered SPWM together but i dont know where to start.