Embedded Systems and Power Electronics

Total Pageviews

About Me

My photo
I am currently a PhD student at UC Berkeley, following a 6-year journey working at Apple after my undergrad years at Cornell University. I grew up in Dhaka, Bangladesh where my interest in electronics was cultivated, resulting in the creation of this blog.

BTemplates.com

Powered by Blogger.

Feb 24, 2013

N-Channel MOSFET High-Side Drive: When, Why and How?



I had previously shown how to drive N-channel MOSFETs in low-side configuration. You can find the tutorial here:

http://tahmidmc.blogspot.com/2012/12/low-side-mosfet-drive-circuits-and_23.html

I’ve been requested to write a tutorial/article regarding high-side MOSFET drive. So, here I’ll talk about N-channel MOSFET high-side drive.

Let’s first look at the common low side configuration.

 Fig. 1 - N-channel MOSFET configured as low-side switch

Now let’s look at a MOSFET configured as a high-side switch.

Fig. 2 - N-channel MOSFET configured as high-side switch


You can quite easily see the difference between the high-side configuration and the low-side configuration. In the low-side configuration, the load is connected between the drain and +V, while the source is connected to ground. Thus, the gate drive is referenced to ground. So by applying a voltage of >7V (for Power MOSFETs) or >4V (for Logic Level MOSFETs), the MOSFET can be fully turned on.

Now let’s talk about the high-side configuration. The load is connected between the source and ground with the drain connected to +V. Thus the gate drive is not referenced to ground as source is not connected to ground and gate drive is related to VGS (voltage at gate with respect to source). I’ll talk about this in a while.

The difference in operation for the MOSFET configured as high-side switch as opposed to the MOSFET configured as low-side switch is that, it acts as a current source whereas the MOSFET configured as low-side switch acts as a current sink.

Now let’s go back to the high-side drive. Let’s say you apply a voltage of 12V (with reference to ground) to the MOSFET gate. However, when the MOSFET is on, voltage at source is equal to +V. Let’s assume +V is +15V. Now the problem is +12V gate drive (with reference to ground) will not keep the MOSFET on. When the MOSFET is on, the MOSFET source will be at a potential of +15V. To be on, the MOSFET must have +8V VGS minimum. So, if source is at +15V, the voltage at the gate with respect to ground must be at least +23V. If source was at +300V, for example, gate drive would require a minimum of +308V with respect to ground. This is if the gate drive is referenced to ground. If you have a separate isolated power supply whose ground and the ground of the MOSFET-based circuit are isolated, then  you can use that to drive the MOSFET as well.

There are quite a few ways to drive MOSFETs in high-side configuration. The first thing that might come into the minds of many of you would be a boost converter circuit or a charge pump circuit to use as the drive voltage for gate drive. This concept is sometimes used and isn’t wrong. However, it is usually used when voltage gap between control circuit voltage and gate drive requirement is small. If you needed to step up voltage from 12V to 40V for example, you might be able to accomplish it quite easily. However, a problem arises when there is the need to step up voltages from 12V to, say, 300V. In such situations, other solutions must be sought.

One simple solution is to use a gate drive transformer. This is not a method I prefer and thus I will not talk about it here. If you are interested, I might write another article just for gate drive transformers.

Another solution is to use a separate/isolated power supply whose ground is separate from the ground of the MOSFET-based circuit. See Fig. 3 below.

The other popular method is to use bootstrap based drive. In this drive method, a capacitor is charged to the required VGS – let’s say 10V – when the MOSFET is off. Then this capacitor is used during driving the MOSFET to provide an additional 10V over the source.

First I’ll talk about the use of a separate/isolated power supply. Here’s a circuit diagram illustrating this:

Fig. 3 - Driving a high-side N-channel MOSFET from a separate/isolated power supply (click image to enlarge)

When a logic high is given to “Drive Signal”, potential at optocoupler pin 4 (emitter of optocoupler transistor) is about +12V with respect to the ground / negative terminal/point of BAT1 – the separate/isolated power supply. This point is connected to Q1 source. Thus Q2 turns on. About +12V, with respect to Q1 source, is provided to Q1 gate. Thus Q1 is driven on.

