View previous topic :: View next topic |
Author |
Message |
edbfmi1
Joined: 18 Jul 2006 Posts: 103
|
Zero Crossing detection fluctuations |
Posted: Wed Mar 01, 2023 12:37 pm |
|
|
Hello all.
I am using a PIC18f56k42 processor.
CCS PCH Compile version v5.102
MPLAB Version 5.35
I am having issues with my zero crossing detection.
I have deleted basically all the code and am only running the External Interrupt on PinB0 to look for the zero crossing. When that interrupt is detected I toggle an LED.
The LED turns on/off right on the zero crossing as long as I don't enable any of the timer interrupts.
As soon as I enable the interrupts my zero crossing LED will turn on randomly from the zero crossing point up to 50-100uSec past the zero crossing.
I am not using the timers anywhere in this stripped down code. I am only enabling them.
Any help would be appreciated.
Code: |
#include <18f56k42.H>
#device ADC = 10
#include <stdio.h>
#include <time.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <Smart_E_BMLCD420.c>
#include <24L16.c>
#fuses NOEXTOSC, NOWDT, PROTECT, NOPUT, NOLVP
#DEVICE HIGH_INTS=TRUE
#use delay(internal = 32MHz)
//zero cross interrupt
#byte OPTION_REG = GETENV("SFR:INTCON0") // get OPTION_REG register address
#bit INTEDG = OPTION_REG.0
//power off interrupt set
#bit INTEDG2 = OPTION_REG.1
#byte bytestore = GETENV("SFR:PIE0")
#bit ioc = bytestore.7 //need to set to 1
//#byte intreg = GETENV("SFR:IOCBN")
//#bit iocaddress = intreg.4
//setting each interrupt to the right ports
//set int0pps to portb pin0 for zcd
//#byte zcd_reg = GETENV("SFR:INT0PPS")
//set int1pps to portb pin 4 for line voltage
#byte bytestore1 = GETENV("SFR:INT1pps")
#bit p5 = bytestore1.5
#bit p4 = bytestore1.4
#bit p3 = bytestore1.3
#bit p2 = bytestore1.2
#bit p1 = bytestore1.1
#bit p0 = bytestore1.0
//internal oscillator frequency
#byte bytestore2 = GETENV("SFR:OSCFRQ")
#bit os0 = bytestore2.0
#bit os1 = bytestore2.1
#bit os2 = bytestore2.2
#bit os3 = bytestore2.3
// adc control
#byte bytestore3 = GETENV("SFR:ADCON0")
#bit adc4 = bytestore3.4
#bit adc7 = bytestore3.7
#define gate2 PIN_B2
#define inputsignal PIN_B0
//#define triac_gate PIN_A7
//switches
#define troubleshootmode PIN_B3
#define sensor PIN_B1
//input voltage detecter
#define involt PIN_A7
//buttons
#define up PIN_A0 //tested (middle right)
#define left PIN_A2 //top left
#define ESTOP PIN_A3 //botton right
#define right PIN_A4 //middle left
#define down PIN_A5 //top right
//heartbeat
#define light PIN_E0
//debug switch
#define Running PIN_D0
#define Enable PIN_D1
//valve addon board
#define valvecheck PIN_F0
#define v1in PIN_D4
#define v2in PIN_D5
#define v3in PIN_D6
#define v3in PIN_A6 //temp for testing
#define v4in PIN_D7
#define v1out PIN_C1
#define v2out PIN_C0
#define v3out PIN_A6
#define v4out PIN_A7
//speed setting
#define speedcontrol PIN_A1
//valve timers
int16 v1time = 0;
int16 v2time = 0;
int16 v3time = 0;
int16 v4time = 0;
int v1state = 0;
int v2state = 0;
int v3state = 0;
int v4state = 0;
float v1to = 0.0; //valve timing
float v1tf = 0.0;
float v2to = 0.0;
float v2tf = 0.0;
float v3to = 0.0;
float v3tf = 0.0;
float v4to = 0.0;
float v4tf = 0.0;
clock_t cycletime(void);
//interupts
int ZC = 0; //zc counting
int16 wait = 0; //part of firing time based on speed no compensate
int16 set = 0; //firing time
int16 wait2 = 0; //used for ramp wait time
int16 set2 = 0; //used for ramp set time
//wait auxilary
int1 waitredo = 1; //used to re-calculate wait only on setting changes
//estop
int powerset = 0; //estop store
//main config variables
float speed = 0.0; //user speed input
int1 speededit = 0; //if the user can change the speed based on control run state
float ramp = 0.0; //ramp time
float TDON=0.0; //tdon time
float TDOFF=0.0; //tdoff time
int waveform = 1; //full or half wave
int HZ = 0; //50 or 60 hz
//min and max speed and areas
int16 maxvalue = 0; //max linear slice across curve
int16 minvalue = 0; //min ''
int1 maxset = 0; //has max been set yet
int1 minset = 0; //has min been set yet
int16 maxmaster = 0; //max master area under curve
int16 minmaster = 0; //min master area under curve
//timing
int16 repeat = 0; //button push and hold multi move timing
//button state
int buttonpressed = 0; //what button was pressed
int1 single = 0; //button timing checker
int buttonlast = 0; //what was the last button hit, used for making sure not to double react to same push
int selectedlast = 0; //last spot cursor was
//voltage variance
int32 masterspeed = 0; //storage for master area value
int32 masterspeedavg = 0; //used for storing averaging value
int masterspeedcount = 0; //used to count averaging value
int masterspeedcheck= 0; //verify that enough loops have passed before setting masterspeed
//compensation
int32 tweakspeed = 8000; //storage for compensation value
int error3count = 0;
int1 tweaklock=0; //locking tweaking on or off 1=off 0=on(default)
//troubleshooting mode
int1 troublemode = 0; //screen set to show extra parameters and lock cursor
//ramping
int16 rampcount = 0; //how many ramp cycles
int1 ramplockout = 0; //is ramp active or not
int16 rampfactor = 0; //ramp step size
//td
int1 tdstate = 1; //input from sensor
int1 tdfinish = 0;
int16 rscounter = 0; //counting delays
int16 rscounter2 = 0; //transfer partial used delays
int1 speedlock = 0; //
int1 tdactive = 0; //
int1 tdpartial = 0; //checking if tdoff is partially done when finished ramping
int1 tdfstart = 0; //used to track status of tdf
int1 tdostart = 0; //**tdo
//program parameters
int1 wavefireedge = 0; //what edge is next to fire
int1 addchip = 0; //adder board chip.... not currently implemented
//error tracking
int errorcycle = 0;
int1 errorlock = 0; //lock out errors
int1 errordebounce=0; //debounce for error message 11
int1 errormessagelock = 0; //locking the error message line
int1 error1 = 0; //storring which errors are active
int1 error2 = 0;
int1 error3 = 0;
int1 error4 = 0;
int1 error5 = 0;
int1 error6 = 0;
int1 error7 = 0;
int1 error8 = 0;
int1 error9 = 0;
int1 error10 = 0;
int1 error11 = 0;
int1 error12 = 0;
int1 error13 = 0;
//interlock and ext sensor
int1 runmaster = 0; //I/O run state
//other
//override normal zcd rules
int1 override = 0; //shut off commands
int superoverride = 0; //""
//voltage
int linelow = 0; //use to count low voltage loops
int linehigh =0; //use to count high voltage loops
int16 lineload = 0; //line reading
int16 steadylineload = 0; //lineload reading steady
int1 linecheck = 0; //when to check voltage
int vfilter = 0; //noise filter for voltage check
int1 vlock = 0; //lock for when voltage is low
int loops =0;
int1 voltagetest = 0; //voltage testing check
int vlevel = 0; //is the control running 110 or 220. 0=110 1=220
int1 interlock = 0; //check if control shutoff by each other
int sensing = 0; //check if control is shutoff and interlock output needs to change
int1 cycle = 0; //only check once per zcd
int1 tdflock = 0; //regulates ordering of ramp tdon and tdoff
int1 ramptdf = 0; //regulates ordering of ramp tdon and tdoff
//password stuff
int1 passlock = 0; //password state of locked or unlocked
int16 password = 0; //password
//0-5v ext speed control
int1 speedenable = 0; //not implemented but used to turn off speed movement by button when run by external signal
//int16 linevolt = 0;
//area
int1 areatrigger = 1; //
int1 areaside = 0; //
int loopingcount = 0; //
int pastgoodloop = 0; //
int goodloop = 0; //number of loops in accepted range
int pastloopingcount = 0; //past number of loops
int16 creading = 0; //current area reading
int16 preading = 0; //past area reading
int16 mastercompread = 0; //master area reading to compare to
int1 runcheck = 0; //make sure running when compensating and setting masterspeed
int1 readinglock = 0; //verify normal reading cycle
int pgstart = 0; //
int16 HZloop = 0; //used to determine hz on startup
int1 zcdtimer = 0; //used to make sure zcd occurs if not need to put control to sleep
int1 timer3side = 0; //edge of reading for area
int1 rampstartlock = 0;
int resetstart = 0;
int savepowerfault = 0; //did the power get turned off by button or low power
int1 factoryreset1 = 0; //after factory reset put cursor at first slot
int firststart = 0;
int1 testflag = 0;
int testcount = 0;
int1 tdrampstop = 0;
int32 areasum = 0;
int32 pastareasum = 0;
int16 displaysum = 0;
//******************************************************************************
#INT_EXT HIGH // external interrupt ISR
void EXT_ISR()
{
output_toggle(light);
}
//never call more than once this is for setup bits only! **********************
void bitsetup()
{
ioc = 1; //power off interrupt settings
//iocaddress = 1;
INTEDG2 = 0;
//setup port for ext1_ISr to be b4
p5 = 0;
p4 = 0;
p3 = 1;
p2 = 1;
p1 = 0;
p0 = 0;
os0 = 0;
os1 = 1;
os2 = 1;
os3 = 0;
adc4 =0;
adc7 =1;
}
//main**************************************************************************
void main()
{
lcd_init();
bitsetup(); //initialization bits for interrupts
firststart = 0;
output_drive(gate2);
output_low(gate2);
port_b_pullups(FALSE);
clear_interrupt(INT_EXT);
enable_interrupts(INT_EXT);
enable_interrupts(GLOBAL);
//enable_interrupts(RTCC_INTERNAL);
//setup_timer_0(RTCC_INTERNAL|RTCC_DIV_2|RTCC_8_BIT);
setup_timer_1(T1_INTERNAL|T1_DIV_BY_1);
setup_timer_2(T2_CLK_INTERNAL|T2_DIV_BY_128,1,16);
setup_timer_3(T3_INTERNAL|T3_DIV_BY_1);
setup_timer_5(T5_INTERNAL|T5_DIV_BY_2);
enable_interrupts(INT_TIMER1);
enable_interrupts(INT_TIMER2); //checking for voltage
enable_interrupts(INT_TIMER3);
enable_interrupts(INT_TIMER5);
//enable_interrupts(INT_EXT);
setup_adc(ADC_CLOCK_DIV_16);
//setup_adc()
setup_adc_ports(12);
setup_adc_ports(13);
setup_adc_ports(40);
set_adc_channel(13);
while(TRUE)
{
}
}
|
|
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Wed Mar 01, 2023 12:58 pm |
|
|
Quote: |
void main()
{
enable_interrupts(GLOBAL);
setup_timer_1(T1_INTERNAL|T1_DIV_BY_1);
setup_timer_2(T2_CLK_INTERNAL|T2_DIV_BY_128,1,16);
setup_timer_3(T3_INTERNAL|T3_DIV_BY_1);
setup_timer_5(T5_INTERNAL|T5_DIV_BY_2);
enable_interrupts(INT_TIMER1);
enable_interrupts(INT_TIMER2); //checking for voltage
enable_interrupts(INT_TIMER3);
enable_interrupts(INT_TIMER5);
|
Where are the interrupt handlers for all these interrupts that you've enabled ? |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9221 Location: Greensville,Ontario
|
|
Posted: Wed Mar 01, 2023 1:03 pm |
|
|
If you enable an interrupt , you NEED to have the 'handler' (ISR) for it,even if it it does nothing.Without that the PIC can go 'far, far away' and possibly lockup.
The delay you see in the response may be due to the CCS Interrupt 'task manager' that has to decode WHICH interrupt has occurred.
In what you posted there are 5 (?) possible interrupts, so the PIC has to figure out which has occurred and it's priority (if set...).
Others will know and explain better than me. |
|
|
edbfmi1
Joined: 18 Jul 2006 Posts: 103
|
|
Posted: Wed Mar 01, 2023 1:08 pm |
|
|
My bad.
I accidentally deleted them when I scrubbed all the code.
The zero crossing fluctuation was happening when the entire program was running so I deleted everything else except the ZCD interrupt to check it out.
Forgot to leave the handlers in.
I will add them back in and evaluate the zero crossing again. |
|
|
edbfmi1
Joined: 18 Jul 2006 Posts: 103
|
|
Posted: Wed Mar 01, 2023 1:26 pm |
|
|
Okay that works better in the test code (5ish us fluctuation), but when I run my full program I still get about a 50 us fluctuation.
We are running the processor at 16mhz. Am I expecting too much? Is this slight fluctuation to be expected?
Thanks for all your help. |
|
|
waffles
Joined: 21 Dec 2021 Posts: 8
|
|
Posted: Wed Mar 01, 2023 2:04 pm |
|
|
It looks like your device has a vectored interrupt controller, so you might be able to squeeze a little bit more control over latency by adding this line -
Code: |
#device vector_ints
|
|
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9221 Location: Greensville,Ontario
|
|
Posted: Wed Mar 01, 2023 2:34 pm |
|
|
Depending on your code cutting ability...
Using your own Interrupt 'manager' or whatever they call the code that decides HOW to respond to interrupts.
The CCS supplied one will save a LOT of registers/ information as well as 'search' for which interrupt has occurred.
Also be sure NOT to use 'math' or 'prints' inside any ISR !
Perhaps fast_IO() might make a slight speed improvement ? |
|
|
waffles
Joined: 21 Dec 2021 Posts: 8
|
|
Posted: Wed Mar 01, 2023 3:24 pm |
|
|
That's one of the benefits when using chips that feature the vectored interrupt controller - there's no need to scan for the interrupt source, so the only block of (hopefully fixed) latency is saving registers etc...
I've been using it recently with a modern chip to do some composite video overlay shenanigans (in combination with abusing the heck out of SPI, CLCs and DMA). |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9221 Location: Greensville,Ontario
|
|
Posted: Wed Mar 01, 2023 4:06 pm |
|
|
In theory, upping to 64MHz would 1/2 the latency..... |
|
|
edbfmi1
Joined: 18 Jul 2006 Posts: 103
|
|
Posted: Wed Mar 01, 2023 4:17 pm |
|
|
We are running the chip at 32MHz now and our flutter is within acceptable limits. We still get a sporadic Zero Crossing that is 50uS later then the actual Zero Crossing. We are going to write a little code to ignore these "blips" and see if that works so we can move on with the program. Just irks me to not know what is causing this annoying blip.
Thanks again for all the input! |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9221 Location: Greensville,Ontario
|
|
Posted: Wed Mar 01, 2023 6:30 pm |
|
|
What is the hardware used to detect ZC and how do you know it's 50us delay ?
Any chance it's some form of EMI or other 'noise' ? Say a coffee pot or soldering iron coming on ? Neighbor's big air compressor coming on ?? Is it totally 'sporadic' or random or some 'pattern' to the blips ?
I know it can be very frustrating to find and really the software 'mask' really isn't a great solution...but if it works, ok......
Jay |
|
|
edbfmi1
Joined: 18 Jul 2006 Posts: 103
|
|
Posted: Thu Mar 02, 2023 11:51 am |
|
|
I agree temtronic, a software mask is never desirabe. I would love to identify this gremlin and remove it instead of working around it.
I can't get the image of my ZCD circuit to download, any help on how to do this would also be appriciated. I tried using the "Img" button above like I use the "Code" button to insert code but I can't insert the image. I have tried .jpg, .png and .pdf files but they will not load.
Basically it is a simple OpAmp (LM358) tied to the mains via 2.4M Ohm resistors with a couple back to back diodes across the Inverting and Non-Inverting inputs. The output is tied to 5VDC with a 10K pull up resistor and connected to PortRB0. I have used this circuit many times in the past for Zero Crossing Detection without issues.
To check the ZCD crossing I toggle an LED as soon as I get into the External Interrupt that is triggered by the PortRB0 ZCD and I watch that signal with my O-Scope.
As long as I do not enable any timers I do not get this random delay of approximately 50uSec before turning on the LED at the zero crossing.
Once any of the timers are enabled I get the random delay.
}
Code: | #INT_EXT HIGH // external interrupt ISR
void EXT_ISR()
{
output_toggle(light);
}
|
|
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19495
|
|
Posted: Thu Mar 02, 2023 12:42 pm |
|
|
Use one of the image hosting sites, and just post a link to it.
Genuine zero crossing detection in all circumstances is actually very
hard. The best way is normally to use a PLL locked to the mains and
detect the crossing from this. This avoids the noise problems that
apply to direct detection off the mains. |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9221 Location: Greensville,Ontario
|
|
Posted: Thu Mar 02, 2023 1:35 pm |
|
|
Also important to have isolation between 'mains' and PIC.
I used to use opto ZC phototransistors. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19495
|
|
Posted: Fri Mar 03, 2023 10:22 am |
|
|
If you look at IC's doing good zero cross detection, they have usually
something like a fourth order low pass or band pass filter before the
actual detection stage, to give a nice smooth sine wave to work with.
Not done without 'good reason'...... |
|
|
|