[PIC] Power factor measurement using PIC18f4520

Status
Not open for further replies.

Very convincing!!

How logical and straightforward!!

Ok, so no escape from using interrupt, especially for such task.

The problem is that I`m using the BASIC compiler [I haven't written any assembly code since the day I was introduced to this software]; how ever, there is an ASM function that can introduce an assembly code within the compiler.

I will see what I can do...

Thanks for such clearance!!
 

Hello,

The ZCD for voltage/current are well soldered as described in earlier posts, with safety clamping diodes branched.

Imaging the results that I've come about about interrupt here is what I've found:

First, to understand the interrupt realm and process, and for what it is used, one can directly understand it by referring to betwixt as well as schmitt trigger classical examples: an interrupt forces the micro-controller to spend most importance upon a critical internal/external task that cannot be postponed or ignored.

Now, in order to use interrupt routines we must acknowledge the PIC first, that we want to use interrupt, specify what pins of INT pins are going to be used for interrupt, and last, tell how the interrupt would take place (wether on the rising edge, that means when the signal is rising from 0 to +5V, or vice-versa when decreasing from +5V to 0, called falling edge, by taking into consideration that the default way is the rising edge).

Technically speaking, inside PIC18F4520, there is the INTCON register:

The bit #7 of the above mentioned register called the GIE [Global Interrupt Enable], when set to 1, tells the PIC that we are going to use interrupt.

Bit #4 of INTCON called the INTE [Interrupt Enable], that once set to 1, tells the PIC that we will use RB0 as interrupt.


To tell the PIC whether to detect the external interrupt on rising/falling edge, we will be concerned in another register called OPTION register:

Bit #6 of OPTION register called INTEDG when set to 1, will order the PIC to detect any interrupt on increasing (rising) edge, which is the default state so no need to get into this register and set it.

After making all these introduction about how to introduce the PIC to interrupt usage, we must know about INTF [Interrupt Flag] that is set to 1 whenever an interrupt has occurred. this INTF is bit #1 of INTCON register, and the note here, that whenever an interrupt has occured, and INTF = 1 and the ISR is done, this bit should be manually set to 0.

The explanations about interrupt are wide, and vast, especially when coding in assembly, we need to start the main function, and place an address reserved for ISR, and so on, but using PIC18 IDE Simulator, here is what's present about interrupt (this means that luckily, there is direct-approach function for using interrupt, that simplify and does all preceeding explanations automatically):

The following is from BASIC Compiler Reference Manual:

Interrupt routines (high and low priority) should be placed as all other subroutines after the END statement. They should begin with ON LOW INTERRUPT or ON HIGH INTERRUPT statement and end with RESUME statement. If arithmetic operations, arrays or any other complex statements are used in interrupt routine, then SAVE SYSTEM statement should be placed right after ON LOW/HIGH INTERRUPT statement to save the content of registers used by system. However, it is always a good programming practice to keep interrupt routines as small as possible. ENABLE LOW, ENABLE HIGH, DISABLE LOW and DISABLE HIGH statements can be used in main program to control GIEH and GIEL bits in INTCON register. RESUME statement will set the appropriate GIEH or GIEL bit and enable new interrupts

In the above, I've understood where to place the ISR, how to set for rising/falling edge, and how to RETFIE from interrupt.

Focusing on this project, I`m interested in making RB0 and RB1 as interrupt pins, that detect on rising edge.
A timer is launched when RB0 detects an interrupt, and stops when RB1 detects an interrupt. I must return the value of the timer from ISR, and send it to display.

