PDA

View Full Version : Measuring audio phase shift through a circuit with a PIC



HankMcSpank
- 5th September 2010, 00:19
Need a sanity check here.

I'd like to measure the phase shift through an audio circuit at different test frequencies, therefore to measure this with a PIC.

In principle it's fairly simple - ie time a an audio signal for one complete cycle, store the result, immediately start timing how long until the output signal gets to the same point (ie use a comparator for both signals). Do some maths with the resulting times...this will yield the phase shift in degrees.

I've setup something along these lines.

(the input to my audio circuit is fed into the PIC comparator1, the output of my audio circuit feeds comparator2. Also timer1 is mapped to comparator 1, an timer2 is mapped to comparator 2 from a 'clock counting' perspective)

++++++++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++++++++++++++++++++++++++++++
enable comparator1 interrupts

when comparator1 interrupts, start timer1 running

when comparator1 interrupts again, do the following....

stop timer1 running & store the 'count' (this gives a clock pulse 'count' for the arriving frequency, but since I'm only after phase shift...which is a time based measurement, I don't calculate the frequency)

clear timer2
start timer2
enable comparator2 interrupts

Ok, now when comparator2 interrupts, stop timer2 & store the count


My problem is that I can't seem to get the count of timer2 up in count value (no matter what I do with the prescalers) .

For example I know the phase of the input vs output for my circuit 180 degrees out of phase (I can see this on my scope - it's a simple inverting opamp setup to test this all out), but I'm getting readings like this...

comp1=2434 comp2=151
comp1=2432 comp2=151
comp1=2431 comp2=151
comp1=2431 comp2=151
comp1=2433 comp2=151
comp1=2432 comp2=151
comp1=2433 comp2=151
comp1=2432 comp2=151


Now for a signal that is 180 degrees out phase, I reckon comparator 2 should interrupt 'in or around' half the time that timer1 counted.

Is my methodology wrong, is is my code wrong?

(the prescalers prob aren't right below, but my issue is that no matter what I set the prescalers to, the second timer2 count (ie the inverted signal) never get's anythere near 50% of timer1)





@ __CONFIG _FCMEN_OFF & _INTRC_OSC_NOCLKOUT & _WDT_OFF & _MCLRE_OFF & _CP_OFF & _IESO_OFF & _BOR_OFF & _PWRTE_OFF
'************************************************* **********************************************

'16F690 Pin Connections....
' PIN# NAME USE & CONNECTION
' 1 Vdd +5VDC power supply
' 7 RC3 C12IN3- (external comparator1 input)
' 14 RC2 C12IN3- (external comparator2 input)
' 20 Vss Ground
'************************************************* ************************************
DEFINE OSC 8 ' set Oscillator at 8Mhz.
DEFINE NO_CLRWDT 1

DEFINE HSER_SPBRG 25 'HSEROUT Stuff.
DEFINE HSER_TXSTA 24h 'HSEROUT Stuff.
DEFINE HSER_CLROERR 1 'HSEROUT Stuff.

txsta = %10100100 'setup the tx register
RCSTA.7 = 1 ' Enable RB7 for TX USART
INTCON.0 = 0 ' clears the RABIF Flag (to 0), COULD be 1 on reset (unique to F690)
ANSEL = 0 'disable AtoD.
ANSELH = 0 'disable AtoD.

'Turn on & Set up Comparator 1
CM1CON0 = %10100111 'Comparator1 On
VRCON = %0000001 'turn on internal reference


'Turn on & Set up Comparator 2
CM2CON0 = %10100110 'turn on comparator 2.

Comp1Time var word ' used to amalgamate TMR1 High & Low Bytes.
Frequency var word 'used to convert the count to frequency.
Comp2Time VAR WORD
PHASE var word


INCLUDE "DT_INTS-14.bas" ' Base Interrupt System PO90OOO9
INCLUDE "ReEnterPBP.bas" ' Include if using PBP interrupts

ASM
INT_LIST macro ; IntSource, Label, Type, ResetFlag?
INT_Handler CMP1_INT, _Comp1_Int, PBP, yes
INT_Handler CMP2_INT, _Comp2_Int, PBP, yes
endm
INT_CREATE ; Creates the interrupt processor
ENDASM
T1CON = $10 ;

T1CON.0=0 'stop tmr1
TMR1H = 0 'Set the high part of the timer1 value to 0
TMR1L = 0 'Set the low part of the timer1 value to 0

@ INT_ENABLE CMP1_INT ; enable Comparator 1 interrupts

t2con = %00000000010 ' tmr2 prescaler 16
TMR2 = 0 'Set the high part of the timer2 value to 0

Comp1Time = 0 'clear down Comp1Time, prior to starting.
comp2Time = 0 'clear down Comp2Time, prior to starting

'Main body of Code********************************************** ***********************************************
Main:
HSEROUT ["comp1=", dec Comp1Time,9, "comp2=", dec comp2Time,13, 10]
pause 10
goto Main
end

'Comparator1 Interrupt Handler+++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++
Comp1_Int:

if T1CON.0= 0 then 'if timer1 is not running then...
TMR1H = 0 'Set the high part of the timer value to 0
TMR1L = 0 'Set the low part of the timer value to 0
T1CON.0= 1 'start timer
else 'therefore if it is running, stop the timer & calculate the number of 'clock' counts between comparator interrupts....
T1CON.0= 0 'stop tmr1
TMR2 = 0 'clear tmr2
T2CON.2= 1 'start tmr2
@ INT_ENABLE CMP2_INT
Comp1Time.Lowbyte = TMR1L 'puts the timer's low byte in Comp1Time's lower 8 bits
Comp1Time.Highbyte = TMR1H 'puts the timer's high byte in Comp1Time's upper 8 bits

endif
@ INT_RETURN

'Comparator2 Interrupt Handler+++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++
Comp2_Int:
@ INT_DISABLE CMP2_INT
T2CON.2= 0 'stop timer2
Comp2Time = tmr2
@ INT_RETURN

Jerson
- 7th September 2010, 02:03
I haven't checked your code, but, from the description, I think you need to look at it differently to get the correct results. Try it this way.

enable comparator1 and 2 interrupts

when comparator1 interrupts,
clear timer1
clear timer2
start timer1 and timer 2 running

when comparator1 interrupts again, do the following....

stop timer1 running & store the 'count' (this gives a clock pulse 'count' for the arriving frequency, but since I'm only after phase shift...which is a time based measurement, I don't calculate the frequency)

when comparator2 interrupts, stop timer2 & store the count

HankMcSpank
- 7th September 2010, 10:19
I haven't checked your code, but, from the description, I think you need to look at it differently to get the correct results. Try it this way.

enable comparator1 and 2 interrupts

when comparator1 interrupts,
clear timer1
clear timer2
start timer1 and timer 2 running

when comparator1 interrupts again, do the following....

stop timer1 running & store the 'count' (this gives a clock pulse 'count' for the arriving frequency, but since I'm only after phase shift...which is a time based measurement, I don't calculate the frequency)

when comparator2 interrupts, stop timer2 & store the count

Thanks for you input....seems reasonable - I'll be revisiitng this tonight (last night I got waylaid trying to get PIC to control a digital pot via SPI .....failed - http://www.picbasic.co.uk/forum/showthread.php?t=13659&p=93540#post93540 )

Using your revised approach, I reckon to calculate the phase change it's this....

time lag (ie phase difference) = timer2 - timer1

then to work out the phase ....time lag x 360/timer1/


So an example, lets say timer1 = 2000 & timer2 = 2,200

this would give a difference (time lag) of 200 clock pulses

Now bearing in mind timer1 = 360 degrees (one complete cycle)

Therefore to establish out how many single degrees of phase...

200 * 360 /2000 ....which means in this example the phase shift is a 36 degrees lag.


Putting that into actual frequency ....

clock = 2us
one 'wave' period = 2000 x 2us = 4ms
In Hertz = 1/0.004 = 250hz

time lag of second waveform = 200 x 2us = 400us

Now being lazy, to verify, using an online phase calculator & inputing 250Hz & a lag time of 400us...

http://www.sengpielaudio.com/calculator-timedelayphase.htm

= 36 degrees.

HankMcSpank
- 7th September 2010, 12:41
I haven't checked your code, but, from the description, I think you need to look at it differently to get the correct results. Try it this way.

enable comparator1 and 2 interrupts

when comparator1 interrupts,
clear timer1
clear timer2
start timer1 and timer 2 running

when comparator1 interrupts again, do the following....

stop timer1 running & store the 'count' (this gives a clock pulse 'count' for the arriving frequency, but since I'm only after phase shift...which is a time based measurement, I don't calculate the frequency)

when comparator2 interrupts, stop timer2 & store the count

Actually, just re-reading your sequence - that sequence will give me the lag time direct (eg wrt a small phase lag, then comp2 will interrupt shortly after comp1)

the upper frequency that I'm hoping to measure phase shift is about 1.4Kz - a 1 deg phase shift in time for 1.4Khz is just 2us (ie 1/1440 divided by 360). At 8Mhz Osc, I'm not sure there'll be sufficient time to do much at all before the Comp2 interrupt arrives!

HankMcSpank
- 11th September 2010, 15:36
Ok. I've hit a wall - & I can't get passed it - I need a leg up!

Te recap, I'm using a PIC to measure frequency (80Hz thru 1.4Khz) by counting the number of clock pulses between successive comparator interrupts. It works well.

I now need to measure phase shift .....but how to actually use the PIC hardware options available, to detect the amount of clock pulse between the non phase shifted signal & phase shifted signal's leading edges?

Here's a schematic of my test circuit...

http://img830.imageshack.us/img830/9609/schem.jpg (http://img830.imageshack.us/i/schem.jpg/)

I detect frequency as follows...

DT's Comp1 interrupt - start a timer
DT's Comp1 interrupts again - stop a timer.

so want to add in phase shift detect by bringing in another timer and measuring the clock counts until the second (lagging) signal's leading edge arrives, therefore the enlarged picture....

Comp1 interrupt - start a timer
Comp1 interrupts again - stop a timer.
start another timer
second (phase shifted) signal's leading edge arrives - stop the other timer.



here's the bit I'm using for frequency detection (for clarity I've ommited the config/registers as this bit works)...




INCLUDE "DT_INTS-14.bas" ' Base Interrupt System PO90OOO9
INCLUDE "ReEnterPBP.bas" ' Include if using PBP interrupts

ASM
INT_LIST macro ; IntSource, Label, Type, ResetFlag?
INT_Handler CMP2_INT, _Comp2_Int, PBP, yes
endm
INT_CREATE ; Creates the interrupt processor
ENDASM

@ INT_ENABLE CMP1_INT ; enable Comparator 1 interrupts
@ INT_ENABLE CMP2_INT ; enable Comparator 1 interrupts

Comp1Time = 0 'clear down Comp1Time, prior to starting.
comp2Time = 0 'clear down Comp2Time, prior to starting

'Main body of Code********************************************** ***********************************************
Main:

HSEROUT ["comp2=", dec comp2Time,9,"comp1=", dec Comp1Time, 13, 10]
goto Main
end

'Comparator1 Interrupt Handler+++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++
Comp2_Int:
if T1CON.0= 0 then 'if timer1 is not running then...
TMR1H = 0 'Set the high part of the timer value to 0
TMR1L = 0 'Set the low part of the timer value to 0
T1CON.0= 1 'start timer
else
T1CON.0= 0 'stop timer1
Comp2Time.Lowbyte = TMR1L 'puts the timer's low byte in Comp1Time's lower 8 bits
Comp2Time.Highbyte = TMR1H 'puts the timer's high byte in Comp1Time's upper 8 bits
Comp2Time = Comp2Time
endif
@ INT_RETURN



but when I add in a little bit extra (bolded below in red), the HSEROUT chokes....



@ INT_ENABLE CMP1_INT ; enable Comparator 1 interrupts
@ INT_ENABLE CMP2_INT ; enable Comparator 1 interrupts

Comp1Time = 0 'clear down Comp1Time, prior to starting.
comp2Time = 0 'clear down Comp2Time, prior to starting

'Main body of Code********************************************** ***********************************************
Main:

HSEROUT ["comp2=", dec comp2Time,9,"comp1=", dec Comp1Time, 13, 10]
goto Main
end

'Comparator1 Interrupt Handler+++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++
Comp2_Int:
if T1CON.0= 0 then 'if timer1 is not running then...
TMR1H = 0 'Set the high part of the timer value to 0
TMR1L = 0 'Set the low part of the timer value to 0
T1CON.0= 1 'start timer1
else
T1CON.0= 0 'stop timer1
TMR2 = 0 'clear tmr2
T2CON.2 = 1 'start tmr2
Comp2Time.Lowbyte = TMR1L 'puts the timer's low byte in Comp1Time's lower 8 bits
Comp2Time.Highbyte = TMR1H 'puts the timer's high byte in Comp1Time's upper 8 bits
Comp2Time = Comp2Time
endif
@ INT_RETURN

Comp1_Int: ' store the amount of clock pulses elapsed since routine above finished
T2CON.2 = 0
COMP1TIME = TMR2
TMR2= 0
@ INT_RETURN

Jerson
- 11th September 2010, 19:14
Hi Hank

I'm sorry, I did not reply earlier. I thought you would find a way ;)

Ok, I will not get into the code you wrote nor will I write some, but, this is how I do a phase comparison on my projects. A 16 bit timer is free running all the time. No start/stop sequences. Comparator interrupts too are always enabled. No need to start/stop them. Remember Timer always counts up and rolls over, so CurrTimer nearly always is > PrevTimer. On rollover, the result of CurrTimer-PrevTimer is still valid since the bits are limited to a word size. So, 1000-500 is still same as 465-65500 (+ 1) when you do the 16 bit subtract.

Define CurrTimer TMR1 ' alias CurrTimer as timer1
PrevTimer var word
FreqCount var word
PhaseCount var word

On comp1 interrupt
FreqCount = CurrTimer - PrevTimer ' timer ticks between 2 edges of the same input give time proportional to the frequency
PrevTimer = CurrTimer ' This is the new reference for phase measurement

On comp2 interrupt
PhaseCount = CurrTimer - PrevTimer ' gives the time between non shifted and shifted waves

Convert phasecount to corresponding time as
Phase(Degrees) = PhaseCount(uS)*180(degrees) / (180deg counts(us))

Convert freqcount to corresponding freq similarly

I hope this give you some hint. It's near bedtime and I can barely keep my eyes open. If you need some more hints, let me know, I'll try to help :)

This code is definitely not PBP as I've just put the ideas forth.

Regards

HankMcSpank
- 11th September 2010, 19:22
Many thanks...I'll study your post in detail tonight (after I've got the kids off to bed!)

