PDA

View Full Version : Frequency detection (audio)



HankMcSpank
- 7th June 2012, 14:31
I keep revisiting this project, then parking it & then resurrecting it...and guess what I've just resurrected it!

I seek to derive the frequency of a plucked guitar note so I can do lots of funky frequency related stuff to the signal

I've presently got a PIC (16f1828) a comparator setup & I'm looking at the number of counts timer1 has incremented between each DT comparator interrupt.

for now I'm usiing a sig gen with a sine wave (yes I realise a guitar note is way more complicated...but I'll cross that bridge blah blah)

Anyway, by & large it's working OK.....the TMR1 count jitters about a bit, but it's close enough. but what I'm seeing is that with some frequencies the count jitters *way* too much.

For example, heres a stable-ish(!) timer1 count for 1khz into the PIC comparator.....

3208
3203
3194
3183
3161
3163
3163
3156
3150
3152
3148
3141
3139

Ok so it's a little jittery but in percentage terms the jitter is still fairly low, but look what happens when I then feed 800hz into the same same setup....

4955
180
4982
205
5012
176
4936
176
4963
183
4988
207
5001
176
4936
176
4957

...the Timer1 count between comparator interrupts is all over the shop!

Can anyone enlighten me why this might be happening?

here's the interrupt SETUP......



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



CMP2_Interrupt: ' COMPARATOR2 INTERRUPT.
@ INT_DISABLE CMP2_INT
hserout [dec TMR1,13,10]
TMR1=0
@ INT_ENABLE CMP2_INT
@ INT_RETURN



Is my approach wrong here?


Basically I want this type of sequence...

1. Guitar string plucked - PIC detects this (envelope detector)
2. PIC waits about 0.5s (to let the complex harmonics drop out)
3. PIC then attemps to detect frequency for a period of about 0.3s
4. Use a look up table for number of counts
5. Go do stuff with the outcome!


Now step 4 (go do stuff) I don't need help on - it's step 3 that I'm wondering what the best approach would be?

cncmachineguy
- 7th June 2012, 14:38
Hi Hank, just stabbing at this, you second set of numbers is not really all over, every other one is close to the last. I am guessing here maybe timer is overflowing with the lower freq?

HankMcSpank
- 7th June 2012, 14:56
Hi Hank, just stabbing at this, you second set of numbers is not really all over, every other one is close to the last. I am guessing here maybe timer is overflowing with the lower freq?

hi bert,

Very plausible/good idea, but alas, it's not down to Timer1 overflowing (I worked out that with a 16 bit timer @16Mhz oscillator I can go down to about 58hz before worrying about overflows), for example if i take the frequency even lower than 800hz, it goes fairly stable again, here's 500Hz...

3120
3218
3212
3208
3202
3198
3194
3184
3182
3179
3173
3168
3163
3154
3151
3150

cncmachineguy
- 7th June 2012, 15:29
Are my eyes lying to me or are they the same-ish as for 1K? One must ask them selves How can this be?

BTW Nice to be back and looking at hanks fun stuff :)

HankMcSpank
- 7th June 2012, 15:50
Are my eyes lying to me or are they the same-ish as for 1K? One must ask them selves How can this be?

Good spot!

Here's approx the Timer counts I'm seeing for different frequencies...

82.4Hz (lowest note on a guitar) - 42,900 (ish)
330Hz (top E) - 7,300(ish)
600Hz - 1800 (ish)

650Hz -1200(ish)

& then when I go higher in frequency, the timer1 counts start going all over the place ..

Here's 800 hz...

4924
177
4940
177
4965
179
4987

Oddly, when I dial 1000hz in, yep the counts go to 3,200 again ....so clearly something is wrong with my detection method using interrupts & timer1 counts

(btw I know the frequencies the PIC is handling internally are correct, as I'm outputting the PIC's comparator output to an external pin & attaching a frequency counter to the pin)

Hmm [strokes chin]

Archangel
- 7th June 2012, 16:30
500 hz is the first harmonic negative of 1 Khz. some Pi filters might help or lower the input gain

HankMcSpank
- 7th June 2012, 16:44
500 hz is the first harmonic negative of 1 Khz. some Pi filters might help or lower the input gain

I don't think that's the issue, here's a scope screenshot of what the PIC's internal comparator is seeing internally...


http://www.hostmyjpg.com/images/4c8331896e_comp.jpg (http://www.hostmyjpg.com/)


The white parameters window to the right is my scope's take on the waveform it is displaying (1khz)

The green text on the left is the PIC spitting out the TMR1 counts it's counted between comparator2 interrupts serially to my PC com port.

cncmachineguy
- 7th June 2012, 16:51
Hank,, how bout toggling a pin in your ISR to show when the PIC thinks it should start and stop counting.
EDIT : OK I see youo have done that already DOH!!

