Sunday, February 17, 2013

Sine Wave Generation with "Fast PWM Mode" of AVR - using ATmega16


I had previously shown how to generate sinusoidal pulse width modulation (SPWM) signals using the ECCP module in a PIC for generating a sine wave output for use in DC-AC inverter. I have had requests from people asking how to generate the same SPWM signals with other microcontrollers that don't have the ECCP module, such as the super popular PIC16F877A. And so I had written another article where I showed how to generate sine wave using SPWM with the CCP module of a PIC. This concept, as I had mentioned in that tutorial, can be extended to use for any microcontroller that has a PWM module. And so, I’ve decided to demonstrate how to generate sine wave using SPWM with an Atmel AVR microcontroller.

The microcontroller I’ve chosen is the ATMEGA16. However, the concept can be used on any AVR that has a PWM module. The output sine wave is to have a frequency of 50Hz. I have chosen to use a switching frequency of 16kHz for SPWM.

So, here I talk about how to generate sine wave using sinusoidal pulse width modulation (SPWM) signals using the PWM module as can be commonly found on most Atmel AVR microcontrollers. If you’re curious regarding my previous tutorials that revolved around the Microchip PIC microcontrollers, you should go through the other articles related to generating SPWM to get an idea of what I'm talking about regarding sine wave generation:



Now let's move on to sine wave generation using ATMEGA16.

Let’s take a look at the code first:

//----------------------------------------------------------------------------------------
//Programmer: Syed Tahmid Mahbub
//Target Microcontroller: ATMEGA16
//Compiler: mikroC PRO for AVR (Can easily port to any other compiler)
//-----------------------------------------------------------------------------------------
 



unsigned int sin_table[32]={0, 100, 199, 296, 390, 480, 566, 645, 718, 
783, 840, 889, 928, 958, 979, 989, 989, 979, 958, 928, 889, 840, 783,
 718, 645, 566, 480, 390, 296, 199, 100,0};

#define MOSA PORTD0_bit
#define MOSB PORTD1_bit
#define MOSC PORTD2_bit
#define MOSD PORTD3_bit

unsigned char FlagReg;
#define Direction FlagReg.B0
//0 -> MOS A + D
//1 -> MOS B + C

unsigned int TBL_POINTER_NEW, TBL_POINTER_OLD, TBL_POINTER_SHIFT, SET_FREQ;
unsigned int TBL_temp;
unsigned char DUTY_CYCLE;

void interrupt() org IVT_ADDR_TIMER1_OVF{
     TBL_POINTER_NEW = TBL_POINTER_NEW + SET_FREQ;
         if (TBL_POINTER_NEW < TBL_POINTER_OLD){
           if (Direction == 0){
              MOSA = 0;
              MOSD = 0;
              MOSB = 1;
              MOSC = 1;
              Direction = 1;
           }
           else{
                MOSB = 0;
                MOSC = 0;
                MOSA = 1;
                MOSD = 1;
                Direction = 0;
           }
        }
        TBL_POINTER_SHIFT = TBL_POINTER_NEW >> 11;
        DUTY_CYCLE = TBL_POINTER_SHIFT;
        TBL_POINTER_SHIFT = sin_table[DUTY_CYCLE];
        OCR1AH = TBL_POINTER_SHIFT >> 8;
        OCR1AL = TBL_POINTER_SHIFT & 0x0F;
        TBL_POINTER_OLD = TBL_POINTER_NEW;
}

void main() {
     SET_FREQ = 410;
     TBL_POINTER_SHIFT = 0;
     TBL_POINTER_NEW = 0;
     TBL_POINTER_OLD = 0;
     DUTY_CYCLE = 0;
     DDRC = 0xFF;
     DDRD = 0XFF;
     OCR1AH = 0;
     OCR1AL = 0;
     TCCR1A = 0x82;
     ICR1H = 0x03;
     ICR1L = 0xE7;
     //ICR1 = 999 -> TOP -> 16kHz
     TIMSK = 0x04;
     TCCR1B = 0x19;
     SREG_I_bit = 1;
     while(1);
}


That’s the code. Quite simple really. And pretty short!
 
This is the circuit diagram of the SPWM signal generation portion:
Fig. 1 - Circuit diagram of SPWM generation section - microcontroller + AND gates (Click image to enlarge)



Below (Fig. 2) is the circuit diagram for the configuration of the MOSFETs and the drivers - and the synchronization with the signals generated from Fig. 1 above.

 Fig. 2 - MOSFET Configuration Section (Click image to enlarge)



Now let's analyze the code and see what I've done.

Here, I’ve used the Fast PWM mode. I chose to use an oscillator frequency (for ATMEGA16) of 16MHz and an SPWM switching frequency of 16kHz. For this, I selected ICR1 as the TOP and assigned 999 to ICR1. I used a prescaler divider (N) = 1. I used PWM mode 14. So that gives a switching frequency of:

Fig. 3 - Calculating/Verifying switching frequency



In PWM mode 14, ICR1 sets TOP and OCR1 sets the duty cycle.


I’ve assigned 0x82 to TCCR1A and 0x19 to TCCR1B. This sets PWM mode to mode 14. This also sets Compare Output mode for OCR1A to non-inverting mode: Clear OCR1A on compare match, clear at BOTTOM. I have selected the sine table such that the peak value is smaller than TOP. To generate the sine table, I used my software “Smart Sine” with a peak value of 990. Also notice how there’s a zero at the beginning and ending of the sine table. These act to create a “deadtime” so that there isn’t a short circuit due to cross-conduction between the MOSFETs in the same leg being driven. I did this by generating a sine table with 31 values and a peak of 990 and then adding a zero at the end. See Fig. 5 below for demonstration of the deadtime. For my software "Smart Sine", visit this page:

http://tahmidmc.blogspot.com/2012/10/smart-sine-software-to-generate-sine.html

The SPWM generation is done by the single PWM module and which MOSFETs to send the signals to is set by the "Direction" bit and the hardware trick employing the AND gates. When "Direction" is equal to 0, the high side MOSFET A is kept on for 10ms during which time the SPWM signals on PWM (OC1A) output (PORTD5) are sent to low side MOSFET D by sending a "1" to PORTD3, which, with the help of the AND gate "diverts" the OC1A signal to the low side MOSFET D (see Fig. 1 above). The same thing is achieved when "Direction" is equal to 1, just with high side MOSFET C and low side MOSFET B. When MOSFETs A and D are operated, MOSFETs B and C are kept off and vice versa. For MOSFET drive, we need 4 pins for the 4 drive signals and I’ve chosen PORTD bits 0 to 3. I’ve arbitrarily chosen these four pins but you can choose any four pins really. The MOSFETs are first turned off before the other two are turned on, as can be seen in the code block:

           if (Direction == 0){
              MOSA = 0;
              MOSD = 0;
              MOSB = 1;
              MOSC = 1;
              Direction = 1;
           }
           else{
                MOSB = 0;
                MOSC = 0;
                MOSA = 1;
                MOSD = 1;
                Direction = 0;
           }

Use of the table pointer in the interrupt:

When Timer/Counter 1 reaches TOP, TOV1 flag is set and since interrupt is enabled, the ISR in the corresponding interrupt vector is served. There table pointer is made use of to retrieve the required values off the sine table and assign them to OCR1AH and OCR1AL – the 16-bit resultant in OCR1A sets the duty cycle. The sine table stores the duty cycle in such a way that when these are subsequently sent to the MOSFETs in the specific order in which they are set, at the required frequency, the output, when filtered, will produce a sine wave.

Every 62.5us the ISR is served – 62.5us because the SPWM frequency was set to 16kHz. Time period for a 16kHz frequency is 62.5us as time period = 1 / freqeuncy.

In the main() function, I had assigned the value of 410 to SET_FREQ. SET_FREQ determines the output frequency of the sine wave, which in our case here is 50Hz. You’ll soon see how.

In the interrupt, the variable TBL_POINTER_NEW is updated – every 62.5us it is increased by 410. TBL_POINTER_NEW is a 16-bit variable and at the beginning of program execution has a value of 0. So, after the first interrupt it holds 410, after the second interrupt 820, after the third 1230 and so on. After 159 interrupts, TBL_POINTER_NEW holds 65190. So, at the next interrupt, TBL_POINTER_NEW overflows and holds 64.

The interrupt occurs every 62.5us. At each interrupt TBL_POINTER_NEW is increased by 410. At the 160th interrupt, TBL_POINTER_NEW overflows, just as it does the 320th interrupt, the 480th interrupt and so on.

Initially TBL_POINTER_NEW holds 0 and on subsequent interrupts, TBL_POINTER_NEW holds 410, 820, 1230, 1640, 2050, 2460, 2870, 3280, 3690, 4100 and so on.

TBL_POINTER_SHIFT right-shifts TBL_POINTER_NEW eleven times and stores that value. Right shifting 11 times is the same as dividing by 211 (211 = 2048) – just more efficient. When the microcontroller starts up, TBL_POINTER_SHIFT holds 0 and on subsequent interrupts, TBL_POINTER_SHIFT holds 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3 …..  and so on. Now, the trend is that TBL_POINTER_SHIFT holds the same value for 5 consecutive interrupts – go on, you can do the math. Why 5 times? We’ll see that a little later.

After TBL_POINTER_NEW is shifted 11 times, DUTY_CYCLE is assigned the resultant 5-bit value. This acts as – or rather is – the table pointer and the required value is retrieved off the sine table. This 16-bit value is then split into two 8-bit values to be assigned to OCR1AH and OCR1AL, thus setting the appropriate duty cycle.

Now let’s see why TBL_POINTER_SHIFT is needed to be incremented every 5 interrupts and how we’ve done that.

Our frequency is 50Hz – time period is 20ms. A half cycle takes 10ms. Recall that the sine table used is only for half a cycle. So, the 32 values must be called such that they make up the half cycle. The interrupt service routine is executed each 62.5us and so, retrieving all the values from the sine table and using them to update the duty cycle takes a total time of 62.5us * 32 = 2000us (assuming each value is retrieved/called once). That’s 2ms. But our half cycle is 10ms – 5 times larger than what we have now. So, we increase the time five folds. How? By calling each value from the table 5 times instead of only once. So, the time involved is now 32 * 62.5us * 5 = 10000us = 10ms.

This calculation we’ve just done is related to the frequency – 50Hz desired frequency and thus 10ms half cycle. I’ve shown this “backward” to explain why this has been chosen but remember that the microcontroller doesn’t know you want 50Hz frequency. The microcontroller doesn’t know that the desired time is 10ms.

So, now you know the answer to why I’ve called each sine table value 5 times: to ensure that the sine table that has 32 values occupies the entire 10ms. Now, how did I make it repeat 5 times? By setting SET_FREQ to 410. You might be wondering how this works. Well, it works this way.

SET_FREQ = 410. Each interrupt, TBL_POINTER_NEW is increased by 410. Every 5 interrupts, TBL_POINTER_NEW is increased by 2050 (compared to the initial value). 211 = 2048. Since TBL_POINTER_SHIFT divides TBL_POINTER_NEW by 2048, when TBL_POINTER_NEW increases 5*410 times, TBL_POINTER_SHIFT increases once. So, the number of repeats (5 in this case) is equal to the number of interrupts it takes for TBL_POINTER_SHIFT to increase by one, which is equal to the number of times it takes for SET_FREQ to equal or cross 211 (=2048). Since SET_FREQ = 410, the number of times to cross 2048 is 5 and that is the number of times each table value is called. So, the table pointer is incremented every 5 interrupts. Thus SET_FREQ determines the frequency. If we had set SET_FREQ to 200, the number of times to cross 2048 would be 11, as 200*10 = 2000 (less than 2048) but 200*11 = 2200 (more than 2048). Thus each sine table value would be called 11 times – the table pointer would be incremented every 11 interrupts. The output time period of the sine wave would then be 2 * 32 * 62.5us * 11 = 44000us = 44ms. The output frequency would thus be 22.7 Hz instead of the 50Hz we have now.

SET_FREQ can be calculated as {65536/ (32 * 5)} = 409.6 = 410 rounded off to the nearest integer. That’s SET_FREQ = [216/{(Number of sine table values) * (Number of times each value is to be called)}].

I’ve talked about SET_FREQ, TBL_POINTER_NEW, TBL_POINTER_SHIFT and right shifting eleven times. Now you may ask why eleven times. Why not 5 or 10 or 12 or any other value? The reason is that we have a sine table with 32 values. 32 = 25. Shifting a 16-bit variable eleven times to the right leaves us with a 5-bit value – one between 0 and 31. These 5 bits are the upper most (most significant) 5 bits. This 5-bit value is the sine table pointer. This works in conjunction with the number of sine table values and the value of SET_FREQ to set or determine the output sine wave frequency. If we had 64 sine table values, we would right-shift TBL_POINTER_NEW 10 times instead of 11 so that the shifting operation results in a 6-bit value. 26 = 64 and that is the number of sine table values we have – the table pointer would have a value between 0 and 63.

In the interrupt, after the duty cycle is set, TBL_POINTER_OLD is assigned the value of TBL_POINTER_NEW. When TBL_POINTER_NEW is first assigned 410, TBL_POINTER_OLD holds 0. The 11-bit shifting is done. The duty cycle is set and then TBL_POINTER_OLD is set. So, the TBL_POINTER_OLD update trails TBL_POINTER_NEW update. So, every interrupt until TBL_POINTER_NEW overflows, TBL_POINTER_NEW is greater than TBL_POINTER_OLD.