I wasn't expecting anyone to analyse my code - I only offered it up in case someone wants to get an overall vibe for my approach. Equally, I wouldn't want anyone to write it for me (else I'll never learn) - I seek exactly what you've brought to the table....ideas wrt general approach from those that have trodden similar paths! (the detail, I'll try & work out myself!)

thanks once again.

HankMcSpank
- 12th September 2010, 01:18
Hi Jerson,

Ok, had a bit of time to try & get my head around your method. I'd like to break each of you sentences down a little....



A 16 bit timer is free running all the time. No start/stop sequences. Comparator interrupts too are always enabled. No need to start/stop them.


I'm struggling with why you're letting them free run?

The comparators first - they're gonna be generating a crazy amount of interrupts (to my simple brain, I actually wonder how the code will have time to do anythig else?! lol). Imagine a 1.4khz signal at the comparator pin - that's gonna be an interrupt every 700 us, but wait, there's the other signal presented at the other comparator pin (ie the one I want to measure the phase lag of) & it's generating interrupts too - so that's two interrupts arriving every 700us?!! (potentially the comparator might need to handle much higher frequencies...as high as 5Khz ....my mind boggles at the interrupt rate).The more I ponder this one (I'm into my 3rd night on this & nowhere near a result!), I do woder if there should be some other hardware I shopuld bring into play - to front end some of the work (I was looking at parrallax propellors - http://www.parallax.com/Portals/0/Images/Prod/P/PropellerBlock-L.jpg .....something like that architecture lends it self more to what I'm trying to do here?!)

Now I'm inexperienced & that might not tax the PIC one iota, but bearing in mind my program already uses other interrupts (Interrupt on Change, INT_PIN etc), and it's to my mind my program is getting a bit interrupt busy!

Ok, next (wrt allowing the timer to roll over) - I just can't get this clear in my head...



Remember Timer always counts up and rolls over, so CurrTimer nearly always is > PrevTimer. On rollover, the result of CurrTimer-PrevTimer is still valid since the bits are limited to a word size. So, 1000-500 is still same as 465-65500 (+ 1) when you do the 16 bit subtract.

I'm sure this is becuase I've not grasped the intracacies!

If you reference my schematic above, once again - I'm measuring two signals for phase lag - what happens if if the first signal comes in before the timer rolls over, but by the time the lagged signal come in, the timer has rolled over?

How does the maths work out for that scenario?

Ok, to your code ideas for me to using...



On comp1 interrupt
FreqCount = CurrTimer - PrevTimer ' timer ticks between 2 edges of the same input give time proportional to the frequency


Bearing in mind the source signal is a guitar - the frequency is forever moving (with periods where it's static), so I can't grasp how a moving target so to speak will work with that code approach?



PrevTimer = CurrTimer ' This is the new reference for phase measurement


This is about the one line I can grasp (!) - ie we need to put a stake in the ground to start timing until the second (phase lagged) signal arrives.



On comp2 interrupt
PhaseCount = CurrTimer - PrevTimer ' gives the time between non shifted and shifted waves


Again, what if the phase has moved (becuase the incoming frequency changed) - couldn't you have a situation where the current Timer hasn't rolled over, but the previous timer (with the stored value) did?

BTW - I really do appreciate you taking the time to explain to a dunderbrain like me!

Jerson
- 12th September 2010, 03:24
Hi Jerson,
I'm struggling with why you're letting them free run?

Look at it a bit like you look at a stopwatch. I use the timer as a lap counter. I could choose to start / stop the timer, but it might increase the time spent in the interrupt a bit.



The comparators first - they're gonna be generating a crazy amount of interrupts (to my simple brain, I actually wonder how the code will have time to do anythig else?! lol). Imagine a 1.4khz signal at the comparator pin - that's gonna be an interrupt every 700 us, but wait, there's the other signal presented at the other comparator pin (ie the one I want to measure the phase lag of) & it's generating interrupts too - so that's two interrupts arriving every 700us?!! (potentially the comparator might need to handle much higher frequencies...as high as 5Khz ....my mind boggles at the interrupt rate).

Yes, you are right about the interrupts hogging all the time your processor has. But, you could always run the processor at say 20Mhz, makes it 5 times as fast than the default 4MHz. 5 times more that can be done. I've used this technique at 50Hz to check phase lag on mains signals.



If you reference my schematic above, once again - I'm measuring two signals for phase lag - what happens if if the first signal comes in before the timer rolls over, but by the time the lagged signal come in, the timer has rolled over?

How does the maths work out for that scenario?

Ok, in the case of rollover, let us assume the first reading was 65500. Assume a phase shift of 500 counts. The new value is 66000. After rounding to 16 bits, you get 464 (66000-65536). So, current reading 464 - previous reading 65500 will give you your phase counts as 464-65500 = 01F4 in 16 bits (use the F5/F6 buttons on calc to see the Hex/Dec values). So, for an error of 1 count, you still get the same phase diff.



Bearing in mind the source signal is a guitar - the frequency is forever moving (with periods where it's static), so I can't grasp how a moving target so to speak will work with that code approach?

A phase diff measurement is just that. How you process it after you've found it out is up to you. I can suggest you put the phase count readings through a low pass filter to smooth out the variations.



This is about the one line I can grasp (!) - ie we need to put a stake in the ground to start timing until the second (phase lagged) signal arrives.

Precisely. That is what I am doing when I keep track of the last zero crossing of the first input. Comp1 interrupt. That is your pole in the ground.



Again, what if the phase has moved (becuase the incoming frequency changed) - couldn't you have a situation where the current Timer hasn't rolled over, but the previous timer (with the stored value) did?

It is possible in the real world scenario. For this, you can use an overflow mechanism to weed out this case. Any number bigger than expected range indicates the signal has died out.



BTW - I really do appreciate you taking the time to explain to a dunderbrain like me!
I am glad to help out as I was once in your shoes ;)

HankMcSpank
- 12th September 2010, 19:12
Ok, in the case of rollover, let us assume the first reading was 65500. Assume a phase shift of 500 counts. The new value is 66000. After rounding to 16 bits, you get 464 (66000-65536). So, current reading 464 - previous reading 65500 will give you your phase counts as 464-65500 = 01F4 in 16 bits (use the F5/F6 buttons on calc to see the Hex/Dec values). So, for an error of 1 count, you still get the same phase diff.


But how do you know the timer has rolled over or not? (ie using you analagy how are you counting laps & intra laps)

For example, the frequency might change & therefore you get a a first reading count of 460 (vs 65500 in your example). So how do you know if a reading of 464 relates to the frequency changing massively, or is a result of the timer rolling over? (surely you have to track overflows too? I tried this, but the PIC baulked when I programmed it with my code! Too many interrupts arriving close together possibly?)

Jerson
- 13th September 2010, 01:54
Yes, you need to track overflows in your code - too low frequency. Of course, the same may not be true for the phase. Tracking the overflow needs to be done. I did not have to use such a thing since my frequency in was fixed at 50Hz. However, I can propose, you could use the timer start/stop method and track the timer overflow to indicate a very low frequency or too large values of time coming in.

Possible implementation
Comp1 int
if T1 has overflowed, you have yourself an error condition here. Freq Too Low
FreqCounts = T1
T1 = 0

Comp2 int
PhaseCounts = T1

Again, the 'stick in the mud' is T1 being set to 0. But, here, you get your freqcounts and phasecounts directly.!!!

The time from one zero crossing (T1=0) to the next zero crossing on the same Comparator is the frequency counts. Read this, check for overflow and Set T1 = 0.

The time from the zero crossing of Comp1 to the zero crossing of Comp2 is the phase counts.

Oh, and you need to run your PIC at 20MHz just so you have more time in your ISRs. A helpful suggestion would be to write your ISR in ASM no matter how difficult it seems; PBP adds overhead to the code you write and its better to exit the ISR as quickly as you can to let other blocks of code to run.

HankMcSpank
- 13th September 2010, 02:12
Thanks Jerson....I'll study that later (it's 2.15am here in Lodnon!)

Actually, this looks like it might have some signs of doing what I want...



'Comparator1 Interrupt handler+++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++
Comp1_Int:
@ INT_DISABLE CMP1_INT
Comp1Time.Lowbyte = TMR1L 'puts the timer's low byte in MyTime's lower 8 bits
Comp1Time.Highbyte = TMR1H 'puts the timer's high byte in MyTime's upper 8 bits
TMR1H = 0 'Set the high part of the timer value to 0
TMR1L = 0 'Set the low part of the timer value to 0
@ INT_ENABLE CMP2_INT
'Comp1Time =Comp1Time +4
'dummy = b * c '5,000,000
'frequency = div32 Comp1time
'frequency =frequency/2
'HSEROUT ["mytime = ", dec mytime,9, dec frequency/10,".",dec frequency//10,"Hz",13, 10]

@ INT_RETURN

'Comparator2 Interrupt handler+++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++
Comp2_Int:
@ INT_DISABLE CMP2_INT
Comp2Time.Lowbyte = TMR1L 'puts the timer's low byte in MyTime's lower 8 bits
Comp2Time.Highbyte = TMR1H 'puts the timer's high byte in MyTime's upper 8 bits
TMR1H = 0 'Set the high part of the timer value to 0
TMR1L = 0 'Set the low part of the timer value to 0
@ INT_ENABLE CMP1_INT
@ INT_RETURN



What's getting me bogged down is something as simple as setting my config correct for a 20Mhz external resonator. (the first time I've used an external clock)

I'm putting a 1Khz test signal into comparator1 input pin- the handler it's registering 2500 timer1 'counts' between successive compartor1 interrupts - that doesn't seem correct for a 20Mhz external clock?

at 1Khz, the actual physical time between interrupts should be 1ms - & I'm seeiing a count of 2500 for comparator 1 interrupts.....

therefore 1ms divided by 2500 is 0.0000004 seconds ....or in frequency is 2500000Hz (2.5Mhz - that's not 20Mhz???).

here's my settings...



@ __CONFIG _FCMEN_OFF & _HS_OSC & _WDT_OFF & _MCLRE_OFF & _CP_OFF & _IESO_OFF & _BOR_OFF & _PWRTE_OFF

DEFINE OSC 20 ' set Oscillator at 20Mhz.
OSCCON.0 = 0

what am I setting wrong? (I'm using MPASM assembler)

Jerson
- 13th September 2010, 04:19
Hi Hank

Is it possible you got your T1 prescaler(T1CON) set to 1:8 ? That will explain the counts you see. 20e6 / 8 / 1000 = 2500 counts. You haven't mentioned which pic you use.

I've never used a resonator on my circuits, but, you could try with HS / XT options. Most likely the XT might work.

Regards

HankMcSpank
- 16th September 2010, 00:02
Hi Hank

Is it possible you got your T1 prescaler(T1CON) set to 1:8 ? That will explain the counts you see. 20e6 / 8 / 1000 = 2500 counts. You haven't mentioned which pic you use.

I've never used a resonator on my circuits, but, you could try with HS / XT options. Most likely the XT might work.

Regards

Thanks for bearing with me....and keep those ideas coming! (to my knowledge there are no prescalers set anyhere in my code)

Ok, I think I know (in part) what the problem is....& I throw myself open to public ridicule.:o

My signal path is this...

Signal 1 -> Audio preamp->schmitt trigger-> comparator1 input pin

Signal2 -> Audio preamp-> all pass filter-> schmitt trigger-> comparator2 input pin

When dealing with phase shift - it's easier on the eye to work with sine waves on a scope (for me at least)....*but*, doh, I was aligning aformetnioned 'zer degrees phase shift' in sine wave form as viewed on the scope. Alas, it appears that becuase each of the signals is of different amplitude going into the schmitt tirrger, the schmitt triggers are erhm triggering at slightly different times wrt to one another - which translates itself into phase shift! (now I'm pretty sure I was checking at the input to the pic comparators the other night, so I'm bemused how this has only come to light)

I therefore need to modify my circuit to ensure that the signals feeding into the shcmitt are of exactly the same amplitude (preferable amplified to nice sharp edged square wavea) ....this should ensure they trigger at the same point

Ok, something else I've established tonight ....if I feed signal1 into both schmitt triggers & ultimately into both comparators (so, absolutely no phase shift in play whatsoever then!)....I get a constant count of 29....this stays at 29 no matter what the input frequency - would it therefore be safe to assume that that's how many clock cycles it takes for all of the interrupt routines, storing the counts etc to work?


So therefore my new plan of attack is.....rejig my 'feed' circuitry to ensure an identical signal feeds amplitude into both schmitt triggers....and then also subtract 29 from the comp2 count. (at my upper frequency of 1.5Khz...I get a comp1 count of about 3000...therefore 1%, hich in phase terms is an error of about 3.6 degrees - not ideal. :( )

Would using any other form of interrupts/hardware get this 'floor' count of 29 down? (faster processor, higher frequency oscillator - 40Mhz?)

HankMcSpank
- 16th September 2010, 22:09
Ok, so I impemented my plan (square up the source signa, make them the same amplitude ....then subtract 29 from the final comp2 count).

Works... :)

(green trace is comp1 input, yellow trace is the phase lagged signal into comp2)

http://img716.imageshack.us/img716/7224/phase1.jpg (http://img716.imageshack.us/i/phase1.jpg/)


http://img214.imageshack.us/img214/8198/phase90.jpg (http://img214.imageshack.us/i/phase90.jpg/)



Thanks for all your help........"I have exorcised the demons this code is clear!"

gadelhas
- 16th September 2010, 22:58
Excellent Job. Congratulations!!!

HankMcSpank
- 25th September 2010, 13:39
Excellent Job. Congratulations!!!

Thanks...I've just hit a problem. While larger phase shifts are being measured well, using this method (feeding into 2 x PIC comparators & counting using DT's interrupts) it seems small phase shifts can't be measured accurately. I made a short video to illustrate the problem....

(better to maximise the video to full screen & select 720p towards the right of the youtube video play bar)

http://www.youtube.com/watch?v=k8YDS2zKeas

Here's the video's accompanying text with the video explaining what's going on....

"Here you'll see two square waves feeding into the two comparators on a PIC...the green signal trace is feeding into comparator 1 (comp1 count is the count on the left & has a static count of about 9992) - the yellow trace is a 'phase lagged' signal feeding into the PIC's comparator 2. (the comparator 2 count is the green text on the right) What you see on screen is me reducing the phase lag of the yellow trace - as I reduce the phase lag, you can see the comp2 count decrement (as expected) BUT ...as the phase lagged signal gets into the small lag region, the comp2 count won't decrement below 336 - this suggests using PIC comparators & interrupts isn't workable for measuring small phase shifts?"

The audio signal frequency in that video is about 500Hz...and due to the Comp2 count not decremeting below 336, it means any phase lag below 12.5 degrees can't be measured :( At just 1400Hz (Comp1 count = 3,600, comp2 = count 336 minimum) , it means phase lag under 33 degrees can't be measured :eek:


Here are the two Comparator simple interrupt handlers...



'Comparator1 Interrupt Handler+++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++++++++++++
Comp1_Int:
Comp1Time.Lowbyte = TMR1L 'Store away the timer1 count - this will be the non lagged signal 'period' count.
Comp1Time.Highbyte = TMR1H
TMR1H = 0 'Set timer1 value to 0
TMR1L = 0
@ INT_RETURN

'Comparator2 Interrupt Handler+++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++++++++++++
Comp2_Int:
Comp2Time.Lowbyte = TMR1L 'Store away the timer1 count again (ie once the lagged signal leading edge arrives - this is the lag count)
Comp2Time.Highbyte = TMR1H
@ INT_RETURN



EDIT: Following on from the first blurry video I linked to, I re-made the video in HD & uploaded again (the new URL is edited in above)...be sure to view in 720p on youtube! (else the onscreen text will be blurred)

sinoteq
- 25th September 2010, 14:15
What PIC do you use and how fast do you run it?

HankMcSpank
- 25th September 2010, 14:24
What PIC do you use and how fast do you run it?

I use a 16f690 @20Mhz

The 16F690's Comparator 1 receives the non lagged signal (set to internal VREF to trip at about 1/2 VCC)
The 16F690's Comparator 2 receives the phase lagged signal (set to internal VREF to trip about 1/2 VCC too)

Identical signals are fed into both comparators (bar the phase lag)

DT's interrupts enabled for CMP1 & CMP2.

On the scope the lag time measures about 20us - below which Comp2 will not decrement.

I don't know enough about what's going on under the hood wrt interrupts (or timer1 zero-ing) to shine any more light on why the comp2 count won't go below 336...when clearly it should as the leading edge of the lagged signal gets close to zero lag. So being a bit wet nehind the ears, is there any other possible method I can use that circumvent this problem?

languer
- 28th September 2010, 20:32
So on the picture and data on post#15 things were looking honky-dory (you were measuring at least down to 161 counts on CMP2). Your new posts state that now CMP2 will not go below 336. Basic question; what changed?

prstein
- 28th September 2010, 21:28
I use a 16f690 @20Mhz

The 16F690's Comparator 1 receives the non lagged signal (set to internal VREF to trip at about 1/2 VCC)
The 16F690's Comparator 2 receives the phase lagged signal (set to internal VREF to trip about 1/2 VCC too)

Identical signals are fed into both comparators (bar the phase lag)

DT's interrupts enabled for CMP1 & CMP2.

On the scope the lag time measures about 20us - below which Comp2 will not decrement.

I don't know enough about what's going on under the hood wrt interrupts (or timer1 zero-ing) to shine any more light on why the comp2 count won't go below 336...when clearly it should as the leading edge of the lagged signal gets close to zero lag. So being a bit wet nehind the ears, is there any other possible method I can use that circumvent this problem?

I'm coming into this sort of late, although I've been more-or-less following this thread. Very interesting stuff!

Anyway, here's an idea that may help--just use a single interrupt. When the first comparator causes the interrupt, get your tmr1 count and zero it, then wait in a tight loop, checking PIR2.6. That should reduce latency to an absolute minimum.

something like


'Make sure the Comparator 2 interrupt handler is disabled!
'Comparator1 Interrupt Handler+++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++++++++++++
Comp1_Int:
Comp1Time.Lowbyte = TMR1L 'Store away the timer1 count - this will be the non lagged signal 'period' count.
Comp1Time.Highbyte = TMR1H
TMR1H = 0 'Set timer1 value to 0
TMR1L = 0
PIR2.6 = 0 'Clear the Comparator C2 Interrupt Flag bit
Keep_Waiting:
WHILE PIR2.6 = 0 'Check to see if the Comparator 2 interrupt flag has flipped
goto Keep_Wating
WEND
Comp2Time.Lowbyte = TMR1L 'Store away the timer1 count again (ie once the lagged signal leading edge arrives - this is the lag count)
Comp2Time.Highbyte = TMR1H
@ INT_RETURN

You could also add some sort of check so that it would exit the loop if an unreasonable amount of time has elapsed.

Best Regards,
Paul

HankMcSpank
- 28th September 2010, 22:03
H Paul,

Thank you very much for your input - truthfully, I was was getting too wrapped up in this over the weekend, but after drawing a blank with every avenue explored, I'm taking a self enforced mini break from that particular problem (I often find coming back to a project after a rest sparks new ideas & approaches). re your specific idea...I'll freely admit that I'm sort of out my depth when it comes to the inner working of interrupts (ie Picbasic interrupts vs ASM interrupts vs DT interrupts), but at one stage I do recall getting intensely intimate with the datasheet & having a "what the hell" pop at something *very* similar to what you mention - I saw no difference (but I will revisit in the light of your input)

I could really do with some interrupt big hitters to chime in & outline a little a bit about how to get such time sensitive interrupt timings down to the bare minimum (assuming your suggestion isn't the only attack) ....becuase as it stands right now, I have *NO* (zilch, nada, zero, scratch) idea why that comparator2 interrupt count will not register below 336 when I feed a very slightly phase lagged signal into comp2 ...and alas, the my future (parked) status as a multimillionaire who invented widget X depends on getting that count to reflect the time lag between comp1 & comp2 tripping ;)


I'm in part slightly reticent to even push this one too much in the (highly possible) case there's something inherently wrong in my PIC /PICBasic approach - what I am convinced about is that the inputs to the comparators are without question correct - I may be pants at programming, but I know how to drive a scope, sig gen etc! (I think my youtube video illustrates the issue seems to be a valid one)

if anyone out their in the global 'PIC interest land' feels up to the challenge - they could see themselves what happens when they feed near identically phased signals (ie a clock?) into comparator1 & comparator2 (that said an identical signal might cause problems with a race scenario?...but I concede that most don't have the faciilities or interest to setup an all pass to phase shift the two signals!)...in principle, with identical (phase) signals into both comparators, the count for comparator 2 ought to be very close to zero (like single digits)

But I will come back to this (& first on the list will be your suggestion..if for no other reason thatn your is the only suggestion I have, lol) - I'm presently now working on Project Y - it's not gonna be the blockbuster that Widget X will be ...but got to keep keep my kids in Nike trainers somehow or other :)

Edit: While I'm publicily declaring my woeful lack of grasp of time sensitive h/w & corresponding code....I also wonder if changing my PIC might help to get that comp2 count down? (I'm using a 16F690 not becuase it's necessarily the most suitable - but it came bundled with my PIKIT2 starter kit & I've stuck with it from new and have started to get the hang of it a little - when your grappling in the dark as often as am, you need a torch that's familiar!), but I see a chart like this http://www.microchip.com/en_us/technology/xlp/ & then wonder, is there a more efficient PIC for doing this type of stuff out there...??? (eg pics optimised for single instructions - not even sure what that means but the bar chart looks like a shocker!)

Jerson
- 29th September 2010, 02:08
Hi Hank

Since you're stuck, I wonder if you could post/PM your code of now so that I can take a whack at it. I'll see if I can find your problem with the min value of comp2 value.

The barest minimum code which will compile and show the problem is sufficient. I should be able to run it.

Regards

HankMcSpank
- 29th September 2010, 08:15
Hi Hank

Since you're stuck, I wonder if you could post/PM your code of now so that I can take a whack at it. I'll see if I can find your problem with the min value of comp2 value.

The barest minimum code which will compile and show the problem is sufficient. I should be able to run it.

Regards

Many thanks Jerson.

The latest version of code was part of a hulking long program, but I found an earlier variant, which is purely deals with the comparator interrupt timer1 counting & therefore makes it a lot easier to look at...



@ __CONFIG _FCMEN_OFF & _HS_OSC & _WDT_OFF & _MCLRE_OFF & _CP_OFF & _IESO_OFF & _BOR_OFF & _PWRTE_OFF

DEFINE OSC 20 ' set Oscillator at 20Mhz.
DEFINE NO_CLRWDT 1 ' PBP doesn't clear WDT automatically
DEFINE HSER_TXSTA 24h ' Enable transmit
DEFINE HSER_SPBRG 129 ' set USART to 9600 baud @20Mhz

'************************************************* ********************************
'16F690 Pin Connections....
' PIN# NAME USE & CONNECTION
' 1 Vdd +5VDC power supply
TRISC.4 = 0 ' 6 RC4 C2OUT
TRISC.3 = 1 ' 7 RC3 C12IN3- (comparator1 input)
TRISC.2 = 1 ' 14 RC2 C12IN2- (comparator2 input)
TRISA.2 = 0 ' 17 RA2 C1OUT
' 20 Vss Ground
'************************************************* ********************************
OSCCON.0 = %0001000
txsta = %10100100 'setup the tx register
RCSTA.7 = 1 ' Enable RB7 for TX USART
INTCON.0 = 0 ' clears the RABIF Flag (to 0), COULD be 1 on reset (unique to F690)
ANSEL = 0 'disable AtoD.
ANSELH = 0 'disable AtoD.

'Turn on & Set up Comparator 1
CM1CON0.7 = 1 'Comparator 1 on.
CM1CON0.6 = 0 'C1OUT POLARITY BIT
CM1CON0.5 = 1 '1 = C1OUT is present on the C1OUT pin (pin 17)
CM1CON0.4 = 1 'C1OUT logic - 0 is not inverted (1 for inverted)
CM1CON0.3 = 0 'unimplemented
CM1CON0.2 = 1 'Connect internally to the output of C1VREF (or 0 for the the associated comp 'in' ext pin)
CM1CON0.1 = 1 'this bit with bit 1 set the external incoming pin - in this case c12in3- (pin 7)
CM1CON0.0 = 1 'this bit with bit 1 set the external incoming pin - in this case c12in3- (pin 7)

'Turn on & Set up Comparator 2
CM2CON0.7 = 1 'Comparator 2 on.
CM2CON0.6 = 0 'C1OUT POLARITY BIT
CM2CON0.5 = 1 '1 = C2OUT is present on the C2OUT pin (pin 6)
CM2CON0.4 = 1 'C1OUT logic is not inverted (1 for inverted)
CM2CON0.3 = 0 'unimplemented
CM2CON0.2 = 1 'Connect internally to the output of C1VREF (or 0 for the the associated comp 'in' ext pin)
CM2CON0.1 = 1 'this bit with bit 1 set the external incoming pin - in this case c12in2- (pin 14)
CM2CON0.0 = 0 'this bit with bit 1 set the external incoming pin - in this case c12in2- (pin 14)

' Setup the internal VREF
VRCON.7 = 1 'Comparator1 CV Ref enable bit
VRCON.6 = 1 'Comparator2 CV Ref enable bit
VRCON.5 = 0 'high or low range 0 = High Range, 1 = low range
VRCON.4 = 0 '0.6V Reference Enable bit ....0 is disabled
VRCON.3 = 0 'these 4 bits set the divider of the VREF ladder 16 values - 7 should yield 1/2 VCC
VRCON.2 = 1 'these 4 bits set the divider of the VREF ladder 16 values - 7 should yield 1/2 VCC
VRCON.1 = 1 'these 4 bits set the divider of the VREF ladder 16 values - 7 should yield 1/2 VCC
VRCON.0 = 1 'these 4 bits set the divider of the VREF ladder 16 values - 7 should yield 1/2 VCC


Comp1Time var word ' used to amalgamate TMR1 High & Low Bytes.
Comp2Time var word


INCLUDE "DT_INTS-14.bas" ' Base Interrupt System PO90OOO9
INCLUDE "ReEnterPBP.bas" ' Include if using PBP interrupts

ASM
INT_LIST macro ; IntSource, Label, Type, ResetFlag?
INT_Handler CMP1_INT, _Comp1_Int, PBP, yes
INT_Handler CMP2_INT, _Comp2_Int, PBP, yes
endm
INT_CREATE ; Creates the interrupt processor
ENDASM

T1CON.0= 1 'start timer

Comp1Time = 0 'clear down Comp1Time, prior to starting.
comp2Time = 0 'clear down Comp2Time, prior to starting

@ INT_ENABLE CMP1_INT ; enable Comparator 1 interrupts
@ INT_ENABLE CMP2_INT ; enable Comparator 2 interrupts

'Main body of Code********************************************** ******************
Main:
HSEROUT ["comp1=",dec Comp1Time,9,"comp2Time=", dec comp2time, 13, 10]
PAUSE 80
goto Main
end

'Comparator1 Interrupt Handler+++++++++++++++++++++++++++++++++++++++++++ +++++++++
Comp1_Int:
Comp1Time.Lowbyte = TMR1L 'Store timer1 Low into a variable
Comp1Time.Highbyte = TMR1H 'Store timer1 high into a variable.
TMR1H = 0 'Set the high part of the timer value to 0
TMR1L = 0 'Set the low part of the timer value to 0

@ INT_RETURN


'Comparator2 Interrupt Handler+++++++++++++++++++++++++++++++++++++++++++ +++++++++
Comp2_Int:
Comp2Time.Lowbyte = TMR1L 'Store timer1 Low into a variable
Comp2Time.Highbyte = TMR1H 'Store timer1 high into a variable.
TMR1H = 0 'Set the high part of the timer value to 0
TMR1L = 0 'Set the low part of the timer value to 0

@ INT_RETURN

Jerson
- 29th September 2010, 10:19
Hi Hank

I may be wrong here, but, my suspicion is you are losing time in reentering PBP in your ISRs. See, a lot of PBP registers have to be saved even if they are not going to be used because you flagged your ISR to be coded using PBP functions.

I suggest you slim it down by using ASM inside the ISR and avoiding ReEnterPbp. You should see better results.

I might sound like a heretic here, but I prefer coding my ISR in ASM even though it may be painful to code.

Regards

Acetronics2
- 29th September 2010, 11:09
Hi, Jerson

+ 1000 ...

when I see the interrupts content (!) I do not understand why not to write them in . asm ...

isn't worth saving and restoring all the PBP variables ... as saving and resetting TMR1 only need 4 more " _ " characters ...


Now ... thinking to it ...

if those measurements are " changed " while calculating frequency and Phase ...
not so good ... eh !

do not know about transmitting results nor ... ;) ( didn't see any interrupt disabling around HSEROUT ...)

Alain

HankMcSpank
- 29th September 2010, 13:24
As a relative PIC (& programming) n00b, even I realised that with every interrupt (& there'll be a whole heap of them generated here), some registers need to be stored away to be picked up again once the interrupt has finished. As the incoming audio frequency rises, the interrupt rate gets higher & higher eg at 2.5Khz, that's an interrupt every 400us...but it's worse than that, becuause there are two comparators, so it's actually an interrupt every 200us!

So if having to store/restore registers every 200us, then it obviously makes sense to use the fastest interrupts - that's where I come unstuck ....I only presently know how to us DT's interrupts! (& I have no clue as to how fast they are vs ASM interrupts ...it's all a bit "Whoah...there goes scary stuff" at the moment!)

Re your comment...

I suggest you slim it down by using ASM inside the ISR and avoiding ReEnterPbp. You should see better results.

bearing in mind the actualy routine is quite small....

Comp1 Interrupt routine...
store timer 1 count to a Variable "X"
reset timer 1


Comp2 Interrupt routine...
store timer 1 count to a Variable "y"
reset timer 1

..... would it be possible to at least get me started?

Re the ISR - how would that be triggered?

Re the ISR content - what ASM would that entail?

I'm not expecting anyone to do this for me, but when you don't know where to even start....then any initial guidance is most welcome!

Edit: Also, are there any suitable tools that can help me work out what's going on inside the PIC - I'm thinking along the lines of simulating in step time (vs real time) & seeing what's going on wrt registers.

cncmachineguy
- 29th September 2010, 13:28
'Make sure the Comparator 2 interrupt handler is disabled!
'Comparator1 Interrupt Handler+++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++++++++++++
Comp1_Int:
Comp1Time.Lowbyte = TMR1L 'Store away the timer1 count - this will be the non lagged signal 'period' count.
Comp1Time.Highbyte = TMR1H
TMR1H = 0 'Set timer1 value to 0
TMR1L = 0
PIR2.6 = 0 'Clear the Comparator C2 Interrupt Flag bit
Keep_Waiting:
WHILE PIR2.6 = 0 'Check to see if the Comparator 2 interrupt flag has flipped
goto Keep_Wating
WEND
Comp2Time.Lowbyte = TMR1L 'Store away the timer1 count again (ie once the lagged signal leading edge arrives - this is the lag count)
Comp2Time.Highbyte = TMR1H
@ INT_RETURN

You could also add some sort of check so that it would exit the loop if an unreasonable amount of time has elapsed.

Best Regards,
Paul

I like Pauls approach, Seems to be the least amount of time between Comp1 and 2. one thing I would do, is program his while loop in ASM. This is because I have no idea how many instructions it takes using PBP. I think this will mask the issue of how long it takes to actually enter the handler. Which is what I think above folks are reffering to with the PBP include.

heres the while loop in asm:


BCF PIR2,6 --> clears the comp 2 int flag
HERE --> label to jump to
BTFSS PIR2,6 --> test if comp 2 int flag is set
GOTO HERE --> not set, go back to test
CONTINUE --> Comp 2 int occured, do what ever needs to be done


The above will take exactly 1 instruction to enter the loop, then 3 inscrutions to to run per loop. now for some math:

you are running a 20mHz clock, so thats 5MIPS. so 1 instruction takes .0000002 sec to execute. At 1400 htz, each cycle takes .0071428 sec. that devided by 360 = .00000198sec per degree. so you should be able to measure to almost .5 degree. assuming comp2 fires between BCF and BTFSS, thats 3 instrution cycles, or .0000006 sec.(I think BTFSS may take 2 cycles when true)

Someone please check my math, but these numbers show you have far too much overhead somewhere that can only be explained by the above posts.

HankMcSpank
- 29th September 2010, 13:40
I like Pauls approach, Seems to be the least amount of time between Comp1 and 2. one thing I would do, is program his while loop in ASM. This is because I have no idea how many instructions it takes using PBP. I think this will mask the issue of how long it takes to actually enter the handler. Which is what I think above folks are reffering to with the PBP include.

heres the while loop in asm:


BCF PIR2,6 --> clears the comp 2 int flag
HERE --> label to jump to
BTFSS PIR2,6 --> test if comp 2 int flag is set
GOTO HERE --> not set, go back to test
CONTINUE --> Comp 2 int occured, do what ever needs to be done


The above will take exactly 1 instruction to enter the loop, then 3 inscrutions to to run per loop. now for some math:

you are running a 20mHz clock, so thats 5MIPS. so 1 instruction takes .0000002 sec to execute. At 1400 htz, each cycle takes .0071428 sec. that devided by 360 = .00000198sec per degree. so you should be able to measure to almost .5 degree. assuming comp2 fires between BCF and BTFSS, thats 3 instrution cycles, or .0000006 sec.(I think BTFSS may take 2 cycles when true)

Someone please check my math, but these numbers show you have far too much overhead somewhere that can only be explained by the above posts.

I agree with the maths...and I agree with the summary (something somewhere is taking way too long). Now that I have a few people interested & feeding in ideas , I will pull the breadboard out of cold storage tonight & try Paul's code.


Bert, re this...



BCF PIR2,6 --> clears the comp 2 int flag
HERE --> label to jump to
BTFSS PIR2,6 --> test if comp 2 int flag is set
GOTO HERE --> not set, go back to test
CONTINUE --> Comp 2 int occured, do what ever needs to be done


That looks like what will happen when comp2 interrupts....but within my program, how do I setup the 'trigger' condition ...ie to get the program to go and do that when a comp2 interrupt has arrived?

I will feedback the results (it might be late - ie 21.30 GMT-ish .....got to do the 'kids off to bed' rigamrole first! Hey there's an idea...rather than read them Noddy goes to the Seaside, I could read them some of the ASM code put forward by Bert!! :D )

cncmachineguy
- 29th September 2010, 13:52
Comp1 Interrupt routine...
store timer 1 count to a Variable "X"
reset timer 1


Comp2 Interrupt routine...
store timer 1 count to a Variable "y"
reset timer 1



First I would combine these as Paul suggested. ASM provided in my above post.



..... would it be possible to at least get me started?

Of course! we will!


Re the ISR - how would that be triggered?

triggering is easy, just have to turn on PIE's and GIE.

Off the top of my head, there is some homework to be done. I am not sure how to share variables between ASM and PBP. I don't have my manual with me at work, but I do remember seeing it. Thats needs to be found. While you are reading it, see if says anything about needing to stack PBP stuff when entering ISR. Pic will take care of the important stuff, but what about bank select and such.



Re the ISR content - what ASM would that entail?

Thats the easy part :)



Edit: Also, are there any suitable tools that can help me work out what's going on inside the PIC - I'm thinking along the lines of simulating in step time (vs real time) & seeing what's going on wrt registers.
Do you use MPLAB? If so, I like the mplab sim in under debuggers. it's not real robust, but it will allow you to set things like the input pin, step through the program and watch reg's. I don't know if it works with PBP. :( Beyond that, hardware solutions start with something like ICD. and higher in the money scale.

cncmachineguy
- 29th September 2010, 13:54
Hey there's an idea...rather than read them Noddy goes to the Seaside, I could read them some of the ASM code put forward by Bert!! :D )

LMAO, I always thought datasheets were better for this.

prstein
- 29th September 2010, 14:17
.got to do the 'kids off to bed' rigamrole first! Hey there's an idea...rather than read them Noddy goes to the Seaside, I could read them some of the ASM code put forward by Bert!! :D )

My wife, back when she was studying to be an EMT, would recite her "protocols" (sort of flow charts for emergency medical actions) to get my daughter to go to sleep.

Anyway, I've never used the DT interrupts. I've heard great things about them but never had cause to use them. I would do this by setting the registers for a single interrupt source, i.e. comparator 1. And I would use PBP interrupts (because I am a simple man...) at least at the offset. Once I had that working I would consider something more complicated (ASM interrupts) if the speed wasn't fast enough.

Perhaps in the spirit of keeping it simple, don't use *any* interrupt routine. In the main loop, just keep checking the the state of the Comp1 interrupt flag, something like


PIR2.5 = 0 'Clear the Comp1 interrupt flag
Main_Loop:
WHILE PIR2.5 = 0
GOTO Main_Loop
WEND
Comp1Time.Lowbyte = TMR1L 'Store away the timer1 count - this will be the non lagged signal 'period' count.
Comp1Time.Highbyte = TMR1H
TMR1H = 0 'Set timer1 value to 0
TMR1L = 0
PIR2.6 = 0 'Clear the Comparator C2 Interrupt Flag bit
Keep_Waiting:
WHILE PIR2.6 = 0 'Check to see if the Comparator 2 interrupt flag has flipped
goto Keep_Wating
WEND
Comp2Time.Lowbyte = TMR1L 'Store away the timer1 count again (ie once the lagged signal leading edge arrives - this is the lag count)
Comp2Time.Highbyte = TMR1H
HSEROUT ["comp1=",dec Comp1Time,9,"comp2Time=", dec comp2time, 13, 10]
PIR2.5 = 0 'Clear the Comp1 interrupt flag
goto Main_Loop
END

Best Regards,
Paul

Jerson
- 29th September 2010, 14:32
Ok, just to show you what is involved in ASM interrupts, this is an example for your particular case. This is the first time I am trying something like this using DT_ints :o, so, I may be off the mark. Most times I do it the hard way - pure asm code. Someone please correct me if I am wrong here. You should be able to realize significant improvement in the values you see. Darrell's wrapper code is cool and if you still need to save some more, look at the generated LST file and you should be able to sneak a trick or two from there. The ISR vector is at location 0004 in the PIC. So, you will find a goto INTHAND at that address.

In your case since you are not doing any math functions or gosubs in the INTs, you could keep the PBP code as-is and do away with the ASM. Can save you some agony ;)



@ __CONFIG _FCMEN_OFF & _HS_OSC & _WDT_OFF & _MCLRE_OFF & _CP_OFF & _IESO_OFF & _BOR_OFF & _PWRTE_OFF

DEFINE OSC 20 ' set Oscillator at 20Mhz.
DEFINE NO_CLRWDT 1 ' PBP doesn't clear WDT automatically
DEFINE HSER_TXSTA 24h ' Enable transmit
DEFINE HSER_SPBRG 129 ' set USART to 9600 baud @20Mhz

'************************************************* ********************************
'16F690 Pin Connections....
' PIN# NAME USE & CONNECTION
' 1 Vdd +5VDC power supply
TRISC.4 = 0 ' 6 RC4 C2OUT
TRISC.3 = 1 ' 7 RC3 C12IN3- (comparator1 input)
TRISC.2 = 1 ' 14 RC2 C12IN2- (comparator2 input)
TRISA.2 = 0 ' 17 RA2 C1OUT
' 20 Vss Ground
'************************************************* ********************************
OSCCON.0 = %0001000
txsta = %10100100 'setup the tx register
RCSTA.7 = 1 ' Enable RB7 for TX USART
INTCON.0 = 0 ' clears the RABIF Flag (to 0), COULD be 1 on reset (unique to F690)
ANSEL = 0 'disable AtoD.
ANSELH = 0 'disable AtoD.

'Turn on & Set up Comparator 1
CM1CON0.7 = 1 'Comparator 1 on.
CM1CON0.6 = 0 'C1OUT POLARITY BIT
CM1CON0.5 = 1 '1 = C1OUT is present on the C1OUT pin (pin 17)
CM1CON0.4 = 1 'C1OUT logic - 0 is not inverted (1 for inverted)
CM1CON0.3 = 0 'unimplemented
CM1CON0.2 = 1 'Connect internally to the output of C1VREF (or 0 for the the associated comp 'in' ext pin)
CM1CON0.1 = 1 'this bit with bit 1 set the external incoming pin - in this case c12in3- (pin 7)
CM1CON0.0 = 1 'this bit with bit 1 set the external incoming pin - in this case c12in3- (pin 7)

'Turn on & Set up Comparator 2
CM2CON0.7 = 1 'Comparator 2 on.
CM2CON0.6 = 0 'C1OUT POLARITY BIT
CM2CON0.5 = 1 '1 = C2OUT is present on the C2OUT pin (pin 6)
CM2CON0.4 = 1 'C1OUT logic is not inverted (1 for inverted)
CM2CON0.3 = 0 'unimplemented
CM2CON0.2 = 1 'Connect internally to the output of C1VREF (or 0 for the the associated comp 'in' ext pin)
CM2CON0.1 = 1 'this bit with bit 1 set the external incoming pin - in this case c12in2- (pin 14)
CM2CON0.0 = 0 'this bit with bit 1 set the external incoming pin - in this case c12in2- (pin 14)

' Setup the internal VREF
VRCON.7 = 1 'Comparator1 CV Ref enable bit
VRCON.6 = 1 'Comparator2 CV Ref enable bit
VRCON.5 = 0 'high or low range 0 = High Range, 1 = low range
VRCON.4 = 0 '0.6V Reference Enable bit ....0 is disabled
VRCON.3 = 0 'these 4 bits set the divider of the VREF ladder 16 values - 7 should yield 1/2 VCC
VRCON.2 = 1 'these 4 bits set the divider of the VREF ladder 16 values - 7 should yield 1/2 VCC
VRCON.1 = 1 'these 4 bits set the divider of the VREF ladder 16 values - 7 should yield 1/2 VCC
VRCON.0 = 1 'these 4 bits set the divider of the VREF ladder 16 values - 7 should yield 1/2 VCC


Comp1Time var word ' used to amalgamate TMR1 High & Low Bytes.
Comp2Time var word

;-- Place a copy of these variables in your Main program -------------------
;-- The compiler will tell you which lines to un-comment --
;-- Do Not un-comment these lines --
;---------------------------------------------------------------------------
wsave VAR BYTE $20 SYSTEM ' location for W if in bank0
;wsave VAR BYTE $70 SYSTEM ' alternate save location for W
' if using $70, comment wsave1-3

' --- IF any of these three lines cause an error ?? ------------------------
' Comment them out to fix the problem ----
' -- Which variables are needed, depends on the Chip you are using --
wsave1 VAR BYTE $A0 SYSTEM ' location for W if in bank1
wsave2 VAR BYTE $120 SYSTEM ' location for W if in bank2
;wsave3 VAR BYTE $1A0 SYSTEM ' location for W if in bank3
' --------------------------------------------------------------------------


INCLUDE "DT_INTS-14.bas" ' Base Interrupt System PO90OOO9
'INCLUDE "ReEnterPBP.bas" ' Include if using PBP interrupts

ASM
INT_LIST macro ; IntSource, Label, Type, ResetFlag?
INT_Handler CMP_INT, _Comp1_Int, ASM, yes
INT_Handler CMP2_INT, _Comp2_Int, ASM, yes
endm
INT_CREATE ; Creates the interrupt processor
ENDASM

T1CON.0= 1 'start timer

Comp1Time = 0 'clear down Comp1Time, prior to starting.
comp2Time = 0 'clear down Comp2Time, prior to starting

@ INT_ENABLE CMP1_INT ; enable Comparator 1 interrupts
@ INT_ENABLE CMP2_INT ; enable Comparator 2 interrupts

'Main body of Code********************************************** ******************
Main:
HSEROUT ["comp1=",dec Comp1Time,9,"comp2Time=", dec comp2time, 13, 10]
PAUSE 80
goto Main
end

'Comparator1 Interrupt Handler+++++++++++++++++++++++++++++++++++++++++++ +++++++++
Comp1_Int:
' Comp1Time.Lowbyte = TMR1L 'Store timer1 Low into a variable
' Comp1Time.Highbyte = TMR1H 'Store timer1 high into a variable.
' TMR1H = 0 'Set the high part of the timer value to 0
' TMR1L = 0 'Set the low part of the timer value to 0
asm
movf TMR1L,w
movwf _Comp1Time+0 ; the lsb
movf TMR1H,w
movwf _Comp1Time+1 ; the msb
clrw
movwf TMR1L
movwf TMR1H
endasm

@ INT_RETURN


'Comparator2 Interrupt Handler+++++++++++++++++++++++++++++++++++++++++++ +++++++++
Comp2_Int:
' Comp2Time.Lowbyte = TMR1L 'Store timer1 Low into a variable
' Comp2Time.Highbyte = TMR1H 'Store timer1 high into a variable.
asm
movf TMR1L,w
movwf _Comp2Time+0 ; the lsb
movf TMR1H,w
movwf _Comp2Time+1 ; the msb
endasm

@ INT_RETURN

HankMcSpank
- 29th September 2010, 14:39
Hi Jerson,

So am I getting this right - you are using DT's interrupt as the 'trigger' to enter an actual routine that has ASM within?

Kind of a like hybrid solution?

Apologies for the naive line of questioning, but what's the pros cons of the (3?) type of interrupts available to user of Picbasic....

PICbasic Interrupts
DT's Interrupts,
ASM Interrupts.

hi Paul,

i'm digging that idea about keeping it simple - once we've got to the bottom of the 'overhead', I can think about putting the solution back as an interrupt.

Great stuff guys - I've got a new spring in my step with all this.

Jerson
- 29th September 2010, 14:47
Apologies for the naive line of questioning, but what's the pros cons of the (3?) type of interrupts available to user of Picbasic....

PICbasic Interrupts
DT's Interrupts,
ASM Interrupts.


No need for apologies. We all walked your way once upon a time.....

PBP interrupts - the worst kind - you use them when you cannot think of anything else.
It is as cruel as - execute one PBP statement, look for an interrupt, and repeat
So, the real time nature of interrupts is lost.

DT's interrupts - a superb wrapper to ease programmer pain
Just use it and get your job done without having to bother with registers and internals connected with the particular interrupt you need.

ASM interrupts - for those who like to drive Ferrari
You know to drive it, don't look back. It is the fastest one there is!!! But you need to know how to ....

Your best bet, DT_ints is your friend ;)

Cheers

HankMcSpank
- 29th September 2010, 15:11
Thanks for the clarification...


No need for apologies. We all walked your way once upon a time.....

Your best bet, DT_ints is your friend ;)



Except it seem most are now telling me to use ASM

Does this mean I'm being unfaithful?!!

Darrel - it's not you, i promise ...it's me.:D

mark_s
- 29th September 2010, 16:16
These are some really good ideas comming forth. I can try Paul's idea, of one interupt
and poll the the second in the ISR. I am already polling the CMCON.6 and 7. This should
cut the processing time by a good bit.

Something occured to me, since you are using schmitt triggers on the inputs, you also don't need to use the comparator interupts. You might find the CCP modules could do the same thing in a more effective way? Or change on portb. This also allows you to use other pics that don't have the comparators.

I never put togeather my phase shifter. Need to get back on it.

Interesting project!

prstein
- 29th September 2010, 16:17
Hi Jerson,

Your right, of course, in everything you said. However there is a place for PBP interrupts. First, for beginners (and even people who, like me, use the absolute minimum of assembly) it is simple, straightforward, and easy.

Second, that high overhead isn't a big factor if used judiciously. For example, here's the entire code for the main loop of one of my products:

On Interrupt goto IntHandler
INTCON.1 = 0 'clear the INT0 flag
PIR1.5 = 0 'clear the USART interrupt
enable
'#############
' Start of Main Loop
'#############
Start:
if USB_ON = 1 then
bConnected = true
else 'USB_ON = 0
bConnected = FALSE
endif

if bConnected = false and bStayInFastMode = false then
OSCCON = %10000000 '32kHz, periperals should run in sleep mode
endif
@ nop
@ sleep 'snooze soundly until woken
goto Start
'############
' End of Main Loop
'############
disable


The main loop checks to see if USB (through an FTDI serial converter) is connected, and if so it stays awake. Otherwise it goes to sleep until the interrupt wakes it (which in this case happens to be either a pulse from an external RTC or data in the serial buffer). The other ~1000+ lines of code are after the "disable" so the PBP interrupt overhead is not in them. I'm sure there are other ways to accomplish the same end, but the above works perfectly. I probably could have moved the ENABLE to just before the "@ nop" and saved another few bytes even.

Incidentally, @ nop and @ sleep are the full extent of my assembly knowledge...8^)

With all that said, I really end up not using interrupts very much at all (except to wake a sleeping PIC). What I do is look at the interrupt flags when convenient for the code. This keeps it nice and simple and controlled. Like I said before, I am a simple man.

Again, I'm not disagreeing or disputing anything you said, just trying to carve out some justification for PBP ints for us simpler folk. It just has to work, it doesn't (always) have to be pretty. Ferraris are nice I suppose, but I hear they're high-maintenance and sort of temperamental. I have a real good time kicking around on my little scooter; it certainly gets me there. If someone told me I had to use ASM ints I would have given up before I started.

I really need to look into these DT Ints that everybody keeps talking about...

Best Regards,
Paul

Jerson
- 29th September 2010, 17:10
Hi Jerson,

there is a place for PBP interrupts. First, for beginners (and even people who, like me, use the absolute minimum of assembly) it is simple, straightforward, and easy.

[/CODE]

The main loop checks to see if USB (through an FTDI serial converter) is connected,

Incidentally, @ nop and @ sleep are the full extent of my assembly knowledge...8^)