The numbers should be linear IMHO. so 330 HZ is about half of 600, but your numbers are *4. 330 = 7300 and 600 = 1800. Now between 600 and 650, we have a difference of 600. So that would say the count shoud be 600 per 50hz. so to get to 100hz, we would expect the count to be 10*600=6000 difference. or 7800 (ish). but you are at 49,000 for 82.4. either I am way off on my linear assumption (most likely) or something is really amiss.

HankMcSpank
- 7th June 2012, 17:04
Hank,, how bout toggling a pin in your ISR to show when the PIC thinks it should start and stop counting.
EDIT : OK I see youo have done that already DOH!!


No...I hadn't so put a toggle in my ISR (top tip - tks!)

I've scoped the ISR 'toggled' pin & I'm seeing 250hz on there?!! (therefore 2 x 250hz = an interrupt rate of 500Hz ....this for a 1kHz audio signal?!)

I confirmed my input to the comparator was 1Khz...and also the output from the PIC comparator is definitely 1khz (since I've brought it out on a PIC external pin & scoped it).

So it looks like my comparator interrupt isn't working properly and my interrupt is only happening at half the rate it should???

Any ideas?!!

HankMcSpank
- 7th June 2012, 18:08
Ok, so I took the HSEROUT command out of the ISR ...... & then my ISR 'toggle pin' frequency scoped correct up to around 900hz audio input ....however, above that 900Hz audio input, even with just a basic 'toggle pin' in the ISR, the toggle pin frequency as seen on a scope starts going awry again....so the conclusion here ...it seems that DT's interrupts are taking a bit too long to get in & out for anything above 800hz comparator interrupt rate (this is with a 16Mhz oscillator).

Anyone know of another way of approaching this? (I'd like to be able to to detect up to 1.5Khz)

cncmachineguy
- 7th June 2012, 18:22
Here I go out on a limb that has been broken already, change the type to ASM instead of PBP. The only thig I see in your ISR using PBP is HSEROUT and TMR=0 since you removed the hser, now its only TMR=0 Now keep in mind - YOU ARE NOT SUPPOSED to do this, but for the simple setting of TMR I think you can get away with it. Add BSR=0 as your first line in the ISR. That will stop all the system variables from getting saved and restored everytime you enter the ISR. Sound familiar?

This may be enough to make it happen just as you want. The next approach I can think of is to use the comparitor as the gate for the timer, but that may be how you are doing it now

Also, does the CMP fire an int twice or 4 times per cycle? or just 1 time?

HankMcSpank
- 7th June 2012, 18:32
Thanks for the input Bert, I changed the interrupt to ASM...


ASM
INT_LIST macro ; IntSource, Label, Type, ResetFlag?
INT_Handler CMP2_INT, _CMP2_Interrupt, ASM, YES
endm
INT_CREATE ; Creates the interrupt processor
ENDASM


and modified my ISR...



CMP2_Interrupt: ' COMPARATOR2 INTERRUPT.
BSR=0
toggle portc.7
TMR1=0
@ INT_RETURN


but I still can't my audio input above 900Hz, without the ISR toggle pin scoping wrong :-(

cncmachineguy
- 7th June 2012, 20:23
Hank do the numbers below 900 seem to make sense? We should be able to predict the TMR output based on some simple math. You are running 4MIPS (16Mhz OSC). so that would be .25uS per instruction. If the timer is running from Fosc, a 1 K signal would have a count of 4000? 1/1000 = 1mS. 1mS/.25uS=4000. Assuming the ISR fires 1 time per cycle. Likewise we would expect a count of around 8000 for a 500Hz signal. But either you don't have it set up as I assume, or something is wrong that will require seeing more of your code.

BTW, using my assumptions, 82.4Hz is 48,543.
330Hz = 12,121
600Hz = 6666
650Hz = 6153

I can't decide if that looks linear, but it should.

Also try clearing the TMR1 high byte and low byte seperatly in the ISR. I don't think this should help, but it can't hurt

HankMcSpank
- 7th June 2012, 21:20
Hi Bert,

I re-enabled hserout out in the ISR again, and these are the TMR1 counts I'm getting for successive comparator2 interrupts with an audio input of 82.4Hz (the lowest note/frequency on a standard tuning electric guitar)...

42947
42995
42946
42998
42944
43003
42960
43012
42964
43022
42974
43032
42991
42948
18932
18559
42953
43012
42955
43005
42957
43001
42943


A couple of dodgy ones in there, but those aside, the TMR1 counts are coming in lower at about 43,000?



@ __CONFIG _CONFIG1, _FCMEN_OFF & _FOSC_INTOSC & _WDTE_OFF & _MCLRE_OFF & _CP_ON & _IESO_OFF & _BOREN_OFF & _PWRTE_OFF
@ __CONFIG _CONFIG2, _PLLEN_OFF & _STVREN_OFF & _LVP_OFF
DEFINE OSC 16
INCLUDE "DT_INTS-14.bas"
INCLUDE "ReEnterPBP.bas" ' Include if using PBP interrupts
DEFINE HSER_RCSTA 90h ' Enable serial port & continuous receive
DEFINE HSER_TXSTA 20h ' Enable transmit, BRGH = 0
DEFINE HSER_CLROERR 1 ' Clear overflow automatically
DEFINE HSER_SPBRG 25 ' 38400 Baud @ 16MHz, 0.16%
SPBRGH = 0
BAUDCON.3 = 1 ' Enable 16 bit baudrate generator
'16F1828 Pin settings....
' PIN# NAME USE & CONNECTION
' 1 Vdd +5VDC power supply
TRISA.5 = 0' 2 RA5
TRISA.4 = 1' 3 RA4
TRISA.3 = 1' 4 RA3
TRISC.5 = 0' 5 RC5
TRISC.4 = 0' 6 RC4 C2OUT
TRISC.3 = 1' 7 RC3
TRISC.6 = 0' 8 RC6
TRISC.7 = 0' 9 RC7
TRISB.7 = 0' 10 RB7
TRISB.6 = 1' 11 RB6
TRISB.5 = 1' 12 RB5
TRISB.4 = 1' 13 RB4
TRISC.2 = 1' 14 RC2
TRISC.1 = 1' 15 RC1 C12IN1- (FREQ DETECT)
TRISC.0 = 1' 16 RC0
TRISA.2 = 0' 17 RA2
TRISA.1 = 0' 18 RA1
TRISA.0 = 0' 19 RA0
' 20 Vss Ground
ANSELA = 0
ANSELC = 0

APFCON0.2 = 0 'put hserout onto RB7 pin 10 (Pickit 2 RX pin)

'-------------------------------------------------------------------------
Osccon = %01111010 'sets the internal oscillator
CPSCON0 = 0 ' capacitive sense module off
CM1CON0 = 0 ' comparator1 module off
CM2CON0.7 = 1 ' comparator2 on.
CM2CON0.5 = 1 'enable the output on a pin
CM2CON0.4 = 1 'invert the polarity
CM2CON0.1 = 1 'ENABLE HYSTERISIS
CM2CON1.7 = 1' enable interrupts on _+ve going transition
CM2CON1.5 = 0 ' set comparator comparison input pin to the DAC
CM2CON1.4 = 1 ' set comparator comparison input pin to the DAC
CM2CON1.1 = 0 'SELECT C12IN1- (PIN 15, RC1)
CM2CON1.0 = 1 'SELECT C12IN1- (PIN 15, RC1)

DACCON0.7 = 1 ' DAC ON
DACCON0.6 = 1 ' DAC ON
DACCON0.5 = 0 'put the output of the DAC o the DAC out pin
DACCON0.3 = 0 'SET DAC +VE REFERENCE REFERENCE TO VCC
DACCON0.2 = 0 'SET DAC +VE REFERENCE REFERENCE TO VCC
DACCON1 = %00000001 'sets the DAC voltage output to be very low (to use for the comparator to compare against.
OPTION_REG.7 = 0
ASM
INT_LIST macro ; IntSource, Label, Type, ResetFlag?
INT_Handler CMP2_INT, _CMP2_Interrupt, ASM, YES
endm
INT_CREATE ; Creates the interrupt processor
ENDASM
TMR1 = 0
T1CON = %00000111
@ INT_ENABLE CMP2_INT ; Enable 'Int On Change' interrupts
loop1:
PAUSE 1
GOTO loop1

'Interrupt++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++++++

CMP2_Interrupt: ' COMPARATOR2 INTERRUPT.
@ INT_DISABLE CMP2_INT
BSR=0
hserout [dec tmr1,13,10]
toggle portc.7
TMR1=0
@ INT_ENABLE CMP2_INT
@ INT_RETURN

HankMcSpank
- 7th June 2012, 23:38
Just as an experiment, I would like to see how high in audio frequency I can go with something like the ASM interrupt method discussed in this thread...

http://www.picbasic.co.uk/forum/showthread.php?t=14513 (even though I asked a question in that thread, I confess to not being able to move forward!)

So would anyone do a little bit of ASM code that will toggle portC.7 when comparator2 interrupts on a 16f1828?

HankMcSpank
- 8th June 2012, 13:35
Just thinking out loud here...if all the overhead comes from entering & exiting the interrupt routine ...if I dedicate my main loop solely to measuring high/low transitions on a pin (ie feed the PICs comparator output from its output pin into another digital input pin), would this be workable?

Just pondering how I can get the PIC to detect up to 1.5khz!

pedja089
- 8th June 2012, 20:59
Did you try COUNT or PULSIN or using TIMER to count freq...
I have set TMR0 to overflow each second and trigger interrupt. In ISR read TMR3, then reset it.
It goes to 50KHz without any problem. You also can use shorter time, but then you have lower resolution.

Ramius
- 9th June 2012, 02:06
Hi Hank!
There are several approaches to solving what you wish to do. One site ( http://www.intmath.com/trigonometric-graphs/3-graphs-sin-cos-phase-shift.php ) will give you the math involved in determining a given musical note. Another is ( http://liutaiomottola.com/formulae/freqtab.htm ). If you look at the frequencies for each note notice that if you take the highest "C" note, the next octave of "C" below it is exactly 1/2 the frequency. Where things become "spritual" is that when mixing musical note the mix will give the sum, the difference, and the individual frequencies. The only way I can see to make the distictions would be with "band-pass" filters or a graphic equalizer. You would probably have to take the range of audio (20-20,000 Hz) and divide it into something like 10 or more ranges and then using a high pass and low pass filter "comb" the input sound. Or one big look-up table! The secondary problem would come from how perfectly the guitar is tuned. Hope this helps, Ed

HankMcSpank
- 9th June 2012, 23:55
Thanks guys...I'm now approaching this in a slightly different way & getting reasonable results (I think!)

I'm using timer1 gate & comparator2

Here's the sequence....

Comparator2 goes high....raises a gate on timer1...timer1 starts counting
Comparator2 goes low ....gate drops ...timer1 stops counting
When gate drops a timer1gate interrupt gets flagged
A DT Interrupt traps this (T1GATE_INT) & my program jumps to an ISR
I spit out the contents of timer1 in the ISR serially to my PC.
clear timer1
rinse repeat.

It seems I can get trap upto 4khz audio frequency with nowhere near as many spurious readings as before (I'm still using a sig gen though)....above 4khz the counts start going a bit erratic.

here's the timer1 readings for 4000hz (16Mz Oscillator)...

495
495
495
495
494
495
495
495
494
495
495
495
494
495
495
495
494
495
495
495
494
494
495

(the gate is only trapping 'half a waveform period' so the counts are half what you'd expect when detecting 4khz)

Pretty steady (they should be 500...& I can't quite account for where the missing counts are going!)

Ramius
- 10th June 2012, 00:55
Hi Hank,
This may sound like a dumb question and you are doing an analog to digital conversion at some point? Also if you only wish to work with half wave, a simple diode will do this for you. Best, Ed

HankMcSpank
- 10th June 2012, 10:30
Firstly, I spoke to soon. Whilst this latest method mentioned in my last post has allowed me to detect higher frequencies....I'm still getting erratic results in & around the 450-700Hz audio frequency range! :confused:

Ramius - re Ato D'ng....no this is purely to detect frequency (although I do intend using the pic's AtoD as an envelope detect to actually trigger the frequency detection).

HankMcSpank
- 10th June 2012, 18:02
Ok, it looks like interrupts are a big no-no here so I've established a way of doing this without an interrupt...



T1GCON.4 = 1
Loop1:
pause 500
T1GCON.3 = 1
TMR1=0
while T1GCON.3 = 1
wend
count1=tmr1
HSEROUT [dec count1,13,10]
GOTO Loop1


The above can detect up to 50khz audio input :) (can probably go higher...but the amount of timer1 counts between comparator edges gets less, so the 'percentage error' increases)

Basically I'm using a Timer gate single pulse mode (T1GCON.4 = 1), then forcing the other part of the single pulse mechanism to a 1 ( Single-Pulse Acquisition Status bit T1GCON.3 = 1), after the comparator2 output drops then T1GCON.3 is dropped to a zero automatically, I therefore use a wend to loop until T1GCON.3 = 1 is a 0 ....then I ouput the timer1 counts. My only problem is I'm getting a zero reading intermittently, but other than that, the counts are pretty solid.

here's the TMR counts for 1,000Hz....

1824
1824
1825
1825
1826
0
1824

just got to get to the bottom of why those readings of 0 are creeping in .....

Ramius
- 11th June 2012, 00:43
Hi Hank,
The only thing I can think of is the method you are using to couple the audio signal you wish to measure. Hopefully you are using a capacitor? To chose the best value would be with the formula Xc= .159/F*C "F" is in hertz "C" is in farads and in most audio work .047 mfd is common. Xc give the "resistive" value for a capacitor to AC. Again I am not sure how you would ever obtain great accuracy without conversion to a square wave with a consistant time duration. Again hope this helps. Best, Ed