- Joined
- Jul 4, 2009
- Messages
- 16,532
- Helped
- 5,157
- Reputation
- 10,347
- Reaction score
- 5,215
- Trophy points
- 1,393
- Location
- Aberdyfi, West Wales, UK
- Activity points
- 139,786
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
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.
note that if interrupts are used, you can add more instructions in the "loop:" code without losing accuracy.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
Brian.
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.
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
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.
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.
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.
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.
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.
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
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.
We use cookies and similar technologies for the following purposes:
Do you accept cookies and these technologies?
We use cookies and similar technologies for the following purposes:
Do you accept cookies and these technologies?