When a logic low is given to “Drive Signal”, optocoupler pin 4 (emitter of optocoupler transistor) is at the same potential as the ground / negative terminal/point of BAT1 – the separate/isolated power supply. So Q3 turns on and pulls Q1 gate low. Thus MOSFET Q1 is driven off.

Note that the optocoupler ground is the same ground as the ground of the MOSFET-based circuit.

Here’s the current flow for when the “Drive Signal” is a logic high.

 Fig. 4 - Current flow when driving a high-side N-channel MOSFET on, from a separate/isolated power supply (click image to enlarge)

This driver can be used for any duty cycle – all the way from 0% to 100%. The driving frequency is limited by the speed of the optocoupler. For high frequencies, optically isolated MOSFET drivers may be used instead of the two transistors and the optocoupler – the optically isolated MOSFET driver will be all that’s needed. Some such drivers are TLP250, TLP350, HCPL3120, etc.

Now let’s talk about the boostrap based drive. Here when the high-side MOSFET is off, a capacitor is charged from the driving voltage. The capacitor charges through the load or a supporting low-side MOFSET. When the high-side MOSFET is to be turned on/driven, the voltage on the capacitor is used to drive the high-side MOSFET. Thus the limitation of this method is quite obvious. A large enough capacitor should be used for storing the required energy/charge for keeping the high-side MOSFET on for the required time. At the same time, the capacitor must be large enough that during the entire driving time, the voltage doesn't fall below about 8V, in order to prevent the MOSFET from being only partially on. Thus, the bootstrap based drive can not be used for 100% or close to 100% duty cycle. And the lower the frequency of operation, the larger the required capacitance.

The easiest way to drive a MOSFET using the boostrap based drive is to use a dedicated high side MOSFET driver. Some drivers come with just the high-side driver while many come with both high-side and low-side drivers. IR2117, for example, is one driver that contains a single driver that can be used to drive a high-side MOSFET driver. IR2110, which is arguably the most popular high-low side MOSFET driver, features a high-side driver and a low-side driver in a single device. I’ve written a detailed tutorial regarding the use of IR2110. Here's the tutorial:

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

Besides the IR2110, there are many high-low side drivers available. L6385E and NCP2181 are my personal favorites. You can choose from a wide range of high side and high-low side drivers.

No matter which method you choose, once you know how to handle the drive requirement, it's really easy. In most cases, I use the bootstrap based drivers, although I do occasionally use isolated power-supply based drive. None of these methods are too difficult and I hope I've managed to provide you a clear answer to your question: "Why are high-side N-channel MOSFETs to be driven differently from low-side N-channel MOSFETs, and how do we drive the high-side N-channel MOSFET?" Do let me know your comments and feedback.

Feb 22, 2013

Ferrite Transformer Turns Calculation for Offline SMPS Half-Bridge Converter



On different forums, I often find people asking for help in calculating the required turns for a ferrite transformer they are going to use in offline SMPS half-bridge converters. The half-bridge topology is very popular for offline converters in the power range 100W to 500W, sometimes going up to even 1000W. In an offline SMPS half-bridge converter, the line voltage is rectified and filtered and is then converted to high frequency with 2 MOSFETs – one in high-side configuration and the other in low-side configuration. This high frequency high voltage AC is fed to the ferrite transformer to step down the voltage to low voltage high frequency AC which is then rectified to DC and filtered to provide clean DC output. A vital thing to remember is that in a half-bridge converter, the 2 MOSFETs work along with 2 capacitors to create the high voltage high frequency AC. The configuration of the capacitors, MOSFETs and transformer causes the transformer to be supplied half the voltage of the rectified DC. This means that, compared to a full-bridge converter, half the number of turns is required for the primary, but the power output would be half. Thus power/energy density is halved.

Now let’s move on to the calculation. Calculation of required turns is actually quite simple and I’ll explain this here.

