[PIC] Power factor measurement using PIC18f4520

Status
Not open for further replies.
There are two considerations:

1. Timer0 is configured in 16-bit mode so it's numeric range is 0 to 65535 (decimal). To get most accurate results you want it to count to the biggest number possible between voltage zero crossings but not risk it overflowing (back to zero) if the AC frequency is slightly too low. The prescaler bits in T0CON decide how much the clock is divided before reaching the counter, for example if set to 1:256 it means the count increases once in every 256 clock pulses. The optimum division is the one that makes the count near to but not exceeding 65535 during the time between zero crossings. I'm going to suggest you use 1:4 so the count is 5,000,000 times per second so if set to zero at voltage zero crossing, it would reach 50,000 in the 10mS between zero crossing points (at 50Hz AC, it would be 42,666 with 60Hz AC). So with a 20MHz clock, the value in T0CON should be 0x81.

2. the actual phase angle is proportional to, but not equal to the timer count between voltage and current zero crossings. To convert the value to an angle you need to divide the timer value by a number that converts the timer count into an angle between zero and 360 degrees. You know that zero count must be 0 degrees so there is no constant offset, you also know that the time between voltage zero crossings (10ms at 50Hz) gives a count of 50,000 and that equates to 180 degrees (remember there are two voltage zero crossings per cycle) so the factor to divide by is 50,000/180 = 277.7

I hope I got the calculations right, it's almost half a century since I left school and I was no good at math even then :sad:

You might need to reverse the wires to the CT secondary if you get results working backwards, the voltage ZCD gives a pulse every zero crossing but the curent ZCD produces a square wave at AC frequency, if the wrong edge of the square wave is used the results will be 180 degrees out but swapping the wires will fix that.

I've changed the program to use interrupts, you have to use PORTB.0 and PORTB.1 as the inputs as the pins on PORTC can't be used as interrupt inputs. Again, no promises it will work as I have no hardware (or time!) to test it.
Code:
Define CLOCK_FREQUENCY = 20

Dim t0count As Word
Dim phase_angle As Word
Dim scaling_factor As Word

scaling_factor = 277  'conversion factor from count to phase

'initialize the pic registers
Gosub setup_pic

'initialize the display
Lcdinit
INTCON.GIE = 1  'enable interrupts

'main program loop
loop:
    phase_angle = t0count / scaling_factor
    Lcdout "Phase= ", #phase_angle
    WaitMs 1000
Goto loop

End                                               

setup_pic:

TRISB = 0x03  'TRISB has bits 0 And 1 As inputs
T0CON = 0x81  'T0CON enabled, 16 Bit, internal clock, prescale 1:4
INTCON = 0x50  'only INT0 and peripheral interrupts enabled
INTCON2 = 0x80  'PORTB pull-ups disabled
INTCON3 = 0x04  'INT1 enabled


Return                                            

On Low Interrupt
    Save System
    If INTCON.1 = 1 Then
    t0count = 0
    INTCON.1 = 0  'reset the interrupt flag
    Endif
    Goto end_of_isr
        
    If INTCON3.0 = 1 Then
    T0CON.7 = 0  'stop the counter
    t0count = TMR0H * 256
    t0count = t0count + TMR0L
    T0CON.7 = 1  'start the Count again
    INTCON3.0 = 0  'reset the interrupt flag
    Endif
    
end_of_isr:
Resume
note that if interrupts are used, you can add more instructions in the "loop:" code without losing accuracy.

Brian.
 

Hi and thank you Brian!!

I did the same code, configured the LCD data pins on PORTC, and placed V ZC on PORTB.0 and I ZC on PORTB.1

There was a single results:
Phase = 0

I switched also, the ZC of V and I pins from PORTB.0 to PORTB.1 and vice-versa, also, the results were the same: Phase = 0.

