Creating a Sinusoidal PWM signal for H-Bridge Control

Hey Everyone!

I am new to Arduino and not a very strong programmer. I am using the Arduino Due for a senior design project which involves building a 200W inverter. Our plan is to use the DUE to create the SPWM(sinusoid PWM) signal which whill drive the MOSFET controls in our H-Bridge.

Currently, I am able to generate the 60 Hz sine wave using a lookup table, although this is not working perfectly yet. However, my main concern is generating the Triangle wave to compare it to. I need a 2kHz wave with a 4V pk-pk voltage centered at 0V (-2 to +2V swing). Then I will compare these two signals for their intersection point to create the PWM signal with a variable duty cycle according to the sine wave.

Does anyone know how to go about creating this SPWM signal? Any help would be greatly appreciated!!

You don't need to compare anything to a triangle wave, just directly synthesize the
waveform - the duty cycle is set directly from the sine table.

unsigned int phase = 0 ;  // DDS phase accumulator
int frequency = ?? ;
int delay = ?? ;

#define TABLE_BITS 10
#define SHIFT (32 - TABLE_BITS)
int sine_table [1 << TABLE_BITS] ;  // values suitable for analogWrite...


while (true)
{
  phase += frequency ;
  analogWrite (sine_table [phase >> SHIFT]) ;  // top bits of phase index table
  delayMicroseconds (delay) ;
}

But you'd need a better way for a stable sample frequency than using delayMicrosecnds.

Maybe you could use dma or a timer interrupt.

Hi Im working exactly on the same project

I have the same pronlem with creating the SPWM right now I can get 3 sine wave signals (through pwm) with a variable frequency
Can anyone help me to modifiy my code to add the SPWM

//################################################################################################################
// **INCLUDE FILES & HEADER DATA****INCLUDE FILES & HEADER DATA****INCLUDE FILES & HEADER DATA**
//################################################################################################################

#include "asf.h"		  // ATMEL SOFTWARE  FRAMEWORK
#include "conf_board.h"  // standard Librarey from ASF
#include "conf_clock.h" // Standard Library from ASF
#include "SineArray.h" // Array which contains the sine wave elements

//----------------------------------------------------------------------------------------------------------------




//################################################################################################################
// **Global variables and macros****Global variables and macros****Global variables and macros**
//################################################################################################################




/** PWM frequency in Hz */
#define PWM_FREQUENCY     10000
/** Period value of PWM output waveform */
#define PERIOD_VALUE       4096
/** Initial duty cycle value 0-100 % */
#define INIT_DUTY_VALUE   50

//----------------------------------------------------------------------------------------------------------------

//################################################################################################################
// **GPIO pin definitions for 3 PWM signals****GPIO pin definitions for 3 PWM signals**
//################################################################################################################


/* PIN Definition for the third PWM Channel @ Arduino Due Board = PWM  PIN 07 */
#define PIN_PWM_3DPWM_GPIO     PIO_PC23_IDX
#define PIN_PWM_3DPWM_FLAGS   (PIO_PERIPH_B | PIO_DEFAULT)
#define PIN_PWM_3DPWM_CHANNEL PWM_CHANNEL_6


/* PIN Definition for the second PWM Channel @ Arduino Due Board = PWM  PIN 08 */
#define PIN_PWM_2NDPWM_GPIO     PIO_PC22_IDX
#define PIN_PWM_2NDPWM_FLAGS   (PIO_PERIPH_B | PIO_DEFAULT)
#define PIN_PWM_2NDPWM_CHANNEL PWM_CHANNEL_5	


/* PIN Definition for the first PWM Channel @ Arduino Due Board = PWM  PIN 09 */
#define PIN_PWM_1STPWM_GPIO     PIO_PC21_IDX
#define PIN_PWM_1STPWM_FLAGS   (PIO_PERIPH_B | PIO_DEFAULT)
#define PIN_PWM_1STPWM_CHANNEL PWM_CHANNEL_4

//----------------------------------------------------------------------------------------------------------------

/** PWM channel instance  */
pwm_channel_t g_pwm_channel_instance;



//################################################################################################################
// **Interrupt handler for the PWM controller****Interrupt handler for the PWM controller**
//################################################################################################################