When TBL_POINTER_NEW = 64370 and the ISR is to be executed, TBL_POINTER_OLD also holds 64370. TBL_POINTER_NEW is increased to 64780 and then the sine table value is called and TBL_POINTER_OLD is then updated to 64780. The next interrupt, TBL_POINTER_NEW is increased to 65190 and the “if condition” is again false since at that stage TBL_POINTER_OLD holds 64780 – TBL_POINTER_NEW > TBL_POINTER_OLD. Then at the end of the ISR, TBL_POINTER_OLD is assigned 65190. And now comes the interesting part. TBL_POINTER_NEW now holds 65190 and TBL_POINTER_OLD holds 65190. 410 is to added to TBL_POINTER_OLD and TBL_POINTER_OLD overflows to hold 64. Now, TBL_POINTER_NEW (which holds 64) is less than TBL_POINTER_OLD (which holds 65190) and the if condition is true. So, the direction of MOSFET drive is reversed. If MOSFETs A and D were driven previously, MOSFETs B and C will now be driven and vice versa. Remember that by sending a logic high to PORTD1 or PORTD3, due to the AND gate, we’re just diverting the OC1 PWM to the required low side MOSFET. Thus 2 MOSFETs are driven with the same signals but in alternate half cycles. By inverting the direction of the full-bridge drive, we are carrying out the next half cycle with the same duty cycle values and sequence, but in the opposite direction – as is necessary (think AC waveform and opposite half cycle). This happens every (32*5 = 160) interrupts – when all the values of the table have each been called 5 times. Since TBL_POINTER_OLD then has a small value again, since it overflowed, TBL_POINTER_SHIFT will then be equal to 0, meaning that the table pointer has restarted from zero and the SPWM starts over again. So, TBL_POINTER_SHIFT, on subsequent interrupts has the values: 0,0,0,0,1,1,1,1,1,2,2,2,2,2,3,3,3,3,3,………..30,30,30,30,30,31,31,31,31,31,0,0,0,0,0,1,1,1,1,1,……. And so on.

That's about it regarding the code. Now let's take a look at the simulation results.

Here are the simulation results:
 Fig. 4 - Generated SPWM  Drive Signals (Click image to enlarge)



Fig. 5 - Clear demonstration of the "deadtime" (Click image to enlarge)



 Fig. 6 - Simulation results showing signal frequencies (Click image to enlarge)



Fig. 7 - Generated Sine Wave Signal (Click image to enlarge)

And that’s about it. It’s not too difficult once you grab it. It’s a simple short useful program with quite a lot to understand despite its brevity. I hope you've understood how to generate SPWM signals using just a single PWM module of the AVR microcontroller and can now use it for all your applications! Keep in mind that this isn't restricted to only AVRs but can be used for any microcontroller containing one PWM module. Let me know your feedback and comments!