For explanation, I’ll use an example and go through the calculation process.
Let’s say the ferrite transformer will be used in a 250W converter that will be used to charge a 12V lead acid battery. The selected topology is obviously half-bridge. The power source for the converter is the AC mains. Here I’ll take that to be 220V RMS, 311V peak, 50Hz. So, you must remember that the mains AC should be rectified to DC first. Output voltage of the DC-DC converter stage will be 14V. Switching frequency is 50kHz. The selected core is ETD44. Remember that the output of the transformer will be high frequency AC (50kHz square wave in this case). When I refer to an output of low voltage DC (eg 14VDC mentioned above), this is the DC output obtained after rectification (using schottky, preferably, or ultrafast recovery diodes configured as full-wave rectifier) and filtration (using LC filter). Since I plan to use full-wave rectification (with 2 diodes) at the output, the secondary of the ferrite transformer will be center tapped.

We must take a maximum and minimum input voltage rating for the converter. For our example, these will be a low line voltage of 150V and a high line voltage of 250V. During operation, the output voltage will stay fixed as the converter is expected to have feedback circuitry. 

Vinmin = 150VAC = (150* √2)VDC = 212VDC

Vinmax = 250VAC = (250* √2)VDC = 354VDC

Vinnom = 220VAC = (220* √2)VDC = 311VDC

The formula for calculating the number of required primary turns for a forward-mode converter is:


For our half-bridge transformer, this will be twice the required number of turns, that is, the actual number of primary turns will be half that calculated from the above formula if we use the full voltage, or exactly what is calculated if half the voltage is used. This is because the voltage across the transformer is half the line voltage, as previously mentioned.

So, the actual formula would be:
 


Npri means number of primary turns; Nsec means number of secondary turns; Naux means number of auxiliary turns and so on. But just N (with no subscript) refers to turns ratio.


For calculating the required number of primary turns using the formula, the parameters or variables that need to be considered are:



  • Vin(nom) – Nominal Input Voltage. We’ll take this as 311V. So, Vin(nom) = 311.
  • f – The operating switching frequency in Hertz. Since our switching frequency is 50kHz, f = 50000.
  • Bmax – Maximum flux density in Gauss. If you’re accustomed to using Tesla or milliTesla (T or mT) for flux density, just remember that 1T = 104 Gauss. Bmax really depends on the design and the transformer cores being used. In my designs, I usually take Bmax to be in the range 1300G to 2000G. This will be acceptable for most transformer cores. In this example, let’s start with 1500G. So Bmax = 1500. Remember that too high a Bmax will cause the transformer to saturate. Too low a Bmax will be under utilizing the core.
  • Ac – Effective Cross-Sectional Area in cm2. You will get this information from the datasheets of the ferrite cores. Ac is also sometimes referred to as Ae. For ETD44, the effective cross-sectional area given in the datasheet/specification sheet (I’m referring to TDK E141. You can download it from here: www.tdk.co.jp/tefe02/e141.pdf  ). The effective cross-sectional area (in the specification sheet, it’s referred to as Ae but as I’ve said, it’s the same thing as Ac) is given as 175mm2. That is equal to 1.75cm2. So, Ac = 1.75 for ETD44.
So now, we’ve obtained the values of all required parameters for calculation of Npri – the number of required primary turns.



Vin(nom) = 311                                        f = 50000                              Bmax = 1500                          Ac = 1.75

Plugging these values into the formula:





                                    Npri = 29.6

We won’t be using fractional windings, so we’ll round off Npri to the nearest whole number, in this case, rounded up to 30 turns. Now, before we finalize this and select Npri = 30, we better make sure that Bmax is still within acceptable bounds (it will be since this is such a minor percentage change, but I’ll show this anyways so that you know what to do, just in case). As we’ve increased the number of turns from the calculated figure (up to 30 from 29.6), Bmax will decrease very slightly. We’ll now figure out just how much Bmax has decreased.

 



                                        Bmax = 1481 



The new value of Bmax is well within acceptable bounds and so we can proceed with Npri  = 30.
 

So, we now know that for the primary, our transformer will require 30 turns.

