Sunday, November 4, 2012

Feedback in sine wave inverter (PIC16F series based)

I have previously shown how to calculate the values for the sine table: http://tahmidmc.blogspot.com/2011/01/generation-and-implementation-of-sine.html

I have also shown how to implement SPWM in PIC16: http://tahmidmc.blogspot.com/2012/10/generation-of-sine-wave-using-spwm-in_10.html

Now I will show how to implement feedback for SPWM.

Due to various limitations in PIC16, such as ADC speed, instruction time and the ALU, it is extremely difficult, if not impossible, to calculate in real time the values required for feedback in sinusoidal pulse width modulation (SPWM). Thus, to implement feedback, a different approach must be used. That approach would be to retrieve the values from a sine table that contains the duty cycle values for a specific duty cycle. Here is one sine table I used, for example:

const unsigned char sin_table[416]={
0, 16, 32, 47, 62, 77, 91, 103, 115, 126, 136, 144, 151, 156, 160, 162, 163, 162, 160, 156, 151, 144, 136, 126, 115, 103, 91, 77, 62, 47, 32, 16, //65%

0, 17, 33, 49, 65, 80, 94, 107, 120, 131, 141, 149, 156, 162, 166, 168, 169, 168, 166, 162, 156, 149, 141, 131, 120, 107, 94, 80, 65, 49, 33, 17, //67.5%

0, 17, 34, 51, 67, 82, 97, 111, 124, 135, 146, 154, 162, 167, 172, 174, 175, 174, 172, 167, 162, 154, 146, 135, 124, 111, 97, 82, 67, 51, 34, 17, //70%

0, 18, 35, 53, 69, 85, 101, 115, 128, 140, 150, 160, 167, 173, 178, 180, 181, 180, 178, 173, 167, 160, 150, 140, 128, 115, 101, 85, 69, 53, 35, 18, //72.5%

0, 18, 37, 55, 72, 89, 104, 119, 133, 145, 156, 166, 174, 180, 184, 187, 188, 187, 184, 180, 174, 166, 156, 145, 133, 119, 104, 89, 72, 55, 37, 18, //75%

0, 19, 38, 56, 74, 91, 108, 123, 137, 150, 161, 171, 179, 186, 190, 193, 194, 193, 190, 186, 179, 171, 161, 150, 137, 123, 108, 91, 74, 56, 38, 19, //77.5%

0, 20, 39, 58, 77, 94, 111, 127, 141, 155, 166, 176, 185, 191, 196, 199, 200, 199, 196, 191, 185, 176, 166, 155, 141, 127, 111, 94, 77, 58, 39, 20, //80%

0, 20, 40, 60, 79, 97, 114, 131, 146, 159, 171, 182, 190, 197, 202, 205, 206, 205, 202, 197, 190, 182, 171, 159, 146, 131, 114, 97, 79, 60, 40, 20, //82.5%

0, 21, 42, 62, 82, 100, 118, 135, 151, 165, 177, 188, 197, 204, 209, 212, 213, 212, 209, 204, 197, 188, 177, 165, 151, 135, 118, 100, 82, 62, 42, 21, //85

0, 21, 43, 64, 84, 103, 122, 139, 155, 169, 182, 193, 202, 210, 215, 218, 219, 218, 215, 210, 202, 193, 182, 169, 155, 139, 122, 103, 84, 64, 43, 21, //87.5%

0, 22, 44, 65, 86, 106, 125, 143, 159, 174, 187, 198, 208, 215, 221, 224, 225, 224, 221, 215, 208, 198, 187, 174, 159, 143, 125, 106, 86, 65, 44, 22, //90%

0, 23, 45, 67, 88, 109, 128, 147, 163, 179, 192, 204, 213, 221, 227, 230, 231, 230, 227, 221, 213, 204, 192, 179, 163, 147, 128, 109, 88, 67, 45, 23, //92.5%

0, 23, 46, 69, 91, 112, 132, 151, 168, 184, 198, 210, 220, 228, 233, 237, 238, 237, 233, 228, 220, 210, 198, 184, 168, 151, 132, 112, 91, 69, 46, 23 //95%

//0, 25, 49, 73, 96, 118, 139, 159, 177, 193, 208, 220, 231, 239, 245, 249, 250, 249, 245, 239, 231, 220, 208, 193, 177, 159, 139, 118, 96, 73, 49, 25, //100%


Each set of values corresponding to one duty cycle has 32 values.

A table pointer is used to retrieve the values for a given duty cycle. So, when the value of the table pointer is 0, the program reads the first 32 values (65% duty cycle), then the next 32 values when value of table pointer is 1 and so on.

The microcontroller first starts with the lowest duty cycle and then analyses the output voltage.

If the output voltage must be increased, the value of the table pointer is incremented and so, the next set of values is retrieved, increasing duty cycle and thus output voltage. If output voltage must be decreased, the value of the table pointer is decremented so that the previous set of values is retrieved, lowering duty cycle and thus output voltage.

Here is how the table pointer is updated:

                    FBV = ADC_Get_Sample(FBCh);
                    if (FBV < 512){
                       FB_Step++;
                       if (FB_Step > 12) FB_Step = 12;
                    }
                    else{
                         if (FB_Step > 0){
                            FB_Step--;
                         }
                    }
                    adder = FB_Step << 5;
                    TMR1L = 0;
                    TMR1H = 0;
                    T1IF_bit = 0;

The reference value of the ADC is 5V, so 512 represents a voltage of 2.5V, which is the feedback reference voltage in this example. When voltage on ADC pin is >2.5V, table pointer value is decremented and when it is <2.5V, table pointer value is incremented.

The required set of values is retrieved and applied by something like this:
        TBL_POINTER_NEW = TBL_POINTER_OLD + SET_FREQ;
        if (TBL_POINTER_NEW < TBL_POINTER_OLD){
           P1M1_bit = ~P1M1_bit;
        }
        TBL_POINTER_SHIFT = TBL_POINTER_NEW >> 11;
        DUTY_CYCLE = TBL_POINTER_SHIFT + adder;
        CCPR1L = sin_table[DUTY_CYCLE];
        TBL_POINTER_OLD = TBL_POINTER_NEW;
        TMR2IF_bit = 0;

Now that I've shown how to generate a sine table manually and with "Smart Sine", implement SPWM in PIC16 and now, how to implement feedback, you can easily make a sine wave inverter using the information I've provided. All you need to do is make sure you've understood what I've said and do some research on your own to make the project complete.

To make this clearer and so that you understand better, I am presenting some common questions and their answers.

Question: I am designing a pure sine wave inverter using an output H bridge at 310vdc. Is there any need for feed back control if I keep the 310 Vdc steady?
Answer: If you can keep the 310VDC steady, then there is no need for feedback of SPWM. Your output will be relatively stable, since the DC bus is regulated.

Question: Why is the feedback not necessary, since there will be a voltage drop when there is increase in load? Remember the code is not incrementing or decrementing at this time. Now take an example of your normal modified sine inverter using SG3524 or any PWM IC. The output voltage drops when
there is increase in load but the battery voltage doesn't change. Therefore there will be a need to control the code also. Right?
Answer: When there is increase in load, you will notice that the battery voltage always decreases due to the internal resistance. Measure the battery voltage of the inverter under load and you will see that it decreases as load increases. A battery that measures about 12-13V open circuit may drop to 11V or lower under load. Plus, there is a significant drop by the transformer under load.

Also, think about this: in the H-bridge converter, when load increases (and so current increases) but the +ve bus voltage is constant, where will the voltage be dropped? There is no transformer to deal with, as
the primary side PWM will take care of drops by the transformer (and battery voltage drop) and keep the output fixed. The only place for voltage drop is the MOSFETs and there will be a drop in voltage in the MOSFETs, but not so large that the output voltage will be beyond acceptable bounds.

Question: You have shown 32 values for each duty cycle in the program. Can I use 192 for my 3.5kHz design?
Answer: You can use any number of duty cycle values in an array. 32 is usually sufficient, but with 192, greater precision can be had, especially since the frequency is 3.5kHz and the time period is much longer as compared to 16kHz. So, the SPWM is more accurate. However, just keep in mind that all of the values are stored in program memory. So, it will consume more program memory. You can use the software Smart Sine to generate the sine table: http://tahmidmc.blogspot.com/2012/10/smart-sine-software-to-generate-sine.html
For manual calculation, go through this tutorial: http://tahmidmc.blogspot.com/2011/01/generation-and-implementation-of-sine.html

Question: What about this unsigned char I can see (416) in the code. What is it used for?
Answer: 32 values correspond to the sine wave at one duty cycle. I have set 13 duty cycle settings. 13 * 32 = 416
There are total 416 values in the array, 13 sets, and each set (containing 32 values) corresponds to one duty cycle setting.

Each set of values corresponding to one duty cycle has 32 values.

A table pointer is used to retrieve the values for a given duty cycle. So, when the value of the table pointer is 0, the program reads the first 32 values (65% duty cycle), then the next 32 values when value of table pointer is 1 and so on.

Practical Example:
The microcontroller first starts with the lowest duty cycle and then analyses the output voltage.

If the output voltage must be increased, the value of the table pointer is incremented and so, the next set of values is retrieved, increasing duty cycle and thus output voltage. If output voltage must be decreased, the value of the table pointer is decremented so that the previous set of values is retrieved, lowering duty cycle and thus output voltage. The table pointer keeps increasing by 1 to increase the duty cycle and so, the output voltage of the inverter.
Practical example: At the beginning of program execution, the table pointer is zero and the microcontroller starts with 65% duty cycle. Let's assume we have a 8V (primary) to 256V (secondary) transformer and a full bridge converter is being used to drive the transformer. The bridge is being driven by signals from the microcontroller - these SPWM signals.

The RMS voltage to the transformer primary will be (0.65 * 12/1.4142135)V = 5.5V.
Turns ratio of the transformer (primary:secondary) is 1:32.
So, secondary voltage (assuming 100% transformer efficiency) will be 32*5.5V = 176V.
As we require an output of 220V, this is lower than required. So, output voltage must be increased. So, table pointer is incremented to 1.
Then, duty cycle is 67.5% and output voltage is 183V.
Table pointer is incremented to 2.
Duty cycle is 70% and output voltage is 190V.
Table pointer is incremented to 3.
Duty cycle is 72.5% and output voltage is 197V.
Table pointer is incremented to 4.
Duty cycle is 75% and output voltage is 204V.
Table pointer is incremented to 5.
Duty cycle is 77.5% and output voltage is 210V.
Table pointer is incremented to 6.
Duty cycle is 80% and output voltage is 217V.
Table pointer is incremented to 7.
Duty cycle is 82.5% and output voltage is 224V.
Table pointer is decremented to 6.
Duty cycle is 80% and output voltage is 217V.
Table pointer is incremented to 7.
Duty cycle is 82.5% and output voltage is 224V.
Table pointer is decremented to 6.
Duty cycle is 80% and output voltage is 217V.
.
.
.
.
And so on.

This is how regulation is achieved.

This is all at 12V. But, you can see how the table pointer will be changed to maintain a constant output.

65 comments:

  1. please can u add comments to the code. thanks

    ReplyDelete
    Replies
    1. Take a look here for details:

      tahmidmc.blogspot.com/2013/02/demystifying-use-of-table-pointer-in.html

      Regards,
      Tahmid.

      Delete
  2. Very good explanation. Simple way of feedback instead of complex PID calculations.
    Thank you.
    J.T.Rao

    ReplyDelete
  3. Hey Tahmid great stuff. I wanted to know where to put the feedback code. I put it in the interrupt and in the while loop exclusively; it didn't work. I have a general understanding of what is going on but i get lost on how to make sure that the feed back gets updated every 5 interrupts because it seems that the time and registers take care of that. Thanks

    ReplyDelete
    Replies
    1. Nevermind I figured out what was going on and I modified your code to create a different implementation. I used a 2d array and made the interrupt last longer that way on every interrupt it would grab a value from the table.

      Delete
    2. sir can you guide me for square wave inverter with 16f72 feedback in asm launguge ?

      Delete
    3. Have you started coding? Where are you getting stuck or are facing problems?

      Delete
  4. I'm from Russia.
    I can not code feedback+spwm.
    send me please,tantra-yog@yandex.ru

    ReplyDelete
    Replies
    1. Training is slow, but the result is positive.

      Regards,tantra-yog.

      Delete
  5. Hi,

    I have not understood how you have generated sine table for one duty cycle? For example the first 32 values corresponds to 67% and next 32 set of values corresponds to say 70% duty cycle.How you have calculated could you please explain me.

    Thank you.

    Praveen.
    praveen4u83@gmail.com

    ReplyDelete
    Replies
    1. I calculated the sine wave table with 100% duty cycle (the last set of values in the table). Then for the required duty cycle, I divided/multipllied each value in the table. For example, for 67% duty cycle, I multiplied each value in the table by 0.67 to give th required values.

      Regards,
      Tahmid.

      Delete
  6. Nice Post!! pretty informative..thanks for providing such a nice post.

    sine wave inverter

    ReplyDelete
  7. Check the code.

    const unsigned char sin_table[416]=
    {
    0,16,33,49,64,79,93,106,118,129,138,146,153,158,161,163,163,161,158,153,146,138,129,118,106,93,79,64,49,33,16,0, //65%
    0,17,34,51,67,82,97,110,122,134,143,152,158,164,167,169,169,167,164,158,152,143,134,122,110,97,82,67,51,34,17,0, //67.5%
    0,18,35,52,69,85,100,114,127,138,149,157,164,169,173,175,175,173,169,164,157,149,138,127,114,100,85,69,52,35,18,0, //70%
    0,18,36,54,71,88,103,118,131,143,154,163,170,175,179,181,181,179,175,170,163,154,143,131,118,103,88,71,54,36,18,0, //72.5%
    0,19,38,56,74,91,107,122,136,149,160,169,176,182,186,188,188,186,182,176,169,160,149,136,122,107,91,74,56,38,19,0, //75%
    0,20,39,58,77,94,111,126,141,153,165,174,182,188,192,194,194,192,188,182,174,165,153,141,126,111,94,77,58,39,20,0, //77.5%
    0,20,40,60,79,97,114,130,145,158,170,180,188,194,198,200,200,198,194,188,180,170,158,145,130,114,97,79,60,40,20,0, //80%
    0,21,41,62,81,100,118,134,149,163,175,185,193,199,204,206,206,204,199,193,185,175,163,149,134,118,100,81,62,41,21,0, //82.5%
    0,22,43,64,84,103,122,139,154,168,181,191,200,206,211,213,213,211,206,200,191,181,168,154,139,122,103,84,64,43,22,0, //85
    0,22,44,66,86,106,125,143,159,173,186,197,205,212,216,219,219,216,212,205,197,186,173,159,143,125,106,86,66,44,22,0, //87.5%
    0,23,45,67,89,109,129,147,163,178,191,202,211,218,222,225,225,222,218,211,202,191,178,163,147,129,109,89,67,45,23,0, //90%
    0,23,46,69,91,112,132,150,167,183,196,207,217,224,228,231,231,228,224,217,207,196,183,167,150,132,112,91,69,46,23,0, //92.5%
    0,24,48,71,94,116,136,155,173,188,202,214,223,230,235,238,238,235,230,223,214,202,188,173,155,136,116,94,71,48,24,0, //95%
    };

    unsigned int TBL_POINTER_NEW, TBL_POINTER_OLD, TBL_POINTER_SHIFT, SET_FREQ;
    unsigned int DUTY_CYCLE, FBV, FB_Step, adder;

    void interrupt()
    {
    if (TMR2IF_bit == 1){TBL_POINTER_NEW = TBL_POINTER_OLD + SET_FREQ;
    if (TBL_POINTER_NEW < TBL_POINTER_OLD){P1M1_bit = ~P1M1_bit;}
    TBL_POINTER_SHIFT = TBL_POINTER_NEW >> 11;
    DUTY_CYCLE = TBL_POINTER_SHIFT + adder;
    CCPR1L = sin_table[DUTY_CYCLE];
    TBL_POINTER_OLD = TBL_POINTER_NEW;
    TMR2IF_bit = 0;
    }}

    void main()
    {
    SET_FREQ = 410;
    TBL_POINTER_SHIFT = 0;
    TBL_POINTER_NEW = 0;
    TBL_POINTER_OLD = 0;
    DUTY_CYCLE = 0;
    FB_Step = 0;
    adder = 0;
    T1CON = 0b00001001;//1:1 Prescale Value, Enables Timer1
    ANSEL = 0b00000001;//RA0/AN0 as analogue input.
    TRISA = 0b00000001;//PortA as output except RA0 as input
    ADCON0 = 0b00000011;//Analog Channel (AN0)
    ADCON1 = 0b01100000;//FOSC/64
    CMCON0 = 7;
    PR2 = 249;
    TRISC = 0x3F;
    CCP1CON = 0x4C;
    TMR2IF_bit = 0;
    T2CON = 4;
    while (TMR2IF_bit == 0);
    TMR2IF_bit = 0;
    TRISC = 0;
    TMR2IE_bit = 1;
    GIE_bit = 1;
    PEIE_bit = 1;
    ADC_Init();

    while(1)
    {
    FBV = ADC_Get_Sample(0);
    if (VFB < 512){FB_Step++;
    if (FB_Step > 12) FB_Step = 12;}
    else
    {if (FB_Step > 0){FB_Step--;}}
    adder = FB_Step << 5;
    TMR1L = 0;
    TMR1H = 0;
    T1IF_bit = 0;
    }
    }

    ReplyDelete
    Replies
    1. The feedback option don't work......
      Plz check this code.................

      Delete
  8. I can't get feedback from the following code:
    (Plzz help to build it work properly)

    const unsigned char sin_table[416]=
    {
    0,16,33,49,64,79,93,106,118,129,138,146,153,158,161,163,163,161,158,153,146,138,129,118,106,93,79,64,49,33,16,0, //65%
    0,17,34,51,67,82,97,110,122,134,143,152,158,164,167,169,169,167,164,158,152,143,134,122,110,97,82,67,51,34,17,0, //67.5%
    0,18,35,52,69,85,100,114,127,138,149,157,164,169,173,175,175,173,169,164,157,149,138,127,114,100,85,69,52,35,18,0, //70%
    0,18,36,54,71,88,103,118,131,143,154,163,170,175,179,181,181,179,175,170,163,154,143,131,118,103,88,71,54,36,18,0, //72.5%
    0,19,38,56,74,91,107,122,136,149,160,169,176,182,186,188,188,186,182,176,169,160,149,136,122,107,91,74,56,38,19,0, //75%
    0,20,39,58,77,94,111,126,141,153,165,174,182,188,192,194,194,192,188,182,174,165,153,141,126,111,94,77,58,39,20,0, //77.5%
    0,20,40,60,79,97,114,130,145,158,170,180,188,194,198,200,200,198,194,188,180,170,158,145,130,114,97,79,60,40,20,0, //80%
    0,21,41,62,81,100,118,134,149,163,175,185,193,199,204,206,206,204,199,193,185,175,163,149,134,118,100,81,62,41,21,0, //82.5%
    0,22,43,64,84,103,122,139,154,168,181,191,200,206,211,213,213,211,206,200,191,181,168,154,139,122,103,84,64,43,22,0, //85
    0,22,44,66,86,106,125,143,159,173,186,197,205,212,216,219,219,216,212,205,197,186,173,159,143,125,106,86,66,44,22,0, //87.5%
    0,23,45,67,89,109,129,147,163,178,191,202,211,218,222,225,225,222,218,211,202,191,178,163,147,129,109,89,67,45,23,0, //90%
    0,23,46,69,91,112,132,150,167,183,196,207,217,224,228,231,231,228,224,217,207,196,183,167,150,132,112,91,69,46,23,0, //92.5%
    0,24,48,71,94,116,136,155,173,188,202,214,223,230,235,238,238,235,230,223,214,202,188,173,155,136,116,94,71,48,24,0, //95%
    };

    unsigned int TBL_POINTER_NEW, TBL_POINTER_OLD, TBL_POINTER_SHIFT, SET_FREQ;
    unsigned int DUTY_CYCLE, FBV, FB_Step, adder;

    void interrupt()
    {
    if (TMR2IF_bit == 1){TBL_POINTER_NEW = TBL_POINTER_OLD + SET_FREQ;
    if (TBL_POINTER_NEW < TBL_POINTER_OLD){P1M1_bit = ~P1M1_bit;}
    TBL_POINTER_SHIFT = TBL_POINTER_NEW >> 11;
    DUTY_CYCLE = TBL_POINTER_SHIFT + adder;
    CCPR1L = sin_table[DUTY_CYCLE];
    TBL_POINTER_OLD = TBL_POINTER_NEW;
    TMR2IF_bit = 0;
    }}

    void main()
    {
    SET_FREQ = 410;
    TBL_POINTER_SHIFT = 0;
    TBL_POINTER_NEW = 0;
    TBL_POINTER_OLD = 0;
    DUTY_CYCLE = 0;
    FB_Step = 0;
    adder = 0;
    T1CON = 0b00001001;//1:1 Prescale Value, Enables Timer1
    ANSEL = 0b00000001;//RA0/AN0 as analogue input.
    TRISA = 0b00000001;//PortA as output except RA0 as input
    ADCON0 = 0b00000011;//Analog Channel (AN0)
    ADCON1 = 0b01100000;//FOSC/64
    CMCON0 = 7;
    PR2 = 249;
    TRISC = 0x3F;
    CCP1CON = 0x4C;
    TMR2IF_bit = 0;
    T2CON = 4;
    while (TMR2IF_bit == 0);
    TMR2IF_bit = 0;
    TRISC = 0;
    TMR2IE_bit = 1;
    GIE_bit = 1;
    PEIE_bit = 1;
    ADC_Init();

    while(1)
    {
    FBV = ADC_Get_Sample(0);
    if (VFB < 512){FB_Step++;
    if (FB_Step > 12) FB_Step = 12;}
    else
    {if (FB_Step > 0){FB_Step--;}}
    adder = FB_Step << 5;
    TMR1L = 0;
    TMR1H = 0;
    T1IF_bit = 0;
    }
    }

    ReplyDelete
  9. Sorri error VFB FBV

    const unsigned char sin_table[416]=
    {
    0,16,33,49,64,79,93,106,118,129,138,146,153,158,161,163,163,161,158,153,146,138,129,118,106,93,79,64,49,33,16,0, //65%
    0,17,34,51,67,82,97,110,122,134,143,152,158,164,167,169,169,167,164,158,152,143,134,122,110,97,82,67,51,34,17,0, //67.5%
    0,18,35,52,69,85,100,114,127,138,149,157,164,169,173,175,175,173,169,164,157,149,138,127,114,100,85,69,52,35,18,0, //70%
    0,18,36,54,71,88,103,118,131,143,154,163,170,175,179,181,181,179,175,170,163,154,143,131,118,103,88,71,54,36,18,0, //72.5%
    0,19,38,56,74,91,107,122,136,149,160,169,176,182,186,188,188,186,182,176,169,160,149,136,122,107,91,74,56,38,19,0, //75%
    0,20,39,58,77,94,111,126,141,153,165,174,182,188,192,194,194,192,188,182,174,165,153,141,126,111,94,77,58,39,20,0, //77.5%
    0,20,40,60,79,97,114,130,145,158,170,180,188,194,198,200,200,198,194,188,180,170,158,145,130,114,97,79,60,40,20,0, //80%
    0,21,41,62,81,100,118,134,149,163,175,185,193,199,204,206,206,204,199,193,185,175,163,149,134,118,100,81,62,41,21,0, //82.5%
    0,22,43,64,84,103,122,139,154,168,181,191,200,206,211,213,213,211,206,200,191,181,168,154,139,122,103,84,64,43,22,0, //85
    0,22,44,66,86,106,125,143,159,173,186,197,205,212,216,219,219,216,212,205,197,186,173,159,143,125,106,86,66,44,22,0, //87.5%
    0,23,45,67,89,109,129,147,163,178,191,202,211,218,222,225,225,222,218,211,202,191,178,163,147,129,109,89,67,45,23,0, //90%
    0,23,46,69,91,112,132,150,167,183,196,207,217,224,228,231,231,228,224,217,207,196,183,167,150,132,112,91,69,46,23,0, //92.5%
    0,24,48,71,94,116,136,155,173,188,202,214,223,230,235,238,238,235,230,223,214,202,188,173,155,136,116,94,71,48,24,0, //95%
    };

    unsigned int TBL_POINTER_NEW, TBL_POINTER_OLD, TBL_POINTER_SHIFT, SET_FREQ;
    unsigned int DUTY_CYCLE, FBV, FB_Step, adder;

    void interrupt()
    {
    if (TMR2IF_bit == 1){TBL_POINTER_NEW = TBL_POINTER_OLD + SET_FREQ;
    if (TBL_POINTER_NEW < TBL_POINTER_OLD){P1M1_bit = ~P1M1_bit;}
    TBL_POINTER_SHIFT = TBL_POINTER_NEW >> 11;
    DUTY_CYCLE = TBL_POINTER_SHIFT + adder;
    CCPR1L = sin_table[DUTY_CYCLE];
    TBL_POINTER_OLD = TBL_POINTER_NEW;
    TMR2IF_bit = 0;
    }}

    void main()
    {
    SET_FREQ = 410;
    TBL_POINTER_SHIFT = 0;
    TBL_POINTER_NEW = 0;
    TBL_POINTER_OLD = 0;
    DUTY_CYCLE = 0;
    FB_Step = 0;
    adder = 0;
    T1CON = 0b00001001;//1:1 Prescale Value, Enables Timer1
    ANSEL = 0b00000001;//RA0/AN0 as analogue input.
    TRISA = 0b00000001;//PortA as output except RA0 as input
    ADCON0 = 0b00000011;//Analog Channel (AN0)
    ADCON1 = 0b01100000;//FOSC/64
    CMCON0 = 7;
    PR2 = 249;
    TRISC = 0x3F;
    CCP1CON = 0x4C;
    TMR2IF_bit = 0;
    T2CON = 4;
    while (TMR2IF_bit == 0);
    TMR2IF_bit = 0;
    TRISC = 0;
    TMR2IE_bit = 1;
    GIE_bit = 1;
    PEIE_bit = 1;
    ADC_Init();

    while(1)
    {
    FBV = ADC_Get_Sample(0);
    if (FBV < 512){FB_Step++;
    if (FB_Step > 12) FB_Step = 12;
    }
    else
    {
    if (FB_Step > 0){FB_Step--;}}
    adder = FB_Step << 5;
    TMR1L = 0;
    TMR1H = 0;
    T1IF_bit = 0;
    }
    }

    MicroC , Proteus ... files
    http://yadi.sk/d/q1Is-d3EAFMCY

    the code is not final
    requires adjustment

    Regards,tantra-yog.

    ReplyDelete
    Replies
    1. Friends Check the code...

      Regards,tantra-yog.
      tantra-yog@yandex.ru

      Delete
  10. I have tried to build this code using PIC16F72. But the output shows "not enough RAM in sine_table". The datasheet of the said MCU shows 128x8 bytes of data memory (RAM) is there. If 32 values are used in sine_table, it builds properly. Please offer some comment on this issue.

    ReplyDelete
  11. Tahmid you are the best of the best.

    ReplyDelete
  12. This is good and informative post.

    ReplyDelete
  13. Tahmid I love and highly appreciate your work in Power Electronics & its integration with uC.
    I have been planning to design Complete Sine Wave UPS including Feedback for regulated output voltage using a single Microcontroller PIC16F887 for all purposes, I have completed its LCD Display for Voltages, Battery Percentage, Over Under Voltage Protection & Automatic Switching etc,, BUT I NEED YOUR HELP REGARDING CERTAIN PROBLEMS AND ONE OF THESE IS BELOW...

    I have found this one code in above comments and this work perfectly in Proteus Simulation but for PIC16F684.
    But when I try to build it for PIC16F887 using MikroC it highlights the these two lines as an ERROR..
    T1IF_bit = 0;
    CMCON0 = 7;

    Please help me out to solve this issue PLZZ PLZZ As Soon As Possible....

    const unsigned char sin_table[416]=
    {
    0,16,33,49,64,79,93,106,118,129,138,146,153,158,161,163,163,161,158,153,146,138,129,118,106,93,79,64,49,33,16,0, //65%
    0,17,34,51,67,82,97,110,122,134,143,152,158,164,167,169,169,167,164,158,152,143,134,122,110,97,82,67,51,34,17,0, //67.5%
    0,18,35,52,69,85,100,114,127,138,149,157,164,169,173,175,175,173,169,164,157,149,138,127,114,100,85,69,52,35,18,0, //70%
    0,18,36,54,71,88,103,118,131,143,154,163,170,175,179,181,181,179,175,170,163,154,143,131,118,103,88,71,54,36,18,0, //72.5%
    0,19,38,56,74,91,107,122,136,149,160,169,176,182,186,188,188,186,182,176,169,160,149,136,122,107,91,74,56,38,19,0, //75%
    0,20,39,58,77,94,111,126,141,153,165,174,182,188,192,194,194,192,188,182,174,165,153,141,126,111,94,77,58,39,20,0, //77.5%
    0,20,40,60,79,97,114,130,145,158,170,180,188,194,198,200,200,198,194,188,180,170,158,145,130,114,97,79,60,40,20,0, //80%
    0,21,41,62,81,100,118,134,149,163,175,185,193,199,204,206,206,204,199,193,185,175,163,149,134,118,100,81,62,41,21,0, //82.5%
    0,22,43,64,84,103,122,139,154,168,181,191,200,206,211,213,213,211,206,200,191,181,168,154,139,122,103,84,64,43,22,0, //85
    0,22,44,66,86,106,125,143,159,173,186,197,205,212,216,219,219,216,212,205,197,186,173,159,143,125,106,86,66,44,22,0, //87.5%
    0,23,45,67,89,109,129,147,163,178,191,202,211,218,222,225,225,222,218,211,202,191,178,163,147,129,109,89,67,45,23,0, //90%
    0,23,46,69,91,112,132,150,167,183,196,207,217,224,228,231,231,228,224,217,207,196,183,167,150,132,112,91,69,46,23,0, //92.5%
    0,24,48,71,94,116,136,155,173,188,202,214,223,230,235,238,238,235,230,223,214,202,188,173,155,136,116,94,71,48,24,0, //95%
    };

    unsigned int TBL_POINTER_NEW, TBL_POINTER_OLD, TBL_POINTER_SHIFT, SET_FREQ;
    unsigned int DUTY_CYCLE, FBV, FB_Step, adder;

    void interrupt()
    {
    if (TMR2IF_bit == 1){TBL_POINTER_NEW = TBL_POINTER_OLD + SET_FREQ;
    if (TBL_POINTER_NEW < TBL_POINTER_OLD){P1M1_bit = ~P1M1_bit;}
    TBL_POINTER_SHIFT = TBL_POINTER_NEW >> 11;
    DUTY_CYCLE = TBL_POINTER_SHIFT + adder;
    CCPR1L = sin_table[DUTY_CYCLE];
    TBL_POINTER_OLD = TBL_POINTER_NEW;
    TMR2IF_bit = 0;
    }}

    void main()
    {
    SET_FREQ = 410;
    TBL_POINTER_SHIFT = 0;
    TBL_POINTER_NEW = 0;
    TBL_POINTER_OLD = 0;
    DUTY_CYCLE = 0;
    FB_Step = 0;
    adder = 0;
    T1CON = 0b00001001;//1:1 Prescale Value, Enables Timer1
    ANSEL = 0b00000001;//RA0/AN0 as analogue input.
    TRISA = 0b00000001;//PortA as output except RA0 as input
    ADCON0 = 0b00000011;//Analog Channel (AN0)
    ADCON1 = 0b01100000;//FOSC/64
    CMCON0 = 7;
    PR2 = 249;
    TRISC = 0x3F;
    CCP1CON = 0x4C;
    TMR2IF_bit = 0;
    T2CON = 4;
    while (TMR2IF_bit == 0);
    TMR2IF_bit = 0;
    TRISC = 0;
    TMR2IE_bit = 1;
    GIE_bit = 1;
    PEIE_bit = 1;
    ADC_Init();

    while(1)
    {
    FBV = ADC_Get_Sample(0);
    if (FBV < 512){FB_Step++;
    if (FB_Step > 12) FB_Step = 12;
    }
    else
    {
    if (FB_Step > 0){FB_Step--;}}
    adder = FB_Step << 5;
    TMR1L = 0;
    TMR1H = 0;
    T1IF_bit = 0;
    }
    }

    ReplyDelete
  14. Hi,
    If you are using pic16f887 for this inverter, you can remove or put the comment sign // in front of CMCON0 = 7; and also in front of TMR1L = 0;
    TMR1H = 0;
    T1IF_bit = 0; .when you compile the code it will work. the feedback control works but it is not steady the out put voltage keep flickering and not steady. that is the problem i have notice with this code. if any body has a solution i will like to know.

    ReplyDelete
    Replies
    1. You will need to change the feedback "update frequency" - ie the rate at which feedback occurs - depending on your observed results to obtain better results. Try using an RC low pass filter at the input of the ADC pin responsible for feedback readback. Additionally, you may want to slow down the feedback or speed it up as required to obtain stable results. How large are your observed output voltage variations?

      Let me know how it goes!

      Regards,
      Tahmid.

      Delete
    2. Tahmid Please help me out as its my university project of designing this pure sine wave inverter. I have implemented the configuration but my MicroController got burnt when i turned on the power... I have used the hardware configuration as follows in the image... PLZ HELP

      http://check.wholepk.com/circuit.BMP

      Delete
  15. Hi,
    without load my output voltage is in the range of 270v for a transformer designed for 8v(primary) to 230v(secondary) winding. when i load with 100w, the output voltage drop to 230v, when i increase the load to 200w the output drop to 200v when i adjust the variable resistor that control the feedback, the output voltage changes rapidly between 200,220 and 240. At this point when i switch off the load, the output voltage goes more than300v. What part of the code will i alter to change the feedback "update frequency". and how do i slow down the feedback or speed it up.

    ReplyDelete
  16. Siktec would you plz share your circuit diagram of power stage with microcontroller

    ReplyDelete
  17. Siktec, I think its problem due to calibration of Feedback Control Variable Resistor, you are calibrating it after applying load to transformer, you should calibrate it under no load condition to 230V output, when you will put load, it will occur an increment in RMS voltage through feedback and hence, output voltage would be regulated...!
    It goes more than 300v due to factor that you calibrate it to stable voltage under 200w load.

    ReplyDelete
  18. Hi Tahmid,

    Looks like, for the feedback, you are using "pure" ADC value without any software processing (CMIIW).
    What kind of circuit do you use to feed the ADC with RMS value of the sinewave AC signal?

    Thank you.

    ReplyDelete
    Replies
    1. In this case, the output is rectified, filtered and stepped down with a simple voltage divider.

      Delete
  19. Hi,
    The command "if (FBV < 512){FB_Step++" FBV is always true for FBV < 512. bcoz FBV never > 512. So that the statement " FB_Step = 12" is also always true. I unable to understand the feedback code. plz add comment to the code........

    ReplyDelete
    Replies
    1. FBV is the reading from the ADC. It can be between 0 and 1023. So, it is definitely possible to have FBV > 512.

      Delete
    2. ohh!! I understand.....
      But what I do when I use 16f72. Its range is 0 to 255 only.

      Thanks.

      Delete
    3. You can just change the value of 512 based on your feedback circuit and your expected voltage at the feedback pin.

      Delete
  20. Hi,
    I face some problem using this code. I convert Mikro C code to PicBasic Pro. SPWM is working properly. but when I add feedback to my code nothing is happen. I mean that SPWM work but feedback not work. I design a square wave inverter.

    problem picture 1: http://www.imagesup.net/di-1139633743615.jpg
    problem picture 2: http://www.imagesup.net/?di=1113963382729

    my PicBasic Pro code: (anyone plz check my code if I am worng then correct it)
    DEFINE OSE 10
    ON INTERRUPT GOTO ISR

    '*************************VARIABLES
    tblpointernew VAR WORD
    tblpointerold VAR WORD
    tblpointershift VAR WORD
    setfreq VAR WORD
    tbltemp VAR WORD
    dutycycle VAR BYTE
    flag VAR WORD
    SINVAL VAR BYTE
    VOLT VAR BYTE
    FBV VAR BYTE
    FBS VAR BYTE
    ADDER VAR BYTE
    direction VAR BIT
    '*************************ALIAS
    MOSA VAR PORTC.5
    MOSB VAR PORTC.6

    PEIE VAR INTCON.6
    GIE VAR INTCON.7
    TMR1IF VAR PIR1.0
    TMR2IF VAR PIR1.1
    TMR2IE VAR PIE1.1
    '******************************

    MAIN:
    setfreq = 410
    tblpointernew = 0
    tblpointerold = 0
    tblpointershift = 0
    dutycycle = 0
    FBV = 0
    FBS = 0
    TRISC = 0
    PORTC = 0
    PR2 = 249
    CCPR1L = 0
    CCP1CON = 12
    TMR2IF = 0
    T2CON = $04
    TMR2IF = 0
    TMR2IE = 1
    GIE = 1
    PEIE = 1
    direction = 0

    START:
    ADCIN 0, FBV
    IF FBV < 255 THEN
    FBS = FBS + 1
    IF FBS > 8 THEN FBS = 8
    ELSE
    IF FBS > 0 THEN FBS = FBS - 1
    ENDIF
    ADDER = FBS << 5
    TMR1L = 0 : TMR1H = 0 : TMR1IF = 0
    GOTO START

    DISABLE
    isr:
    IF TMR2IF == 1 THEN
    update:
    tblpointernew = tblpointerold + setfreq
    IF tblpointernew < tblpointerold THEN
    IF direction == 0 THEN
    mosa = 0
    mosb = 1
    direction = 1
    ELSE
    mosa = 1
    mosb = 0
    direction = 0
    ENDIF
    ENDIF
    tblpointershift = tblpointernew >> 11
    dutycycle = tblpointershift + ADDER '(FOR FEEDBACK OPTION)
    LOOKUP dutycycle, [0,16,33,49,64,79,93,106,118,129,138,146,153,158,161,163,163,161,158,153,146,138,129,118,106,93,79,64,49,33,16,0,_ '//65%
    0,18,35,52,69,85,100,114,127,138,149,157,164,169,173,175,175,173,169,164,157,149,138,127,114,100,85,69,52,35,18,0,_ '//70%
    0,19,38,56,74,91,107,122,136,149,160,169,176,182,186,188,188,186,182,176,169,160,149,136,122,107,91,74,56,38,19,0,_ '//75%
    0,20,40,60,79,97,114,130,145,158,170,180,188,194,198,200,200,198,194,188,180,170,158,145,130,114,97,79,60,40,20,0,_ '//80%
    0,22,43,64,84,103,122,139,154,168,181,191,200,206,211,213,213,211,206,200,191,181,168,154,139,122,103,84,64,43,22,0,_ '//85
    0,23,45,67,89,109,129,147,163,178,191,202,211,218,222,225,225,222,218,211,202,191,178,163,147,129,109,89,67,45,23,0,_ '//90%
    0,23,46,69,91,112,132,150,167,183,196,207,217,224,228,231,231,228,224,217,207,196,183,167,150,132,112,91,69,46,23,0,_ '//92.5%
    0,24,48,71,94,116,136,155,173,188,202,214,223,230,235,238,238,235,230,223,214,202,188,173,155,136,116,94,71,48,24,0], sinval '//95%
    CCPR1L = SINVAL
    tblpointerold = tblpointernew
    TMR2IF = 0
    ENDIF
    RESUME
    ENABLE

    ReplyDelete
  21. Hi,
    I design a Full bride Inverter. But I want to add feedback option to my project. I got 50Hz freq from my full bridge.
    plz anyone help me!!!!!!!!!! How I add feedback to my project??????

    my picbasic code::
    DEFINE OSC 16
    ON INTERRUPT GOTO UPDATE

    DIR VAR BYTE
    '************************* Assign some Interrupt associated aliases
    T0IE VAR INTCON.5 ' TMR0 Overflow Interrupt Enable
    T0IF VAR INTCON.2 ' TMR0 Overflow Interrupt Flag
    GIE VAR INTCON.7 ' Global Interrupt Enable
    PS0 VAR OPTION_REG.0 ' Prescaler ratio bit-0
    PS1 VAR OPTION_REG.1 ' Prescaler ratio bit-1
    PS2 VAR OPTION_REG.2 ' Prescaler ratio bit-2
    PSA VAR OPTION_REG.3 ' Prescaler Assignment (1=assigned to WDT 0=assigned to oscillator)
    T0CS VAR OPTION_REG.5 ' Timer0 Clock Source Select (0=Internal clock 1=External PORTA.4)

    PWM1 VAR PORTC.5 ' Alias servo pin1
    PWM2 VAR PORTC.6 ' Alias servo pin2
    TRISC = 000000 ' Configure PORTC as outputs
    PORTC = 0 ' Clear PORTB
    '********************* Initiate the interrupt
    GIE = 0 ' Turn off global interrupts
    PSA = 0 ' Assign the prescaler to external oscillator
    PS0 = 1 ' Set the prescaler
    PS1 = 1 ' to increment TMR0
    PS2 = 1 ' every 256th instruction cycle
    T0CS = 0 ' Assign TMR0 clock to internal source
    TMR0 = 0 ' Clear TMR0 initially
    T0IE = 1 ' Enable TMR0 overflow interrupt
    GIE = 1 ' Enable global interrupts

    MAIN:

    GOTO MAIN

    DISABLE
    UPDATE:
    TMR0 = 100
    IF DIR = 0 THEN
    PWM1 = 1
    PWM2 = 0
    DIR = 1
    ELSE
    PWM2 = 1
    PWM1 = 0
    DIR = 0
    ENDIF
    T0IF = 0
    RESUME
    ENABLE

    ReplyDelete
  22. Without a working code and a basic simulation of such concepts these posts are useless and it serves neither an educational purpose nor a technical one. Sorry, but most of your posts here and in the forums falls in the commercial propaganda category...PIC16 inverter(unorganized, no schematic bogus project), Topo-magic unfinished software for almost three years, PIC controlled AVR with no schematics just pictures of you and some guy that you did him a solid...and the list goes on. If you wanna publish something for free just do it don't tease and post some worthless tripe.

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

    ReplyDelete
  24. Hello Tahmid, please I need your help. I have designed your sine-wave inverter using pic16f690 and IRS2110. I got a clean sine-wave at the output after the filter, especially when loaded with about 100W load. It works perfectly with resistive loads without any issue, but if I connect hair-cut clipper or ac motor, the operation is not steady. They keep turning on and off continuously but the voltage did not flunctuate. I tried different bootstrap caps 10uf,22uf,47uf,100uf and 220uf but its the same issue. Please advice.

    May 29, 2014 at 7:27 AM

    ReplyDelete
    Replies
    1. Did you take a look at the output waveform with an oscilloscope? That will reveal a bit.

      Also, what is the power rating of the clipper / motor you used?

      Delete
    2. Dear Tahmid, thank you for the reply. I used an oscilloscope to observe the waveform, the waveform is pure-sine, I was really impressed, but its like it collapsed at about every 4 seconds. It does it so fast that you can't notice except you observe carefully. I can hear the transformer (8V-260V) breaking every 4seconds. If I connect an lcd tv, dvd or any appliances that uses switching power supply they worked fine. But with devices that rely on ordinary transformer as power source (Inductive loads), they fluctuate every 4 seconds. My lcd tv is 200w and it worked fine but the 10W clipper did not.
      Thank you, I will appreciate your response.

      Delete
    3. Can you observe the output waveform using an oscilloscope and see how long the output is off/low during the cycling shutting off?

      Additionally, could you probe the microcontroller outputs to see what happens to the SPWM drive signals when the output goes off/low? Do all the outputs go low? Or what is observed? That will give some insight into what might possibly be going on.

      Delete
    4. I observed the four output with a scope. The 2 low side signals(16khz) did not go low at the same time. When one is on the other is off. Same thing with the two highside signals(50hz). I also observed one highside and one lowside signals at a time, they did not go off at the same time. My highside mosfets also get hotter than the lowside ones, I used 100uF bootstrap caps. Do you have an idea? Or should I try an isolated highside driver with TLP250 and separate power supply?

      Delete
  25. Hi Tahmid,,
    i have a question related to feedback of spwm inverter....
    you have set a pointer for the feedback...when voltage is less than 2.5V,then it will be increamented to keep the voltage back at its initial value...by increasing or decreasing pointer value,duty cycle changes...So,will duty cycle of 50Hz sinewave ( output of sine wave inverter after filter ) also change? i think it will change..if not then why?

    ReplyDelete
  26. here is how the code for the feedback for guys using basic should look like

    While 1 = 1
    FbackV = ADIn 0 ' get feed back voltage from channel 0

    If FbackV < 512 Then ' is feed back voltage less than 2.5v refrence voltage (this depends on what u want to use as refrence)
    FbStep = FbStep + 1 ' if yes increment fbstep
    If FbStep > 12 Then ' is fbstep greater than 12?
    FbStep = 12 'if yes limit to 12 so that we wont overflow our table (i felt it should be 13 not 12) tahmid please respond
    EndIf

    ElseIf FbackV > 512 Then 'is feedback voltage greater than 2.5v refrence
    FbStep = FbStep - 1 'if yes decrement fbstep
    If FbStep < 0 Then 'is fbstep less than 0
    FbStep = 0 ' limit it to if yes
    EndIf
    EndIf

    Adder = FbStep << 5

    Wend

    Hope this helps

    tunde

    ReplyDelete
  27. Hi Tahmid

    I implemented this code but there is a big issue of o/p voltage stability.
    Lot of oscillations on o/p..No matter how fast the loop executes , there is jitter on o/p
    Secondly, load response is quite poor.
    Do you have an idea how this can be tackled?

    ReplyDelete
  28. Dear tahmid, thanks for this Feedback in SPWM. please help me include a low voltage detector using a comparator in shutting down the spwm when the voltage get low from the battery.. thank u.

    ReplyDelete
    Replies
    1. You can use the internal ADC to measure the battery voltage and shut down the SPWM in software when battery is too low.

      Delete
  29. Send the sine wave inverter circuit and code to stevejossy80@yahoo.com

    ReplyDelete
  30. HI.
    my View is that u dont do your feed back voltage check all d time. And also you need to have something like a band gap before u do ur regulation.
    Say for instance if your output voltage falls between 230v and 225, u dont really need to adjust the voltage as most equipment will function well using this voltage range.
    You can even extend the range between 220 and 230 such that as long as you output voltage is still within this gap u dont go into the voltage compensation loop.

    Example:

    If your reference is 512 which is equivalent to 2.5v on a 10bit adc or 128 on an 8bit adc as the case may be.

    Now, assuming your max output voltage to be 230 volts which is equivalent to 1023 so an output voltage of 200V will be 889. this can actually be your reference. And an output of 220V will be 978 so now you can track your range in something like this.


    while FbackV >978 and <1023
    do watever
    wend

    Compensation:
    if FbackV < 978 then ' that is if output voltage is less than 220volts then do compensation
    do compensation here
    end if

    so like that you can keep your output stable. output fluctuating i think is as a result of d fact that, the code is trying to compensate for change in voltage levels all the time.

    while you voltage is still within the range you set compensation will not happen.

    hope this helps.

    ReplyDelete
    Replies
    1. Hello,
      I have been leaning towards your line of thinking because when I implement the feedback code as given by tahmid, my open circuit voltage was 250Vac but when on load (no matter how small) the voltage drops to 150 and the feedback doesn't come into effect. Hence, I am thinking of sampling the output at an interval of between 4ms to 10ms. So, please, what would you suggest the sampling interval should be?

      Regards
      Faruq

      Delete
  31. Hi Tamhid:

    Can you tell me about the filter you used for producing pure AC wave from SPWM.

    ReplyDelete
  32. hi tahmid

    i am doing project on AC to DC converter

    can this feed back code used in this ac to dc converter ?

    if it can be used then where we have to put this feedback loop

    pls help me..

    ReplyDelete
  33. Thanks Mr. Tahmid it's very helpful details. for a sine wave h bridge is we have to control the pwm of both push pull and h bridge for feedback

    ReplyDelete
  34. Hi
    l was caught up by your wonderful work, and l know you can help me, l bought power inverter using
    PIC16F876A using 20MHz crystal but is damage, l want you to help me to get HEX file for it or program for me, l dont mind if l have to pay

    ReplyDelete
    Replies
    1. hi Fred, How far with your project? Has it worked? what makes you think your inverter is not working because of the Program and nothing else?

      Delete
  35. Anyone here having complete code for SPWM inverter including Feedback for regulated output voltage using a single Microcontroller with LCD Display for Voltages, Battery Percentage, Over Under Voltage Protection & Automatic Switching etc

    ReplyDelete
  36. Dear tahmid, please help me to write the code for the internal ADC to measure the battery voltage and shut down the SPWM in software when battery is too low.

    ReplyDelete
  37. im just reading this post after so long
    but im grateful for it its been very helpful in my design
    although at what point did you connect the feedback ?
    the drain, source where exactly
    and through wha size of resistor
    thank you sir

    ReplyDelete
  38. What is the use of TMR1 code in the Feedback code ?

    ReplyDelete
  39. my array does not access more than the 255th value

    ReplyDelete
  40. 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