135 comments:

  1. Hi Tahmid,

    Your every article/post/blog are giving us the knowledge, thank you for sharing this with us.
    I am designing the High Frequency Sine wave inverter using MOSFET based push pull (12VDC to 400VDC) and IGBT based H-Bridge (400VDC to 230AC). I have use your sine wave calculation but unfortunately our IGBT are failing. Could you please guide for designing the Sine wave table.
    Please find the idea about our inverter
    1. 20KHz MOSFET based Push pull to 12 VDC to 400VDC.
    2. 40KHz IGBT based H-Bridge to 400VDC to 230VAC.

    your early reply will help me for solving my problem.

    Thanks

    ReplyDelete
    Replies
    1. Hi PASH,

      I'm glad to hear that my articles/posts are helping you and others and I hope that they continue to do so.

      As for your problem regarding failing IGBTs:

      You have to ensure that the IGBT can be operated at 40kHz. Ensure that the IGBT can withstand higher than 400V. Ensure that you keep a large enough safety margin. Which IGBT are you using?

      Ensure that your driver circuit is okay. Which driver are you using?

      Ensure that you have used a 1k resistor from gate to emitter. Did you use this 1k resistor from gate to emitter of each IGBT?

      For calculating sine table, use the "Smart Sine" software. It would be helpful to see the sine table you are already using.

      Are you using this code (based on ATMEGA16)? Are you using it as is or did you make any changes?

      Did you use any load? If so, what load did you use? What type is it and what is its rating?

      The solution to your problem can be found if you answer all the questions I have asked above.

      Regards,
      Tahmid.

      Delete
    2. hei bro can u mail me ??? i m from aiub eee intrested about ur project my mail nahideeeaiub@gmail.com me in fb https://www.facebook.com/EEEnahid please give ur contac mail/cellno in my mail or fb id

      Delete
    3. Tahmid: Can you please mention the proteus components you have used with model numbers like MOSFET (2N6782), MOSFET DRIVER (TC4420)

      Delete
    4. I just copied your code, compile it in MikroC for AVR. Designed the Proteus Circuit as per your circuit diagram, imported the hex file to atmega16. and run the simulation i am getting result which is totally different from yours, may be i might be using wrong components, thats why it would be awesome if you can provide the components name.
      Thank You

      Delete
  2. Hi Tahmid,

    Thanks for your quick reply.

    I am using the dsPIC33 device for my inverter application (Referring the Microchips Digital Pure Sine Wave UPS Design Package (220Vac)_082012 ). I am little confuse about how to apply the sine wave table value to PDC1 and PDC2 which is used for H-bridge configuration. PDC1 drive the PWM1H and PWM1L and PDC2 drives the PWM2H and PWM2L.

    Could you please help for applying the sine value to the PDC1 and PDC2.

    Thanks,

    ReplyDelete
    Replies
    1. Hi PASH
      I am a solar enthusiast working on a solar grid tie inverter, i am also trying to do the same thing you had mentioned in these comments.
      It will be very greatful if you can help me making the same.
      Looking forward for your favorable reply soon.
      regards

      Delete
  3. Hi Tahmid,

    You have to ensure that the IGBT can be operated at 40kHz. Ensure that the IGBT can withstand higher than 400V. Ensure that you keep a large enough safety margin. Which IGBT are you using?
    --> I am using 600V IGBT.
    Ensure that your driver circuit is okay. Which driver are you using?
    --> Driver circuit is working fine.
    Ensure that you have used a 1k resistor from gate to emitter. Did you use this 1k resistor from gate to emitter of each IGBT?
    --> Yes.

    For calculating sine table, use the "Smart Sine" software. It would be helpful to see the sine table you are already using.
    --> Referring the Microchip Digital Pure Sine Wave UPS Design Package (220Vac)_082012
    Are you using this code (based on ATMEGA16)? Are you using it as is or did you make any changes?
    --> Referring the Microchip Digital Pure Sine Wave UPS Design Package (220Vac)_082012
    Did you use any load? If so, what load did you use? What type is it and what is its rating?
    --> Currently not using any load becuase not getting proper 230VAC waveform.


    Please help me out for applying the sine value to the PDC1 and PDC2.

    ReplyDelete
  4. Hi Tahmid,

    Thanks for help. My problem is solved, now my inverter code is working.

    Thanks

    ReplyDelete
    Replies
    1. That's awesome!

      It would be great if you could explain what went wrong and what you did to finish it. Then, it will benefit others who might come to this page in search of solutions to similar problems.

      Regards,
      Tahmid.

      Delete
  5. Hi Tahmid,

    Actually, i was doing mistake in assigning the sine table to the Duty cycle, so unfortunately opposite site IGBT were getting ON.

    Thanks,

    ReplyDelete
  6. Dear Tahmid
    I had never seen a contributor like this thanks really. I really appreciate your hard work not alone me all the members those who read this blog.

    Regards
    Veera.!

    ReplyDelete
  7. Resources in Bangladesh are quite scarce, with books, required material and even teachers lacking. What I have learnt, I have learnt from books, the internet and from others. And I want to impart my knowledge - what I have learnt - to help others learn. I aspire to make my blog the best microcontroller and power electronics blog in the world so that anyone who comes here can learn much about electronics - especially microcontroller and power electronics.

    Thank you very much for your compliment.

    Regards,
    Tahmid.

    ReplyDelete
  8. Hai Tahmid..... I read your article.... It is very well explained...Thank you.

    my doubt is...When the table pointer overflows after 159th interrupt.. why it holds 64 and not any other value.? please clarify my doubt.

    ReplyDelete
    Replies
    1. At the 159th interrupt, the table pointer holds 65190. At the beginning of the 160th interrupt, like all other interrupts, 410 is added to the table pointer. 65190+410 = 65600. However, the table pointer is a 16-bit variable. So, it can't hold/store any higher than 65535. Adding one to 65535 results in the table pointer overflowing to 0. 65600-65535 = 65. So, 65600 is 65 higher than the maximum value that can be stored, which is 65535. One higher means that the overflow results in an output of zero. Two higher means that the overflow results in an output of one and so on. Thus 65 higher means that the overflow results in an output of 64.

      Hope this clears up your doubts!

      Regards,
      Tahmid.

      Delete
  9. ya...now its clear to me.... thank you....!

    ReplyDelete
  10. Hi Tahmid can I apply this to PIC18f442

    ReplyDelete
    Replies
    1. This article/tutorial was written, targeted for the ATMEL AVR with the Fast PWM Mode. For PIC, you can make use of the CCP and ECCP modules, depending on which PIC you're using. The PIC18F442 has 2 CCP modules that you can make use of. You can do with only 1 CCP module as well.

      Go through this:

      http://tahmidmc.blogspot.com/2013/02/generation-of-sine-wave-without-eccp_16.html

      Regards,
      Tahmid.

      Delete
  11. Dear Tahmid,
    I'm newbie and work in Bascom Avr and need help. I am conersion this code in Bascom Avr and need same assitance. Please see may code an if possible corrected code.

    '***********************************************************************
    '* Programmer: Syed Tahmid Mahbub
    '* Target Microcontroller: ATMEGA16
    '* Compiler: Bascom AVR
    '***********************************************************************

    $regfile = "m16def.dat" ' specify the used micro
    $crystal = 16000000 ' used crystal frequency
    $baud = 19200 ' use baud rate
    $hwstack = 128 ' default use 128 for the hardware stack
    $swstack = 64 ' default use 64 for the SW stack
    $framesize = 64

    ' variable

    Dim Sin_table(32) As Byte ' Sine_table
    Dim Index As Byte

    'Dim Mosa As Byte , Mosb As Byte , Mosc As Byte , Mosd As Byte

    ' Config Pin
    Config Portd = Output

    Mosa Alias Pind.0
    Mosb Alias Pind.1
    Mosc Alias Pind.2
    Mosd Alias Pind.3

    ' #define MOSA PORTD0_bit
    ' #define MOSB PORTD1_bit
    ' #define MOSC PORTD2_bit
    ' #define MOSD PORTD3_bit

    For Index = 0 To 31
    Sin_table(index + 1) = Lookup(index , Sine_dat) ' Read sine_dat
    Next Index


    Dim Direction As Byte
    ' #define Direction Flagreg.b0 how write this in Bascom
    ' 0 -> MOS A + D
    ' 1 -> MOS B + C


    Dim Tbl_pointer_new As Integer , Tbl_pointer_old As Integer
    Dim Tbl_pointer_shift As Integer , Set_freq As Integer
    Dim Tbl_temp As Integer
    Dim Duty_cycle As Word



    Enable Ovf0
    On Ovf0 Timer1_ovf


    Do
    Set_freq = 410
    Tbl_pointer_shift = 0
    Tbl_pointer_new = 0
    Tbl_pointer_old = 0
    Duty_cycle = 0

    ' DDRC = 0xFF;
    ' DDRD = 0xFF;

    Ddrc = &B11111111
    Ddrd = &B11111111

    Ocr1ah = 0
    Ocr1al = 0

    ' TCCR1A = 0x82;
    ' ICR1H = 0x03;
    ' ICR1l = 0xe7;

    Tccr1a = &B10000010
    Icr1h = &B00000011
    Icr1l = &B11100111

    ' ICR1 = 999 -> TOP -> 16kHz
    'Timsk = 0x04;
    'Tccr1b = 0x19;

    Timsk = &B00000100
    Tccr1b = &B00011001

    Sreg_i_bit = 1 ' this not definition

    Loop

    End 'End Program

    ' Void Interrupt() Org Ivt_addr_timer1_ovf{
    '************************ Interrupt0 ****************************************
    Timer1_ovf:

    Tbl_pointer_new = Tbl_pointer_new + Set_freq
    If Tbl_pointer_new < Tbl_pointer_old Then
    If Direction = 0 Then
    Mosa = 0
    Mosd = 0
    Mosb = 1
    Mosc = 1
    Direction = 1
    Else
    Mosb = 0
    Mosc = 0
    Mosa = 1
    Mosd = 1
    Direction = 0
    End If
    End If

    Tbl_pointer_shift = Tbl_pointer_new >> 11 ' How this write in Bascom
    Duty_cycle = Tbl_pointer_shift
    Tbl_pointer_shift = Sin_table(duty_cycle)
    Ocr1ah = Tbl_pointer_shift >> 8 ' How this write in Bascom
    Ocr1al = Tbl_pointer_shift & 0x0f ' How this write in Bascom
    Tbl_pointer_old = Tbl_pointer_new

    Return


    '************ Sine Table data for 32 samples ****************************
    Sine_dat:
    Data 0 , 100 , 199 , 296 , 390 , 480 , 566 , 645 , 718 , 783 , 840 , 889 , 928 , 958 , 979 , 989 , 989 , 979 , 958 , 928 , 889 , 840 , 783 , 718 , 645 , 566 , 480 , 390 , 296 , 199 , 100 , 0

    Best Regards,

    ivica.lacmanovic@gmail.com

    ReplyDelete
  12. Here the version in BASCOM as requested by ivica.lacmanovic :

    '***********************************************************************
    '* Programm: Danzup version in BASCOM AVR of C program by Syed Tahmid Mahbub
    '* Target Microcontroller: ATMEGA16
    '* Compiler: Bascom AVR
    '* Sinusoidal inverter
    '***********************************************************************
    $regfile = "m16def.dat" ' specify the used micro
    $crystal = 16000000 ' used crystal frequency
    $baud = 19200 ' use baud rate
    $hwstack = 128 ' default use 128 for the hardware stack
    $swstack = 64 ' default use 64 for the SW stack
    $framesize = 64

    Config Portd = Output
    Mosa Alias Portd.0
    Mosb Alias Portd.1
    Mosc Alias Portd.2
    Mosd Alias Portd.3
    Dim Direction As Bit
    ' 0 -> MOS A + D
    ' 1 -> MOS B + C
    Direction = 0
    Dim Tbl_pointer_new As Integer , Tbl_pointer_old As Integer
    Dim Tbl_pointer_shift As Integer , Set_freq As Integer
    Dim Tbl_temp As Integer
    Dim Duty_cycle As Word
    Dim Temp As Integer
    Set_freq = 410
    Tbl_pointer_shift = 0
    Tbl_pointer_new = 0
    Tbl_pointer_old = 0
    Duty_cycle = 0
    Config Timer1 = Pwm , Prescale = 1 , Compare_a_pwm = Clear_down
    Enable Interrupts '= SREG_I_bit = 1; '=
    Enable Ovf1
    On Ovf1 Timer1_ovf
    Ocr1ah = 0
    Ocr1al = 0
    Tccr1a = &H82
    Icr1h = &H03
    Icr1l = &HE7
    ' ICR1 = 999 -> TOP -> 16kHz
    Timsk = &H04
    Tccr1b = &H19
    Do

    Loop

    'eventually here using ADC integrated control output tension

    End 'End Program

    '************************ Interrupt0 ****************************************
    Timer1_ovf:

    Tbl_pointer_new = Tbl_pointer_new + Set_freq
    If Tbl_pointer_new < Tbl_pointer_old Then
    If Direction = 0 Then
    Mosa = 0
    Mosd = 0
    Mosb = 1
    Mosc = 1
    Direction = 1
    Else
    Mosb = 0
    Mosc = 0
    Mosa = 1
    Mosd = 1
    Direction = 0
    End If
    End If


    Temp = Tbl_pointer_new
    Shift Temp , Right , 11
    Duty_cycle = Temp
    Tbl_pointer_shift = Lookup(duty_cycle , Sine_dat)
    Temp = Tbl_pointer_shift
    Ocr1ah = High(temp )
    Ocr1al = Low(temp )
    Tbl_pointer_old = Tbl_pointer_new

    Return


    '************ Sine Table data for 32 samples ****************************
    Sine_dat:
    Data 0 , 100 , 199 , 296 , 390 , 480 , 566 , 645 , 718 , 783 , 840 , 889 , 928 , 958 , 979 , 989 , 989 , 979 , 958 , 928 , 889 , 840 , 783 , 718 , 645 , 566 , 480 , 390 , 296 , 199 , 100 , 0

    ReplyDelete
    Replies
    1. Hi,

      Thanks a lot for posting this BASCOM code!

      Best regards,
      Tahmid.

      Delete
  13. Can we get rid of the AND gates

    ReplyDelete
    Replies
    1. The AND gates have been used so that the two required SPWM signals can be generated from one PWM module. The benefit is simplicity: both in hardware and in software. Of course you can implement differently, both on a hardware and on a software level, to design the circuit, eliminating the AND gates.

      Regards,
      Tahmid.

      Delete
  14. Hello, this is very clean and nice tutorial, thank you. I'm a kind of newbie, so I can't understand the filtering circuit. Could you please, show and explain the filter circuit diagram? Thank you.

    ReplyDelete
    Replies
    1. Hi,

      I'm glad you like the tutorial.

      To demonstrate the output wave signal, I've used an RC filter in the simulation. However, in the actual circuit you'll need to use an LC filter. In practice, I choose the LC filter components by trial and error. For example, I use C = 1uF and adjust L until I get a good output. If I don't, I change C and try again. And so on.

      Regards,
      Tahmid.

      Delete
    2. finally what values of L an C have you used?

      Delete
    3. You will need to determine this experimentally under the specified load conditions.

      Regards,
      Tahmid.

      Delete
  15. plz post a program for generating 50hz pure sine wave in C language which can be burnt on atmega16. also
    a brief description of the program would be a lot helpful.I am a complete novice and have never worked on a micro controller before. please reply asap.

    ReplyDelete
    Replies
    1. Hi,

      This tutorial covers both topics that you've asked. It contains the code and the explanation too. Please go through the tutorial in detail. Feel free to ask any specific questions you may have.

      Regards,
      Tahmid.

      Delete
  16. Hi Tahmid
    I think you have used Proteus for simulations. My doubt is what have you used for high and low side mosfet driver ? Have you used half bridge circuit a you explained in your blog ?
    http://tahmidmc.blogspot.in/2013_01_01_archive.html

    ReplyDelete
    Replies
    1. For simulation's sake, I haven't used a MOSFET bridge stage. I've filtered the output from the microcontroller using an RC filter, to demonstrate the output wave shape.

      For the MOSFET bridge, use high-low side drivers and filter the output using an LC filter. With properly selected filter components, you'll get a clean sine wave output.

      Regards,
      Tahmid.

      Delete
  17. Please anyone upload code version in avr studio 4 with header files.

    ReplyDelete
    Replies
    1. By analyzing the mikroC code, you should be able to port the code over to WinAVR. I did not use much mikroC specific code.

      Regards,
      Tahmid.

      Delete
  18. hi Tahmed
    i use your program but pulses have error
    http://www.fileswap.com/dl/stFSKMClCP/
    i cant explain why ..if u cab help me i will be grateful

    ReplyDelete
  19. Can I use this good for ATmega48? Is it the same with ATmega16 ?

    ReplyDelete
    Replies
    1. You can use the same concept for ATmega48. Consult the datasheets of the two devices and make necessary changes to the code to use ATmega48 instead of ATmega16.

      Regards,
      Tahmid.

      Delete
  20. Sir Thanx for such a great article.. Please can you tell me how to minimise the dead time between two mosfet switching which is 1.8ms(1800us) in our circuit and i want to reduce is to 0.720ms(720us)

    ReplyDelete
    Replies
    1. One simple way of reducing the dead time would be to remove the '0' from the last value of the sine table and instead use a 32-value sine table in which only one of the values is zero.


      Regards,
      Tahmid.

      Delete
    2. This comment has been removed by the author.

      Delete
    3. Thanks for your reply Tahmid. Also I have problem at output of H-Bridge when I connect load more than 6W at secondary(240V) of transformer. When I connect load to transformer, H-bridge waveform get hampered and results into low voltage. The waveform get curve at high duty cycle i.e exact center of positive cycle and negative cycle... I have used IRF3205 Power MOSFETS. Please help me out..

      Delete
    4. What driver did you use for driving the MOSFETs?

      Do you have a picture of the waveform?

      Do the MOSFETs get hot? Do any MOSFETs get damaged?

      Do any other components heat up unusually?

      Delete
  21. hi Mr Tahmid, How I can change the frequency of the sinwave using your code? Could you help me Sir?

    ReplyDelete
    Replies
    1. Could you help me to generate 3 pwm, loaded with 3 sinewave shifted 120 degree from each other... Because I want to drive three phase mosfet

      Delete
    2. I'll write an article on 3-phase SPWM soon.

      Delete
    3. Regarding frequency change, I've covered that in this article. Go through this article thoroughly and then ask specific questions you have.

      Regards,
      Tahmid.

      Delete
  22. Hello Tahmid,

    I am a newbie on programming or micro-controllers. But I wish to work on TMS320F28335, and want to generate sine-PWM. What I have learnt so far is, I can use language C in Code Composer Studio for executing the codes. So could you please give me any guideline regarding generating sine-PWM with C in CCS?

    Kind Regards
    Rafin

    ReplyDelete
    Replies
    1. I don't use CCS, so I can't help on that regard.

      But if you are stuck on any of the mikroC commands, feel free to ask and I'll try to make them clear so that it's easy for you to port to your compiler.

      Regards,
      Tahmid.

      Delete
  23. why Tahmid we cannot simply use 2 pin only PWM OCR1A (Non inverted)& OCR1B(Inverted) and in Overflow ISR just Type OCR1A=sin_table[DUTY_CYCLE]
    OCR1B=sin_table[DUTY_CYCLE]
    index++

    ReplyDelete
    Replies
    1. I don't Talk about frequency Now ....we can Adjust later .....but why you use this all 4 pin ....and also my sine table is 360 deg not only 180deg and later i can grab neutral from center taped transformer.

      Delete
    2. how can I make neutral I had two Live Electricity

      Delete
    3. To make neutral, you need something that stays at 0V while the other (live) changes from +Vpk to -Vpk. You can realize this using a half-bridge drive configuration, not with a full-bridge configuration (as far as I know).

      Regards,
      Tahmid.

      Delete
  24. Hello Tahmid,
    Thanks for your wonderful tutorial,It helped me a lot,
    Could u please help me with to figure out how I can plot sine wave signal from your signal,I have placed the scope on the output of the h bridge and placed buffer drives on all 4 N-Channel Mosfets but I am getting a weird signal o/p
    Protues Dso Config-
    Channel A and Channel B on o/P of the h-bridge And Selected INVERT AND A+B signal on the DSO.
    So I am getting a distorted sinewave

    ReplyDelete
    Replies
    1. For simulation only, just put RC filters at the outputs of the PIC and observe the signals.

      Delete
  25. Dear Tahmid, thank you so much for the wonderful explanation of the code. But I tried the same code, its gives pwm frequency 1000 instead of 16000. Why its happens so ??? Also selection of different oscillator frequency dont effect the output pwm frequency......

    ReplyDelete
    Replies
    1. Did you configure the fuse bits (during programming) to run off the external 16MHz crystal oscillator?

      I think you're running off the internal 8MHz internal RC oscillator with a clock divider of 8. That gets you to a system clock frequency of 1MHz which would explain why you get 1kHz instead of 16kHz. Additionally, that would also explain why changing the oscillator frequency does not change the PWM frequency.

      Let me know how it goes.

      Regards,
      Tahmid.

      Delete
  26. Hello,
    I wanted to thank you for this wonder ful tutorial and help that you provide to us,
    I have a situation -
    My H-Bridge dosnt work as specified in the diagram,Instead it got shorted and drivers get blown up immediately.

    ReplyDelete
    Replies
    1. Which H-bridge driver are you using?

      You need to use a high-low side MOSFET driver stage between the microcontroller output and the H-bridge. You can use to use high-low side drivers such as IR2110. Two IR2110s can be used to drive an H-bridge. Check this out to see how:

      http://tahmidmc.blogspot.com/2013/01/using-high-low-side-driver-ir2110-with.html

      Regards,
      Tahmid.

      Delete
  27. Hello,
    I wanted to thank you for this wonder ful tutorial and help that you provide to us,
    I have a situation -
    My H-Bridge dosnt work as specified in the diagram,Instead it got shorted and drivers get blown up immediately

    ReplyDelete
    Replies
    1. http://s17.postimg.org/bjyldaulb/sdc.png

      SCHEMATIC

      Delete
    2. Hello.. Mr. ashkar,
      Your driver connection is wrong.. try check on internet about the connection..
      thanks..

      Delete
    3. Hi mr thamid. Thanks for great explanation
      How can i implement feedback for this atmega 16

      Delete
  28. hi
    i am working on this project (sine wave inverter using microcontroller atmega16) bt i am getting trouble in understanding code. will u plz explain it in simple language.

    ReplyDelete
  29. actually i want to know how to design the look up table to have different duty cycle???

    ReplyDelete
  30. why you selected switching frequency of 16kHz for SPWM??

    ReplyDelete
  31. This comment has been removed by the author.

    ReplyDelete
  32. I used the internal rc oscillator of 16Mhz instead of the external oscillator cld that be the problem? because the chip programs from avr studio using the hex file from microC Pro for AVR and the leds on the stk500 light up for both PORTB and PORTC as outlined in the code.....

    ReplyDelete
  33. Also, why did you set PORTC as a output port?

    ReplyDelete
  34. What value R and C did you use in the simulation?? I tried various combinations but still not getting a sine wave.....

    ReplyDelete
  35. I require 60 Hz output frequency so I changed the SET_FREQ = 512 which would give 8ms for each half cycle......thats the only thing I changed is that ok or do I need to change other things in the code as well?

    ReplyDelete
  36. I built the dc-dc converter section and I got 298VDC across the capacitor with no load but when I put a 1k,1W resistor in parallel with the capacitor the voltage drops to 38VDC what could be the reason? Also, could the capacitor alone be used after rectification instead of an LC filter?
    Next does the capacitor have to be electrolytic or can it be a non-polarized capacitor?
    Urgently need a reply!!!

    ReplyDelete
  37. Hello Tahmid,
    I simulated the code and circuit in Proteus as you did and it worked but when I build it on the breadboard it doesnt work and I get weird signals on the oscilloscope when I check the output from the MCU. However, I can see the leds on the programmer (stk500) switching just as in Proteus.What could be wrong?

    Please help!!!!!!!

    ReplyDelete
    Replies
    1. Do you have screenshots of the oscilloscope waveforms?

      Delete
  38. Hello Tahmid,,,
    is it possible to generate sinoidal pwm for three phase inverter (three pwm 120 degree phase shifted) using pic32???
    if you have any helping material kindly share me at abdulmateen135@gmail.com

    ReplyDelete
    Replies
    1. Yes, it's definitely possible. Most PIC32 microcontrollers have more than enough PWM channels to generate the 3 signals and provide sufficient processing power to do all required computation. I don't have any material with me now, but this is possibly a topic I will be looking at over this summer.

      Delete
  39. Hello Tahmid. Firstly, I would like to thank you as your blogs have really helped me out a lot for my projects. Secondly, I wish to ask, out of all the Sine PWM generation methods that you have described, which one would you prefer the most? SG3525, PIC, AVR, anything else? My intended application is a Sine PWM inverter.

    ReplyDelete
    Replies
    1. You can't generate SPWM with SG3525 without additional (possibly complicated) circuitry (if at all possible).

      The methods using PIC/dsPIC and AVR are virtually the same - just different implementations with different controllers. Choose one based on your target microcontroller.

      Delete
  40. Hi Tahmid. Many thanks for your great effor. i have one question though. what is the aim of "FlagReg.B0"? i didn't understand it.

    ReplyDelete
    Replies
    1. FlagReg is a variable. B0 is the LSB - ie bit 0 - of that variable. I've used that as a boolean flag to determine the direction of full bridge drive.

      Delete
    2. I get it now!!!thanks again

      Delete
  41. Hi Tahmid! Grt Tutorial..!!
    Correct me if I'm wrong but, instead of calling the same value from the sine table 5 times just to distribute 32 values in 10ms why not use more values in the sine table.
    I mean instead of using a 32 value sine table and then calling the values 5 times (62.5*32*5=10000) we can use a 160 value sine table (which is easy thanks your app) and then call the value without repeating them (62.5*160=10000),

    Reply Thanks..!!

    ReplyDelete
    Replies
    1. That is correct and you can do that, but the tradeoff is the amount of memory you consume.

      Delete
    2. I'm not much into programming so i think i'll go with the 160 value sine table as it simplifies the program. Thanks for the reply..!!

      Delete
  42. Hi Tahmid!
    Referring to fig.2 and fig 4 I think the High and Low MOSFETs of the same SIDE are driven thus shroting the supply. Instead the High mosfet of one side and the low MOSFET of the other side should be driven.
    In reference to the waveforms on the oscilloscope the order of the names in the fig 2 should be" ABCD " instead of "ADCB".
    You are doing a grt job! keep up the good work!
    thanks! reply!

    ReplyDelete
    Replies
    1. Good catch. Thanks for pointing it out! I'll make the correction.

      Delete
  43. Tahmid Sir, I am trying to use your code in making an inverter..But the problem is which transformer should i use?....I want to use that transformer for charging the battery and also for inverting purposes. And How can i adjust the output Voltage for making it constant?.
    Thanks you,
    Auston

    ReplyDelete
    Replies
    1. For making output voltage constant, you're going to need to add a feedback section that varies the sine wave amplitude according to line and load conditions to maintain a constant output. A possible idea is outlined here: http://tahmidmc.blogspot.com/2012/11/feedback-in-sine-wave-inverter-pic16f.html

      For the transformer, you'd be using a regular 50Hz transformer. However, you don't need a center tap since you'll be using full-bridge topology.

      Delete
  44. Hi Tahmid, what is the used of this "unsigned int TBL_temp;" ? is it necessary? will the code work without it? sorry if the question might be silly but I'm new to programming so i'm having hard time understanding the flow of code. Thank you.

    ReplyDelete
    Replies
    1. Thanks for pointing that out. I might just have used that for debugging somewhere. It's not required in the code for proper functioning since I haven't used that anywhere.

      Delete
  45. Hey tahmid can this code be implemented on the arduino mega or uno using the arduino sketch compiler?

    ReplyDelete
    Replies
    1. Yes it can - but not as is. You'd need to make changes due to syntactical differences between mikroC and the Arduino sketch compiler.

      Delete
  46. Whether you have used all the 4 mosfets of n channel type ??

    ReplyDelete
    Replies
    1. Yes I did. But you can use the same software with P-channel or N/P channel combinations if you just modify the driver circuit.

      Delete
  47. Hi Tahmid do you speak Spanish?

    ReplyDelete
  48. 1.I compile the software in proteus as I saw in your blog sine wave inverter using aver atmega16 but I get 8kz at the frequency meter and 25hz at the other frequency meter, what might be the problem as I have tried to adjust to 16kz and 50hz, pls tell me how to do it..
    2. I am into inverter production, although I am using push pull system I want to construct pure sine wave by following your instruction, a. If I go with your program, will it work on 800watts inverter pure sine wave?. B. What number of turns can I wound on primary and secondary to get 220v at the output with 12v battery input

    ReplyDelete
  49. Hi tamid i m using avr (atmega16) for nine switch inverter. please tell me whether it is possible to compare sine wave and triangular wave in avr??

    ReplyDelete
    Replies
    1. Instead of trying to compare the sine wave and triangular wave, generate the sine wave with direct digital synthesis (DDS). That is what I've done here. And it's definitely possible.

      Delete
  50. Hi Tamid I need your help.
    I am using this configuration IR2110 for inverter pure sine wave
    http://4.bp.blogspot.com/-Lld4HStSKlw/UPvns0ckrZI/AAAAAAAAAaA/RFqfmsuv7bM/s1600/IR2110+-+7.png
    The problem is the high side the signal is cut. I can not upload a picture to show the problem.

    ReplyDelete
  51. Hi Tahmid.. i am currently designing a variable speed drive for three phase motors. I want to use Atmega32 to generate 3 pulses that are out of phase (120 degrees) to drive a SPWM inverter. could you please help me out with this.. i am kinda new at programming. my email is mushi@gmail.com

    ReplyDelete
    Replies
    1. So sorry for the wring email address:,,email is mushihamatwi@gmail.com

      Delete
  52. 1.I compile the software in proteus as I saw in your blog sine wave inverter using aver atmega16 but I get 8kz at the frequency meter and 25hz at the other frequency meter, what might be the problem as I have tried to adjust to 16kz and 50hz, pls tell me how to do it..
    2. Can I get pure sine wave with push pull transformer configuration with center tap as positive using avr or pic microcontroller? Pls help me

    ReplyDelete
  53. Hi tamid
    1.I compile the software in proteus as I saw in your blog sine wave inverter using aver atmega16 but I get 8kz at the frequency meter and 25hz at the other frequency meter, what might be the problem as I have tried to adjust to 16kz and 50hz, pls tell me how to do it..
    2. Can I get pure sine wave with push pull transformer configuration with center tap as positive using avr or pic microcontroller? Pls help me

    ReplyDelete
  54. Hi tamid;
    i need to control a 3 phase bldc motor. do i need 6 pwm channels to provide pwm for 3 phases? i dont know much about it?
    there are 4 outputs there abcd so if i use 3 pwm channels there will be 12 outputs. but the driver i am using has only 6 inputs. the motor i need to drive is 36V 250 w motor. Can you please suggest me the way to do this?

    ReplyDelete
  55. Hi Tahmid good tutorial mate.....can u please send me your actual c code as i am weak with programming so i just wanted to understand it practically.....
    Thanks
    id: dipankarsonar@gmail.com

    ReplyDelete
  56. Can you tell me about the filter you used for producing pure AC wave.

    ReplyDelete
  57. hello brother,

    can u give me the code for sine pwm?

    ReplyDelete
  58. This comment has been removed by the author.

    ReplyDelete
  59. hi sir,i want to generate five gating signals for triggering igbt using pic 16f877a.if u have a code plz send me...

    ReplyDelete
  60. So I managed to get this code working for the Arduino Uno with Atmega328P using the Arduino IDE compiler.
    Note that the TIMSK register had to be changed to TIMSK1=0x01 in order to set the TOIE1 flagsince the register name and bit configuration for the Atmega328P is different from that of the Atmega16. I commented out the parts of the code that the Arduino IDE compiler didn't likeand replaced them with my own. I have not tested it on my inverter yet, but the waveform on the oscilloscope looks like the oscilloscope waveform that Tahmid has so I believe it should work.


    This is my code that works for the Arduino IDE compiler (syntax that caused errors is commented out, then replaced by my own code):

    unsigned int sin_table[32]={0, 100, 199, 296, 390, 480, 566, 645, 718, 783, 840, 889, 928, 958, 979, 989, 989, 979, 958, 928, 889, 840, 783, 718, 645, 566, 480, 390, 296, 199, 100, 0};

    //#define MOSA PORTD0_bit
    //#define MOSB PORTD1_bit
    //#define MOSC PORTD2_bit
    //#define MOSD PORTD3_bit

    unsigned int Direction = 0; //my own modification for Direction
    //unsigned char FlagReg;
    //#define Direction FlagReg.B0
    //0 -> MOS A + D
    //1 -> MOS B + C

    unsigned int TBL_POINTER_NEW;
    unsigned int TBL_POINTER_OLD;
    unsigned int TBL_POINTER_SHIFT;
    unsigned int SET_FREQ;
    unsigned int TBL_temp;
    unsigned char DUTY_CYCLE;

    void setup()
    {
    SET_FREQ = 410;
    TBL_POINTER_SHIFT = 0;
    TBL_POINTER_NEW = 0;
    TBL_POINTER_OLD = 0;
    DUTY_CYCLE = 0;
    DDRC = 0xFF;
    DDRD = 0XFF;
    OCR1AH = 0;
    OCR1AL = 0;
    TCCR1A = 0x82;
    ICR1H = 0x03;
    ICR1L = 0xE7;
    //ICR1 = 999 -> TOP -> 16kHz
    //TIMSK = 0x04;
    TIMSK1 = 0x01; //sets TOIE1
    TCCR1B = 0x19;
    //SREG_I_bit = 1;
    pinMode(9, OUTPUT); //sets output at pin 9 OC1A
    sei();
    }
    void loop(){
    }
    ISR(TIMER1_OVF_vect){
    TBL_POINTER_NEW = TBL_POINTER_NEW + SET_FREQ;
    if (TBL_POINTER_NEW < TBL_POINTER_OLD){
    if (Direction == 0){
    //MOSA = 0;
    //MOSD = 0;
    //MOSB = 1;
    //MOSC = 1;
    PORTD = B00000110; //Sets PinD1 and PinD2 HIGH, PinD0 and PinD3 LOW
    Direction = 1;
    }
    else{
    //MOSB = 0;
    //MOSC = 0;
    //MOSA = 1;
    //MOSD = 1;
    PORTD = B00001001; //sets PinD0 and PinD3 HIGH, PinD1 and PinD2 LOW
    Direction = 0;
    }
    }
    TBL_POINTER_SHIFT = TBL_POINTER_NEW >> 11;
    DUTY_CYCLE = TBL_POINTER_SHIFT;
    TBL_POINTER_SHIFT = sin_table[DUTY_CYCLE];
    OCR1AH = TBL_POINTER_SHIFT >> 8;
    OCR1AL = TBL_POINTER_SHIFT & 0x0F;
    TBL_POINTER_OLD = TBL_POINTER_NEW;
    }

    ReplyDelete
    Replies
    1. Also I forgot to mention another change in this code: The Atmega16 that Tahmid used has the OC1A pin at Pin 5 (PD5), however, for the Atmega328P the OC1A pin is at Digital pin 9 (PB1), therefore the only hardware change difference is that you will be using Digital pin 9 on the Arduino UNO for the PWM (instead of Pin 5 which Tahmid used).

      Delete
    2. I was revisiting the site and I know it's been over a year since I posted the above comment. I just thought I should mention that the code I posted worked perfectly for my inverter and I got an A on my project. So yes, it works.

      Delete
  61. hi thanks this code completely work for my open loop system

    but i need code for closed loop system in which i have to maitain the constant dc output voltage with change in load.. and also i have to sense the input ac voltage for comparison with triagular wave to maintain the unity power factor

    plse help me

    ReplyDelete
  62. hi arachnid07

    thank you for guide me ..your code is completely working for my open loop system

    can you pls give me code for closed loop system....

    my project is AC to DC converter..in which i have to sense dc voltage and maitain it constant (400V) ..

    ReplyDelete
  63. Hello!
    I'm sorry, I badly I know English.
    Makes the inverter 50 Hz for your circuit.
    Is it possible to increase the frequency to 16 kHz PWM at least more than 30 kHz,
    applying the crystal oscillator is 12 MHz. And also there is a possibility to increase the number of sine values?
    I do not understand why in programming could not change the source code.
    Help please write the code.

    ReplyDelete
  64. Can you post the c code or concept without using of AND gate

    ReplyDelete
  65. Arachnid,

    Well done with the arduino sketch!

    Compiling perfect.

    ReplyDelete
  66. Dear Tahmid,
    After I understood your code, I wrote my own to drive two MOSFETs to connected to a 12 - 0 - 12V @ 5A transformer. I first got 390VAC at the output without any filter and when I put a 100nf filter I got around 160VAC without load. when I put load, say a 20W CFL, the voltage drops to 50VAC. If I drive two MOSFETs via push pull configuration what secondary voltage transformer will I need? supply voltage 12VDC and MOSFETS are driven via an optocoupler PC817

    ReplyDelete
  67. Dear Sir,
    I have been doing research on your blog for a long time but it is hard to comprehend the programming concept in PWM generation. It is an elite thinking material. By the way, i was successful in building the inverter with push pull technique but it appears to have alot of distortions.
    If you could allow me to show the pics, perhaps we could discuss further.

    ReplyDelete
    Replies
    1. Arduino programming looks intimidating at first to someone who has little experience with it. But honestly this level of programming is at the undergraduate level. If you pull up the microprocessor datasheets and look up and diligently study the Arduino tutorials on the PWM modes, interrupts, and timer/counter registers, then study this for about five hours a day; even a total novice should be able to grasp the above code and write their own in about 1 week (if you include the debugging which is probably half the work).

      Delete
  68. hi dear i need to shift sine wave this code i can chang the frquncy and amplitude ,but i neet to shift output sine wave tnks for help

    ReplyDelete
  69. Salem Tahmid
    j'espère que tu vas bien.
    je suis un Doctorant en électrotechnique, j'ai une réalisation à effectuer dont elle comprend une partie de programmation d'un microcontrôleur PIC.
    depuis longtemps je suis ton blog, car il est intéressant et tu est aussi un vrai connaisseur dans le domaine. Alors, j'espère que tu vas m'aider à réaliser mon travail, car j'en ai vraiment besoin de ton aide. Si tu acceptes de m'aider fais moi un signe de OK, sinon je te souhaite une bonne continuité dans tout les cas. Amicalement.

    ReplyDelete
  70. on which PIN sin wave is generated

    ReplyDelete
  71. how to get sine wave, is it external crystal then how to enable external crystal in proteus

    ReplyDelete
  72. Hello..Kindly send me the code for SPWM using AVR Studio 4 for atmega 8 microcontroller @ gourabagrawal@gmail.com. Thanks in advance.

    ReplyDelete
  73. Dear Tahmid

    Your postings on power electronics and awesome.It is helping lot of people including myself to understand.
    Can you please help me to monitor voltage, current and frequency of SMPS circuits with circuit diagram and code for AVR or PIC.
    Thanks
    Vino

    ReplyDelete
  74. Dear Tahmid

    Your postings on power electronics and awesome.It is helping lot of people including myself to understand.
    Can you please help me to monitor voltage, current and frequency of SMPS circuits with circuit diagram and code for AVR or PIC.
    Thanks
    Vino

    ReplyDelete
  75. good afternoon, Tahmid. Can I ask whether you have made an example on how to generate three phase spwm for an inverter with a fixed frequency? for example: a fixed output of 60hz. Thank you.. I am hoping for a reply

    ReplyDelete
  76. Hi Tahmid, What is problem If I replace IR2110 by IR2101 or 2106?

    ReplyDelete
  77. Hi Tashmind,,,,
    It was a very good explanation you havev provided.It was very helpful.It will be greateful if you explain me the line
    "unsigned char FlagReg;
    #define Direction FlagReg.B0"

    ReplyDelete
  78. Hi Mr. Tahmid
    Thank you for that information
    And i want ask you about feedback issu.
    I made inverter circuit with feedback
    But when put load 200 watt the output voltage down .
    Why?

    ReplyDelete