I really end up not using interrupts very much at all (except to wake a sleeping PIC). What I do is look at the interrupt flags when convenient for the code. This keeps it nice and simple and controlled. Like I said before, I am a simple man.

I have a real good time kicking around on my little scooter; it certainly gets me there. If someone told me I had to use ASM ints I would have given up before I started.


Hey Paul

Sorry if I stepped on some toes here. In fact, I was seriously thinking of editiing my post, but, history has had me earlier too ;) So, this is one boo-boo from me to stay here for posterity to see :)

Ok, I understand the use of ON INTERRUPT as you use it. My point was about driving home the real-time performance hit and how high the latencies can be. Waking a PIC from sleep? LoL You must be doing much more than that for sure.

I do not want my post to sound discouraging. Obviously, that will be the first thing a newbie will try when they want interrupts (because.... it is in the Manual)

I do not have a ferrari either and hope I could buy something better than my old faithful scooter which I have just put up for sale :))
http://www.youtube.com/watch?v=c-tpM_AkfOo

Just to make it clear, I learn new things out here almost everyday. Some of the folks who call themselves newbies and amateurs really astonish me with their coding style. If they only start giving back the little they know, I'm sure they will learn a lot more.

But, seriously, if you want good realtime performance out of a PIC, DT_ints is the way to go. One more plug for DT_ints

Cheers

prstein
- 29th September 2010, 18:02
Hi Jerson,

None of my toes were stepped upon; I appreciate that you presented the the opportunity to discuss my own take on interrupts and regret if I came off as defensive. Mr. McSpank appears to be quite sharp but I worry a little about making the learning curve too steep.

I truly appreciate reading your posts--please don't hold back because of me!

Your scooter looks nice but the shipping charges would be prohibitive...and my Kymco would be jealous.

Best Regards,
Paul

HankMcSpank
- 29th September 2010, 19:17
So on the picture and data on post#15 things were looking honky-dory (you were measuring at least down to 161 counts on CMP2). Your new posts state that now CMP2 will not go below 336. Basic question; what changed?


Apologies...I've only just seen your question from a day or so ago - firstly that's very sharp of you to pick up on that detail

What changed?

I wish I knew....when it looked hunky dory...I didn't probe to deeply (I guess the - at the time - relief overcame me & I was a bit premature saying all was well!), I sort of put it to one side...a couple of nights later I came back to it & couldn't get the darned count down.

I've not changed the circuit....and have only window dressed the code (I can't genuinely say what I might have benn window dressed - but it will have been minimal!), but early indications here since getting home is that it might be related to the amount of text I'm squirting out with the HSEROUT command (which I queried myself the other night...and acetronics spotted earlier today!)

I only had a slim window of opprtunity before domestic commitments kicked in....but when 1000Hz (a nice round number to work with) was fed into comp1 I saw the count drop from 5,000 to something like 4600 when I put an Hserout in amongst the count storing! So is it related to amount of debug text I wonder?

I'm using Paul's excellent idea of keeping it simple & plagiarising the simple code he kindly provided (less his one typo! :D ) ...first step is to get a good solid Comp 1 derived timer count for 1000Hz (which @20Mhz should be 5000 - and indeed it is using paul's code )...I'll then augment from there (ie bring comp2 into play)

HankMcSpank
- 29th September 2010, 21:53
Ok, success (of sorts)

By keeping it simple, this code works (many thanks to Paul, whose code sample I used, albeit in McSpank-esque form here)...



PIR2.5 = 0 'Clear the Comp1 interrupt flag
Comp1Time = 0

Main_Loop:
WHILE PIR2.5 = 0
GOTO Main_Loop
WEND
TMR1H = 0
TMR1L = 0
PIR2.5 = 0
Comp1_count:
WHILE PIR2.5 = 0
goto Comp1_count
wend
Comp1Time.Highbyte = TMR1H
Comp1Time.Lowbyte = TMR1L
TMR1H = 0
TMR1L = 0
PIR2.6 = 0
'HSEROUT [dec Comp1Time,13, 10]
Comp2_count:
WHILE PIR2.6 = 0
goto Comp2_count
wend
Comp2Time.Highbyte = TMR1H
Comp2Time.Lowbyte = TMR1L
TMR1H = 0
TMR1L = 0
HSEROUT ["C1=",dec Comp1Time,9,"C2=", dec comp2time, 13, 10]
PIR2.5 = 0
goto Main_Loop


ie the C2 count decrements nice & smoothly (& below 336 - yay!) & totally in line with the amount of phase lag....

(green trace is comp1 - non lagged, yellow trace is into comp2 & lagged a miniscule amout)

http://img340.imageshack.us/img340/2760/phewj.jpg (http://img340.imageshack.us/i/phewj.jpg/)

.....I can get all the way down to 13-14(ish) below which the C2 count gets very jittery (ie successive counts eg 5, 11, 6, 12, 5, - you can see some 19s in that stream above)....but a comp2 count of 13 @1kHz is granular enough for me (accuracy to within 0.936 degrees @1kHz). I'm elated...*BUT*

Quite clearly my original way is causing some issues (p& almost certainly to do with my poor sequence &/or coding), so if this way of doing it works, how can I integrate it into my main program?

My first attempt just now failed...

Here's the screen output...

C1=4909 C2=13
C1=4903 C2=13
C1=4909 C2=13
C1=4909 C2=13
hello
C1=4903 C2=13
C1=4909 C2=13

ie that C1 count has dropped?!! (even though the incoming frequency has remained at 1Khz)

here's what I did (disclaimer: I didn't know how to get the main body of program to 'jump' to to the interrupt handler when Comp1 interrupted, so I deferred back to DT's method as the initial 'trigger')...



Comp1Time var word ' used to amalgamate TMR1 High & Low Bytes.
Comp2Time var word


INCLUDE "DT_INTS-14.bas" ' Base Interrupt System PO90OOO9
INCLUDE "ReEnterPBP.bas" ' Include if using PBP interrupts

ASM
INT_LIST macro ; IntSource, Label, Type, ResetFlag?
INT_Handler CMP1_INT, _Comp1_Int, PBP, yes
endm
INT_CREATE ; Creates the interrupt processor
ENDASM

T1CON.0= 1 'start timer

Comp1Time = 0 'clear down Comp1Time, prior to starting.
comp2Time = 0 'clear down Comp2Time, prior to starting

@ INT_ENABLE CMP1_INT ; enable Comparator 1 interrupts
PIR2.5 = 0 'Clear the Comp1 interrupt flag
Comp1Time = 0
Comp2Time = 0

'Main body of Code********************************************** ******************

Main:
hserout ["hello", 13, 10]
pause 20
goto Main

END

'**********************Comp1 interrupt handler******************************************* **************
Comp1_Int:
Comp1_Loop:
WHILE PIR2.5 = 0
GOTO Comp1_Loop
WEND
TMR1H = 0
TMR1L = 0
PIR2.5 = 0
Comp1_count:
WHILE PIR2.5 = 0
goto Comp1_count
wend
Comp1Time.Highbyte = TMR1H
Comp1Time.Lowbyte = TMR1L
TMR1H = 0
TMR1L = 0
PIR2.6 = 0
'HSEROUT [dec Comp1Time,13, 10]
Comp2_count:
WHILE PIR2.6 = 0
goto Comp2_count
wend
Comp2Time.Highbyte = TMR1H
Comp2Time.Lowbyte = TMR1L
TMR1H = 0
TMR1L = 0
HSEROUT ["C1=",dec Comp1Time,9,"C2=", dec comp2time, 13, 10]
PIR2.5 = 0

@ INT_RETURN

languer
- 29th September 2010, 21:55
This is most excellent.

You may have already corrected this, but on the original code the analog inputs for the comparators are not enabled (ANSEL.6 and ANSEL.7). Minor thing, but shouldn't hurt to follow the datasheet.

I do agree with other suggestions, since the inputs are already digital there is not need to use the comparators. But to use the IOC (interrupt-on-change) you would most likely need to go for PORTA or PORTB (which have individual IOC bits for each input).

General suggestion for interrupt use: keep everything short; do only the absolute minimum inside the interrupt routine.

Even though the use of the HW USART (HSEROUT) is a plus, you should still try to do this outside the interrupt routine. Do the timer stuff inside the interrupt (i.e. clear and set timer, etc). Make a flag that tells you the interrupt "fired"; and in the main loop manipulate the data to be sent out (and send it out). I think the CCP module maybe a much better candidate for what you are doing (duh, except you would need a PIC with two CCP modules - never mind).

HankMcSpank
- 29th September 2010, 22:17
You may have already corrected this, but on the original code the analog inputs for the comparators are not enabled (ANSEL.6 and ANSEL.7). Minor thing, but shouldn't hurt to follow the datasheet.


I had thought the ANSEL turned on/off the AtoD for a given pin - certainly my comparators have been working with the corresponding ANSEL deselected? I had a similar discussion with Melanie & she considered them digital....

http://www.picbasic.co.uk/forum/showthread.php?t=12196&p=81127#post81127 extract here....



Whether you consider a Comparator OUTPUT as analog I suppose is up to you. I consider Comparators INPUT as ANALOG but technically their OUTPUT is DIGITAL since it can only be a High or a Low. RC4 is the OUTPUT for Comparator 2. If you are using Comparator 2, you DON'T want to set that pin to ANALOG (because ANALOG refers really only to INPUTS), but you want DIGITAL OUTPUT.


Since then I've assumed digital & therefore deselected the analog!!




I do agree with other suggestions, since the inputs are already digital there is not need to use the comparators. But to use the IOC (interrupt-on-change) you would most likely need to go for PORTA or PORTB (which have individual IOC bits for each input).


I already use IOC (for switching) in the program that this melarkey is destined for - in my blissfully naive planet of "don't understand", for now, I'd prefer to keep the two interrupt variants apart! (for fear of IOC contention)



General suggestion for interrupt use: keep everything short; do only the absolute minimum inside the interrupt routine.


I would agree with that, but what I've found is that unless I do stuff inside the routine, then the counts go to pot! (that's not to say I shouldn't strive to do such stuff outside the routine, but as of yet, I'm having the most success keeping it 'controlled' where I know where/when it's going to be used!



Even though the use of the HW USART (HSEROUT) is a plus, you should still try to do this outside the interrupt routine. Do the timer stuff inside the interrupt (i.e. clear and set timer, etc). Make a flag that tells you the interrupt "fired"; and in the main loop manipulate the data to be sent out (and send it out). I think the CCP module maybe a much better candidate for what you are doing.

I'd love to get that HSEROUT outside....but I think therein lies the possible original problem?

It would be nice to slowly build the picture up to see when the trouble occurs, but to do that I need some clever person to tell me how I can 'trigger' the code that 'works' above into action while the main program is running?

HankMcSpank
- 30th September 2010, 00:11
Ok, this seems to work (ie it allows a 'main body' of program to run, with an interrupt handler)....




Comp1Time var word
Comp2Time var word
phase_shift var word
c var word
dummy var word


T1CON.0= 1 'start timer

Comp1Time = 0 'clear down Comp1Time, prior to starting.
comp2Time = 0 'clear down Comp2Time, prior to starting

PIR2.5 = 0 'Clear the Comp1 interrupt flag

INCLUDE "DT_INTS-14.bas" ' Base Interrupt System PO90OOO9
INCLUDE "ReEnterPBP.bas" ' Include if using PBP interrupts

ASM
INT_LIST macro ; IntSource, Label, Type, ResetFlag?
INT_Handler CMP1_INT, _Comp1_Int, PBP, yes
endm
INT_CREATE ; Creates the interrupt processor
ENDASM

T1CON.0= 1 'start timer

Comp1Time = 0 'clear down Comp1Time, prior to starting.
comp2Time = 0 'clear down Comp2Time, prior to starting

@ INT_ENABLE CMP1_INT ; enable Comparator 1 interrupts

Top:
c = 3600
dummy = comp2time * c
phase_shift = DIV32 comp1time
HSEROUT [" C1=",dec Comp1Time,9," C2=", dec comp2time,9, "phase_shift=", dec phase_shift/10,".",dec phase_shift//10, 13, 10]
goto top

Comp1_Int:
PIR2.5 = 0
Loop1:
WHILE PIR2.5 = 0
GOTO Loop1
WEND
TMR1H = 0
TMR1L = 0
PIR2.5 = 0

Comp1_count:
WHILE PIR2.5 = 0
goto Comp1_count
wend
Comp1Time.Highbyte = TMR1H
Comp1Time.Lowbyte = TMR1L+2
TMR1H = 0
TMR1L = 0
PIR2.6 = 0
'HSEROUT [dec Comp1Time,13, 10]

Comp2_count:
WHILE PIR2.6 = 0
goto Comp2_count
wend
Comp2Time.Highbyte = TMR1H
Comp2Time.Lowbyte = TMR1L+6
TMR1H = 0
TMR1L = 0
if comp2time> comp1time-23 then comp2time = 0
'HSEROUT ["C1=",dec Comp1Time,9,"C2=", dec comp2time, 13, 10]
PIR2.5 = 0
@ INT_RETURN



The actual final phase calculation jitters about a bit (but I suspect that down to the somewhat jittery quality of my audio input signal into the PIC comparators)...

http://img43.imageshack.us/img43/6195/finalxz.jpg (http://img43.imageshack.us/i/finalxz.jpg/)

Now I'm sure there are more efficient ways of doing the above - but for now, it work s& I'm chuffed!

The actual final phase calculation jitters about a bit (but I suspect that down to the somewhat jittery quality of my audio input signal into the PIC comparators!)...

http://img43.imageshack.us/img43/6195/finalxz.jpg (http://img43.imageshack.us/i/finalxz.jpg/)


Listen guys - a great collaborative effort there - I can't thank you all enough....hopefully this gives a bit of closure to a long & painful saga!

prstein
- 30th September 2010, 00:48
Hi Hank,

One more suggestion...

It takes a long time (relatively speaking) to send out your serial string. In PBP Ints, I'm pretty sure the interrupt is not responded to until the HSEROUT line is finished. So if code space isn't a particular issue at this time, try changing

HSEROUT [" C1=",dec Comp1Time,9," C2=", dec comp2time,9, "phase_shift=", dec phase_shift/10,".",dec phase_shift//10, 13, 10]

to (get ready for it...)

HSEROUT [" "]
HSEROUT ["C"]
HSEROUT ["1"]
HSEROUT ["="]
HSEROUT [dec Comp1Time]
HSEROUT [9]
HSEROUT [" "]
HSEROUT ["C"]
HSEROUT ["2"]
HSEROUT ["="]
HSEROUT [dec comp2time]
HSEROUT [9]
HSEROUT ["p"]
HSEROUT ["h"]
HSEROUT ["a"]
HSEROUT ["s"]
HSEROUT ["e"]
HSEROUT ["_'}
HSEROUT ["s"]
HSEROUT ["h"]
HSEROUT ["i"]
HSEROUT ["f"]
HSEROUT ["t"]
HSEROUT ["="]
HSEROUT ["dec phase_shift/10]
HSEROUT ["."]
HSEROUT [dec phase_shift//10]
HSEROUT [13]
HSEROUT [10]

Or/and shorten what you are sending....

Best Regards,
Paul

languer
- 30th September 2010, 18:05
This is an excellent work, and very easy to follow and read through. You should consider placing this in the Wiki or Project section.

In a sort of backwards way, I had one of those aha moments out of this thread.


I had thought the ANSEL turned on/off the AtoD for a given pin - certainly my comparators have been working with the corresponding ANSEL deselected? I had a similar discussion with Melanie & she considered them digital....
This is absolutely true for the outputs (i.e. comparator output) as they will be either high or low. This is, however, not true for the inputs (i.e. comparator input) as your are using their analog nature to compare (in your case) against a reference (this is also alluded to on the post you mention);which is what I was referring to (ANSEL.6 and ANSEL.7). In summary, comparator outputs are digital, comparator inputs are analog.

This is what was nagging me; something does not look correct, yet it behaves correctly (maybe). Then came the aha moment. You run the analog signal through the schmidt triggers effectively converting it from analog to digital. Then you feed the signal into the comparator. So why do you even need a comparator? You really don't, BUT, for your system you really need some sort of comparator (not an analog one, a digital one so you can trigger on its change of state). And, perhaps unbeknownst to you, that's what you created. By letting the comparator input pins be digital, you created a digital comparator (sort of like a logic gate) - very cool.

With that said, you probably could save the schmidt triggers and run the analog waveforms straight to the comparator (they would have to be conditioned / buffered before hitting the comparator). But you have the concept working, so I would not change it now.

Again, very nice job.

HankMcSpank
- 30th September 2010, 18:56
This is an excellent work, and very easy to follow and read through.

With that said, you probably could save the schmidt triggers and run the analog waveforms straight to the comparator (they would have to be conditioned / buffered before hitting the comparator). But you have the concept working, so I would not change it now.

Again, very nice job.

Thanks...& as it goes I'm pleased how the contributions rolled in - all had value...( and also gave me a 'second wind' so to speak) .

In the end I do take the analogue sine waves, square them up by amplifiyng it a *lot* (my circuit will have a couple of unused opamp stages, so I figured it would save component space by using those vs Schmitts) ...then push it into the PIC comparators (so no schmitts were harmed in the making of this thread anymore).

It works, but I'm not sure how robust the resulting comparator triggering will be under certain circumstances - early on in the thread there were defintiely some severe phase outcome errors when the analog signals into the comparators were not of the same magnitude ...obvious in retrospect, but when you're getting wrapped up with all manner of new learning experiences blah blah (I now need to spend a bit more quality time, trying different permutations)

My last workin g code I posted (late last night)....it all works, biut I'm sure seasoned programmers wince at the way I'm using DT's stuff - not sure one little bit if that's an acceptable practise. For example, where I setup the interrupts....



ASM
INT_LIST macro ; IntSource, Label, Type, ResetFlag?
INT_Handler CMP1_INT, _Comp1_Int, PBP, yes
endm
INT_CREATE ; Creates the interrupt processor
ENDASM


the last two columns...are they right? (Hank makes a mental note -hmm, really must get around to learning programming properly, rather than winging/kludging/pleading whenever a need arrives ...and that includes interrupts!)

Being an onld electronics type, I too initially related a PIC's comparator inputs to being analog devices...and to tell you the truth I'm still not sure if from a PIC perspective, whether its comparator inputs are analog or digital...I just roll with what works...and deselecting ANSEL for the pins involved works! (perhaps someone can chime in what the true definition of ANSEL funtion is - ie is it purely related to the AtoD functionality or does it mean 'analogue' in general?)


PS tks Paul for the stuff about HSerout - & whilst that 'tip' is somewhat ugly on the eye, it all makes complete sense!

languer
- 30th September 2010, 20:12
perhaps someone can chime in what the true definition of ANSEL funtion is - ie is it purely related to the AtoD functionality or does it mean 'analogue' in general?
I thought I answered this in the previous post. In any case, ANSEL controls whether the respective PIC inputs are set to analog or digital. It does not care about the ADC. Other registers allow you to decide whether the respective analog input is used for the ADC or the Comparator, or ...

In your case you lucked out because even though you did not set the inputs to analog, you had already squared up the inputs to make them sort of digital.

HankMcSpank
- 30th September 2010, 20:34
I thought I answered this in the previous post. .

Apologies, I'd picked up (misread), that there was still a degree of uncertainty!



In your case you lucked out because even though you did not set the inputs to analog, you had already squared up the inputs to make them sort of digital.

That sums it up nicely - got it! (that said, what you're saying would suggest, it wouldn't be the comparator' Internal VREF deciding the 'flip/trigger point', but the PIC's internal high low digital detection circuitry? (ie when is a '1' a 1' and when is it a '0')