SPWM Timing Issue [SOLVED]

Hello,
I have been trying to generate a SPWM with variable frequency and amplitude. And I have succeed to do so.
Here are the results:

Now the problem is this:

As you can see the timing of the square wave is not correct. Because of this if I connect a H-Bridge with LC filter to convert the SPWM to sine:

As you can see the sine is not correct. This is due to the square wave timing issue.
Can somebody help me to fix this. Please. :slight_smile:

Here, is the code:

#include <avr/io.h>
#include <avr/interrupt.h>

#define LookupEntries (512)

static int microMHz = 16;   // clock frequency in MHz
static int freq, amp = 1024;// Sinusoidal frequency
static long int period;     // Period of PWM in clock cycles. 1600 gives 10KHz.
static unsigned int lookUp[LookupEntries];
static char theTCCR1A = 0b10000010; //varible for TCCR1A
static unsigned long int phaseinc, switchFreq;
static double phaseincMult;

int setFreq(int freq);         //set in Hertz
int setSwitchFreq(int sfreq);  //set in Hertz
int setAmp(float _amp);        //set in % (0 - 100)
void makeLookUp(void);
void registerInit(void);

void setup(){ 
  Serial.begin(9600);
  makeLookUp();
  setSwitchFreq(10000);  
  setFreq(50);
  setAmp(100);
  registerInit();
}

void loop(){
/*
The code in the loop reads analog values from pins A1 and A2 so that potentiometers can be connected.
These values are used to vary the amplitude and frequency of the sine wave.
The switching frequency also toggles between 5 and 15Khz.
*/
  static int ampVal, freqVal, anologVal;

  anologVal = analogRead(A0);
  if(anologVal > freqVal*1.01 || anologVal < freqVal*0.99){
    freqVal = anologVal;
    setFreq(map(freqVal, 0, 1023, 5, 50));
    Serial.println("phaseinc");
    Serial.print(phaseinc>>23);
    Serial.print(".");
    Serial.print(phaseinc&0x007FFFFF);
    Serial.print("\n"); 
  }
  
  anologVal = analogRead(A0);
  if(anologVal > ampVal*1.01 || anologVal < ampVal*0.99){
    ampVal = anologVal;
    setAmp(map(ampVal, 0, 1023, 0, 100));
    Serial.println("amplitude");    
    Serial.println(amp);
  }
  
  delay(20);
  static char cnt = 0;
  cnt++;
  if(cnt == 100){
    setSwitchFreq(15000);
    cnt = 0;
  } else if(cnt == 50){
    setSwitchFreq(5000);
  }
}

ISR(TIMER1_OVF_vect){
  static unsigned long int phase, lastphase;
  static char delay1, trig = LOW;

  phase += phaseinc;

  if(delay1 == 1){
    theTCCR1A ^= 0b10100000;// Toggle connect and disconnect of compare output A and B.
    TCCR1A = theTCCR1A;
    delay1 = 0;  
  } 
  else if((phase>>31 != lastphase>>31) && !(phase>>31)){
    delay1++;      
    trig = !trig;
    digitalWrite(13,trig);
    digitalWrite(10,!trig);
  }
  
  lastphase = phase;
  OCR1A = OCR1B = ((lookUp[phase >> 23]*period) >> 12)*amp >> 10;
}

int setFreq(int _freq){
  if(_freq < 0 || _freq > 1000){ // returns -1 if the frequency value is invalid
    return 0;
  } else {
    freq = _freq;
    phaseinc = (unsigned long int) phaseincMult*_freq;
    return 1;
  }
}
    
int setSwitchFreq(int sfreq){
  double temp;
  
  if(sfreq <= 0 || sfreq > 20000){
    return 0;
  } else {
    switchFreq = sfreq;
    period = microMHz*1e6/sfreq;
    //sindevisions*decimalbits/1MHz = 
    //1024*2^23/1e6 = 8,589.934592
    phaseincMult = (double) period*8589.934592/microMHz;
    phaseinc = (unsigned long int) phaseincMult*freq;
    ICR1   = period;
  }
}

int setAmp(float _amp)
{
  if(_amp < 0 || _amp > 100){
    return 0;
  } else {
    amp = map(_amp,0,100,0,1024);
    return 1;
  }  
}

void makeLookUp(void){
  double temp;

    cli(); //disable global interupts while lookup table is made
    TCCR1A = 0b00000010; //disconnect compare A and B while lookup table is generated
    
    for(int i = 0; i < LookupEntries; i++){ // Generating the look up table.
      temp = sin(i*M_PI/LookupEntries)*4096;
      lookUp[i] = (int)(temp+0.5);       // Round to integer.    
    }

    TCCR1A = theTCCR1A; // reconnect compare outputs
    sei(); //re-enable interupts now that table has been made
}

void registerInit(void){
  // Register initilisation, see datasheet for more detail.
  TCCR1A = theTCCR1A; // 0b10000010;
  /*10 clear on match, set at BOTTOM for compA.
   00 compB disconected initially, toggled later to clear on match, set at BOTTOM.
   00
   10 WGM1 1:0 for waveform 15.
   */
  TCCR1B = 0b00011001;
  /*000
   11 WGM1 3:2 for waveform 15.
   001 no prescale on the counter.
   */
  TIMSK1 = 0b00000001;
  /*0000000
   1 TOV1 Flag interrupt enable.
   */
  sei();             // Enable global interrupts.
  // Set outputs pins.
  pinMode(11,OUTPUT); // Set 11 and 12 as outputs
  pinMode(12,OUTPUT);
  pinMode(13, OUTPUT); // Set trigger pin to output
  pinMode(10, OUTPUT); 
}