void PWM_Handler(void)
{
	static uint32_t size;						 /* Size of the sine array*/
	static uint32_t ul_count = 0;				 /* PWM counter value */
	static uint32_t ul_duty1 = INIT_DUTY_VALUE;  /* PWM duty cycle rate */
	static uint32_t ul_duty2 = INIT_DUTY_VALUE;  /* PWM duty cycle rate */
	static uint32_t ul_duty3 = INIT_DUTY_VALUE;  /* PWM duty cycle rate */
	
	uint32_t events = pwm_channel_get_interrupt_status(PWM);

			 /* Standard frequency range e.g LENZE VFD 0-300 HZ */
			 /* Ground sine wave frequency = (PWM_Freq/ Size of sine wave array)
			  = 10 000 HZ/ 120 = 83.3 Hz*/
			 
			 
			  ul_count++;
			
			
			
			/* to vary the sine wave frequency the  u_count must be re defined due to the desired frequency*/
		    //ul_count= ul_count+2 ;
		
		
		/* to determine the size of the sine array*/
		size = sizeof(sine)/2; 
		
		
				
				if (ul_count >= size) {
					ul_count = 0;
				}
				
				
				/* Overlay a sine wave array to a  PWM signal */
				/* each output signal is 120 degree phase shifted to the other one */
				ul_duty1 = sine[ul_count%size];              // 1st sine wave output 0 degree phase shift
				ul_duty2 = sine[(ul_count+(size/3)) %size]; // 2nd sine wave output 120 degree phase shift
				ul_duty3 = sine[(ul_count+(2*size/3))%size];// 3rd sine wave output 240  degree phase shift 

			
			/* Set new duty cycle for the 3 pwm signals */
			
				g_pwm_channel_instance.channel = PIN_PWM_1STPWM_CHANNEL;
				pwm_channel_update_duty(PWM, &g_pwm_channel_instance, ul_duty1);
				g_pwm_channel_instance.channel = PIN_PWM_2NDPWM_CHANNEL;
				pwm_channel_update_duty(PWM, &g_pwm_channel_instance, ul_duty2);
				g_pwm_channel_instance.channel = PIN_PWM_3DPWM_CHANNEL;
				pwm_channel_update_duty(PWM, &g_pwm_channel_instance, ul_duty3);
			
		}



//########################################################################################################
// ******MAIN MENU*************************MAIN MENU*******************************MAIN MENU***************
//########################################################################################################


