Rough 'n Ready Audio Frequency extraction with a PIC


Closed Thread
Results 1 to 34 of 34
  1. #1
    Join Date
    Mar 2009
    Posts
    653

    Default Rough 'n Ready Audio Frequency extraction with a PIC

    I've read a fair bit about this, but I'd like a bit of guidance & a few concepts confirmed or corrected!

    I'd like to be able to establish which note is being played on a guitar. I'm only interested from the 'D' string upwards.

    The D string frequency is 146.8Hz - which means that one cycle takes 6.81ms. Ok, that's the bit I'm sure about - the rests is fuzzy !

    I don't want a lot of complication in the supporting electronics -so ideally I'd like to use the internal oscillator (yes, I know it's rough & ready & not particularly accurate, but I'm not going to be using this to tune the guitar).

    I'd thought about using the comparator & basically counting how many cycles have been received in a given time frame - I'd like the time frame to be as low as possible.

    Re PICs & floating point - I believe PICs don't do FP - but I'm not sure of the impact to my situation....I'm figuring that the PIC dispenses with the decimal places & just give the integer? (does it round up or just ignore them all together?)

    I've created a table to show the lowest possible sampling time frame to be able to get a unique integer for each note, which for this situation works out at about 100ms...

    (btw: I didn't know how to format the Excel cells to look ok on here, so I used the code tags - which seems to work ok!)

    Code:
    Note	Freq		Cycles in 100mS
    D	146.83		14.683
    D#	155.56		15.556
    E	164.81		16.481
    F	174.61		17.461
    F#	185		18.5
    G	196		19.6
    G#	207.65		20.765
    A	220		22
    A#	233.08		23.308
    B	246.94		24.694
    C	261.63		26.163
    C#	277.18		27.718
    D	293.67		29.367
    D#	311.13		31.113
    E	329.63		32.963
    F	349.23		34.923
    F#	369.99		36.999
    G	392		39.2
    G#	415.3		41.53
    A	440		44
    A#	466.16		46.616
    B	493.88		49.388
    C5	523.25		52.325
    C#	554.37		55.437
    D	587.33		58.733
    D#	622.25		62.225
    E	659.26		65.926
    F	698.46		69.846
    F#	739.99		73.999
    G	783.99		78.399
    E	329.63		32.963
    F	349.23		34.923
    F#	369.99		36.999
    G	392		39.2
    G#	415.3		41.53
    A	440		44
    A#	466.16		46.616
    B	493.88		49.388
    C5	523.25		52.325
    C#	554.37		55.437
    D	587.33		58.733
    D#	622.25		62.225
    E	659.26		65.926
    F	698.46		69.846
    F#	739.99		73.999
    G	783.99		78.399
    G#	830.61		83.061
    A	880		88
    A#	932.33		93.233
    B	987.77		98.777
    C	1046.5		104.65
    C#	1108.74		110.874
    D	1174.66		117.466
    D#	1244.5		124.45
    E	1318.52		131.852
    As you can see, by using 100mS, the sample period 'integer' is unique (ie for the number of complete cycles completed).

    It should then be a case of a simple Look up Table to work out which note is being played, eg...

    If Sample count = 14 then D note is being played.
    if Sample count = 22 the 'A' note is being played.

    And so on.

    So at this embryonic stage...

    Is my line of thought correct?

    Is there any clever workaround available meaning I could bring that 100mS sampling period down?
    Last edited by HankMcSpank; - 7th March 2010 at 11:15.

  2. #2
    Join Date
    May 2004
    Location
    NW France
    Posts
    3,614


    Did you find this post helpful? Yes | No

    Wink Make it simple ???

    Hi, Hank

    Why not use a simple NE 567, which will do all the job ???

    Alain
    ************************************************** ***********************
    Why insist on using 32 Bits when you're not even able to deal with the first 8 ones ??? ehhhhhh ...
    ************************************************** ***********************
    IF there is the word "Problem" in your question ...
    certainly the answer is " RTFM " or " RTFDataSheet " !!!
    *****************************************

  3. #3
    Join Date
    Mar 2009
    Posts
    653


    Did you find this post helpful? Yes | No

    Default

    Quote Originally Posted by Acetronics View Post
    Hi, Hank

    Why not use a simple NE 567, which will do all the job ???

    Alain
    Were the fun in that?!

    Seriously, I've already got a PIC in my circuit with a fair amount of unused pins...I could quite readily utilize one of those pins to give me the note info - I don't need much in the way of granularity - just frequency resolution down to rough semitones, which I reckon the PIC I'm using ought to be able to establish reasonably easily. I also don't want to add in more components if possible (hence using the PIC since it's already there!)

  4. #4
    Join Date
    Jan 2009
    Location
    Miami, Florida USA
    Posts
    644


    Did you find this post helpful? Yes | No

    Default

    originally posted by hankmcspank

    Were the fun in that?!
    ???????????????

    Robert

  5. #5
    Join Date
    May 2004
    Location
    NW France
    Posts
    3,614


    Did you find this post helpful? Yes | No

    Default

    Quote Originally Posted by rsocor01 View Post
    ???????????????

    Robert

    just wait and see ...

    and finally laugh.

    Alain
    ************************************************** ***********************
    Why insist on using 32 Bits when you're not even able to deal with the first 8 ones ??? ehhhhhh ...
    ************************************************** ***********************
    IF there is the word "Problem" in your question ...
    certainly the answer is " RTFM " or " RTFDataSheet " !!!
    *****************************************

  6. #6
    Join Date
    Mar 2009
    Posts
    653


    Did you find this post helpful? Yes | No

    Default

    Quote Originally Posted by rsocor01 View Post
    ???????????????

    Robert
    I meant if simply used dedicated ICs to solve each and every one of my needs - I wouldn't learn anything about PIC programming (the fun bit)

    Quote Originally Posted by Acetronics View Post
    just wait and see ...

    and finally laugh.

    Alain
    it was a meant to be a quip.

    If I'm to grasp PIC programming, then I need a small bite sized goal/challenges (& grasp some concepts)...sure there's normally dedicated h/w for just about everything nowadays, but I won't learn much if I simply throw in a dedicated IC for each & every requirement....hence my line of questiong & reluctance to add in extra h/w,
    Last edited by HankMcSpank; - 7th March 2010 at 13:45.

  7. #7
    Join Date
    Sep 2007
    Location
    USA, CA
    Posts
    271


    Did you find this post helpful? Yes | No

    Default

    If you have a super clean sine wave signal with no distortion, you might be able to do this with a pic that is also performing other tasks. Otherwise, it is a very difficult task involving FFT or DFT programming--certainly not the standard place to start learning PIC programming. (And PBP is a pretty code-heavy compiler, so it may not be able to process the data fast enough, even as slow as 150Hz.)

    The biggest drawbacks I see to the NE567 is high voltage requirement and high current draw. On the other hand, it is the perfect simple solution to a potentially challenging problem.

  8. #8
    Join Date
    Mar 2009
    Posts
    653


    Did you find this post helpful? Yes | No

    Default

    Well it's not my first PIC project - that was PIC AGC...then I got the relaxtaion ocillator setup to do PIC capaciitve touch - but this is my first exploration into frequency counters (ie my latest goal if you like)

    Re the waveform, I'd be trying to glean the frequency after the complex harmonics have dropped out, here's the waveform (it's been amplified to get it up nearer the 5V that the PIC prefers) ....



    ...pretty clean & pretty sine-waveish too!

    ok, if you think it's do-able, I'll push on & give it a pop.

    So what would be the best way to set up a 100mS sampling 'window' to see how many transitions (cycles) the comparator has toggled in that time?
    Last edited by HankMcSpank; - 7th March 2010 at 16:55.

  9. #9
    Join Date
    Feb 2006
    Location
    Gilroy, CA
    Posts
    1,530


    Did you find this post helpful? Yes | No

    Default

    Well that sure does look clean! For your rough and ready approach, how about a simple count command like this:

    COUNT PORTB.1,100,W1

    That will count pulses during a 100ms period. Other way to do it for more accuracy is interrupts. But as you say, you just are going to do a lookup table.

    Edit:
    For the accuracy you need between D and D# etc, you would need to sample for 1 second using this method. If that's too long, you would have to go with interrupts, and measure time between pulses. Another quick and dirty try might be pulsin? Not sure how well that would work on an analog signal though. Maybe you could average a few...
    Last edited by ScaleRobotics; - 7th March 2010 at 17:37.

  10. #10


    Did you find this post helpful? Yes | No

    Default

    Here's another approach.
    You can use timer 1 as a frequency counter. Not sure if will be accurate enough? You did not say which pic you are using? Timer 1 external input is normally on portc. There should be a way to route the comparator output to tmr1? You will have to play with the pause statement to calibrate the output ie "pause 97" to compensate for the delays in pbp.


    CCP1CON = %00000101 ' Capture every rising edge
    T1CON = %00000011 ' No prescale/Osc off/Sync on/External source/TMR1 on

    Freq var word ' 100ms count result

    LOOP:

    TMR1H = 0 ' Clear Timer1 high 8-bits
    TMR1L = 0 ' Clear Timer1 low 8-bits
    T1CON.0 = 1 ' Start 16-bit timer
    Pause 100 ' Capture 100 mS of Input
    T1CON.0 = 0 ' Stop 16-bit Timer
    Freq.BYTE0 = TMR1L ' Read Low 8-bits
    Freq.BYTE1 = TMR1H ' Read High 8-bits

    GoTo LOOP

  11. #11
    Join Date
    May 2004
    Location
    NW France
    Posts
    3,614


    Did you find this post helpful? Yes | No

    Wink

    Hi, Hank

    Tenaja perfectly told what I was thinking to ... except it exists a CMOS version of the 567 ... much less consuming.

    now, if you measure a nice filtered sinewave ... without distortion. you must have calculated a very, very, very nice filter ... ( I'm not talking about harmonics, but other very close frequencies that can be mixed with ...).


    using the comparator section to create an interrupt , or better the CCP capture feature ... will give you the info with less than a cycle delay ...

    You call that " fun " ... small gambler you are !!!

    Alain
    ************************************************** ***********************
    Why insist on using 32 Bits when you're not even able to deal with the first 8 ones ??? ehhhhhh ...
    ************************************************** ***********************
    IF there is the word "Problem" in your question ...
    certainly the answer is " RTFM " or " RTFDataSheet " !!!
    *****************************************

  12. #12
    Join Date
    Mar 2009
    Posts
    653


    Did you find this post helpful? Yes | No

    Default

    Guys...thanks for your input - I'll take time later to consider them all in detail.

    In the meantime, a bit more info.

    I need to capture the frequency soon after the string is plucked but not too soon else too many complex harmonics are still embedded in the signal. I'd imagine coming in to make my 'sample' about 300ms after the pluck (I have a preset PIC threshold that when breached assumes a note has been plucked, which will allow me to pause 300ms before sampling).

    I can't imagine going past 500ms after the note has been plucked (else the note will have decayed too much) so this only gives a maximum window of about 200ms to glean what the frequency is.

    If it helps, I'm using a 16F690.

    Acetronics - just seen your post - I'm using a sustainer which thankfully drops out all harmonics and leaves a relatively pure fundamental ( http://www.youtube.com/user/hankmcsp.../0/u6gm5fY1wM0 ie a nice sinewave!) Wrt to sustain - some frquencies are quite tricky to sustain, so I need to know when they're plucked to trigger special handling
    Last edited by HankMcSpank; - 7th March 2010 at 19:05.

  13. #13
    Join Date
    Mar 2009
    Posts
    653


    Did you find this post helpful? Yes | No

    Default

    Got to laugh - 16f690 ...absolute mare for a noobesque personage like meself..

    I wouldn't mind but I've tackled so much with that PIC variant...but comparators? WTF?! The datasheet has ma heed buzzin.

    I've decided a nice easy entry into the world of comparators is now going to be with a simpler 12F683. Once I nail the concepts with that, then I shall take a few swigs of stiff whisky & get out the 16F690 datasheet again - were microchip having a laugh with that on?

  14. #14
    Join Date
    Jul 2003
    Location
    Colorado Springs
    Posts
    4,959


    Did you find this post helpful? Yes | No

    Default

    I've thought about doing something like that too,

    And I think without external hardware and using the comparator, the DFT idea Josuetas was working with a while back might have the best chance at detecting the notes.
    It may even be able to detect multiple notes at the same time (chords).

    I played with the spreadsheet, but didn't try any hardware.

    And whether the resolution will be tight enough to distinguish 9/8, 10/9 and 16/15ths steps, I don't know either.
    It was originally for DTMF tones.

    Might be worth a look though.
    http://www.picbasic.co.uk/forum/show...2606#post42606

    And the original AppNote (AN257) ...
    http://www.microchip.com/stellent/id...pnote=en024294

    <br>
    Last edited by Darrel Taylor; - 7th March 2010 at 23:45.
    DT

  15. #15
    Join Date
    Mar 2009
    Posts
    653


    Did you find this post helpful? Yes | No

    Default

    Thanks Darrel....I'll check it out later.

    So here's the ludicrously simple 'get me up & running' setup I have (lifted/edited fromthe Microchip Tips' n trick sheet)



    I put a sawtooth from a sig gen on the comparator input, but get nowt on the output

    Now like I say, I always struggle with the registers - and even then, it's been a while - so try not to laugh...

    Code:
    @MyConfig = _INTRC_OSC_NOCLKOUT & _WDT_OFF & _PWRTE_ON  
    @MyConfig = MyConfig & _MCLRE_OFF 
    
    DEFINE OSC 4           ' Internal 4MHz
    
    CMCON0 = %00000001   	'CIN pins are configured as analog, COUT pin configured as Comparator output?
    ADCON0 = %00000000	'ADC disabled?
    ANSEL = %00001111	'All Analogue inputs enabled?
    TRISIO = %111011	'GPIO2 output (Comparator Out)
    
    ' PIN# NAME     USE & CONNECTION
    '  1   Vdd      +5VDC power supply
    '  2   GPIO.5   Debug Port (PicKit2 needs switching from here to pin Pin 7 to allow programming)
    '  3   GPIO.4   
    '  4   GPIO.3   
    '  5   GPIO.2   Comparator Out (GP2)
    '  6   GPIO.1   CIN-  Signal In  (GP1)
    '  7   GPIO.0   CIN+  trigger level (GP0)
    '  8   Vss      Power supply ground
    '-------------------------------------------------------------------------



    EDIT Ok, spotted it..

    Code:
    ANSEL = %00001011		'All Analogue inputs except AN2 enabled
    & here we have an output...



    Now...what to do with the output?!!!
    Last edited by HankMcSpank; - 8th March 2010 at 00:05.

  16. #16
    Join Date
    Jan 2009
    Location
    California, USA
    Posts
    323


    Did you find this post helpful? Yes | No

    Default

    Quote Originally Posted by HankMcSpank View Post
    Now...what to do with the output?!!!
    On the rising edge of your square wave, start a timer running. When the next rising edge comes along, grab the timer count so you can calculate the period of the square wave, and reset the timer for the next go around.

    If your waveform is symmetrical you could start the timer on the rising edge and stop on the falling edge and get your frequency in 1/2 cycle.
    Maybe...


    steve

  17. #17
    Join Date
    Mar 2009
    Posts
    653


    Did you find this post helpful? Yes | No

    Default

    Well I went for the most simplistic approach first....

    Code:
    temp var byte
    temp = 0
    
    start:
        count  GPIO.4, 100, temp
        DEBUG  "Count = ", DEC temp, 13, 10 
        temp = 0
        GOTO start
    end
    & it works - the count for 100ms is pretty much bang on ...does jitter about by 1 or so at the lower frequencies

    I realise for a 'sig gen' sourcedsine wave this isn't much of a breakthrough (other than it is for me getting all this comparator stuff melarkey working) - I'm sure guitar signal might be a bit more tricky (one for tomorrow night now).

    Here's 659Hz (Top E String fretted at the 12th), the PIC 100mS pule in count for that frequency matches what I'd anticipated on my spreadsheet...

    Last edited by HankMcSpank; - 8th March 2010 at 01:31.

  18. #18
    Join Date
    Mar 2009
    Posts
    653


    Did you find this post helpful? Yes | No

    Default

    Time to revisit this old thread of mine, as I now have a cool use for frequency detection in my ongoing guitar project 'X'.

    Basically, I want a PIC on my cct to establish when the same guitar note has been played for 'x' seconds (user configurable) - once this condition has been met, then the PIC will inoke some cool 'stuff' to kick in!

    I parked this 'early exploratory thread' having gotten the comparator/frequency detect to work simply on a basic 12f683 - back then interrupts were fairly new to me...whilst now I don't pretend that I'm interrupt savvy (I don't need to be thanks to Darrel's routines!)...I think it's time to revisit & try to apply to the more advanced PIC I'm using now - a 16F690.

    Now looking at the comparator module on the 16f690 datasheet - wooooaaah.....a bit scary. So a couple of early questions before I have a pop at this tonight.

    Due to the layout of my board, pin 7 looks like a prime candidate for getting my analogue signal into the PIC - could someone please confirm that this is indeed a valid comparator input pin?!!



    Ok, wrt what signal I'm feeding into the comparator - essentially with no guitar signal present the quiescent voltage will be +4V (which is also the PIC rail voltage) - in my cct config, a diode half wave rectifies the guitar signal so when a signal is present ....the PIC will just see negative portions of the signal.

    So, with respect to the voltage to 'flip' the comparator - I guess, I'd need something in the region of +3V? (ie any incoming signal with a negative swing of 1V will flip the comparator). a quick look at the datasheet suggests there are 16 'reference levels' that can be used internally within the PIC - cool, looks like that should be ok (I'll need to have a dabble with the VRCON register)

    Ok, now assuming I get the comparator 'flipping' at the right voltage level .....what to do with it to get the most dependable frequency detection going on.

    Just to reiterate - I would like to to detect when the guitar frequency has NOT changed for 'x' seconds (the' x' being user selectable selectable by a pot - I have this particular bit covered off already)

    I see that DT's interrupts have a comparator interrupt....presumably that can be brought into play here?

    So what would be a good building block here?

    1. Start a countdown timer configured for 'x' seconds. (as mentioned above)
    2. start looping (say a 300ms loop...whatever time window is needed to detect individual fret frequencies for the frequency range of guitar ...approx 70Hz thru 1300khz))
    3. Clear comparator 'count' (a program variable)
    4. Enable comparator interrupts
    5. Start counting comparator interrupts
    6. Stop loop, compare present number of comparator interrupt count with previous ...
    i) if comparator count is 'within limits' then keep repeating (same note has been held held)
    ii) if 'outside limits...then reset step 1 countdown timer (ie a different note detected, so reset & start again)

    7. countdown timer expires - condition therefore met (ie same note held for x seconds)....go and invoke some cool stuff!!


    Any thoughts, problems etc? (I should point out that the guitar has a sustain device onboard, so the note can/will ring on forever - also such a sustained note is pretty sinuisoidal, stable....so the comparator should flip reasonably dependably)
    Last edited by HankMcSpank; - 20th August 2010 at 11:24.

  19. #19
    Join Date
    Mar 2009
    Posts
    653


    Did you find this post helpful? Yes | No

    Default

    Well, I got a simple form of frequency detection going on with my 16f690 last night.

    I fed a sine wave into one of the 16f690s comparator input pins & used DT's comparator interrupt routine - the associated handler incremented a count with every comparator flip (interrupt),

    I then simply used timer1 set to overflow at 0.5s - at overflow the comparator count is multiplied by two (to get the equivalent count for 1 second - so the end result was in Hertz).

    I was getting reasonable accurate frequency detection - the 'count' number danced about +/- 2 or 3 (therefore ultimately 2 or 3Hz) ...which I'm pretty sure could probably be averaged out to bring this error down a little).

    I wasn't able to get the 16f690' internal VREF to work (I was using the external VREF pin set at 1/2 VCC)

    This VREF issue aside (which I'll revist soon), what I now need is a 'rolling window' in code, which goes something like thus...

    "If the comparator count hasn't changed - give or take - for the past 1.5 seconds (ie rolling window), then that must mean a note is presently being held, if so ...go & do stuff!"

    Now there are only two timers on the 16f690....and I was really hoping to keep one of them in reserve for flashing LEDS (the LED 'flash rate' depending on the mode), so I'm looking for creative ideas that would allow me to apply the above 'condition' without wasting anther timer!

    Grateful for any input here!
    Last edited by HankMcSpank; - 26th August 2010 at 11:25.

  20. #20
    Join Date
    Mar 2009
    Posts
    653


    Did you find this post helpful? Yes | No

    Default

    Ok, I think I have the loose makings of the necessary code for a rolling 1 second window to check whether the frequency has remained the same...

    (the signal to be monitored is fed into comparator1)

    Code:
    INCLUDE "DT_INTS-14.bas"
    INCLUDE "ReEnterPBP.bas"
    INCLUDE "Elapsed_INT.bas"  ; Elapsed Timer Routines
    
    ASM
    INT_LIST  macro    ; IntSource,        Label,  Type, ResetFlag?
            INT_Handler   TMR1_INT,  _timer1_interrupt,   PBP,  yes
            INT_Handler   CMP1_INT,  _comparator_interrupt,   PBP,  yes
        endm
        INT_CREATE            ; Creates the interrupt processor
    ENDASM
    
    @   INT_ENABLE  TMR1_INT  ; Enable Timer 1 Interrupts
    @   INT_ENABLE  CMP1_INT  ; Enable comparator1 Interrupts 
    
    nochange_count var word   'count number of 'timer overflows' where frequency hasn't changed
    
    comparator_count var word   'a variable to count number of comparator interrupts (frequency detection)
    
    loop:
    if nochange_count > 3 then     'ie frequency has remained the same for 1 second
    nochange_count = 0 
    gosub dostuff
    endif
    goto loop
    
    
    '---[Timer1 - interrupt handler]--------------------------------------------------
    timer1_interrupt:        'prescaler set for circa 250ms
    if comparator_count = previous_comparator_count then   'needs a 5% 'deviation' allowable error to go here!!
    nochange_count = nochange_count+1   'count how many times frequency has NOT changed
    else
    nochange_count = 0 ' if frequency has changed, then zero counter and start over
    endif
    previous_comparator_count =  comparator_count
    comparator_count = 0
    @ INT_RETURN
    
    '---[Comparator1 - interrupt handler]--------------------------------------------------
    comparator_interrupt:
    comparator_count = comparator_count +1
    @ INT_RETURN
    There may be some syntax errors in there (I'm at work!), but that's the basic framework - what I don't know how to do is the 'deviation' part (bolded in red above).

    How can I have an 'if' condition that checks to establish if present count is 'within 5%' of previous count' (plus or minus), therefore....

    if comparator_count = previous_comparator_count '+ or -' 5%

    Two 'AND'ed ifs?

    Many thanks!
    Last edited by HankMcSpank; - 26th August 2010 at 13:39.

  21. #21
    Join Date
    Mar 2009
    Posts
    653


    Did you find this post helpful? Yes | No

    Default

    At the risk of talking to myself (first sign of madness)....

    I now have the 'framework' of code needed to detect when a frequency is stable- happy.

    What I've got issues with is my raw (frequency) input data being a bit 'jittery'. By this I mean if I dial in 100Hz on my sig gen, for every 0.5sec I get readings like this

    50 49 50 50 51 50 51 50 49 50 50 50 51 51 50 50 (it should be a steady reading of 50)

    ....& so on.

    Now for a reasding of 50, a plus or minus variance of 1 either side equates to jitter 'error' of 4% ....and this is for digital?!!!

    So my approach must be bad! (ok, so the title of the thread is rough 'n ready frequency extrcaction - but a 4% jitter erroris just too rough!)

    Now for the life of me I can't figure out why a stable clean sine wave into this chain should cause jitter ...

    Sig gen Sine Wave (ie clean & steady as a rock) ->PIC comparator->PIC comparator generates an interrupt->comparator generated interrupts are counted->tmr1 overflows & comparator interrupt 'total count' is stored

    Now I can average it out (but this will incur delay in detecting steady frequency - which is my goal) , but I'd rather get to the root of the jitter in the first place!
    Last edited by HankMcSpank; - 27th August 2010 at 10:37.

  22. #22
    Join Date
    Nov 2003
    Location
    Wellton, U.S.A.
    Posts
    5,924


    Did you find this post helpful? Yes | No

    Default

    At the risk of talking to myself (first sign of madness)....
    I am listening.
    I am learning.

    VERY INTERESTING STUFF!!!
    As a moderator I will ask you to consider writing an article out of all of this when you feel the project is ready.

    Please keep it coming!!!
    Dave
    Always wear safety glasses while programming.

  23. #23
    Join Date
    Mar 2003
    Location
    Commerce Michigan USA
    Posts
    1,166


    Did you find this post helpful? Yes | No

    Default

    HankMcSpank ,I would personally count the time between interrupts instead of the number of interrupts in 1/2 second. That way you can increase the low end resolution you are looking for. Even a 1000 hz clock will give you .1% resolution at 100 hz....

    Dave Purola,
    N8NTA

  24. #24
    Join Date
    Mar 2009
    Posts
    653


    Did you find this post helpful? Yes | No

    Default

    Quote Originally Posted by Dave View Post
    HankMcSpank ,I would personally count the time between interrupts instead of the number of interrupts in 1/2 second. That way you can increase the low end resolution you are looking for. Even a 1000 hz clock will give you .1% resolution at 100 hz....

    Dave Purola,
    N8NTA

    Dave you're right on the money there - I'm now getting much better stability with your suggested method, here's what I'm seeing onscreen for Timer1 counts between comparator interrupts (this was for approx 100Hz)...

    Timer1 Count= 5007
    Timer1 Count= 5005
    Timer1 Count= 5007
    Timer1 Count= 5004
    Timer1 Count= 5007
    Timer1 Count= 5004
    Timer1 Count= 5007
    Timer1 Count= 5005
    Timer1 Count= 5007
    Timer1 Count= 5004
    Timer1 Count= 5007
    Timer1 Count= 5005
    Timer1 Count= 5007
    Timer1 Count= 5004
    Timer1 Count= 5007
    Timer1 Count= 5004
    Timer1 Count= 5007
    Timer1 Count= 5004



    ...so my jitter is now something like 0.001% - that couls be down to my sigen not being stable even - it has been known to drift about a bit)

    I've got a bit of a mental block with the last remaining bit though (converting to frequency in hertz! - which for all I don't actually need the result in Hz - as I'll just be comparing on the timer count alone to establish whether of not the frequency is stable - it'd be a nice to have!!)

    So, what I'm seeing with my timer counts...

    A 'Timer1 count' of 5000 equals 100Hz
    A 'Timer1 count' of 500 = 1000Hz
    Therefore the timer clock cycle = 2us (0.000002s)

    What formula do I use to convert my Timer1 count into Hertz? (it's the floating point aspect that messing me up - else I'd just use this simple formula....

    1 / ('timer1 count' * 0.000002)

    If I add to the left side of the equation (to take the right hand side out of 'decimals' territory)...it cranks up into the millions - tilt!!!!

    So how should I approach this Hertz conversion?
    Last edited by HankMcSpank; - 27th August 2010 at 20:17.

  25. #25
    Join Date
    Feb 2006
    Location
    Gilroy, CA
    Posts
    1,530


    Did you find this post helpful? Yes | No

    Default

    Quote Originally Posted by HankMcSpank View Post


    A 'Timer1 count' of 5000 equals 100Hz
    A 'Timer1 count' of 500 = 1000Hz
    Therefore the timer clock cycle = 2us (0.000002s)

    What formula do I use to convert my Timer1 count into Hertz? (it's the floating point aspect that messing me up - else I'd just use this simple formula....

    1 / ('timer1 count' * 0.000002)

    If I add to the left side of the equation (to take the right hand side out of 'decimals' territory)...it cranks up into the millions - tilt!!!!

    So how should I approach this Hertz conversion?
    Hank, it looks like you are getting great results. Despite what it may look like, you really are not talking to yourself! A lot of people are interested in what you are doing, myself very much included.

    I often have to take a step back to figure out math .. or try to. For your equation,

    1/(counts * .000002) = hertz
    you can change it by multiplying both top and bottom by 500000.

    That gives you

    500,000/counts = hertz
    Then using one of your data points for counts:

    500,000/5007 = 99.86 hertz
    But since that doesn't fit in a pic well, and since I am sure you want the tenth and hundreds, we multiply the top by 100. Giving us:

    50,000,000/5007 = 9986 hundredths hertz. (really 99.86 hertz)
    To make it work inside a pic, check out the DIV32 function. It allows a number as big as 2,147,483,647 to be divided by a number as large as 32,767. Looks like you are good on both "counts"
    Last edited by ScaleRobotics; - 28th August 2010 at 03:18. Reason: Took out "code" tags, which seem to make people sick to their stomachs.
    http://www.scalerobotics.com

  26. #26
    Join Date
    Mar 2009
    Posts
    653


    Did you find this post helpful? Yes | No

    Default

    Thanks for the moral (& maths!) support.....at last things are starting to take shape (a *big* thanks - as ever - to all on here!)

    Re getting my 'time between comparator interrupts' converted into Hertz, sure enough when I tried to compile the following...

    Code:
    Frequency = 50000000/MyTime  'MyTime is a variable to store the number of timer1 clock counts
    ....some vomit actually jumped out of my screen (I can report that my wife is not happy with the mess on the carpet)

    So I then took one look at some DIV32 examples & augmented the screen's 'dirty protest' on the carpet with my own personal contribution ...I think I need to lie down (perhaps take up another hobby...or maybe dust that 'bus tickets of the world' collection of mine down & actually sort them into chronological order)
    Last edited by HankMcSpank; - 27th August 2010 at 23:22.

  27. #27
    Join Date
    Mar 2009
    Posts
    653


    Did you find this post helpful? Yes | No

    Default

    Ok, a wet VAc has sorted most of my carpet problems, but going straight to the manual for info about using DIV32 has seemingly brought more jitter to the fore....

    First here's what I'm doing....

    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 comparator input)     
    '  20  Vss      Ground
    '*************************************************************************************
    DEFINE  OSC 4          ' set Oscillator at 4Mhz.
    DEFINE  NO_CLRWDT 1   ' PBP doesn't clear WDT automatically
    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.
    CM2CON0 = 0   'turn off comparator 2.
    
    'Turn on & Set up Comparator 1
    CM1CON0 = %11100011    'Comparator ext op pin disabled (op of comparator avaible internally only), compare against external VREF
    VRCON = %0100001       'turn on internal reference
    TRISA.2 = 0            'allow the comparator 1 output pin as an output
    
    MyTime       var  word    ' used to amalgamate TMR1 High & Low Bytes.
    Frequency    var  word    'used to convert the count to frequency.
    
    ' the following is pretty much a straight lift from the compiler manual (DIV32 section)
    a Var Word
    b Var Word
    c Var Word
    dummy Var Word
    b = 500
    c = 1000
              
    INCLUDE "DT_INTS-14.bas"     ' Base Interrupt System
    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 = $11               ; Prescaler setting 2us between clocks
    
    @ INT_ENABLE  CMP1_INT     ; enable Comparator 1 interrupts
    
    T1CON.0=0 'stop the timer
    TMR1H = 0 'Set the high part of the timer value to 0
    TMR1L = 0 'Set the low part of the timer value to 0
    
    MyTime = 0 'clear down Mttime, prior to starting.
    
    
    'Main body of Code*********************************************************************************************
    Main:
            pause 10
            goto Main
            end
       
    'Comparator1 Interrupt Handler+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++        
    Comp1_Int:
            if T1CON.0= 0 then 'if timer1 is not running...
            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 the timer
            MyTime.Lowbyte = TMR1L 'puts the timer's low byte in MyTime's lower 8 bits 
            MyTime.Highbyte = TMR1H 'puts the timer's high byte in MyTime's upper 8 bits
    
            dummy = b * c '  'a straight lift from the compiler manual (DIV32 section)
            a = DIV32 MYTIME  'use the DIV32 to divide 50,000,000 by the comparator 'time between counts' 
            hserout ["Comparator1_Count = ", dec MyTime, 9, "Frequency = ", dec a,13, 10]       
            endif
            
    @ INT_RETURN

    ....& here's the onscreen hserout data....

    (just to remind you 'Comparator1_Count' is the 'number of clocks' counted between successive comparator interrupts - and the frequency is the DIV32 result of 500000 divided by comparator1_count number )

    Comparator1_Count = 1960 Frequency = 255
    Comparator1_Count = 1961 Frequency = 254
    Comparator1_Count = 1960 Frequency = 255
    Comparator1_Count = 1960 Frequency = 255
    Comparator1_Count = 1961 Frequency = 254
    Comparator1_Count = 1960 Frequency = 255
    Comparator1_Count = 1960 Frequency = 255
    Comparator1_Count = 1961 Frequency = 254
    Comparator1_Count = 1959 Frequency = 255
    Comparator1_Count = 1960 Frequency = 255
    Comparator1_Count = 1961 Frequency = 254
    Comparator1_Count = 1959 Frequency = 255
    Comparator1_Count = 1961 Frequency = 254
    Comparator1_Count = 1960 Frequency = 255
    Comparator1_Count = 1960 Frequency = 255
    Comparator1_Count = 1961 Frequency = 254
    Comparator1_Count = 1960 Frequency = 255
    Comparator1_Count = 1960 Frequency = 255



    (FWIW My sig gen was showing 255.2Hz on its LED display & using a calculator to divide 500000 by the 'average' comparator1_count of say 1960, works out at 255.1Hz)

    so as a percentage the 'variance/jitter' on the lefthand figure (Comparator1_Count) is miniscule....but using the DIV32 seems crank up the 'error' when converted to Hertz? (eg 254, 255, 255, 254)

    What am I doing wrong?!
    Last edited by HankMcSpank; - 28th August 2010 at 00:52.

  28. #28
    Join Date
    Feb 2010
    Location
    USA, New England
    Posts
    164


    Did you find this post helpful? Yes | No

    Default

    HankMcSpank,

    I suspect you are expecting to see a rounded final value when in fact your final value is truncated.

    1960/500,000 = 255.102, PBP truncates to 255 (same as rounding)
    1961/500,000 = 254.972, PBP truncates to 254 (would round to 255!)


    If you add "half of a whole value" before truncating it is the same as rounding. You can't add 0.5 to the DIV32 result because we aren't using floating point. So we add it before the division:

    1960/500,500 = 255.488, PBP truncates to 255
    1961/500,500 = 255.227, PBP truncates to 255 (all is good)


    You've may still have some granularity issues but this should get you closer.

    I remember hearing a long while back that "floating point math is for weenies who can't think big." 8^)

    Best Regards,
    Paul

  29. #29
    Join Date
    Mar 2009
    Posts
    653


    Did you find this post helpful? Yes | No

    Default

    Hi Paul,

    I was aware that the PIC simply threw the decimal portion away, but I'm no mathmetician & just wanted to clarify that I wasn't approaching this incorrectly!

    Thank you for your suggestion...but my goal here is simply to detect the frequency remaining the same for a set length of time (ie the guitar player is 'holding' a note for more than x seconds)....so I don't actually even need to convert to hertz - for simplicity, I can just work in counts.

    It might be handy one day to detect the actual 'Note Played'. when I think my approach would be along the lines of a monster lookup table (with a 'window' either side of the centre frequency for each note)...

    Code:
                      Hertz     Clock Count
    
    	 (from ) 	78	6410  
    Open	E	82.41	6067
    	 (to) 	84.85	5893  
    			
    	 	84.86	5892
    Fret 1	F	87.31	5727
    	 	89.90	5562
    			
    	 	89.91	5561
    Fret 2	F#	92.50	5405
    		95.24	5250
    			
    	 	95.25	5249
    Fret 3	G	98.00	5102
    		100.91	4955
    			
    	 	100.92	4954
    Fret 4	G#	103.83	4816
    		106.91	4677
    			
    	 	106.92	4676
    Fret 5	A	110.00	4545
    	 	113.26	4415
    			
    	 	113.27	4414
    Fret 6	A#	116.54	4290
    	 	120.00	4167
    & so on (I guess Hertz could be derived to decimal points using an even bigger lookup table?!)


    PS Once you get to the highest note on the fretboard (E on the 24th fret - 1318.5Hz) , the compartor clock count between interrupts comes down to something around 379....so it might be worth messing with the prescaler to give a bit more granularity)
    Last edited by HankMcSpank; - 28th August 2010 at 11:26.

  30. #30
    Join Date
    Feb 2006
    Location
    Gilroy, CA
    Posts
    1,530


    Did you find this post helpful? Yes | No

    Default

    Hank, if you have a garbage can handy, bring it next to your computer before you read on........

    Name:  404.jpg