In any design, if you need to adjust the values, you can easily do so. But always remember to check that Bmax is acceptable. 

  • I’ve started off with a set Bmax and gone on to calculate Npri from there. You can also assign a value of Npri and then check if Bmax is okay. If not, you can then increase or decrease Npri as required and then check if Bmax is okay, and repeat this process until you get a satisfactory result. For example, you may have set Npri = 20 and calculated Bmax and decided that this was too high. So, you set Npri = 30 and calculated Bmax and decided it was okay. Or you may have started with Npri = 40 and calculated Bmax and decided that it was too low. So, you set Npri = 30 and calculated Bmax and decided it was okay.
Now it’s time to move on to the secondary. The output of our DC-DC converter is 14V. Keep in mind that there will be a voltage drop due to the output rectifiers. So, the transformer output must be [14 + (Total Voltage Drop Due to Diodes)]V at all input voltages, from all the way up from 354VDC (254VAC) to all the way down to 212VDC (150VAC). To keep the voltage drop due to the diodes a minimum, use schottky didoes.

Naturally, feedback will be implemented to keep the output voltage fixed with line and load variations – changes due to mains voltage change and also due to load change. So, some headroom must be left for feedback to work. So, we’ll design the transformer with secondary rated at 16V. This headroom compensates for voltage drops due to output rectifier diodes. Feedback will just adjust the voltage required by changing the duty cycle of the PWM control signals. Besides that, the headroom also compensates for some of the other losses in the converter and thus compensates for the voltage drops at different stages – for example, in the MOSFETs, in the transformer itself, in the output inductor, etc.

This means that the output must be capable of supplying 14V with input voltage equal to 212VDC and also input voltage equal to 354VDC. For the PWM controller, we’ll take maximum duty cycle to be 98%. The gap allows for dead-time.


At minimum input voltage (when Vin = Vinmin), duty cycle will be maximum. Thus duty cycle will be 98% when Vin = 212VDC = Vinmin. At maximum duty cycle = 98%, average voltage to transformer = 0.98 * 0.5 * 212V = 103.88V.

So, voltage ratio (primary : secondary) = 103.88V : 16V = 6.493

Since voltage ratio (primary : secondary) = 6.493, turns ratio (primary : secondary) must also be 6.493 as turns ratio (primary : secondary) = voltage ratio (primary : secondary). Turns ratio is designated by N. So, in our case, N = 6.493 (I’ve taken N as the ratio primary line voltage : secondary).

Npri = 30
Nsec = Npri / N = 30 / 6.493 = 4.62

Round off to the nearest whole number. Nsec = 5

Now, notice how this rounding up is not an insignificant rounding up. So, let’s try to keep Nsec = 5 and adjust Npri again.

Npri = N * Nsec

Npri = 5 * 6.493 = 32.5 = 33 (rounded off to the nearest integer)

Now let’s check if Bmax is okay with Npri = 33, ie, if Bmax is within acceptable bounds.




  
                                    Bmax = 1346


Bmax = 1346 is okay. So, Npri = 33 and Nsec = 5. Thus 5 + 5 turns are required for the secondary. With proper implementation of feedback, a constant 12VDC output will be obtained throughout the entire input voltage range of 150VAC to 250VAC.

Of course, notice here that Bmax is very small and can be increased to reduce the required turns. So, let’s reduce Nsec from 5 to 4.

Nsec = 4

Npri = N * Nsec = 6.493 * 4 = 25.97 = 26 (rounded off to nearest integer)

Checking Bmax again:
  



                                   Bmax = 1709 

Here, one thing to note is that even though I took 98% as the maximum duty cycle, maximum duty cycle in practice will be smaller since our transformer was calculated to provide 16V output. In the circuit, the output will be 16V (transformer output will be 14V + Voltage drop of diode), so the duty cycle will be even lower. However, the advantage here is that you can be certain that the output will not drop below 12V even with heavy loads since a large enough headroom is provided for feedback to kick in and maintain the output voltage even at high loads and low line voltages.

If any auxiliary windings are required, the required turns can be easily calculated. Let me show with an example. Let’s say we need an auxiliary winding to provide 17.5V. I know that the output 14V will be regulated, whatever the input voltage may be, within the range initially specified (Vinmin to Vinmax – 150VAC to 250VAC). So, the turns ratio for the auxiliary winding can be calculated with respect to the secondary winding. Let’s call this turns ratio (auxiliary : secondary) NA.