I`m finding it hard to start, is there any codes in BASIC that serves above state?

Much thanks.
 

Let me elaborate a bit more detail:

1. Microprocessor receives an interrupt; it issues an acknowledgement. Without this acknowledgement, the interrupt is said to be ignored. To receive an interrupt, interrupts must be enabled.

2. Microprocessor queries the source of the interrupt and the source must identify (this is not applicable in the current case) but will be important to know because in many cases interrupts are multiplexed and the microprocessor has only one pin reserved for interrupts. Interrupt controllers are basically doing this job of multiplexing and assigning the priorities of the different sources. In such cases, the controller tells the microprocessor the original source that caused the interrupt.

3. Microprocessor does some homework and housekeeping: it disables interrupt for the time being and checks whether any other interrupt is pending to be serviced. It may also see whether any other interrupts are currently being processed. Then, depending on lots of considerations, it decides to allow the interrupt. It simply branches to a subroutine (ISR) corresponding to the source of interrupt. As usual, it will save the registrars and the program counter on the stack so that after the interrupt service routine is completed, it knows where to go...

4. Once it enters the interrupt service routine, it is usual to enable interrupts again (all this while, the microprocessor was unable to accept fresh interrupts) unless you are doing a critical job and do not want to be disturbed. This service routine has usual codes and returns back with a special code (so that the compiler knows that you are returning from an interrupt). The important thing is that it is possible to have interrupt within an ISR and that may be nested quite deep. Unless, of course, you do not enable interrupts when you enter the ISR, which is not a good practice because you may miss some important interrupts. Usually you can block interrupts with lower priorities than the current one being serviced.

5. There are software interrupts that are called from subroutines and they act like subroutines and I do not know much about them. They are very useful when you want to run multiple tasks or threads on the same CPU (not applicable for the current case).

6. For all practical purposes, the interrupt on the PIC pins are just calls to some subroutines. Do not put too many instructions in the ISR. 1-5 statements are fine without calls to user routines.
 

Hi, thanks.

Could you practice your 'elaboration' into a BASIC compiler code that serves what's described above?
I don't know how to start.
 

I could do with Schmitt trigger's "person" here, it sounds like someone useful to have around. :lol:

Khaled, your understanding is good and accurate. Oshonsoft automatically save the critical registers when an interrupt occurs and put them back to their original values with the RESUME statement. There is a danger with interrupts that registers or variables get changed by the instructions in the ISR so when it finishes and the main program continues, they may contain unexpected values. Remember that the main program is suspended by the interrupt so it has no knowledge that something might be altering things 'behind it's back'.

PIC18F devices have two 'levels' of interrupt called low priority and high priority. Essentially they are the same and low priority is compatible with most other PIC families. The difference between them is that high priority interrupts can suspend low priority ones, just like low priority can suspend the main program flow. For your application you do not need to use two priority levels, one is sufficient. In the 18F4520, low priority interrupts make the program jump to address 0018h and high priority interrupts make it jump to 0008h. These are called the interrupt vectors, the interrupt code itself doesn't have to be at those addresses, they are usually used to hold jump instructions to the real ISRs where there is more space to hold larger code.

The golden rule with interrupts is to get out of them as fast as possible. Each 'level' of interrupts switches off it's corresponding GIE bit to prevent other interrupts occuring and the RESUME statement turns it back on again. While the GIE bit is turned off, other interrupts may be requested (their interrupt flags set) but no action will be taken. If after RESUME, there are any set flags, they will immediately trigger another interrupt. It follows that if you spend too much time in an ISR, there is a good chance you will miss repeated interrupts.

The other thing to observe is where the interrupt trigger came from, you already found the RB0 interrupt input but there are many more. If you look in the PIxx registers you will see you can select which ones are active and which ones are assigned to each priority. All you have to do is work out the bit pattern to select the interrupts you need and set the register to that value. When the ISR is entered, the first thing you normally do is check to see which flags are set so you know where the request came from.

I'll see if I can find any example code for you. As I stated earlier, I use 'C' or assembly language for almost all my programming and I don't think I've ever written a 'serious' program in BASIC so I have no examples of my own to share.

Brian.
 

Thank you Brian!

I get your point. Interrupt coding is a difficult task to be done, sure accurate, but very difficult to exercice especially for the first time.

I'm very afraid of crossing deadline for the project, a massive failure would take place.

Meanwhile studying and searching for interrupt tutorials, we will make a small test:

How about to make a "transient" test, just to testify results as follows:

1. I will not rush the PIC into any task
2. The first and last task is about measuring power factor for the 60W fan
3. Make a normal subroutine, using ADC from PORTA.2 and PORTA.3
4. Start a timer when HIGH logic is there on PORTA.2
5. Stop the same timer when HIGH logic is there on PORTA.3
6. Make the conversion for the timer into a phase angle as: 1/50 corresponds to 360 degrees then timer corresponds to C degree
7. Make cos(angle of timer)
8. Sends to display

The program directly begins with ADc tests and conversion only.

How does that sounds like? How to set a timer in BASIC

Thanks
 

You can do it that way but the zero crossing signals are already at 5V (or very close) so you don't need to use the ADC unless you are measuring the current as well. If a current measurement is needed rather than just knowing the phase difference, you need to adopt a different approach because the current monitor is only checking the polarity at the moment (zero crossing is when it reverses).

At the moment, the waveform from the LM358 will be a square wave, with the same timing as the sine wave from the CT transformer but it's amplitude will be constant. There isn't much point in measuring the voltage if it's constant! To measure the current you need to convert the AC voltage in the CT secondary to a DC voltage the ADC can measure. It is possible to measure it as AC by offsetting the CT voltage but it's more complicated electronically and in software. Converting AC to DC then measuring it is far easier. It should be possible to measure voltage and the zero crossing by using both op-amps in the LM358 so it doesn't need too many extra parts

Please confirm first that you need to measure the current as well as the phase.

Brian.
 


Thanks Brian,

Sorry for bad statement, I only need to know the phase angle, no current measurement is required.

Just the code for setting and stopping the timer according when signals from ZCD are as you mentioned.

Thanks
 

That makes it much easier because you already have the logic level signals to use. If you wanted to measure the current it would need a linear amplifier and rectifier as well.
All you need to do is use one signal (Voltage ZC) to start the timer and the other (current ZC) to stop it.
Do that, but in step 3 do not use the ADC result, just read the logic state of the pin instead.

See if you can get it to work, it will be easy to convert it to use interrupts later.

Brian.
 

Thanks Brian!

First of all, I've checked the ZCD circuits using an oscilloscope: they worked great, with accurate graph as in datasheets, or samples about H11AA1 also LM358.

For coding, I will read time between two zero crossings, using a normal if-else subroutine, but can you advice me how to make a timer using PIC18 IDE? I cannot establish a timer, and in the help manual there isn't any topic concerned about timers.

I must make a "manual" one, any ideas?
 



Brian,

The input of ZCD of V is on PORTC.0 and other one is on PORTC.1

The following is the first step test code:
Code:
loop:
If PORTC.0 = 1 Then
	ASM:        BCF T0CON,T08BIT
	ASM:        BCF T0CON,T0CS
	ASM:        BSF T0CON,TMR0ON
	
	
	If PORTC.1 = 1 Then
		ASM:        BCF T0CON,TMR0ON
		ASM:        RETURN
		Else
			Goto loop
		Endif
Endif

Lcdout "Htime= ", #TMR0H
Lcdcmdout LcdLine2Home
Lcdout "Ltime= ", #TMR0L
WaitMs 1000
End

On the LCD I keep seeing, the value 255 (corresponding to FF?), how to determine the value in seconds of the timer?
 

The timer is part of the silicon, you have to do some configuring so it counts at the speed you want then it runs by itself. What you need to do is reset it to zero when the voltage ZC is detected then read it when the current ZC is detected. You can't use the value in the timer directly because it will be a binary number and probably doesn't translate to a displayable character, you have to do some math on it first.

For example: if the AC is 50HZ, each cycle takes 20mS (1/50 seconds). If you want to measure to an accuracy of 1 degree and have a maximum phase angle of 359 degrees, you would have to make the timer increment from 0 to 359 in 20mS so it would have to run at 18,000 counts per second (18KHz). If you wanted a 0.1 degree accuracy you would count at 10 times the frequency.

I've been busy in meetings all day and it's late at night here so I'm too tired write any sensible code right now but I have copied your 'first step code' in to the compiler and I'll look at it in the morning. I'm in danger of falling asleep at the keyboard so if I continue you will probably get pages of zzzzzzzzzzzzzzzzzzzzzzzzzzz............:smile:

Brian.
 

Good morning/night Brian!

So you really use a normal keyboad I don't know why I thought you'd programmed something to take control directly without pressing a pushbutton.

Take care, and take your time also, I will post whatever I'm up to.


Much thanks
 

I'm still working on my "thoughts to text" converter but it's probably just as well it doesn't work!

I've re-written the code you produced. I have no way to check if it works because I do not have any 18F4250 processors here but you should be able to see the methods I used. Depending on the clock speed you use, you will have to change the "Define CLOCK_FREQUENCY = 4" to the value of your crystal. You will almost certainly also have to change the prescaler values for TMR0 and the "scaling_factor" number. These depend on the crystal so I can't give you exact numbers to use.

Note that it relies on the voltage ZC pulse to reset the timer to zero, if that signal is missing but PORTC.1 goes high, it will show random numbers because the counter will free run.
Code:
Define CLOCK_FREQUENCY = 4

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

scaling_factor = 1  'conversion factor from count to phase

'initialize the pic registers
Gosub setup_pic

'initialize the display
Lcdinit

'main program loop
loop:
If PORTC.0 = 1 Then
	TMR0L = 0  'reset the count at voltage ZC
	TMR0H = 0
Endif

If PORTC.1 = 1 Then
	T0CON.7 = 0  'stop the count at current ZC
	t0count = TMR0H * 256
	t0count = t0count + TMR0L
	phase_angle = t0count / scaling_factor
	Lcdout "Phase= ", #phase_angle
	T0CON.7 = 1  'start the Count again
Endif

Goto loop

End                                               

setup_pic:

TRISC = 0x03  'TRISC has bits 0 And 1 As inputs
T0CON = 0x80  'T0CON enabled, 16 Bit, internal clock, prescale 1: 2

Return

Brian.
 
Thank you Brian for huge effort you've done!

First of all, my external crystal oscillator is of 20MHz, therefore the Define CLOCK_FREQUENCY is changed to 20.

I've added a
Code:
WaitMs 1000
statement right after the
Code:
LCDOUT "Phase= ", #phase_angle
in order to not overlap values (since no LCD clearing is done at the first loop).

Here are the observations:

Case1: When ZCD/V circuit ON and ZCD/I OFF: Nothing on the display (since no LCD statements are accorded to High logic of PORTC.0 pin)

Case2: When ZCD/V circuit OFF and ZCD/I ON: The display keep presenting this value:
Phase = 1
Phase = 1
Phase = 4
Phase = 7
Phase = 1 and so on, but in major cases (almost all cases, Phase = 1)

Case3: When both ZCD circuits are ON: same results as Case2 that refers to Phase = 1, meaning that the power factor (Cos(Phase)) is almost equal to 1, for the 60W inductive fan.

For the scaling_factor: 1/(20*10^6) corresponds to 360 degrees (full cycle), how must this factor be changed?


As for the prescaler setting, I don't know what is the base to make a decision for it, but for a 20MHz, If I use a prescaler of 1:4, reducing large (considerably) 20Mhz, to reduce the amount of counting per cycle, wouldn't it be good?
 

Hi,

I set the prescaler for 1:256 by putting into T0CON the following binary value: 10000111 that corresponds to 0X87 (instead of 0X80).

What about scaling_factor??
 

Before going further, the results you see may be due to restrictions in the Oshonsoft licence. Please do this and tell me what you see:

1. In the main window, click "Help".
2. then click "View License Information"
3. it should open a small window with your name in it. Click the "info" button at the bottom and tell me what it shows.

Some of the math routines may give different results depending on which sections of the license are activated.

Sorry for the delay, I tried to ask this earlier but got called away before I could send the message.

Brian.
 

Thanks Brian!!

You have right, by doing the info process, all functions are DISABLED:
USB Support - Disabled
Floating Point Math Support - Disabled
32-bit Integer Math Support - Disabled
String Data Type Support - Disabled
Structured Language Support - Disabled
Special Functions Pack1 - Disabled

Should I buy these licenses? I bought the floating point add-on, from another user than the licensed simulator user, copied the reg files into the file main folder, but didn't work.

Thanks
 

The license files are locked to the name of the person who purchased them and the compiler files on the public download page at Oshonsoft's web site are demo versions. When you buy the program and/or licenses a link is sent to the registered versions and customized '.reg' files.

If you want to continue using BASIC, I strongly suggest you at least buy the floating point support, the others are useful but not essential. What is happening when you see those number in post #76 is you are seeing only the integer part of the division result.

I'm not sure if BASIC is mandatory for your project but I find it very restrictive. If you can change to 'C' you will find programming is much easier.

Brian.
 

Hi Brian and thank you!!!

Well using PIC18 IDE is mandatory in this project.

However I have bought the licenses, and by viewing license info, the following are enabled:

Floating Point Math Support - Enabled
32-bit Integer Math Support - Enabled
Structured Language Support - Enabled


I compiled your code into the modified compiler, and same results were shown, but the difference is that I made the definitions for the three variables you created as Single not as Word.

The output difference is only 3 zeroes after comma, like:
Phase = 1.000
Phase = 4.000
Phase = 7.000
Phase = 1.000

And so on...

The LCD display is TOOOO fast, since no Lcdmcdout Lcdclear nor Waitms statements are made.

What shall we change scaling_factor value to? And since the external crystal is of 20MHz as stated, what value of prescale must be made (T0CON = 0x87)??


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…