Views: 1141
Size:  44.1 KB

    It looks like you are on the right track to me. If you like your decimals, use the 50 million instead of the 500,000. Then, when you go to print out your number, you can add your decimal back in like this.

    Code:
    LCDOut dec frequency/100,".",dec frequency//100," Hertz"
    


    For more nausea, check out http://members.cox.net/mathmistakes/music.htm

    Not sure if this could help you in any way. A lookup is probably the way to go, but I think this is interesting about the mathematical relationship between the notes.

    There are some
    important mathematical relationships between the notes played in music and the frequency of those notes.
    There are two constant values in music. The first is that the A note that is 9 white keys below middle C has
    a frequency of 440 hz. The second constant value in music is the 12th root of 2 (1.0594630943593...) which is the ratio of the frequencies between half tones. So, the frequency of A# is 440 × 1.059... = 466.16376... The
    frequency of B is 466.1637 × 1.0594 = 493.8833. After you do this 12 times you end up with A an octave higher
    which equals 880hz. Doubling the frequency creates a note an octave higher. Reversely, dividing the frequency in
    half creates a note an octave lower.
    I think you might be able to do some interesting things with the constant. The tool to convert things like that to fractions a PIC can use is here: http://www.miscel.dk/MiscEl/miscel.html

    Name:  miscel.PNG