NA = Naux / Nsec = (Vaux+Vd)/ (Vsec + Vdsec). Vdsec is the output diode forward drop (at the secondary). Vd is the output diode forward drop at the auxiliary. Let’s assume that in our application, schottky rectifiers with Vd = 0.5V is used.

So, NA = 18.0V/14.5V = 1.24 

Naux / Nsec = NA 

 Naux = Nsec * NA = 4 * 1.24 = 4.96

Let’s round off Naux to 5. Since the rounding up is very small (from 4.96 to 5), the output voltage will be pretty close to the desired voltage, but I'll just show you how to calculate what the output voltage is.



(Vaux + Vd) / (Vsec + Vdsec) = NA = Naux / Nsec = 5 / 4 = 1.25

(Vaux + Vd) = (Vsec + Vdsec) * NA = 14.5V * 1.25 = 18.13V

Vaux = 17.63V

That is great for an auxiliary supply. If in your designs, you ever find that Vaux is far too off the required voltage, a simple voltage regulator (using 78XX for example) should be used to provide the stable auxiliary voltage.

Another option is to recalculate Npri and Nsec to accommodate for a near accurate auxiliary voltage but you can just use a voltage regulator to simplify things. After all, the voltage regulator will keep the output voltage regulated stable.

So, there we have it. Our transformer has 26 turns for primary, 4 turns + 4 turns for secondary and 5 turns for auxiliary.

Here’s our transformer:




Here’s the transformer at work in a circuit (block diagram):






Calculating required number of turns for a transformer for an offline SMPS half-bridge converter is actually a simple task and I hope that I could help you understand how to do this. I hope this tutorial helps you in your ferrite transformer designs for offline SMPS half-bridge converters. Do let me know your comments and feedback.

Feb 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!


Feb 16, 2013

Sine Wave Generation without ECCP - Using single CCP Module of PIC16F877A




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.

So, here I talk about how to generate the same SPWM signals using just one CCP module as can be commonly found on so many microcontrollers. This allows much greater flexibility in microcontroller selection.