int main(void)
{
	/* Initialize the SAM system */
	sysclk_init();
	board_init();

	/* GPIO Pin configuration */
	gpio_configure_pin(PIN_PWM_3DPWM_GPIO, PIN_PWM_3DPWM_FLAGS);  // for PWM PIN 3
	gpio_configure_pin(PIN_PWM_2NDPWM_GPIO, PIN_PWM_2NDPWM_FLAGS);  // for PWM PIN 2
	gpio_configure_pin(PIN_PWM_1STPWM_GPIO, PIN_PWM_1STPWM_FLAGS);  // for PWM PIN 1
	
	/* Enable PWM peripheral clock */
	pmc_enable_periph_clk(ID_PWM);

	/* Disable PWM channels */
	pwm_channel_disable(PWM, PIN_PWM_1STPWM_CHANNEL); // @ PWM PIN 9
	pwm_channel_disable(PWM, PIN_PWM_2NDPWM_CHANNEL);  // @ PWM PIN 8
	pwm_channel_disable(PWM, PIN_PWM_3DPWM_CHANNEL);  // @ PWM PIN 7

	/* Set PWM clock A as PWM_FREQUENCY*PERIOD_VALUE (clock B is not used) */
	pwm_clock_t clock_setting = {
		.ul_clka = PWM_FREQUENCY * PERIOD_VALUE,
		.ul_clkb = 0, // clock B is disabled
		.ul_mck = sysclk_get_cpu_hz()
	};
	pwm_init(PWM, &clock_setting);
	
	
	/* Initialize PWM channel for PWM PIN 7 */
	/* Period is left-aligned */
	g_pwm_channel_instance.alignment = PWM_ALIGN_LEFT;
	/* Output waveform starts at a low level */
	g_pwm_channel_instance.polarity = PWM_LOW;
	/* Use PWM clock A as source clock */
	g_pwm_channel_instance.ul_prescaler = PWM_CMR_CPRE_CLKA;
	/* Period value of output waveform */
	g_pwm_channel_instance.ul_period = PERIOD_VALUE;
	/* Duty cycle value of output waveform */
	g_pwm_channel_instance.ul_duty = INIT_DUTY_VALUE;
	g_pwm_channel_instance.channel = PIN_PWM_3DPWM_CHANNEL;
	pwm_channel_init(PWM, &g_pwm_channel_instance);
		
 
	
		
	/* Initialize PWM channel for PWM PIN 8  */
	/* Period is left-aligned */
	g_pwm_channel_instance.alignment = PWM_ALIGN_LEFT;
	/* Output waveform starts at a low level */
	g_pwm_channel_instance.polarity = PWM_LOW;
	/* Use PWM clock A as source clock */
	g_pwm_channel_instance.ul_prescaler = PWM_CMR_CPRE_CLKA;
	/* Period value of output waveform */
	g_pwm_channel_instance.ul_period = PERIOD_VALUE;
	/* Duty cycle value of output waveform */
	g_pwm_channel_instance.ul_duty = INIT_DUTY_VALUE;
	g_pwm_channel_instance.channel = PIN_PWM_2NDPWM_CHANNEL;
	pwm_channel_init(PWM, &g_pwm_channel_instance);

	/* Enable channel counter event interrupt */
	pwm_channel_enable_interrupt(PWM, PIN_PWM_2NDPWM_CHANNEL, 0);

	/* Initialize PWM channel for PWM PIN 9 */
	/* Period is left-aligned */
	g_pwm_channel_instance.alignment = PWM_ALIGN_LEFT;
	/* Output waveform starts at a low level */
	g_pwm_channel_instance.polarity = PWM_LOW;
	/* Use PWM clock A as source clock */
	g_pwm_channel_instance.ul_prescaler = PWM_CMR_CPRE_CLKA;
	/* Period value of output waveform */
	g_pwm_channel_instance.ul_period = PERIOD_VALUE;
	/* Duty cycle value of output waveform */
	g_pwm_channel_instance.ul_duty = INIT_DUTY_VALUE;
	g_pwm_channel_instance.channel = PIN_PWM_1STPWM_CHANNEL;
	pwm_channel_init(PWM, &g_pwm_channel_instance);
	

	
	/* Disable channel counter event interrupt */
	pwm_channel_disable_interrupt(PWM, PIN_PWM_1STPWM_CHANNEL, 0);

	/* Configure interrupt and enable PWM interrupt */
	NVIC_DisableIRQ(PWM_IRQn);
	NVIC_ClearPendingIRQ(PWM_IRQn);
	NVIC_SetPriority(PWM_IRQn, 0);
	NVIC_EnableIRQ(PWM_IRQn);

	/* Enable PWM channels  */
	pwm_channel_enable(PWM, PIN_PWM_1STPWM_CHANNEL);  // @ PWM PIN 9
	pwm_channel_enable(PWM, PIN_PWM_2NDPWM_CHANNEL); // @ PWM PIN 8
	pwm_channel_enable(PWM, PIN_PWM_3DPWM_CHANNEL); //@ PWM PIN 7

	
	//###########################################################################################################
	// ******MAIN LOOP*************************MAIN LOOP*******************************MAIN LOOP***************
	//###########################################################################################################
	while (1) {
	}
}

@so3ody
See your own thread.

Can you share the header and code separately? I ma working on the same project and i need help. I need to implement it all using arduino uno

Hi,

If you want to use the triangle comparison method then yes you have to generate a triangle wave and a sine wave and compare them to each other. But that is the purely analog method.
A triangle wave in a digital environment is just a stepped wave, because each change in the triangle amplitude is a step not a perfect ramp like it is in the analog world.
So to generate a triangle wave digitally internal to the micro chip, you would just add another step for each sample time period, then at the peak you would start to subtract a step and keep subtracting until you get to the lowest peak, then start adding a step again.

So lets say your step is 10, and your peaks are at 0 and 100. Starting from zero we would have:
dv=10;
V=0;

then a loop with this in it:
V=V+dv;

and if V becomes greater than 100, then start subtracting:
V=V-dv;

and if V becomes less than 0, then start adding again:
V=V+dv;

Of course this assumes that your loop is timed perfectly so that each loop time is the same regardless of any conditional jumps, or else you can do this during start up so you can calculate everything beforehand and store it in a table.

The other method where you use the sine calculations to determine the pulse widths is more straightforward. You can perhaps do some calculations for each technique and compare outputs, then compare the Fourier components to see which one (if any) has less harmonic distortion.

You also have to decide how you want to drive the H bridge, either always on+ or on-, or also having one or more types of 'off' states. So you could have from 2 up to 4 different H bridge states (or maybe 5).