|
|
View previous topic :: View next topic |
Author |
Message |
John_Lintern
Joined: 30 Sep 2004 Posts: 14
|
How to output 0°,120° & 240° phase shift from squarewave |
Posted: Tue Nov 15, 2005 6:06 am |
|
|
Please help.... I'm so near yet so far !!
I have a squarewave input of a variable frequency from 15Hz to 30Hz.
I want 3 outputs to fire the gates of 3 triacs.
These outputs need to be phase shifted by 0° (for phase A), 120° (for phase B) and 240° (for phase C) with reference to this variable frequency input.
I then want to add a delay to the 3 outputs.
This delay will be used to control the firing angle and therefore the power delivered to the 3 phase load.
I have managed to get the 3 outputs at 0°, 120° and 240°.
This gives a 0° firing angle on each phase.
The triacs need to be fired on the rising edges as well as the falling edges of all 3 phases.
I have taken a snapshot from the scope to help explain, but I dont know how to put the picutre into this post ?
I have already spent alot of time and effort trying to do this but failed.
Does anyone have any ideas how to go about this, or has anyone done anything similar ?
Below is a description of the method I have tried....
**********************************************************
I will use an example with an input frequency of 16.67Hz, which gives a nice round 60ms period to work with.
I worked out that the rising and falling edges of all phases occured every 10ms.
So I wrote my code to measure the period and divide the value by 6.
For a 0° firing angle on all 3 phases with a period of 60ms...
Phase A Rising Edge - occurs on the rising edge of the input
Phase C Fallin Edge - occurs 10ms after the rising edge of the input
Phase B Rising Edge - occurs 20ms after the rising edge of the input
Phase A Fallin Edge - occurs 30ms after the rising edge of the input
Phase C Rising Edge - occurs 40ms after the rising edge of the input
Phase B Fallin Edge - occurs 50ms after the rising edge of the input
So I used the timer1 compare interrupt and loaded these values into the compare register CCPR2 each time round.
Using a PIC16F877A with a 20MHz crystal, I set Timer1 to a prescale of 8 so it increments every 1.6us.
As the firing points need to occur every 10ms (for a period of 60ms) then the Timer1 compare value for each interrupt should occur every 6,250.
Therefore, the Timer1 values for each of the firing points are as follows...
Phase A Rising Edge - occurs on rising edge of input
Phase C Fallin Edge - occurs when Timer1 reaches 6,250
Phase B Rising Edge - occurs when Timer1 reaches 12,500
Phase A Fallin Edge - occurs when Timer1 reaches 18,750
Phase C Rising Edge - occurs when Timer1 reaches 25,000
Phase B Fallin Edge - occurs when Timer1 reaches 31,250
Timer1 is set to zero on every rising edge of the squarewave input.
This method works fine and gives a fixed firing angle of 0° for the triacs on all 3 phases A, B and C.
Then things got really messy when I tried to add a delay so I could adjust the firing angle.
I set Timer2 to interrupt every 125us, but this is only enabled when Timer1 reaches the compare value.
When Timer2 interrupts occur, a variable (called 'delay') is decremented until it reaches zero.
'delay' is loaded with a value read from the ADC.
Therefore, 'delay' gets decremented every 125us until it reaches zero.
So if the 'delay' value was 10, it would take 1.25ms for the delay to reach zero.
When 'delay' reaches zero, the output for the necessary triac (phase A, B or C) is set from its normally high state to low.
Timer2 interrupts again 125us later and the same output is set back to high.
Timer2 interrupts again 125us later and the same output is set back to low.
This happens 8 times so that the output gives four 125us negative pulses - this ensures that the triac is turned on.
When all four pulses have fired the Timer2 interrupts are disabled.
This method works UNTIL the 'delay' approaches the same time as the Timer1 compare interrupt.
At that point the outputs all screw up.
I then came up with a solution which made things even more complicated.
I worked out that if the 'delay' approaches the Timer1 compare interrupt, I could change the firing sequence of the outputs.
The value of the delay also required modifying depending on what firing sequence was being used.
I worked out that as 'delay' approaches the first Timer1 compare interrupt, instead of the firing sequence being A, C then B, the firing sequence becomes B, A then C.
As the 'delay' approaches the second Timer1 compare interrupt, instead of the firing sequence being B, A then C, the firing sequence becomes C, B then A.
But the outputs still screw up during the transition of one firing sequence to another.
There must be an easier and better way of doing this ????
The code is shown below....
Code: |
//**************************** #include files **********************************
#include <16F874A.h>
#device adc=8
#FUSES NOWDT //No Watch Dog Timer
#FUSES RC //Resistor/Capacitor Osc with CLKOUT
#FUSES NOPUT //No Power Up Timer
#FUSES NOPROTECT //Code not protected from reading
#FUSES NODEBUG //No Debug mode for ICD
#FUSES NOLVP //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NOCPD //No EE protection
#FUSES NOWRT //Program memory not write protected
#FUSES BROWNOUT //Reset when brownout detected
#use delay(clock=20000000)
#use rs232(baud=38400,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=8,BRGH1OK)
//************************* Pre Compiler Definitions ***************************
#define TRIAC_GATE_1 PIN_D0 // Definition of Port D bit 0 - used to fire triac gate
#define TRIAC_GATE_2 PIN_D1 // Definition of Port D bit 1 - used to fire triac gate
#define TRIAC_GATE_3 PIN_D2 // Definition of Port D bit 2 - used to fire triac gate
#define PORTD_ADDRESS 0x08 // Address of PORT D (0x8 for PIC16F874A, 0x0F83 for PIC18F452)
#define StartMeasurement 0 //
#define EndMeasurement 1 //
//************************** Variable Declarations *****************************
unsigned int delay=0; // delay used for triac firing angle
unsigned int count=0; // counter used for triac firing angle
unsigned int TriacOutput=0; //
unsigned int16 TriacDelay=0;
unsigned int32 Period; //
unsigned int Timer1_Overflow; //
float Period_sec=0;
float Period_Hz=0; //
int1 PeriodMode=0; //
unsigned int Delay_ADC=0; //
unsigned int16 PhaseSample=0; //
unsigned int FiringSequence[6]={1,2,3,4,5,6};
unsigned int FiringSequence_Index=0;
unsigned int16 FiringPoints[5]={0};
unsigned int FiringPoints_Index=0;
float Delay_ms=0;
float PhaseSample_ms=0;
int16 PhaseSampleCount=0;
//*********************** 16 bit Timer Compare 2 Interrupt *********************
#int_ccp2
CCP2_isr()
{
FiringPoints_Index++;
if(FiringPoints_Index>4)
{
FiringPoints_Index=0;
}
CCP_2=FiringPoints[FiringPoints_Index];
switch (FiringSequence[FiringSequence_Index])
{
case 1: // Phase A Rising Edge
TriacOutput=0;
break;
case 2: // Phase C Falling Edge
TriacOutput=2;
break;
case 3: // Phase B Rising Edge
TriacOutput=1;
break;
case 4: // Phase A Falling Edge
TriacOutput=0;
break;
case 5: // Phase C Rising Edge
TriacOutput=2;
break;
case 6: // Phase B Falling Edge
TriacOutput=1;
break;
}
delay=TriacDelay;
count = 0;
set_timer2(0);
enable_interrupts(INT_TIMER2); // Start 8 bit timer by enabling interrupts
}
//**************************** Timer1 Interrupt ********************************
//**** (used to check if voltage zero crossings occur, interrupt = 26.21ms) ****
#int_TIMER1
TIMER1_isr()
{
Timer1_Overflow++; // increment when TMR1 overflows
}
//**************************** Timer2 Interrupt ********************************
//***** (used as a counter for firing triac, interupt occurs every 125us) ******
#int_TIMER2
TIMER2_isr()
{
if (delay !=0) // Wait for Triac Gate firing point
{
--delay;
}
else
{
switch (count)
{
case 0:
bit_clear (*PORTD_ADDRESS,TriacOutput);
break;
case 1:
bit_set (*PORTD_ADDRESS,TriacOutput);
break;
case 2:
bit_clear (*PORTD_ADDRESS,TriacOutput);
break;
case 3:
bit_set (*PORTD_ADDRESS,TriacOutput);
break;
case 4:
bit_clear (*PORTD_ADDRESS,TriacOutput);
break;
case 5:
bit_set (*PORTD_ADDRESS,TriacOutput);
break;
case 6:
bit_clear (*PORTD_ADDRESS,TriacOutput);
break;
case 7:
bit_set (*PORTD_ADDRESS,TriacOutput);
break;
case 8:
if(FiringSequence_Index==5)
{
FiringSequence_Index=0;
disable_interrupts(INT_CCP2);
}
else
{
enable_interrupts(INT_CCP2);
}
FiringSequence_Index++;
disable_interrupts(INT_TIMER2); // disable TMR2 interrupts (gets re-enabled on voltage zero crossings)
break;
default:
break;
}
++count;
}
}
//********************** Zero Crossing Interrupt (RB0) *************************
#int_EXT
EXT_isr()
{
switch (PeriodMode)
{
case StartMeasurement:
set_timer1(0);
Timer1_Overflow=0;
PeriodMode=EndMeasurement;
break;
case EndMeasurement:
Period=get_timer1()+((int32)Timer1_Overflow*65535);
PeriodMode=StartMeasurement;
set_timer1(0);
break;
default:
break;
}
FiringPoints_Index=0;
CCP_2=FiringPoints[FiringPoints_Index];
FiringSequence_Index=0;
switch (FiringSequence[FiringSequence_Index])
{
case 1: // Phase A Rising Edge
TriacOutput=0;
break;
case 5: // Phase C Rising Edge
TriacOutput=2;
break;
case 6: // Phase B Falling Edge
TriacOutput=1;
break;
}
delay=TriacDelay;
count = 0;
set_timer2(0);
enable_interrupts(INT_TIMER2); // Start 8 bit timer by enabling interrupts
}
//************************* Main C Program Entry point *************************
void main()
{
//**************************** System Initialisation ***************************
setup_adc_ports(ALL_ANALOG);
setup_adc(ADC_CLOCK_INTERNAL);
setup_psp(PSP_DISABLED);
setup_spi(FALSE);
setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);
setup_timer_1(T1_INTERNAL|T1_DIV_BY_8);
setup_timer_2(T2_DIV_BY_1,125,5); // At 20mhz, the timer will increment every 800ns, will overflow every 25.2us and interrupt every 125.0us.
setup_ccp1(CCP_CAPTURE_RE);
setup_ccp2(CCP_COMPARE_INT);
set_tris_b (0b00000001); // set PORTB bit 0 as external interrupt, all other bits as outputs.
set_tris_d (0b00000000); // set PORTD as outputs.
setup_comparator(NC_NC_NC_NC);
setup_vref(FALSE);
output_high(TRIAC_GATE_1); // Turn on Port B bit 1 - used to fire triac gate (triac is off when gate is high)
output_high(TRIAC_GATE_2); // Turn on Port B bit 2 - used to fire triac gate (triac is off when gate is high)
output_high(TRIAC_GATE_3); // Turn on Port B bit 3 - used to fire triac gate (triac is off when gate is high)
enable_interrupts(INT_EXT);
ext_int_edge( L_TO_H );
enable_interrupts(INT_TIMER1);
disable_interrupts(INT_TIMER2);
disable_interrupts(INT_CCP2);
enable_interrupts(GLOBAL);
//************************* main program starts here ***************************
do
{
Period_sec=(float)Period*1.6e-6;
Period_Hz=1/(Period_sec);
PhaseSample=Period/6;
FiringPoints[0]=PhaseSample;
FiringPoints[1]=PhaseSample*2;
FiringPoints[2]=PhaseSample*3;
FiringPoints[3]=PhaseSample*4;
FiringPoints[4]=PhaseSample*5;
set_adc_channel(0); // select A/D channel to measure frequency when triacs should be fully off
delay_us(10);
Delay_ADC=read_adc();
Delay_ms=(Delay_ADC*125e-6);
PhaseSample_ms=(Period_sec/6);
PhaseSampleCount=(int16)(PhaseSample_ms/125e-6);
if(Delay_ms<(PhaseSample_ms-0.001))
{
FiringSequence[0]=1;
FiringSequence[1]=2;
FiringSequence[2]=3;
FiringSequence[3]=4;
FiringSequence[4]=5;
FiringSequence[5]=6;
TriacDelay=(int16)Delay_ADC;
}
if((Delay_ms>=(PhaseSample_ms-0.001))&&(Delay_ms<((PhaseSample_ms)*2)-0.001))
{
FiringSequence[0]=6;
FiringSequence[1]=1;
FiringSequence[2]=2;
FiringSequence[3]=3;
FiringSequence[4]=4;
FiringSequence[5]=5;
TriacDelay=(int16)Delay_ADC-PhaseSampleCount;
}
if(Delay_ms>((PhaseSample_ms*2)-0.001))
{
FiringSequence[0]=5;
FiringSequence[1]=6;
FiringSequence[2]=1;
FiringSequence[3]=2;
FiringSequence[4]=3;
FiringSequence[5]=4;
TriacDelay=(int16)Delay_ADC-(PhaseSampleCount*2);
}
}
while(TRUE);
} // MAIN PROGRAM END
|
|
|
|
Mark
Joined: 07 Sep 2003 Posts: 2838 Location: Atlanta, GA
|
|
Posted: Tue Nov 15, 2005 7:03 am |
|
|
Use the CCP module.
Trigger on the rising edge to reset the timer value.
Use the CCP interrupt to fire the triac.
Keep the turn on times in a sorted array.
One by one, load them into the CCP compare register.
When the CCP int fires, turn on that triac.
This is very similar to the approach that I use for phase controlled dimmers. |
|
|
John_Lintern
Joined: 30 Sep 2004 Posts: 14
|
|
Posted: Tue Nov 15, 2005 9:15 am |
|
|
Thanks Mark,
I have written code to calculate the CCP1 interrupts for the rising and falling edges on each phase.
Using a period which gives a Timer1 measurement of 37,500 (60ms period) and a delay of 12,500 (20ms) I get the following values.
A_RisingEdge=12500;
C_FallingEdge=31250;
B_RisingEdge=25000;
A_FallingEdge=6250;
C_RisingEdge=37500;
B_FallingEdge=18750;
This is correct so thats a good start.
The only thing now is that I need to load the array for the CCP1 interrupt with these values, but in order of size !!!
How do I do this ??
I need some code which will put the above values into the array in numerical order, so I would get.....
FiringPoints[0]=6250;
FiringPoints[1]=12500;
FiringPoints[2]=18750;
FiringPoints[3]=25000;
FiringPoints[4]=31250;
FiringPoints[5]=37500;
Also, when I go to fire the triac I need to see which output needs to be fired.
In this example, I would need some code to determine that if CCP1 is set to 6250, then output A (A_FallingEdge) needs to be fired.
Or if CCP1 is set to 18750, then output B (B_FallingEdge) needs to be fired.
How can I go about doing this ??
My code so far is below....
Code: |
//**************************** #include files **********************************
#include <16F874A.h>
#device adc=8
#FUSES NOWDT //No Watch Dog Timer
#FUSES RC //Resistor/Capacitor Osc with CLKOUT
#FUSES NOPUT //No Power Up Timer
#FUSES NOPROTECT //Code not protected from reading
#FUSES NODEBUG //No Debug mode for ICD
#FUSES NOLVP //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NOCPD //No EE protection
#FUSES NOWRT //Program memory not write protected
#FUSES BROWNOUT //Reset when brownout detected
#use delay(clock=20000000)
#use rs232(baud=38400,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=8,BRGH1OK)
//************************* Pre Compiler Definitions ***************************
#define TRIAC_GATE_1 PIN_D0 // Definition of Port D bit 0 - used to fire triac gate
#define TRIAC_GATE_2 PIN_D1 // Definition of Port D bit 1 - used to fire triac gate
#define TRIAC_GATE_3 PIN_D2 // Definition of Port D bit 2 - used to fire triac gate
#define PORTD_ADDRESS 0x08 // Address of PORT D (0x8 for PIC16F874A, 0x0F83 for PIC18F452)
#define StartMeasurement 0 //
#define EndMeasurement 1 //
//************************** Variable Declarations *****************************
unsigned int delay=0; // delay used for triac firing angle
unsigned int count=0; // counter used for triac firing angle
unsigned int TriacOutput=0; //
unsigned int16 TriacDelay=0;
unsigned int32 Period; //
unsigned int Timer1_Overflow; //
int1 PeriodMode=0; //
unsigned int Delay_ADC=0; //
unsigned int16 PhaseSample=0; //
unsigned int16 A_RisingEdge=0; //
unsigned int16 C_FallingEdge=0; //
unsigned int16 B_RisingEdge=0; //
unsigned int16 A_FallingEdge=0; //
unsigned int16 C_RisingEdge=0; //
unsigned int16 B_FallingEdge=0; //
unsigned int16 DelayValue=0;
unsigned int16 HalfPeriod=0;
unsigned int FiringSequence[6]={1,2,3,4,5,6};
unsigned int FiringSequence_Index=0;
unsigned int16 FiringPoints[5]={0};
unsigned int FiringPoints_Index=0;
//*********************** 16 bit Timer Compare 2 Interrupt *********************
#int_ccp2
CCP2_isr()
{
FiringPoints_Index++;
if(FiringPoints_Index>4)
{
FiringPoints_Index=0;
}
CCP_2=FiringPoints[FiringPoints_Index];
switch (FiringSequence[FiringSequence_Index])
{
case 1: // Phase A Rising Edge
TriacOutput=0;
break;
case 2: // Phase C Falling Edge
TriacOutput=2;
break;
case 3: // Phase B Rising Edge
TriacOutput=1;
break;
case 4: // Phase A Falling Edge
TriacOutput=0;
break;
case 5: // Phase C Rising Edge
TriacOutput=2;
break;
case 6: // Phase B Falling Edge
TriacOutput=1;
break;
}
delay=TriacDelay;
count = 0;
set_timer2(0);
enable_interrupts(INT_TIMER2); // Start 8 bit timer by enabling interrupts
}
//**************************** Timer1 Interrupt ********************************
//**** (used to check if voltage zero crossings occur, interrupt = 26.21ms) ****
#int_TIMER1
TIMER1_isr()
{
Timer1_Overflow++; // increment when TMR1 overflows
}
//**************************** Timer2 Interrupt ********************************
//***** (used as a counter for firing triac, interupt occurs every 125us) ******
#int_TIMER2
TIMER2_isr()
{
if (delay !=0) // Wait for Triac Gate firing point
{
--delay;
}
else
{
switch (count)
{
case 0:
bit_clear (*PORTD_ADDRESS,TriacOutput);
break;
case 1:
bit_set (*PORTD_ADDRESS,TriacOutput);
break;
case 2:
bit_clear (*PORTD_ADDRESS,TriacOutput);
break;
case 3:
bit_set (*PORTD_ADDRESS,TriacOutput);
break;
case 4:
bit_clear (*PORTD_ADDRESS,TriacOutput);
break;
case 5:
bit_set (*PORTD_ADDRESS,TriacOutput);
break;
case 6:
bit_clear (*PORTD_ADDRESS,TriacOutput);
break;
case 7:
bit_set (*PORTD_ADDRESS,TriacOutput);
break;
case 8:
if(FiringSequence_Index==5)
{
FiringSequence_Index=0;
disable_interrupts(INT_CCP2);
}
else
{
enable_interrupts(INT_CCP2);
}
FiringSequence_Index++;
disable_interrupts(INT_TIMER2); // disable TMR2 interrupts (gets re-enabled on voltage zero crossings)
break;
default:
break;
}
++count;
}
}
//********************** Zero Crossing Interrupt (RB0) *************************
#int_EXT
EXT_isr()
{
switch (PeriodMode)
{
case StartMeasurement:
set_timer1(0);
Timer1_Overflow=0;
PeriodMode=EndMeasurement;
break;
case EndMeasurement:
Period=get_timer1()+((int32)Timer1_Overflow*65535);
PeriodMode=StartMeasurement;
set_timer1(0);
break;
default:
break;
}
enable_interrupts(INT_CCP2);
FiringPoints_Index=0;
CCP_2=FiringPoints[FiringPoints_Index];
FiringSequence_Index=0;
switch (FiringSequence[FiringSequence_Index])
{
case 1: // Phase A Rising Edge
TriacOutput=0;
break;
case 5: // Phase C Rising Edge
TriacOutput=2;
break;
case 6: // Phase B Falling Edge
TriacOutput=1;
break;
}
delay=TriacDelay;
count = 0;
set_timer2(0);
enable_interrupts(INT_TIMER2); // Start 8 bit timer by enabling interrupts
}
//************************* Main C Program Entry point *************************
void main()
{
//**************************** System Initialisation ***************************
setup_adc_ports(ALL_ANALOG);
setup_adc(ADC_CLOCK_INTERNAL);
setup_psp(PSP_DISABLED);
setup_spi(FALSE);
setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);
setup_timer_1(T1_INTERNAL|T1_DIV_BY_8);
setup_timer_2(T2_DIV_BY_1,125,5); // At 20mhz, the timer will increment every 800ns, will overflow every 25.2us and interrupt every 125.0us.
setup_ccp1(CCP_CAPTURE_RE);
setup_ccp2(CCP_COMPARE_INT);
set_tris_b (0b00000001); // set PORTB bit 0 as external interrupt, all other bits as outputs.
set_tris_d (0b00000000); // set PORTD as outputs.
setup_comparator(NC_NC_NC_NC);
setup_vref(FALSE);
output_high(TRIAC_GATE_1); // Turn on Port B bit 1 - used to fire triac gate (triac is off when gate is high)
output_high(TRIAC_GATE_2); // Turn on Port B bit 2 - used to fire triac gate (triac is off when gate is high)
output_high(TRIAC_GATE_3); // Turn on Port B bit 3 - used to fire triac gate (triac is off when gate is high)
enable_interrupts(INT_EXT);
ext_int_edge( L_TO_H );
enable_interrupts(INT_TIMER1);
disable_interrupts(INT_TIMER2);
disable_interrupts(INT_CCP2);
enable_interrupts(GLOBAL);
//************************* main program starts here ***************************
do
{
PhaseSample=Period/6;
HalfPeriod=Period/2;
A_RisingEdge=DelayValue; //
A_FallingEdge=A_RisingEdge+(HalfPeriod); //
if(A_RisingEdge>HalfPeriod)
{
A_FallingEdge=A_FallingEdge-Period;
}
B_RisingEdge=DelayValue+((PhaseSample)*2); //
B_FallingEdge=B_RisingEdge+(HalfPeriod); //
if(B_RisingEdge>HalfPeriod)
{
B_FallingEdge=B_FallingEdge-Period;
}
C_RisingEdge=DelayValue+((PhaseSample)*4); //
C_FallingEdge=C_RisingEdge+(HalfPeriod); //
if(C_RisingEdge>HalfPeriod)
{
C_FallingEdge=C_FallingEdge-Period;
}
set_adc_channel(0); // select A/D channel to measure frequency when triacs should be fully off
delay_us(10);
Delay_ADC=read_adc();
}
while(TRUE);
} // MAIN PROGRAM END
[code/]
|
|
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|