Arduino Uno SPWM Inverter

This project is my experiment to produce a pure sine wave using the sinusoidal pulse width modulation (abbreviated SPWM) method, using Arduino Uno as an SPWM generator as a part of an Inverter.

Because Arduino needs a circuit to drive High-Side and Low-Side MOSFETs to connect to Full-Bridge MOSFETs then I used a faulty EGS002 board on the part of the microcontroller (EG8010) as this driver.

Arduino Uno SPWM Circuit.

In the picture below is the connection between the Arduino Uno and the EGS002 (the circuit in the box).

I connected the d9, d10, d11, and d12 pins on the Arduino to the SPWMOutX pads of EG8010 on the EGS002. Whereas Pins 1LO, 1HO, 2LO, and 2HO are pins on the SIP Header EGS002.

The following circuit is a Full-Bridge MOSFET circuit for the EGS002 SPWM Inverter, and I used it in the Arduino Uno SPWM Inverter experiment. The pins used are precisely the same because the driver for the SPWM generator follows EGS002. The thick line in the circuit is a line that has a large current so in PCB manufacturing this line must be quite large.

Arduino Uno SPWM Coding.

The scope of work for writing Arduino code is only for making pure sine waves using the Pulse Width Modulation (PWM) method and I haven’t thought about voltage feedback, protection and so on.

Look Up Table

For this purpose, I decided to use carrier frequency at 10kHz. So, it’s not too high or too low either.

At this carrier frequency to produce one cycle of a 50Hz sine wave, we need 200 PWM cycles. The calculation is like this:

PWM pulse = F carrier / F sine

PWM pulse = 10.000 Hz / 50 Hz = 200

The sine wave cycle consists of a positive half-cycle and a negative half-cycle, each having the same pattern with the opposite polarity. For this reason, the Look-Up Table (LUT) stores 100 entries for a half cycle of a sine wave.

At 10kHz carrier frequency, the Pulse occurs every 100 microseconds so at every 100 microseconds we must update the value of pulse width. And I’m using a spreadsheet program to calculate those values.

So :

LookUpTable[0] = 0 (sine value at 0 microseconds),

LookUpTable[1] = 0.03125 (sine value at 100 microseconds),

LookUpTable[2] = 0.0625 (sine value at 200 microseconds), etc.

By using 16-bit TIMER/COUNTER 1 as a control for PWM generation in Phase Frequency Correct PWM mode (WGM mode 8) with no Prescaler, the ICR1 value is 800 SysClk.

ICR1   = 800;   /* Counter TOP value (at 16MHz XTAL, SPWM carrier freq. 10kHz, 200 samples/cycle).*/

The next step is to normalize the LUT value to that, so:

LookUpTable[0] = 0 * 800 –> lookUp1[0] = 0,

LookUpTable[1] = 0.03125 * 800 –> lookUp1[1] = 25,

LookUpTable[2] = 0.0625 * 800 –> lookUp1[2]= 50,.. etc.

become:

int lookUp1[] = {
0,  25, 50, 75, 100,125,150,175,199,223,247,271,294,318,341,363,385,407,429,450,
470,490,510,529,548,566,583,600,616,632,647,662,675,689,701,713,724,734,744,753,
761,768,775,781,786,790,794,796,798,800,800,800,798,796,794,790,786,781,775,768,
761,753,744,734,724,713,701,689,675,662,647,632,616,600,583,566,548,529,510,490,
470,450,429,407,385,363,341,318,294,271,247,223,199,175,150,125,100,75, 50, 25};

Because OC1A (pin 9) and OC1B (pin 10) drive High Side MOSFET and Low Side MOSFET respectively, then both pins must not be at the same logic at a time, in other words, must be complementary so the register setting is like this:

TCCR1A = 0b10110000;          
   /*    0b10xxxxxx Clear OC1A/OC1B on compare match when up-counting. Set on compare match when down counting
         0bxx11xxxx Set OC1A/OC1B on compare match when up-counting. Clear on compare match when down counting.
         0bxxxxxx00 WGM1 1:0 for waveform 8 (phase freq. correct).       */

You can find The details of the TIMER/COUNTER 1 register in the datasheet of ATmega 328 on page 89. See https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-7810-Automotive-Microcontrollers-ATmega328P_Datasheet.pdf

Interrupt Service Routine.

The Interrupt Service routine for TIMER/COUNTER1 does all tasks to generate Sinusoidal Pulse Width Modulation

In the ISR the value in lookUp1 is read and entered in the OCR1X register after being corrected with the modulation index value and also adding the dead time value.

The dtX is the dead time value and 0.7 is the modulation index value currently set at a fixed value.

The dead time I used here is 312 nanoseconds ( 5 x 62.5 nS = 312.5 nS).

    OCR1A = int(lookUp1[num]*0.7)+dtA;  // SPWM width update   
    OCR1B = int(lookUp1[num]*0.7)+dtB;  // note: 0.7 used to reduce inveter output voltage  

To determine the width of the SPWM pulse we use the value stored in OCR1X.

100 lookUp values can be loaded into the OCR1X which are only for the half cycle of the sine wave as described earlier, then the half value also uses this value only the polarity is reversed by resetting the TCCR1A register.

      if(ph==0){             // OC1A as SPWM out
        TCCR1A = 0b10110000; // clear OC1A, set OC1B on compare match 
        dtA=0;               // no dead time
        dtB=5;              // adding dead time to OC1B
      }

The above code is for the positive cycle and the bellows are for the negative cycle.

else {
        TCCR1A = 0b11100000; // OC1B as SPWM out
        dtA=5;              
        dtB=0;
      }

The rest of the code in the ISR is utilised for fundamental frequency pulse with a duty cycle of 50%.

That’s All.

Here is the complete code for Arduino Uno Sinusoidal Pulse Width Modulation, which you can download here

Hope you enjoy this work, I’m very happy if this can be useful for all of us.