So, let's talk about generating 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.
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.
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
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){
groupA = 0;
groupD = 0;
groupB = 1;
groupC = 1;
Direction = 1;
}
else{
groupB = 0;
groupC = 0;
groupA = 1;
group = 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
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.
SPWM code (utilizing the ECCP module in PIC16F684 )
Embedded C Code
/
//----------------------------------------------------------------------------------------
//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);
}
//-------------------------------------------------------------------------------------
SPWM code (utilizing the CCP using PIC16F877A microcontroller.).
Embedded C Code
//----------------------------------------------------------------------------------------
//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 groupA at RD0_bit;
sbit groupB at RD1_bit;
sbit groupC at RD2_bit;
sbit groupD at RD3_bit;
unsigned char FlagReg;
sbit Direction at FlagReg.B0;
//0 -> group A + D
//1 -> group 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){
groupA = 0;
groupD = 0;
groupB = 1;
groupC = 1;
Direction = 1;
}
else{
groupB = 0;
groupC = 0;
groupA = 1;
groupD = 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);
}
nice
ReplyDelete