I power the PIC, from the same source of the LOAD: 220VAC transformed to 12VAC, then bridge rectified, then resistors voltage divided to 5V with a 0.33uF as a filtering capacitor (which isn't enough to eliminate all ripples and give a constant DC voltage).

Also, the PIC circuit, with LCD, are circuited on breadboards (for test), the ZCD circuits are soldered.

Does this has to do with a hardware problem? The 60W fan runs great, no problems with it.

Thank you!
 

Hello,

I was thinking that maybe a 1:4 is too small for a 20MHz crystal oscillator, and forces the timer to go back to zero (what is obvious on LCD), so I exceeded it to the maximum of 1:256 by setting T0CON = 0X87, and I also changed the scaling_factor value accordingly to 433.

At first instance, the LCD results were fixed, whether ZCD circuits were ON or OFF, at a "garbage" value of a mixture of digits and signs (like ^48568.08), then results were constant with Phase = 0

When I changed the prescaler option, to 1:128 and decreasing, results were also the same of 0.

I checked if there are any hardware problem, like conductor fail between different terminals, but hardware is completely O.K


Thanks

- - - Updated - - -

Hello,

I was thinking that maybe a 1:4 is too small for a 20MHz crystal oscillator, and forces the timer to go back to zero (what is obvious on LCD), so I exceeded it to the maximum of 1:256 by setting T0CON = 0X87, and I also changed the scaling_factor value accordingly to 433.

At first instance, the LCD results were fixed, whether ZCD circuits were ON or OFF, at a "garbage" value of a mixture of digits and signs (like ^48568.08), then results were constant with Phase = 0

When I changed the prescaler option, to 1:128 and decreasing, results were also the same of 0.

I checked if there are any hardware problem, like conductor fail between different terminals, but hardware is completely O.K


Thanks
 

I am away until late today (on a walk) but tell me if you have an oscilloscope you can use to monitor the signals. I am thinking the best way to debug this is to temporarily make one of the other PIC pins a 'debugging' output. If the voltage ZC makes the pin go high and the current ZC makes it low again, it should produce a pulse with width proportional to the phase difference. If it does, it confirms the phase is being detected and the problem is in software, if there is no pulse it is either a hardware problem or the software isn't reading the input pins properly. The pulse cound be very short so an oscilloscope would be best to see it.

Brian.
 

Thank you Brian!

I already did this test:

I put ZCD circuit of voltage on an oscilloscope(alone) I saw pulses of almost 5V whenever a zero is crossed.

I put ZCD circuit of current alone on oscilloscope: I saw a square wave signal with a peak of almost 5V

I put both ZCD circuits together with a small inductive fan, I saw both square wave signal with the ZCDV of repeated pulses together, there was a displacement between these two signals.

Hardware is working good.

In the interrupt routine, I think the software for some reason is stuck at the statement where t0count = 0, or the counter is reversing backward to zero, because of an overflow.

Please note that the fan is operated at 190VAC not 220VAC with frequency of 50Hz.

Thank you
 

Good, the displacement between the waveforms is what you need to quantify. The timer should be counting all the time but reset to zero when the voltage ZC is detected. The count should stop when the current ZC is detected so the number in the counter 'freezes' and can be read, the bigger the displacement between the waveforms, the larger the count will be so it can be converted mathematically to the phase angle.

It looks like either the counter isn't resetting or isn't stopping for some reason so the interrupts are not working as they should. For this we need to make a temporary modification to the program so we can se what the software is doing. I'm going to use pins PORTB.2, PORTB.3 and PORTB.4, change these if you are already using them for something else, they will only be used as outputs.
Code:
Define CLOCK_FREQUENCY = 20

Dim t0count As Word
Dim phase_angle As Word
Dim scaling_factor As Word

scaling_factor = 277  'conversion factor from count to phase

'initialize the pic registers
Gosub setup_pic

'initialize the display
Lcdinit
INTCON.GIE = 1  'enable interrupts

'main program loop
loop:
	If t0count.1 Then
		PORTB.4 = 1
	Else
		PORTB.4 = 0
	Endif
	phase_angle = t0count / scaling_factor
	Lcdout "Phase= ", #phase_angle
	WaitMs 1000
Goto loop

End                                               

setup_pic:

TRISB = 0x03  'TRISB has bits 0 And 1 As inputs
T0CON = 0x81  'T0CON enabled, 16 Bit, internal clock, prescale 1: 4
INTCON = 0x50  'only INT0 and peripheral interrupts enabled
INTCON2 = 0x80  'PORTB pull-ups disabled
INTCON3 = 0x04  'INT1 enabled


Return                                            

On Low Interrupt
	Save System
	If INTCON.1 = 1 Then
	PORTB.2 = 1
	t0count = 0
	INTCON.1 = 0  'reset the interrupt flag
	PORTB.2 = 0
	Endif
	Goto end_of_isr
		
	If INTCON3.0 = 1 Then
	PORTB.3 = 1
	T0CON.7 = 0  'stop the counter
	t0count = TMR0H * 256
	t0count = t0count + TMR0L
	T0CON.7 = 1  'start the Count again
	INTCON3.0 = 0  'reset the interrupt flag
	PORTB.3 = 0
	Endif
	
end_of_isr:
Resume

This is what it should do:
1. PORTB.2 should go high then low if the voltage zero crossing interrupt was triggered
2. PORTB.3 should go high then low is the current zero crossing interrupt was triggered
3. PORTB.4 will probably toggle randomly. It is a copy of bit 1 of the counter but the LCD delay will stop it showing the counter in 'real time' and the copy it takes will be at unpredicatable times. If it changes at all it means the counter is running.

Note that the pulses in 1 and 2 may be very short but they should be every 20mS.

Let me know if you can see them, it will reveal if the interrupts and counter are working properly.

Brian.
 

Hello Brian and thank you!!

I did your test.

Same results on LCD: "Phase= 0"
On PORTB.2,3,4 I placed LEDs with specific resistor series connected: RED LED, with series resistor of 220Ohms.

The LED on PORTB.4 was NEVER ON

The LED on PORTB.3 was NEVER ON

The LED on PORTB.2 was NEVER ON

I wasn't able to see them.

But this has two reasons, either the duration between ON and OFF is too short, I cannot see it with bare eye else the interrupts/counter are not working properly.

Thanks
 

The LED flashes would only last for the length of a few instructions so at 20MHz they would only be a few nS, far too fast to see, that's why I suggested an oscilloscope.
I will see if I can find a better way to check activity but it will have to wait until tomorrow.

Brian.
 

Hello Brian,

Why in the interrupt code you've wrote:
"On low interrupt" , and actually the ZCD circuits produce high logic when zero is detected? The on low interrupt tells the PIC to detect if any interrupt is there, detect it when decreasing from 5V to 0V (falling edge), whereas on oscilloscope you see rising edge when zero is detected.

How does it sound like to decrease the clock signal from 20MHz to a 4MHz in order to reduce counts per cycle?


Thanks
 

If you decrease the speed of the counter it reduces accuracy, also although in this case it would not be critical, it slows down the response to the interrupts. I would leave it at 20MHz.

"on low interrupt" has nothing to do with the logic level edges, that is configured by bits 5 and 6 in the INTCON2 register. It refers to the two interrupt priorities in that type of PIC (and most PIC18 devices). The other type is "high interrupt". A both interrupts can suspend the normal "loop:" code and make the ISR run instead but the "high" priority can also suspend a "low" interrupt service routine if one is running. In your program it isn't necessary to use both priorities and it might make the result less accurate.

I explain using an example:
1. You are watching a DVD movie.
2. Telephone rings --> your press 'Pause' --> answer the call. This is the low priority interrupt
3. Fire alarm goes off --> You say 'I'll call you back' and hang up --> put the fire out! This is the high priority interrupt.
4. With the fire extinguished and alarm cancelled --> you call back and finish the conversation.
5. You hang up and resume playing the DVD.

In your program, the idea is to start and stop the counter with the interrupts, it would be more complicated to use low priority for one interrupt and high priority for the other, there would be a risk of the start/stop getting out of sequence.

Unfortunately, Oshonsoft does not have a good debugging system so it is almost impossible to simulate the interrupts and check the program reacts to them as intended. I will try writing it in 'C' so I can simulate it at real speed and confirm the values in the registers are correct then copy them back in to the BASIC program for you.

Brian.
 

Hello Brian,

Thanks a lot!

That would be too kind from you.

I appreciate that.

Meanwhile converting from C to BASIC, how about adding an assembly code for interrupt routine, using ASM statement and see what happens.


Thank you
 

Hello,

@betwixt

I switched on the same circuit today, of same previous code (including LED flashing), without any single change.
Yesterday it was showing under all circumstances a zero value.
Now, the "Phase= 222" is stuck at this value, and keeps showing 222 even if load is disconnected.

Even if VZCD is ON/OFF also IZCD, the phase is showing a value equal to 222.

Thank you
 

The '222' is probably just a random number from the memory addresses holding the t0count variable. It doesn't go back to zero unless a voltage ZC is detected so it is a clue that the interrupts are not enabled properly.

I had an unexpected visitor today so I haven't had time to look at the software again. I'll try again later.

Brian.
 

Hello,

Ok, take care and take your time.

In INTCON = 0X51, this refers to setting bits from 7 to 0 as 01010000 in binary.

The first 0 corresponds to GIE bit, isn't this setting it as off? And in the beginning of the program you've wrote INTCON.GIE = 1.

Isn't this enabling, later then disabling Global Interrupt Enable?



Thank you
 

I put the instructions to configure the registers in a subroutine called "setup-pic", it gets called to put sensible values in the registers then returns (the 'Return ' instruction) back to where it was called from. The GIE bit is set later as it should be. Microchip recommend you do not enable the GIE in the same instruction as the individual interrupt enable bits.

I have some working code in 'C' but just got called away for a few hours so it isn't finished yet. I should have the results for you tomorrow.

Brian.
 

Thank you!
 

Hello,

@betwixt

There is something not right!

I've tried the following code:
Code:
ASM:        org 0x800
Define LCD_LINES = 4
Define LCD_CHARS = 16
Define LCD_BITS = 8  'allowed values are 4 and 8 - the number of data interface lines
Define LCD_DREG = PORTC
Define LCD_DBIT = 0  '0 or 4 for 4-bit interface, ignored for 8-bit interface
Define LCD_RSREG = PORTD
Define LCD_RSBIT = 0
Define LCD_EREG = PORTD
Define LCD_EBIT = 2
Define LCD_RWREG = PORTD  'set to 0 if not used, 0 is default
Define LCD_RWBIT = 1  'set to 0 if not used, 0 is default
Define LCD_COMMANDUS = 2000  'delay after LCDCMDOUT, default value is 5000
Define LCD_DATAUS = 100  'delay after LCDOUT, default value is 100
Define LCD_INITMS = 100  'delay used by LCDINIT, default value is 100
'the last three Define directives set the values suitable for simulation; they should be omitted for a real device
Lcdinit 0  'initialize LCD module; cursor is blinking
TRISB = 0x03
T0CON = 0x81

Dim timer As Single
Dim timer1 As Single


main:

TMR0L = 0
TMR0H = 0
timer = 0
loop:

If PORTB.0 Then
T0CON.7 = 1
Lcdout "VZCD ON"


If PORTB.1 Then
T0CON.7 = 0
Lcdcmdout LcdLine2Home
Lcdout "IZCD ON"
Else
Goto loop

Endif
Else
Goto loop
Endif


End

It must that once a zero crossing is detected, a statement of "VZCD ON" must appear on the LCD, but for some reason it doesn't, by taking into consideration that the MCU is not busy doing any thing else.

From another side, when placing a DC voltage metering on PORTB.0 (location of VZCD output), when the VZCD is OFF it shows almost 5V, when VZCD is ON it shows almost zero, meaning that the zero detection of the VZCD is done at falling edge detection, where the voltage at its corresponding pin is equal to 0.

Also from another side, referring to the above logic, the VZCD ON statement must appear if PORTB.0 is equal to 1 (HIGH), so when saying that output of VZCD is 5V is when it is turned off, I kept the VZCD turned off (so the RB.0 is HIGH) even though the statement "VZCD ON" did not appear on LCD.

The zero crossings is not detected by the VZCD, even though on oscilloscope it shows a repeated 5V dirac function (when zero is crossed)

Why is this corruption?
 

Sorry - I got dragged away again.

I have it working in 'C' with the interrupt method but the reason it isn't working with either type of code is because on that type of PIC, PORTB defaults to being an analog input after power-up or reset. Try adding these lines after the LCDInit instruction:

INTCON = 0x10
INTCON2 = 0xE0
INTCON3 = 0x08
ADCON1 = 0x0F

and see if it fixes it.

Brian.
 
Hello,

Thank you Brian!!

I did copy these four lines, and pasted them under LCDINIT statement, due to processor manufacturing...

The results were almost the same:

Sometimes the LCD won't show any output.
Sometimes the LCD shows only Pha out of Phase= statement
Sometimes the LCD shows Phase= 0

Thank you
 


Hello,

Is changing from PIC18F4520, to another 18F series would fix this problem?
If yes, what type?

Thanks
 

Status
Not open for further replies.
Cookies are required to use this site. You must accept them to continue using the site. Learn more…