Views: 1487
Size:  29.4 KB

    Then you could possibly use 3118/2943 or 196/185 get to that constant. Again, not sure that would be useful for a tuner. Maybe more useful for tone generation.

    Walter
    http://www.scalerobotics.com

  31. #31
    Join Date
    Mar 2009
    Posts
    653


    Did you find this post helpful? Yes | No

    Default

    Quote Originally Posted by scalerobotics View Post

    It looks like you are on the right track to me. If you like your decimals, use the 50 million instead of the 500,000. Then, when you go to print out your number, you can add your decimal back in like this.

    [code]
    LCDOut dec frequency/100,".",dec frequency//100," Hertz"[FONT=Verdana]
    Nice one & that suggestion of yours puts the decimal point in nicely...
    Code:
    Count    Converted to frequency...
    1935	258.39 Hertz
    1936	258.26 Hertz
    1936	258.26 Hertz
    1936	258.26 Hertz
    1935	258.39 Hertz
    1935	258.39 Hertz
    1935	258.39 Hertz
    1936	258.26 Hertz
    1936	258.26 Hertz
    1936	258.26 Hertz
    1936	258.26 Hertz
    1935	258.39 Hertz
    1935	258.39 Hertz
    1935	258.39 Hertz
    1937	258.13 Hertz
    1936	258.26 Hertz
    1936	258.26 Hertz
    1935	258.39 Hertz
    1935	258.39 Hertz
    (My sig gen actually shows 258.4 on its LED readout when the above was seen)

    The closest musical note to 258.3(ish)Hz is C (261.63Hz) ....the semi-tones either side of C, are B (246.94Hz) & C# (277.18Hz) ....so the error of ± 0.2Hz I'm seeing above isn't likely to be that much of a problem in the great scheme of things.

    Many thanks for all your input - I might now just go off on a tangent & have a dabble with guitar to midi!
    Last edited by HankMcSpank; - 28th August 2010 at 16:01.

  32. #32
    Join Date
    Feb 2006
    Location
    Gilroy, CA
    Posts
    1,530


    Did you find this post helpful? Yes | No

    Default

    I just noticed that my answer will overflow when at the higher frequencies. So for a single decimal point it should stay within limits throughout the range you gave in post 1.

    Code:
    b=5000
    c=1000
    dummy = b * c       '5,000,000 
     frequency = div32 mytime
    LCDOut dec frequency/10,".",dec frequency//10," Hertz"
    Last edited by ScaleRobotics; - 28th August 2010 at 16:40.
    http://www.scalerobotics.com

  33. #33
    Join Date
    Mar 2009
    Posts
    653


    Did you find this post helpful? Yes | No

    Default

    Cool Walter.

    All is well (± 0.1Hz)...


    154.3Hz
    154.3Hz
    154.3Hz
    154.3Hz
    154.3Hz
    154.3Hz
    154.2Hz
    154.2Hz
    154.3Hz
    154.3Hz
    154.3Hz
    154.3Hz


    So, for anyone who may find this thread via a search, to summarize here's the code to frequency detect audio signals fed into pin 7 of a 16f690 (using the PIC's internal comparator to generate interrupts, which are in turn 'timed' successively)....

    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 comparator input)
    '10   RB7     HSEROUT Pin (fed into Pickit2's pin 4 to display data onsreen via the Pickit2's UART tool)
    '19   RA0      Ext VREF for Comparator1   
    ' 20  Vss      Ground
    '*************************************************************************************
    DEFINE  OSC 4          ' set Oscillator at 4Mhz.
    DEFINE  NO_CLRWDT 1   ' PBP doesn't clear WDT automatically
    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.
    CM2CON0 = 0   'turn off comparator 2.
    
    'Turn on & Set up Comparator 1
    CM1CON0 = %11100011    'Comparator ext op pin disabled (op of comparator avaible internally only), compare against external VREF
    
    MyTime       var  word    ' used to amalgamate TMR1 High & Low Bytes.
    Frequency    var  word    'used to convert the 'count' to frequency.
    
    ' the following is pretty much a straight lift from the compiler manual (DIV32 section)
    a Var Word
    b Var Word
    c Var Word
    dummy Var Word
    
    'these two below will later set the dummy variable to total  5,000,000 in the interrupt handler for DIV32 to use
    b = 5000   
    c = 1000
    
    MyTime = 0 'clear down Mytime, prior to starting.
              
    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 = $11               ; Prescaler setting - this one means 2us between successive clocks
    
    @ INT_ENABLE  CMP1_INT     ; enable Comparator 1 interrupts
    
    T1CON.0=0 'stop the timer
    TMR1H = 0 'Set the high part of the timer value to 0
    TMR1L = 0 'Set the low part of the timer value to 0
    
    
    
    'Main body of (dummy) Code*********************************************************************************************
    Main:
            pause 10
            goto Main
            end
       
    'Comparator1 Interrupt Handler+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++        
    Comp1_Int:
            if T1CON.0= 0 then 'if timer1 is not running...
            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 the timer
            MyTime.Lowbyte = TMR1L 'puts the timer's low byte in MyTime's lower 8 bits 
            MyTime.Highbyte = TMR1H 'puts the timer's high byte in MyTime's upper 8 bits
            dummy = b * c       '5,000,000 
            frequency = div32 mytime ' this convertes to Hz, but with no decimal points
            HSEROUT [dec frequency/10,".",dec frequency//10,"Hz",13, 10]   .....this places a decimal point in the right place to make the reading easier on the eye.
            endif 
    @ INT_RETURN
    Note: for the comparator to have something to erhm 'compare' against, I've still not got the PIC's internal voltage reference to work yet, so I was feeding 1/2 VCC externally into the 16F690's RA0 Pin 19 (the bolded bit in this command sets this up.... CM1CON0 = %11100011 )
    Last edited by HankMcSpank; - 28th August 2010 at 17:40.

  34. #34
    Join Date
    Mar 2009
    Posts
    653


    Did you find this post helpful? Yes | No

    Default

    To get more resolution/accuracy for calculating phase, I really need to crank the OSC up to 20Mhz (ie use an ext oscillator).

    Ok, the lowest note on a guitar is about 82Hz (std tuning)

    Is my line of thinking correct here.?...

    One full cycle @82hz takes 0.012195122 seconds

    One PIC clock cycle @20Mhz is 0.000000125 seconds.

    therefore the number of clock cycles that will occure when a note of 82Hz is played will be 243,902.

    I'm using a 16F690, which has a 16 bit timer - a 16 bit timer will over flow at a count of 65536 - alas this number is smaller than 243,902.

    therefore to be able to count 82Hz at 20Mhz, I need a tmr overflow interrupt handler to increment a variable with each tmr overflow (ie count the number of tmr overflows). therefore at 82Hz signal and 20Mhz clock a 16 bit timer will over flow 3 times.

    So (you still awake at the back?), to get the true total number of PIC clock counts @ 82Hz, I need to multiply the stored tmr overflow (3) count by 65536 & then add in the existing tmr count number. chunky numbers in play for a device costing $2.

    Is the PIC gonna start struggling with the above...or will it take it in its stride? (is there a better route I should wrt all of this?!!)
    Last edited by HankMcSpank; - 8th September 2010 at 11:32.

Similar Threads

  1. Measuring change of frequency with PIC
    By Aussie in forum mel PIC BASIC Pro
    Replies: 5
    Last Post: - 19th July 2007, 01:47
  2. PIC Audio
    By toofastdave in forum mel PIC BASIC Pro
    Replies: 28
    Last Post: - 27th June 2007, 13:49
  3. Pic driven digital audio delay
    By skimask in forum Off Topic
    Replies: 12
    Last Post: - 19th April 2007, 20:42
  4. Audio Encoding and playback in a PIC
    By Rob in forum mel PIC BASIC Pro
    Replies: 8
    Last Post: - 24th March 2005, 08:56
  5. Frequency Counter using PIC and PicBasic
    By PICtron in forum mel PIC BASIC Pro
    Replies: 31
    Last Post: - 28th January 2005, 06:20

Members who have read this thread : 1

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