You should go through the other articles related to generating SPWM with the ECCP module (if you haven't already gone through them, that is) to get an idea of what I'm talking about regarding sine wave generation with the ECCP module and about sine wave generation in general, really:


The code I had previously used (utilizing the ECCP module) is:


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


unsigned char sin_table[32]={0,25,49,73,96,118,137,
159,177,193,208,220,231,239,245,249,250,249,245,
239,231,220,208,193,177,159,137,118,96,73,49,25};


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

void interrupt(){
     if (TMR2IF_bit == 1){
        TBL_POINTER_NEW = TBL_POINTER_OLD + SET_FREQ;
        if (TBL_POINTER_NEW < TBL_POINTER_OLD){
           CCP1CON.P1M1 = ~CCP1CON.P1M1; //Reverse direction of full-bridge
        }
        TBL_POINTER_SHIFT = TBL_POINTER_NEW >> 11;
        DUTY_CYCLE = TBL_POINTER_SHIFT;
        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;
     ANSEL = 0; //Disable ADC
     CMCON0 = 7; //Disable Comparator
     PR2 = 249;
     TRISC = 0x3F;
     CCP1CON = 0x4C;
     TMR2IF_bit = 0;
     T2CON = 4; //TMR2 on, prescaler and postscaler 1:1
     while (TMR2IF_bit == 0);
     TMR2IF_bit = 0;
     TRISC = 0;
     TMR2IE_bit = 1;
     GIE_bit = 1;
     PEIE_bit = 1;
    
     while(1);
}
//-------------------------------------------------------------------------------------




That's the previous code. Now let's look at the code based on a single CCP module and not the ECCP module. For this, I chose the super popular PIC16F877A microcontroller. The chosen frequency, like before, is 16kHz. The code is:
 

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



unsigned char sin_table[32]={0, 25, 50, 75, 99, 121, 143, 163, 181,
198, 212, 224, 234, 242, 247, 250, 250, 247, 242, 234, 224, 212, 198,
181, 163, 143, 121, 99, 75, 50, 25,0};


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

sbit MOSA at RD0_bit;
sbit MOSB at RD1_bit;
sbit MOSC at RD2_bit;
sbit MOSD at RD3_bit;

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

void interrupt(){
     if (TMR2IF_bit == 1){
        TBL_POINTER_NEW = TBL_POINTER_OLD + SET_FREQ;
        if (TBL_POINTER_NEW < TBL_POINTER_OLD){
           //CCP1CON.P1M1 = ~CCP1CON.P1M1; //Reverse direction of full-bridge
           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;
        CCPR1L = sin_table[DUTY_CYCLE];
        TBL_POINTER_OLD = TBL_POINTER_NEW;
        TMR2IF_bit = 0;
     }
}

void main() {
     SET_FREQ = 410;
     PORTD = 0;
     TRISD = 0;
     PR2 = 249; // 16kHz
     CCPR1L = 0;
     CCP1CON = 12; //PWM mode
     TRISC = 0xFF;
     TMR2IF_bit = 0;
     T2CON = 0x04; //TMR2 on
     while (TMR2IF_bit == 0);
     TMR2IF_bit = 0; //Clear TMR2IF
     PORTC = 0;
     TRISC = 0;
     TMR2IE_bit = 1;
     GIE_bit = 1;
     PEIE_bit = 1;
    
     while (1);
    
}


Now let's talk about the changes I've made in order to be able to use a single CCP module instead of the ECCP module.

When the ECCP module is used, it generates the SPWM signals and sends the modulation signals to the required "MOSFETs" (of course there's a drive circuit in between) depending on the "direction" as dictated by CCP1CON.P1M1 (bit 7 of CCP1CON register). Since this bit does not exist in the CCP module (obviously, since it's "uni-directional"), this functionality must be achieved in software. Since we don't have the ECCP module and have chosen to use a single CCP module only, the 4 drive signals come from other pins not associated to the PWM module. I've chosen PORTD bits 0 to 3. Of course, you can select any other 4 pins.

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)

The SPWM generation is done by the single CCP module and which MOSFETs to send the signals to is set by the "Direction" bit and the hardware trick employing the AND gate. When "Direction" is equal to 0, the high side MOSFET A is kept on for 10ms during which time the SPWM signals on CCP1 output (RC2) are sent to low side MOSFET D by sending a "1" to RD3, which, with the help of the AND gate "diverts" the CCP1 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. 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;
           }


To understand how the timing and the table pointer operation work, go through this:

Demystifying The Use of Table Pointer in SPWM - Application in Sine Wave Inverter

I've modified the sine table to increase the deadtime. Notice how there's a 0 at both the start and the end. This achieves the additional deadtime. See Fig. 4 below. I did this by using my software "Smart Sine" to generate a sine table with 31 values and then adding a 0 at the end.
 
Besides that, the other functionality are the same - the PWM initialization and setting, the table and table pointer are used the same way as before. So make sure you go through this tutorial if you aren't completely clear regarding it:

Demystifying The Use of Table Pointer in SPWM - Application in Sine Wave Inverter

For the MOSFET drivers, you require high/low side MOSFET drivers. One of the most popular such driver is the IR2110. For a thorough tutorial on using the IR2110, go through this tutorial:
http://tahmidmc.blogspot.com/2013/01/using-high-low-side-driver-ir2110-with.html

Here are the simulation results:


 Fig. 3 - Generated SPWM  Drive Signals (Click image to enlarge)



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


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


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


The operation is quite simple to understand. The trick lies in a simple software modification and the use of the external AND gates. It's quite simple really! All we've needed are 5 IO pins from the PIC16F877A leaving all the other IO pins unused - for use for so many other tasks you can carry out. Observe how the main function in the code is not doing anything and all is done in the interrupt. Notice the empty endless while(1) loop where you can carry out any other required task.

I hope you've understood how to generate SPWM signals using just the single CCP module of a microcontroller and can now use it for all your applications! Keep in mind that this isn't restricted to only PICs but can be used for any microcontroller containing one PWM module. Let me know your feedback and comments.