And I am using Arduino Mega

If changing the "amplitude" of the PWM signal is a requirement, which I guess is currently fixed at 5.0 volts, how do you wish to achieve that ? Or do you mean duty cycle or phase ?

Or is the final objective to produce a sine wave ?

Anyway, include a schematic diagram including the components you have connected to the Arduino and the measuring point of that "sine" wave.

Dont worry about the Inductor Value
There's a bug in Proteus which reads it as 5mh

OK. So you are using (in a simulated environment) an Arduino Mega to drive an IRF840 based H bridge directly to control an LC ( 2.2uF / 5mH ) network to generate a sine wave. I’ve never seen this before, but it certainly looks interesting.

From the simulation trace, it looks like you’ve got quite far although the “sine” wave has a few rough spots.

Can you choose mosfets, i.e. logic level mosfets, which have a defined behaviour at 5 volts, for example IRL3705N. I’m not certain this will help, but it is one thing less that has to be considered.

Edit

Can you say at which points those coloured traces are measured ?
Can you also illustrate somehow what the traces should look like.

What is happening here. Your schematic does not show what is connected to pin A0.
Further, it appears that the same value, A0, conditions both frequency and amplitude.
The comment refers to pins A1 and A2 which don’t appear to be used.

void loop(){
/*
The code in the loop reads analog values from pins A1 and A2 so that potentiometers can be connected.
These values are used to vary the amplitude and frequency of the sine wave.
The switching frequency also toggles between 5 and 15Khz.
*/
  static int ampVal, freqVal, anologVal;

  anologVal = analogRead(A0);
  if(anologVal > freqVal*1.01 || anologVal < freqVal*0.99){
    freqVal = anologVal;
    setFreq(map(freqVal, 0, 1023, 5, 50));
    Serial.println("phaseinc");
    Serial.print(phaseinc>>23);
    Serial.print(".");
    Serial.print(phaseinc&0x007FFFFF);
    Serial.print("\n");
  }
 
  anologVal = analogRead(A0);
  if(anologVal > ampVal*1.01 || anologVal < ampVal*0.99){
    ampVal = anologVal;
    setAmp(map(ampVal, 0, 1023, 0, 100));
    Serial.println("amplitude");   
    Serial.println(amp);
  }
 
  delay(20);
  static char cnt = 0;
  cnt++;
  if(cnt == 100){
    setSwitchFreq(15000);
    cnt = 0;
  } else if(cnt == 50){
    setSwitchFreq(5000);
  }
}

I guess you have got some code and have been asked to modify it.
If that is correct, were you able to test the original code base before making changes to it ?

The frequency and and amplitude were controlled by two different pins A1 And A2. I modified it to be controlled by a single pin A0. I just connected a constent 2.5 volts to it
Here is the original code:

It designed to work with Arduino Uno. I modified a line to get it to work with arduino mega. And the original just showed the two SPWM not the square wave. Though it showed a square on pin 13 with each half cycle it was intended to work as a trigger pin to oscilloscope, As I wanted an inverted square on another pin I just added an "!" operator. After testing it seems it was not perfectly timed with each SPWM cycle I just wanted to offset the PWM a little bit :slight_smile:

      temp = sin(i*M_PI/LookupEntries)*4096;
      lookUp[i] = (int)(temp+0.5);       // Round to integer.

I think there is a problem in this part of makeLookUp(). The sin() function returns a value between -1 and 1 but the lookUp array is 'unsigned int'. You should probably not try to put negative numbers in an unsigned array.

Perhaps you can add 1 to the sin() and multiply by 2048 to get a 0-4096 range instead of the -4096 to +4096 range.

Changed as you said:

temp = sin(i*M_PI/LookupEntries)*2048;
      lookUp[i] = (int)(temp+0.5);       // Round to integer.

Still no improvement :frowning:

Can you say which Arduino pins are used to measure which coloured traces ?
In your last diagram, which traces do you consider "wrong", the pink and yellow or blue and green or what ?

After some tinkering I found the problem :slight_smile:
The problem was from this part of the code:

if(delay1 == 1){[color=#222222][/color]
    theTCCR1A ^= 0b10100000;// Toggle connect and disconnect of compare output A and B.[color=#222222][/color]
    TCCR1A = theTCCR1A;[color=#222222][/color]
    delay1 = 0; [color=#222222][/color]
  }[color=#222222][/color]
  else if((phase>>31 != lastphase>>31) && !(phase>>31)){[color=#222222][/color]
    delay1++;     [color=#222222][/color]
    trig = !trig;[color=#222222][/color]
    digitalWrite(13,trig);[color=#222222][/color]
    digitalWrite(10,!trig);[color=#222222][/color]
  }

The else if condition was only triggering alternating cycles thats why there was a offset in triggering of the square wave
I moved the triggering function to if delay == 1 function. Now it works like a charm :slight_smile: