Measuring audio phase shift through a circuit with a PIC


Closed Thread
Results 1 to 40 of 50

Hybrid View

  1. #1
    Join Date
    Mar 2009
    Posts
    653

    Default Measuring audio phase shift through a circuit with a PIC

    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)



    Code:
    @ __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
    Last edited by HankMcSpank; - 5th September 2010 at 00:29.

  2. #2
    Join Date
    Nov 2005
    Location
    Bombay, India
    Posts
    966


    Did you find this post helpful? Yes | No

    Default

    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

  3. #3
    Join Date
    Mar 2009
    Posts
    653


    Did you find this post helpful? Yes | No

    Default

    Quote Originally Posted by Jerson View Post
    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/show...3540#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/calcula...delayphase.htm

    = 36 degrees.
    Last edited by HankMcSpank; - 7th September 2010 at 10:28.

  4. #4
    Join Date
    Mar 2009
    Posts
    653


    Did you find this post helpful? Yes | No

    Default

    Quote Originally Posted by Jerson View Post
    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!

  5. #5
    Join Date
    Mar 2009
    Posts
    653


    Did you find this post helpful? Yes | No

    Default

    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...



    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)...

    Code:
    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....
    Code:
    @ 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
    Last edited by HankMcSpank; - 11th September 2010 at 15:43.

  6. #6
    Join Date
    Nov 2005
    Location
    Bombay, India
    Posts
    966


    Did you find this post helpful? Yes | No

    Default

    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

  7. #7
    Join Date
    Mar 2009
    Posts
    653


    Did you find this post helpful? Yes | No

    Default

    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.

Members who have read this thread : 0

You do not have permission to view the list of names.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts